[Refactor] (MG_Util/ShaderTranspiler): replace Photon shared vec3 regex hack with SPIR-V pass

Replace the application-specific PackPhotonSharedVec3Memory GLSL regex
patch with a general DecomposeWorkgroupVec3Pass SPIR-V optimization pass.

The new pass decomposes vec3/ivec3/uvec3/bvec3 Workgroup (shared) memory
variables into scalar arrays (e.g. shared vec3 arr[N][M] -> shared float
arr[N][M][3]), rewriting whole-vector loads/stores into per-component
scalar loads/stores. Component-level accesses (e.g. arr[i].x) are
unchanged since a trailing component index into a float[3] yields the
same scalar pointer as it did for a vec3.

Unlike the regex hack, the pass is application-agnostic: it does not
match on variable names, array dimensions, or shader pack identity, and
runs at the SPIR-V level before SPIRV-Cross decompilation.

Registered in SanitizeAndOptimizeBinary after AggressiveDCE so dead
workgroup accesses are already eliminated. Asserts on unsupported
OpAtomic*/OpCopyMemory targeting vec3 workgroup pointers.

Adds ProgramUtilTest.DecomposeWorkgroupVec3InSpirvPass covering array
declaration, +=, whole load/store, component access, and row-copy loop.
This commit is contained in:
2026-07-05 23:48:27 +08:00
parent 93cf3559e1
commit b40def47eb
6 changed files with 589 additions and 42 deletions
@@ -25,7 +25,6 @@
#include <MG_State/GLState/FramebufferState/FramebufferObject.h>
#include <algorithm>
#include <cctype>
#include <regex>
namespace MobileGL::MG_Backend::DirectGLES {
constexpr Bool PREFER_MAP_BUFFER_RANGE_FOR_BUFFER_SYNC = false;
@@ -60,46 +59,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
return source;
}
static String PackPhotonSharedVec3Memory(String source) {
constexpr const char* declaration = "shared vec3 shared_memory[256][9];";
const SizeT declarationPos = source.find(declaration);
if (declarationPos == String::npos) {
return source;
}
source.replace(declarationPos, String(declaration).size(),
"shared float shared_memory[256][9][3];\n"
"void StorePhotonSharedMemory(uint row, uint column, vec3 value)\n"
"{\n"
" shared_memory[row][column][0] = value.x;\n"
" shared_memory[row][column][1] = value.y;\n"
" shared_memory[row][column][2] = value.z;\n"
"}\n"
"vec3 LoadPhotonSharedMemory(uint row, uint column)\n"
"{\n"
" return vec3(shared_memory[row][column][0], shared_memory[row][column][1], "
"shared_memory[row][column][2]);\n"
"}\n");
source = std::regex_replace(
source, std::regex(R"(shared_memory\[([^\]]+)\]\[([^\]]+)\] \+= ([^;]+);)"),
"StorePhotonSharedMemory($1, $2, LoadPhotonSharedMemory($1, $2) + ($3));");
source = std::regex_replace(
source, std::regex(R"(shared_memory\[([^\]]+)\]\[([^\]]+)\] = ([^;]+);)"),
"StorePhotonSharedMemory($1, $2, $3);");
source = std::regex_replace(source, std::regex(R"(shared_memory\[([^\]]+)\]\[([^\]]+)\](?!\[))"),
"LoadPhotonSharedMemory($1, $2)");
source = std::regex_replace(source, std::regex(R"(LoadPhotonSharedMemory\(0,)"),
"LoadPhotonSharedMemory(0u,");
source = std::regex_replace(source, std::regex(R"(vec3 ([A-Za-z_][A-Za-z0-9_]*)\[9\] = shared_memory\[0\];)"),
"vec3 $1[9];\n"
" for (uint photon_band = 0u; photon_band < 9u; ++photon_band)\n"
" {\n"
" $1[photon_band] = LoadPhotonSharedMemory(0u, photon_band);\n"
" }");
return source;
}
String InjectUniformAfterVersion(String source, const String& declaration) {
const SizeT versionPos = source.find("#version");
if (versionPos == String::npos) {
@@ -1851,7 +1810,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
source = ClampNormFallbackOutputs(std::move(source), glShaderType,
m_snormFallbackClampOutputMask,
m_unormFallbackClampOutputMask);
source = PackPhotonSharedVec3Memory(std::move(source));
// Patch for Photon compiler precision issue
String findStr = "1000000.0";