mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-11 21:58:31 +09:00
120 lines
5.9 KiB
C++
120 lines
5.9 KiB
C++
// MobileGL - MobileGL/MG_Util/ShaderTranspiler/SpirvPasses/StripUboMemberRelaxedPrecisionPass.cpp
|
|
// Copyright (c) 2025-2026 MobileGL-Dev
|
|
// Licensed under the GNU Lesser General Public License v3.0:
|
|
// https://www.gnu.org/licenses/gpl-3.0.txt
|
|
// https://www.gnu.org/licenses/lgpl-3.0.txt
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
// End of Source File Header
|
|
|
|
#include "StripUboMemberRelaxedPrecisionPass.h"
|
|
|
|
#include "spirv.hpp"
|
|
#include "source/opt/def_use_manager.h"
|
|
#include "source/opt/instruction.h"
|
|
#include "source/opt/ir_context.h"
|
|
#include "source/opt/module.h"
|
|
|
|
#include <unordered_set>
|
|
#include <vector>
|
|
|
|
namespace MobileGL {
|
|
namespace MG_Util {
|
|
namespace ShaderTranspiler {
|
|
namespace {
|
|
using spvtools::opt::Instruction;
|
|
using spvtools::opt::IRContext;
|
|
|
|
// Marks `typeId` and every struct type reachable through its members
|
|
// (following arrays) for decoration stripping.
|
|
void CollectStructTypes(IRContext* context, uint32_t typeId,
|
|
std::unordered_set<uint32_t>& structTypeIds) {
|
|
Instruction* typeInst = context->get_def_use_mgr()->GetDef(typeId);
|
|
if (typeInst == nullptr) return;
|
|
|
|
switch (typeInst->opcode()) {
|
|
case spv::Op::OpTypeStruct: {
|
|
if (!structTypeIds.insert(typeId).second) return; // already visited
|
|
for (uint32_t member = 0; member < typeInst->NumInOperands(); ++member) {
|
|
CollectStructTypes(context, typeInst->GetSingleWordInOperand(member), structTypeIds);
|
|
}
|
|
break;
|
|
}
|
|
case spv::Op::OpTypeArray:
|
|
case spv::Op::OpTypeRuntimeArray:
|
|
CollectStructTypes(context, typeInst->GetSingleWordInOperand(0), structTypeIds);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
} // namespace
|
|
|
|
spvtools::opt::Pass::Status StripUboMemberRelaxedPrecisionPass::Process() {
|
|
auto* irContext = context();
|
|
auto* defUseMgr = irContext->get_def_use_mgr();
|
|
|
|
// Uniform blocks: StorageClass Uniform variables whose pointee struct carries
|
|
// the Block decoration (BufferBlock/StorageBuffer SSBOs are left alone - they
|
|
// are not stage-matched by member precision in this pipeline's ESSL output).
|
|
std::unordered_set<uint32_t> blockStructIds;
|
|
for (Instruction& annotation : irContext->module()->annotations()) {
|
|
if (annotation.opcode() != spv::Op::OpDecorate) continue;
|
|
if (static_cast<spv::Decoration>(annotation.GetSingleWordInOperand(1)) != spv::Decoration::Block) {
|
|
continue;
|
|
}
|
|
blockStructIds.insert(annotation.GetSingleWordInOperand(0));
|
|
}
|
|
if (blockStructIds.empty()) return Status::SuccessWithoutChange;
|
|
|
|
std::unordered_set<uint32_t> structTypeIds;
|
|
for (Instruction& variable : irContext->module()->types_values()) {
|
|
if (variable.opcode() != spv::Op::OpVariable) continue;
|
|
if (static_cast<spv::StorageClass>(variable.GetSingleWordInOperand(0)) !=
|
|
spv::StorageClass::Uniform) {
|
|
continue;
|
|
}
|
|
|
|
Instruction* pointerType = defUseMgr->GetDef(variable.type_id());
|
|
if (pointerType == nullptr || pointerType->opcode() != spv::Op::OpTypePointer) continue;
|
|
uint32_t pointeeId = pointerType->GetSingleWordInOperand(1);
|
|
|
|
// Instance-arrayed blocks: unwrap the array around the block struct.
|
|
Instruction* pointee = defUseMgr->GetDef(pointeeId);
|
|
while (pointee != nullptr && (pointee->opcode() == spv::Op::OpTypeArray ||
|
|
pointee->opcode() == spv::Op::OpTypeRuntimeArray)) {
|
|
pointeeId = pointee->GetSingleWordInOperand(0);
|
|
pointee = defUseMgr->GetDef(pointeeId);
|
|
}
|
|
if (pointee == nullptr || pointee->opcode() != spv::Op::OpTypeStruct) continue;
|
|
if (blockStructIds.find(pointeeId) == blockStructIds.end()) continue;
|
|
|
|
CollectStructTypes(irContext, pointeeId, structTypeIds);
|
|
}
|
|
if (structTypeIds.empty()) return Status::SuccessWithoutChange;
|
|
|
|
std::vector<Instruction*> decorationsToRemove;
|
|
for (Instruction& annotation : irContext->module()->annotations()) {
|
|
if (annotation.opcode() != spv::Op::OpMemberDecorate) continue;
|
|
if (static_cast<spv::Decoration>(annotation.GetSingleWordInOperand(2)) !=
|
|
spv::Decoration::RelaxedPrecision) {
|
|
continue;
|
|
}
|
|
if (structTypeIds.find(annotation.GetSingleWordInOperand(0)) == structTypeIds.end()) continue;
|
|
decorationsToRemove.push_back(&annotation);
|
|
}
|
|
if (decorationsToRemove.empty()) return Status::SuccessWithoutChange;
|
|
|
|
for (Instruction* decoration : decorationsToRemove) {
|
|
irContext->KillInst(decoration);
|
|
}
|
|
return Status::SuccessWithChange;
|
|
}
|
|
|
|
spvtools::Optimizer::PassToken
|
|
StripUboMemberRelaxedPrecisionPass::CreateStripUboMemberRelaxedPrecisionPass() {
|
|
return spvtools::Optimizer::PassToken(MakeUnique<StripUboMemberRelaxedPrecisionPass>());
|
|
}
|
|
} // namespace ShaderTranspiler
|
|
} // namespace MG_Util
|
|
} // namespace MobileGL
|