mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-12 22:28:32 +09:00
[Fix] (MG_Backend/DirectVulkan): Add shader position transforms & correct namespace name.
This commit is contained in:
@@ -15,14 +15,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
|
|
||||||
void Clear(GLbitfield mask) {
|
void Clear(GLbitfield mask) {
|
||||||
if (USE_TMP_IMPL) {
|
if (USE_TMP_IMPL) {
|
||||||
MobileGL::Backend::DirectVulkan::TmpImpl::Clear(mask);
|
MobileGL::MG_Backend::DirectVulkan::TmpImpl::Clear(mask);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrawElements(GLenum mode, GLsizei count, GLenum type, const void* indices) {
|
void DrawElements(GLenum mode, GLsizei count, GLenum type, const void* indices) {
|
||||||
if (USE_TMP_IMPL) {
|
if (USE_TMP_IMPL) {
|
||||||
MobileGL::Backend::DirectVulkan::TmpImpl::DrawElements(mode, count, type, indices);
|
MobileGL::MG_Backend::DirectVulkan::TmpImpl::DrawElements(mode, count, type, indices);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
pVulkanRenderer->RenderFrame();
|
pVulkanRenderer->RenderFrame();
|
||||||
|
|||||||
@@ -8,7 +8,296 @@
|
|||||||
|
|
||||||
#include "ProgramManager.h"
|
#include "ProgramManager.h"
|
||||||
|
|
||||||
|
#include "MG_Util/Debug/Log.h"
|
||||||
|
#include "spirv-tools/libspirv.h"
|
||||||
|
#include "spirv-tools/optimizer.hpp"
|
||||||
|
#include "source/opt/constants.h"
|
||||||
|
#include "source/opt/instruction.h"
|
||||||
|
#include "source/opt/ir_builder.h"
|
||||||
|
#include "source/opt/ir_context.h"
|
||||||
|
#include "source/opt/module.h"
|
||||||
|
#include "source/opt/pass.h"
|
||||||
|
#include "source/opt/type_manager.h"
|
||||||
|
|
||||||
|
#include <bit>
|
||||||
|
|
||||||
namespace MobileGL::MG_Backend::DirectVulkan::VkManager {
|
namespace MobileGL::MG_Backend::DirectVulkan::VkManager {
|
||||||
|
namespace {
|
||||||
|
using ProgramObject = MG_State::GLState::ProgramObject;
|
||||||
|
using ShaderObject = MG_State::GLState::ShaderObject;
|
||||||
|
|
||||||
|
struct PositionTargetInfo {
|
||||||
|
uint32_t variableId = 0;
|
||||||
|
uint32_t vectorTypeId = 0;
|
||||||
|
uint32_t floatTypeId = 0;
|
||||||
|
uint32_t vectorPtrTypeId = 0;
|
||||||
|
uint32_t memberIndex = 0;
|
||||||
|
bool isMember = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool IsVec4Float32(spvtools::opt::IRContext* context, uint32_t typeId, uint32_t* outFloatTypeId) {
|
||||||
|
auto* vecInst = context->get_def_use_mgr()->GetDef(typeId);
|
||||||
|
if (!vecInst || vecInst->opcode() != spv::Op::OpTypeVector) return false;
|
||||||
|
if (vecInst->GetSingleWordInOperand(1) != 4) return false;
|
||||||
|
|
||||||
|
const uint32_t floatTypeId = vecInst->GetSingleWordInOperand(0);
|
||||||
|
auto* floatInst = context->get_def_use_mgr()->GetDef(floatTypeId);
|
||||||
|
if (!floatInst || floatInst->opcode() != spv::Op::OpTypeFloat) return false;
|
||||||
|
if (floatInst->GetSingleWordInOperand(0) != 32) return false;
|
||||||
|
|
||||||
|
if (outFloatTypeId) *outFloatTypeId = floatTypeId;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ResolveDirectPositionTarget(spvtools::opt::IRContext* context, uint32_t variableId,
|
||||||
|
PositionTargetInfo* outTarget) {
|
||||||
|
auto* varInst = context->get_def_use_mgr()->GetDef(variableId);
|
||||||
|
if (!varInst || varInst->opcode() != spv::Op::OpVariable) return false;
|
||||||
|
if (varInst->GetSingleWordInOperand(0) != static_cast<uint32_t>(spv::StorageClass::Output)) return false;
|
||||||
|
|
||||||
|
auto* ptrTypeInst = context->get_def_use_mgr()->GetDef(varInst->type_id());
|
||||||
|
if (!ptrTypeInst || ptrTypeInst->opcode() != spv::Op::OpTypePointer) return false;
|
||||||
|
if (ptrTypeInst->GetSingleWordInOperand(0) != static_cast<uint32_t>(spv::StorageClass::Output))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
PositionTargetInfo target{};
|
||||||
|
target.variableId = variableId;
|
||||||
|
target.vectorTypeId = ptrTypeInst->GetSingleWordInOperand(1);
|
||||||
|
if (!IsVec4Float32(context, target.vectorTypeId, &target.floatTypeId)) return false;
|
||||||
|
target.vectorPtrTypeId = varInst->type_id();
|
||||||
|
target.isMember = false;
|
||||||
|
|
||||||
|
*outTarget = target;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t FindOutputVectorPointerTypeId(spvtools::opt::IRContext* context, uint32_t vectorTypeId) {
|
||||||
|
auto* vectorType = context->get_type_mgr()->GetType(vectorTypeId);
|
||||||
|
if (!vectorType) return 0;
|
||||||
|
spvtools::opt::analysis::Pointer ptrType(vectorType, spv::StorageClass::Output);
|
||||||
|
return context->get_type_mgr()->GetTypeInstruction(&ptrType);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ResolveMemberPositionTarget(spvtools::opt::IRContext* context, uint32_t structTypeId, uint32_t memberIndex,
|
||||||
|
PositionTargetInfo* outTarget) {
|
||||||
|
auto* structInst = context->get_def_use_mgr()->GetDef(structTypeId);
|
||||||
|
if (!structInst || structInst->opcode() != spv::Op::OpTypeStruct) return false;
|
||||||
|
if (memberIndex >= structInst->NumInOperands()) return false;
|
||||||
|
|
||||||
|
const uint32_t vectorTypeId = structInst->GetSingleWordInOperand(memberIndex);
|
||||||
|
uint32_t floatTypeId = 0;
|
||||||
|
if (!IsVec4Float32(context, vectorTypeId, &floatTypeId)) return false;
|
||||||
|
|
||||||
|
const uint32_t vectorPtrTypeId = FindOutputVectorPointerTypeId(context, vectorTypeId);
|
||||||
|
if (vectorPtrTypeId == 0) return false;
|
||||||
|
|
||||||
|
for (auto& inst : context->module()->types_values()) {
|
||||||
|
if (inst.opcode() != spv::Op::OpVariable) continue;
|
||||||
|
if (inst.GetSingleWordInOperand(0) != static_cast<uint32_t>(spv::StorageClass::Output)) continue;
|
||||||
|
|
||||||
|
auto* ptrTypeInst = context->get_def_use_mgr()->GetDef(inst.type_id());
|
||||||
|
if (!ptrTypeInst || ptrTypeInst->opcode() != spv::Op::OpTypePointer) continue;
|
||||||
|
if (ptrTypeInst->GetSingleWordInOperand(0) != static_cast<uint32_t>(spv::StorageClass::Output))
|
||||||
|
continue;
|
||||||
|
if (ptrTypeInst->GetSingleWordInOperand(1) != structTypeId) continue;
|
||||||
|
|
||||||
|
PositionTargetInfo target{};
|
||||||
|
target.variableId = inst.result_id();
|
||||||
|
target.vectorTypeId = vectorTypeId;
|
||||||
|
target.floatTypeId = floatTypeId;
|
||||||
|
target.vectorPtrTypeId = vectorPtrTypeId;
|
||||||
|
target.memberIndex = memberIndex;
|
||||||
|
target.isMember = true;
|
||||||
|
*outTarget = target;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FindPositionTarget(spvtools::opt::IRContext* context, PositionTargetInfo* outTarget) {
|
||||||
|
Vector<Pair<uint32_t, uint32_t>> memberCandidates;
|
||||||
|
constexpr uint32_t kDecorationBuiltIn = static_cast<uint32_t>(spv::Decoration::BuiltIn);
|
||||||
|
constexpr uint32_t kBuiltInPosition = static_cast<uint32_t>(spv::BuiltIn::Position);
|
||||||
|
|
||||||
|
for (auto& inst : context->module()->annotations()) {
|
||||||
|
if (inst.opcode() == spv::Op::OpDecorate) {
|
||||||
|
if (inst.NumInOperands() < 3) continue;
|
||||||
|
if (inst.GetSingleWordInOperand(1) != kDecorationBuiltIn) continue;
|
||||||
|
if (inst.GetSingleWordInOperand(2) != kBuiltInPosition) continue;
|
||||||
|
if (ResolveDirectPositionTarget(context, inst.GetSingleWordInOperand(0), outTarget)) return true;
|
||||||
|
} else if (inst.opcode() == spv::Op::OpMemberDecorate) {
|
||||||
|
if (inst.NumInOperands() < 4) continue;
|
||||||
|
if (inst.GetSingleWordInOperand(2) != kDecorationBuiltIn) continue;
|
||||||
|
if (inst.GetSingleWordInOperand(3) != kBuiltInPosition) continue;
|
||||||
|
memberCandidates.emplace_back(inst.GetSingleWordInOperand(0), inst.GetSingleWordInOperand(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& [structTypeId, memberIndex] : memberCandidates) {
|
||||||
|
if (ResolveMemberPositionTarget(context, structTypeId, memberIndex, outTarget)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InsertPositionFixup(spvtools::opt::IRContext* context, spvtools::opt::Instruction* insertBefore,
|
||||||
|
const PositionTargetInfo& target, uint32_t halfConstId, bool doYFlip, bool doZRemap) {
|
||||||
|
using namespace spvtools::opt;
|
||||||
|
InstructionBuilder builder(context, insertBefore,
|
||||||
|
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
|
||||||
|
|
||||||
|
uint32_t positionPtrId = target.variableId;
|
||||||
|
if (target.isMember) {
|
||||||
|
const uint32_t memberIndexId = builder.GetUintConstantId(target.memberIndex);
|
||||||
|
if (memberIndexId == 0) return false;
|
||||||
|
auto* access = builder.AddAccessChain(target.vectorPtrTypeId, target.variableId, {memberIndexId});
|
||||||
|
if (!access) return false;
|
||||||
|
positionPtrId = access->result_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* position = builder.AddLoad(target.vectorTypeId, positionPtrId);
|
||||||
|
if (!position) return false;
|
||||||
|
auto* x = builder.AddCompositeExtract(target.floatTypeId, position->result_id(), {0});
|
||||||
|
auto* y = builder.AddCompositeExtract(target.floatTypeId, position->result_id(), {1});
|
||||||
|
auto* z = builder.AddCompositeExtract(target.floatTypeId, position->result_id(), {2});
|
||||||
|
auto* w = builder.AddCompositeExtract(target.floatTypeId, position->result_id(), {3});
|
||||||
|
if (!x || !y || !z || !w) return false;
|
||||||
|
|
||||||
|
if (!doYFlip && !doZRemap) return false;
|
||||||
|
|
||||||
|
uint32_t yValueId = y->result_id();
|
||||||
|
if (doYFlip) {
|
||||||
|
auto* negY = builder.AddUnaryOp(target.floatTypeId, spv::Op::OpFNegate, y->result_id());
|
||||||
|
if (!negY) return false;
|
||||||
|
yValueId = negY->result_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t zValueId = z->result_id();
|
||||||
|
if (doZRemap) {
|
||||||
|
auto* zPlusW = builder.AddBinaryOp(target.floatTypeId, spv::Op::OpFAdd, z->result_id(), w->result_id());
|
||||||
|
if (!zPlusW) return false;
|
||||||
|
auto* mappedZ =
|
||||||
|
builder.AddBinaryOp(target.floatTypeId, spv::Op::OpFMul, zPlusW->result_id(), halfConstId);
|
||||||
|
if (!mappedZ) return false;
|
||||||
|
zValueId = mappedZ->result_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* fixedPosition = builder.AddCompositeConstruct(target.vectorTypeId,
|
||||||
|
{x->result_id(), yValueId, zValueId, w->result_id()});
|
||||||
|
if (!fixedPosition) return false;
|
||||||
|
|
||||||
|
return builder.AddStore(positionPtrId, fixedPosition->result_id()) != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
class GlToVulkanPositionFixPass final : public spvtools::opt::Pass {
|
||||||
|
public:
|
||||||
|
const char* name() const override { return "gl-to-vulkan-position-fix"; }
|
||||||
|
explicit GlToVulkanPositionFixPass(ShaderTransformFlags transformFlags)
|
||||||
|
: m_transformFlags(transformFlags) {}
|
||||||
|
|
||||||
|
Status Process() override {
|
||||||
|
if (!m_transformFlags) return Status::SuccessWithoutChange;
|
||||||
|
PositionTargetInfo target{};
|
||||||
|
if (!FindPositionTarget(context(), &target)) return Status::SuccessWithoutChange;
|
||||||
|
|
||||||
|
auto* floatType = context()->get_type_mgr()->GetType(target.floatTypeId);
|
||||||
|
if (!floatType) return Status::SuccessWithoutChange;
|
||||||
|
|
||||||
|
const uint32_t halfBits = std::bit_cast<uint32_t>(0.5f);
|
||||||
|
const auto* halfConst = context()->get_constant_mgr()->GetConstant(floatType, {halfBits});
|
||||||
|
auto* halfInst = context()->get_constant_mgr()->GetDefiningInstruction(halfConst);
|
||||||
|
if (!halfInst) return Status::SuccessWithoutChange;
|
||||||
|
const uint32_t halfConstId = halfInst->result_id();
|
||||||
|
|
||||||
|
const bool doYFlip = (m_transformFlags & ShaderTransformBit::PositionYFlip);
|
||||||
|
const bool doZRemap = (m_transformFlags & ShaderTransformBit::PositionZRemap);
|
||||||
|
|
||||||
|
bool modified = false;
|
||||||
|
for (auto& entryPoint : get_module()->entry_points()) {
|
||||||
|
if (entryPoint.opcode() != spv::Op::OpEntryPoint) continue;
|
||||||
|
if (entryPoint.NumInOperands() < 2) continue;
|
||||||
|
|
||||||
|
const auto model = static_cast<spv::ExecutionModel>(entryPoint.GetSingleWordInOperand(0));
|
||||||
|
if (model != spv::ExecutionModel::Vertex && model != spv::ExecutionModel::TessellationEvaluation &&
|
||||||
|
model != spv::ExecutionModel::Geometry) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* function = context()->GetFunction(entryPoint.GetSingleWordInOperand(1));
|
||||||
|
if (!function) continue;
|
||||||
|
|
||||||
|
for (auto& bb : *function) {
|
||||||
|
for (auto instIter = bb.begin(); instIter != bb.end(); ++instIter) {
|
||||||
|
auto* inst = &*instIter;
|
||||||
|
const bool needsFixup =
|
||||||
|
(model == spv::ExecutionModel::Geometry && inst->opcode() == spv::Op::OpEmitVertex) ||
|
||||||
|
(model != spv::ExecutionModel::Geometry && inst->opcode() == spv::Op::OpReturn);
|
||||||
|
if (!needsFixup) continue;
|
||||||
|
|
||||||
|
modified |= InsertPositionFixup(context(), inst, target, halfConstId, doYFlip, doZRemap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!modified) return Status::SuccessWithoutChange;
|
||||||
|
context()->InvalidateAnalysesExceptFor(spvtools::opt::IRContext::kAnalysisDefUse |
|
||||||
|
spvtools::opt::IRContext::kAnalysisInstrToBlockMapping);
|
||||||
|
return Status::SuccessWithChange;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ShaderTransformFlags m_transformFlags;
|
||||||
|
};
|
||||||
|
|
||||||
|
spvtools::Optimizer::PassToken CreateGlToVulkanPositionFixPass(ShaderTransformFlags transformFlags) {
|
||||||
|
return spvtools::Optimizer::PassToken(MakeUnique<GlToVulkanPositionFixPass>(transformFlags));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TransformSpirvForVulkanPositionFix(const Vector<Uint32>& input, Vector<Uint32>& output,
|
||||||
|
ShaderTransformFlags transformFlags) {
|
||||||
|
if (input.empty()) {
|
||||||
|
output.clear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!transformFlags) {
|
||||||
|
output = input;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
spvtools::Optimizer optimizer(SPV_ENV_VULKAN_1_3);
|
||||||
|
spvtools::OptimizerOptions options;
|
||||||
|
options.set_run_validator(false);
|
||||||
|
optimizer.RegisterPass(CreateGlToVulkanPositionFixPass(transformFlags));
|
||||||
|
|
||||||
|
const bool success = optimizer.Run(input.data(), input.size(), &output, options);
|
||||||
|
if (!success) {
|
||||||
|
MGLOG_E("Vulkan: failed to run GL->Vulkan position fix pass");
|
||||||
|
output = input;
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderStage PickClipFixupStage(const Vector<SharedPtr<ShaderObject>>& shaders) {
|
||||||
|
bool hasGeometry = false;
|
||||||
|
bool hasTessEval = false;
|
||||||
|
bool hasVertex = false;
|
||||||
|
|
||||||
|
for (const auto& shader : shaders) {
|
||||||
|
if (!shader) continue;
|
||||||
|
const auto stage = shader->GetShaderStage();
|
||||||
|
hasGeometry |= (stage == ShaderStage::Geometry);
|
||||||
|
hasTessEval |= (stage == ShaderStage::TessEval);
|
||||||
|
hasVertex |= (stage == ShaderStage::Vertex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasGeometry) return ShaderStage::Geometry;
|
||||||
|
if (hasTessEval) return ShaderStage::TessEval;
|
||||||
|
if (hasVertex) return ShaderStage::Vertex;
|
||||||
|
return ShaderStage::Unknown;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
ProgramManager::~ProgramManager() {
|
ProgramManager::~ProgramManager() {
|
||||||
for (auto& [_, stages] : m_cache) {
|
for (auto& [_, stages] : m_cache) {
|
||||||
DestroyStages(stages);
|
DestroyStages(stages);
|
||||||
@@ -16,7 +305,7 @@ namespace MobileGL::MG_Backend::DirectVulkan::VkManager {
|
|||||||
m_cache.clear();
|
m_cache.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
ProgramManager::HashType ProgramManager::ComputeSpvHash(MG_State::GLState::ProgramObject* program) const {
|
ProgramManager::HashType ProgramManager::ComputeSourceSpvHash(MG_State::GLState::ProgramObject* program) const {
|
||||||
if (!program) return 0;
|
if (!program) return 0;
|
||||||
XXH64_state_t* state = XXH64_createState();
|
XXH64_state_t* state = XXH64_createState();
|
||||||
XXH64_reset(state, 0xC0FFEEu);
|
XXH64_reset(state, 0xC0FFEEu);
|
||||||
@@ -30,6 +319,60 @@ namespace MobileGL::MG_Backend::DirectVulkan::VkManager {
|
|||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ProgramManager::HashType ProgramManager::ComputeSpvHash(const Vector<Vector<Uint32>>& spirvs) const {
|
||||||
|
XXH64_state_t* state = XXH64_createState();
|
||||||
|
XXH64_reset(state, 0xC0FFEEu);
|
||||||
|
for (const auto& spv : spirvs) {
|
||||||
|
if (spv.empty()) continue;
|
||||||
|
XXH64_update(state, spv.data(), spv.size() * sizeof(Uint32));
|
||||||
|
}
|
||||||
|
HashType hash = XXH64_digest(state);
|
||||||
|
XXH64_freeState(state);
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProgramManager::BuildPipelineSpirvModules(MG_State::GLState::ProgramObject* program,
|
||||||
|
Vector<Vector<Uint32>>& outSpirvs,
|
||||||
|
ShaderTransformFlags transformFlags) const {
|
||||||
|
outSpirvs.clear();
|
||||||
|
if (!program) return;
|
||||||
|
|
||||||
|
auto& spirvs = program->GetGeneratedSpirv();
|
||||||
|
auto& shaders = program->GetAttachedShaders();
|
||||||
|
const ShaderStage fixupStage = PickClipFixupStage(shaders);
|
||||||
|
|
||||||
|
outSpirvs.reserve(spirvs.size());
|
||||||
|
for (SizeT i = 0; i < spirvs.size(); ++i) {
|
||||||
|
Vector<Uint32> module = spirvs[i];
|
||||||
|
|
||||||
|
ShaderStage stage = ShaderStage::Unknown;
|
||||||
|
if (i < shaders.size() && shaders[i]) stage = shaders[i]->GetShaderStage();
|
||||||
|
|
||||||
|
if (!module.empty() && fixupStage != ShaderStage::Unknown && stage == fixupStage) {
|
||||||
|
Vector<Uint32> transformed;
|
||||||
|
TransformSpirvForVulkanPositionFix(module, transformed, transformFlags);
|
||||||
|
module = Move(transformed);
|
||||||
|
}
|
||||||
|
outSpirvs.push_back(Move(module));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgramManager::HashType ProgramManager::ComputeSpvHash(MG_State::GLState::ProgramObject* program,
|
||||||
|
ShaderTransformFlags transformFlags) const {
|
||||||
|
Vector<Vector<Uint32>> spirvs;
|
||||||
|
BuildPipelineSpirvModules(program, spirvs, transformFlags);
|
||||||
|
return ComputeSpvHash(spirvs);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgramManager::HashType ProgramManager::ComputeProgramHash(MG_State::GLState::ProgramObject* program,
|
||||||
|
ShaderTransformFlags transformFlags) const {
|
||||||
|
if (!program) return 0;
|
||||||
|
const HashType sourceHash = ComputeSourceSpvHash(program);
|
||||||
|
auto it = m_cache.find(program);
|
||||||
|
if (it != m_cache.end() && it->second.sourceHash == sourceHash) return it->second.hash;
|
||||||
|
return ComputeSpvHash(program, transformFlags);
|
||||||
|
}
|
||||||
|
|
||||||
VkShaderStageFlagBits ProgramManager::ToVkStage(ShaderStage stage) const {
|
VkShaderStageFlagBits ProgramManager::ToVkStage(ShaderStage stage) const {
|
||||||
switch (stage) {
|
switch (stage) {
|
||||||
case ShaderStage::Vertex:
|
case ShaderStage::Vertex:
|
||||||
@@ -57,21 +400,24 @@ namespace MobileGL::MG_Backend::DirectVulkan::VkManager {
|
|||||||
}
|
}
|
||||||
stages.modules.clear();
|
stages.modules.clear();
|
||||||
stages.stages.clear();
|
stages.stages.clear();
|
||||||
|
stages.sourceHash = 0;
|
||||||
stages.hash = 0;
|
stages.hash = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector<VkPipelineShaderStageCreateInfo>& ProgramManager::CreatePipelineShaderStages(
|
Vector<VkPipelineShaderStageCreateInfo>& ProgramManager::CreatePipelineShaderStages(
|
||||||
MG_State::GLState::ProgramObject* program) {
|
MG_State::GLState::ProgramObject* program, ShaderTransformFlags transformFlags) {
|
||||||
auto& entry = m_cache[program];
|
auto& entry = m_cache[program];
|
||||||
HashType newHash = ComputeSpvHash(program);
|
HashType sourceHash = ComputeSourceSpvHash(program);
|
||||||
if (!entry.stages.empty() && entry.hash == newHash) return entry.stages;
|
if (!entry.stages.empty() && entry.sourceHash == sourceHash) return entry.stages;
|
||||||
|
|
||||||
DestroyStages(entry);
|
DestroyStages(entry);
|
||||||
entry.hash = newHash;
|
entry.sourceHash = sourceHash;
|
||||||
|
|
||||||
if (!program) return entry.stages;
|
if (!program) return entry.stages;
|
||||||
|
|
||||||
auto& spirvs = program->GetGeneratedSpirv();
|
Vector<Vector<Uint32>> spirvs;
|
||||||
|
BuildPipelineSpirvModules(program, spirvs, transformFlags);
|
||||||
|
entry.hash = ComputeSpvHash(spirvs);
|
||||||
auto& shaders = program->GetAttachedShaders();
|
auto& shaders = program->GetAttachedShaders();
|
||||||
|
|
||||||
for (SizeT i = 0; i < spirvs.size(); ++i) {
|
for (SizeT i = 0; i < spirvs.size(); ++i) {
|
||||||
@@ -87,7 +433,7 @@ namespace MobileGL::MG_Backend::DirectVulkan::VkManager {
|
|||||||
|
|
||||||
VkPipelineShaderStageCreateInfo stage{VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO};
|
VkPipelineShaderStageCreateInfo stage{VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO};
|
||||||
ShaderStage shaderStage = ShaderStage::Unknown;
|
ShaderStage shaderStage = ShaderStage::Unknown;
|
||||||
if (i < shaders.size()) shaderStage = shaders[i]->GetShaderStage();
|
if (i < shaders.size() && shaders[i]) shaderStage = shaders[i]->GetShaderStage();
|
||||||
stage.stage = ToVkStage(shaderStage);
|
stage.stage = ToVkStage(shaderStage);
|
||||||
stage.module = module;
|
stage.module = module;
|
||||||
stage.pName = "main";
|
stage.pName = "main";
|
||||||
|
|||||||
@@ -13,6 +13,13 @@
|
|||||||
#include "MG_State/GLState/ProgramState/ProgramObject.h"
|
#include "MG_State/GLState/ProgramState/ProgramObject.h"
|
||||||
|
|
||||||
namespace MobileGL::MG_Backend::DirectVulkan::VkManager {
|
namespace MobileGL::MG_Backend::DirectVulkan::VkManager {
|
||||||
|
enum class ShaderTransformBit : Uint {
|
||||||
|
None = 0,
|
||||||
|
PositionYFlip = 1 << 0,
|
||||||
|
PositionZRemap = 1 << 1,
|
||||||
|
};
|
||||||
|
using ShaderTransformFlags = Flags<ShaderTransformBit>;
|
||||||
|
|
||||||
class ProgramManager {
|
class ProgramManager {
|
||||||
public:
|
public:
|
||||||
using HashType = Uint64;
|
using HashType = Uint64;
|
||||||
@@ -24,17 +31,25 @@ namespace MobileGL::MG_Backend::DirectVulkan::VkManager {
|
|||||||
ProgramManager& operator=(const ProgramManager&) = delete;
|
ProgramManager& operator=(const ProgramManager&) = delete;
|
||||||
|
|
||||||
Vector<VkPipelineShaderStageCreateInfo>& CreatePipelineShaderStages(
|
Vector<VkPipelineShaderStageCreateInfo>& CreatePipelineShaderStages(
|
||||||
MG_State::GLState::ProgramObject* program);
|
MG_State::GLState::ProgramObject* program,
|
||||||
|
ShaderTransformFlags transformFlags = ShaderTransformBit::PositionZRemap);
|
||||||
|
HashType ComputeProgramHash(MG_State::GLState::ProgramObject* program,
|
||||||
|
ShaderTransformFlags transformFlags = ShaderTransformBit::PositionZRemap) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct ProgramStages {
|
struct ProgramStages {
|
||||||
|
HashType sourceHash = 0;
|
||||||
HashType hash = 0;
|
HashType hash = 0;
|
||||||
Vector<VkPipelineShaderStageCreateInfo> stages;
|
Vector<VkPipelineShaderStageCreateInfo> stages;
|
||||||
Vector<VkShaderModule> modules;
|
Vector<VkShaderModule> modules;
|
||||||
};
|
};
|
||||||
|
|
||||||
void DestroyStages(ProgramStages& stages);
|
void DestroyStages(ProgramStages& stages);
|
||||||
HashType ComputeSpvHash(MG_State::GLState::ProgramObject* program) const;
|
HashType ComputeSourceSpvHash(MG_State::GLState::ProgramObject* program) const;
|
||||||
|
HashType ComputeSpvHash(MG_State::GLState::ProgramObject* program, ShaderTransformFlags transformFlags) const;
|
||||||
|
HashType ComputeSpvHash(const Vector<Vector<Uint32>>& spirvs) const;
|
||||||
|
void BuildPipelineSpirvModules(MG_State::GLState::ProgramObject* program, Vector<Vector<Uint32>>& outSpirvs,
|
||||||
|
ShaderTransformFlags transformFlags) const;
|
||||||
VkShaderStageFlagBits ToVkStage(ShaderStage stage) const;
|
VkShaderStageFlagBits ToVkStage(ShaderStage stage) const;
|
||||||
|
|
||||||
VulkanContext& m_ctx;
|
VulkanContext& m_ctx;
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
#include <xxhash.h>
|
#include <xxhash.h>
|
||||||
|
|
||||||
#define DEBUG_TRACE_POINT() MGLOG_D("DV TmpImpl Trace: %s:%d", __func__, __LINE__)
|
#define DEBUG_TRACE_POINT() MGLOG_D("DV TmpImpl Trace: %s:%d", __func__, __LINE__)
|
||||||
namespace MobileGL::Backend::DirectVulkan::TmpImpl {
|
namespace MobileGL::MG_Backend::DirectVulkan::TmpImpl {
|
||||||
namespace {
|
namespace {
|
||||||
class PendingClearInfo;
|
class PendingClearInfo;
|
||||||
void BeginRenderPass(GLbitfield clearMask, const PendingClearInfo* clearInfo);
|
void BeginRenderPass(GLbitfield clearMask, const PendingClearInfo* clearInfo);
|
||||||
@@ -411,7 +411,7 @@ namespace MobileGL::Backend::DirectVulkan::TmpImpl {
|
|||||||
if (view != VK_NULL_HANDLE) trash.mipViews.push_back(view);
|
if (view != VK_NULL_HANDLE) trash.mipViews.push_back(view);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
frame.TrashImages.push_back(std::move(trash));
|
frame.TrashImages.push_back(Move(trash));
|
||||||
|
|
||||||
res.view = VK_NULL_HANDLE;
|
res.view = VK_NULL_HANDLE;
|
||||||
res.image = VK_NULL_HANDLE;
|
res.image = VK_NULL_HANDLE;
|
||||||
@@ -1434,8 +1434,8 @@ namespace MobileGL::Backend::DirectVulkan::TmpImpl {
|
|||||||
|
|
||||||
DestroyBackendFramebuffer(backend);
|
DestroyBackendFramebuffer(backend);
|
||||||
|
|
||||||
backend.attachments = std::move(newAttachments);
|
backend.attachments = Move(newAttachments);
|
||||||
backend.attachmentIndex = std::move(newIndex);
|
backend.attachmentIndex = Move(newIndex);
|
||||||
backend.extent = extent;
|
backend.extent = extent;
|
||||||
backend.colorAttachmentCount = nEffectiveBuffers;
|
backend.colorAttachmentCount = nEffectiveBuffers;
|
||||||
backend.hasDepth = useDepthAttachment;
|
backend.hasDepth = useDepthAttachment;
|
||||||
@@ -2119,6 +2119,16 @@ namespace MobileGL::Backend::DirectVulkan::TmpImpl {
|
|||||||
Memset(g.nullUbo.mapped, 0, 256);
|
Memset(g.nullUbo.mapped, 0, 256);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VkManager::ShaderTransformFlags GetShaderTransformFlags() {
|
||||||
|
VkManager::ShaderTransformFlags flags = VkManager::ShaderTransformBit::PositionZRemap;
|
||||||
|
const auto& currentDrawFBO =
|
||||||
|
MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject();
|
||||||
|
if (currentDrawFBO == MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo->defaultFBO) {
|
||||||
|
flags |= VkManager::ShaderTransformBit::PositionYFlip;
|
||||||
|
}
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
ProgramResource& GetProgramResource(ProgramObject* program) {
|
ProgramResource& GetProgramResource(ProgramObject* program) {
|
||||||
auto& slot = g.programs[program];
|
auto& slot = g.programs[program];
|
||||||
Uint64 newHash = ComputeProgramSpvHash(program);
|
Uint64 newHash = ComputeProgramSpvHash(program);
|
||||||
@@ -2131,10 +2141,11 @@ namespace MobileGL::Backend::DirectVulkan::TmpImpl {
|
|||||||
|
|
||||||
slot.program = program;
|
slot.program = program;
|
||||||
slot.spvHash = newHash;
|
slot.spvHash = newHash;
|
||||||
slot.programHash = newHash;
|
|
||||||
|
|
||||||
|
VkManager::ShaderTransformFlags transformFlags = GetShaderTransformFlags();
|
||||||
if (!g.programMgr) g.programMgr = MakeUnique<DV::ProgramManager>(*g.ctx);
|
if (!g.programMgr) g.programMgr = MakeUnique<DV::ProgramManager>(*g.ctx);
|
||||||
slot.shaderStages = &g.programMgr->CreatePipelineShaderStages(program);
|
slot.shaderStages = &g.programMgr->CreatePipelineShaderStages(program, transformFlags);
|
||||||
|
slot.programHash = g.programMgr->ComputeProgramHash(program, transformFlags);
|
||||||
|
|
||||||
ReflectProgramResources(program, slot);
|
ReflectProgramResources(program, slot);
|
||||||
|
|
||||||
@@ -2239,7 +2250,7 @@ namespace MobileGL::Backend::DirectVulkan::TmpImpl {
|
|||||||
VkRenderPass renderPass, Uint32 colorAttachmentCount, Bool hasDepth,
|
VkRenderPass renderPass, Uint32 colorAttachmentCount, Bool hasDepth,
|
||||||
Uint64 renderPassKey) {
|
Uint64 renderPassKey) {
|
||||||
if (!g.programMgr) g.programMgr = MakeUnique<DV::ProgramManager>(*g.ctx);
|
if (!g.programMgr) g.programMgr = MakeUnique<DV::ProgramManager>(*g.ctx);
|
||||||
prog.shaderStages = &g.programMgr->CreatePipelineShaderStages(prog.program);
|
prog.shaderStages = &g.programMgr->CreatePipelineShaderStages(prog.program, GetShaderTransformFlags());
|
||||||
if (!prog.shaderStages || prog.shaderStages->empty()) {
|
if (!prog.shaderStages || prog.shaderStages->empty()) {
|
||||||
MGLOG_E("Vulkan: shader stages are empty for program %u (hash=%llu)", prog.program->GetExternalIndex(),
|
MGLOG_E("Vulkan: shader stages are empty for program %u (hash=%llu)", prog.program->GetExternalIndex(),
|
||||||
static_cast<unsigned long long>(prog.programHash));
|
static_cast<unsigned long long>(prog.programHash));
|
||||||
@@ -2274,8 +2285,8 @@ namespace MobileGL::Backend::DirectVulkan::TmpImpl {
|
|||||||
key.depthTest = rs.DepthTestEnabled ? 1u : 0u;
|
key.depthTest = rs.DepthTestEnabled ? 1u : 0u;
|
||||||
key.depthWrite = rs.DepthMask ? 1u : 0u;
|
key.depthWrite = rs.DepthMask ? 1u : 0u;
|
||||||
key.depthFunc = static_cast<Uint32>(rs.DepthFunc);
|
key.depthFunc = static_cast<Uint32>(rs.DepthFunc);
|
||||||
key.cullEnable = rs.CullFaceEnabled ? 1u : 0u;
|
key.cullEnable = 0;
|
||||||
key.cullMode = static_cast<Uint32>(rs.CullFaceModeSetting);
|
key.cullMode = 0;
|
||||||
key.colorMask = (rs.ColorMask.x() ? 1u : 0u) | (rs.ColorMask.y() ? 2u : 0u) | (rs.ColorMask.z() ? 4u : 0u) |
|
key.colorMask = (rs.ColorMask.x() ? 1u : 0u) | (rs.ColorMask.y() ? 2u : 0u) | (rs.ColorMask.z() ? 4u : 0u) |
|
||||||
(rs.ColorMask.w() ? 8u : 0u);
|
(rs.ColorMask.w() ? 8u : 0u);
|
||||||
key.srcRGB = static_cast<Uint32>(rs.BlendStates[0].SrcFactorRGB);
|
key.srcRGB = static_cast<Uint32>(rs.BlendStates[0].SrcFactorRGB);
|
||||||
@@ -2337,7 +2348,7 @@ namespace MobileGL::Backend::DirectVulkan::TmpImpl {
|
|||||||
DEBUG_TRACE_POINT();
|
DEBUG_TRACE_POINT();
|
||||||
VkPipelineRasterizationStateCreateInfo raster{VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO};
|
VkPipelineRasterizationStateCreateInfo raster{VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO};
|
||||||
raster.polygonMode = VK_POLYGON_MODE_FILL;
|
raster.polygonMode = VK_POLYGON_MODE_FILL;
|
||||||
raster.cullMode = rs.CullFaceEnabled ? ToVkCullMode(rs.CullFaceModeSetting) : VK_CULL_MODE_NONE;
|
raster.cullMode = VK_CULL_MODE_NONE;
|
||||||
raster.frontFace = VK_FRONT_FACE_CLOCKWISE;
|
raster.frontFace = VK_FRONT_FACE_CLOCKWISE;
|
||||||
raster.lineWidth = 1.0f;
|
raster.lineWidth = 1.0f;
|
||||||
|
|
||||||
@@ -2703,7 +2714,7 @@ namespace MobileGL::Backend::DirectVulkan::TmpImpl {
|
|||||||
VK_VERIFY(vkCreateFramebuffer(g.ctx->GetDevice(), &fbci, nullptr, &fb), "vkCreateFramebuffer");
|
VK_VERIFY(vkCreateFramebuffer(g.ctx->GetDevice(), &fbci, nullptr, &fb), "vkCreateFramebuffer");
|
||||||
fbs.push_back(fb);
|
fbs.push_back(fb);
|
||||||
}
|
}
|
||||||
g.swapchain->SetFramebuffers(std::move(fbs));
|
g.swapchain->SetFramebuffers(Move(fbs));
|
||||||
}
|
}
|
||||||
|
|
||||||
void DestroyFrameResources() {
|
void DestroyFrameResources() {
|
||||||
@@ -2722,7 +2733,7 @@ namespace MobileGL::Backend::DirectVulkan::TmpImpl {
|
|||||||
for (Uint32 i = 0; i < frames; ++i) {
|
for (Uint32 i = 0; i < frames; ++i) {
|
||||||
auto fr = MakeUnique<DV::FrameContext>();
|
auto fr = MakeUnique<DV::FrameContext>();
|
||||||
fr->Initialize(*g.ctx, g.commandPool);
|
fr->Initialize(*g.ctx, g.commandPool);
|
||||||
g.frames.push_back(std::move(fr));
|
g.frames.push_back(Move(fr));
|
||||||
}
|
}
|
||||||
g.frameDescriptorPools.clear();
|
g.frameDescriptorPools.clear();
|
||||||
g.frameDescriptorPools.resize(frames);
|
g.frameDescriptorPools.resize(frames);
|
||||||
@@ -3445,4 +3456,4 @@ namespace MobileGL::Backend::DirectVulkan::TmpImpl {
|
|||||||
}
|
}
|
||||||
clearInfo.mask |= validMask;
|
clearInfo.mask |= validMask;
|
||||||
}
|
}
|
||||||
} // namespace MobileGL::Backend::DirectVulkan::TmpImpl
|
} // namespace MobileGL::MG_Backend::DirectVulkan::TmpImpl
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <Includes.h>
|
#include <Includes.h>
|
||||||
|
|
||||||
namespace MobileGL::Backend::DirectVulkan::TmpImpl {
|
namespace MobileGL::MG_Backend::DirectVulkan::TmpImpl {
|
||||||
void Present();
|
void Present();
|
||||||
void FrameBegin();
|
void FrameBegin();
|
||||||
void InitVulkan(ANativeWindow* window);
|
void InitVulkan(ANativeWindow* window);
|
||||||
@@ -50,4 +50,4 @@ namespace MobileGL::Backend::DirectVulkan::TmpImpl {
|
|||||||
GLsizei height);
|
GLsizei height);
|
||||||
void GenerateMipmap(GLenum target);
|
void GenerateMipmap(GLenum target);
|
||||||
void Present();
|
void Present();
|
||||||
} // namespace MobileGL::Backend::DirectVulkan::TmpImpl
|
} // namespace MobileGL::MG_Backend::DirectVulkan::TmpImpl
|
||||||
@@ -89,7 +89,7 @@ namespace MobileGL {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CreateWindowSurfaceForVulkan(NativeWindowType window) {
|
void CreateWindowSurfaceForVulkan(NativeWindowType window) {
|
||||||
MobileGL::Backend::DirectVulkan::TmpImpl::InitVulkan(window);
|
MobileGL::MG_Backend::DirectVulkan::TmpImpl::InitVulkan(window);
|
||||||
return;
|
return;
|
||||||
MG_Backend::DirectVulkan::pVulkanRenderer = MakeUnique<MG_Backend::DirectVulkan::VulkanRenderer>(window);
|
MG_Backend::DirectVulkan::pVulkanRenderer = MakeUnique<MG_Backend::DirectVulkan::VulkanRenderer>(window);
|
||||||
MG_Backend::DirectVulkan::pVulkanRenderer->Initialize();
|
MG_Backend::DirectVulkan::pVulkanRenderer->Initialize();
|
||||||
@@ -105,7 +105,7 @@ namespace MobileGL {
|
|||||||
}
|
}
|
||||||
|
|
||||||
EGLBoolean SwapBuffers(EGLDisplay dpy, EGLSurface draw) {
|
EGLBoolean SwapBuffers(EGLDisplay dpy, EGLSurface draw) {
|
||||||
MobileGL::Backend::DirectVulkan::TmpImpl::Present();
|
MobileGL::MG_Backend::DirectVulkan::TmpImpl::Present();
|
||||||
return EGL_TRUE;
|
return EGL_TRUE;
|
||||||
if (!MG_Backend::DirectVulkan::pVulkanRenderer) {
|
if (!MG_Backend::DirectVulkan::pVulkanRenderer) {
|
||||||
MGLOG_E("EGLForVulkan::SwapBuffers called but VulkanRenderer is null");
|
MGLOG_E("EGLForVulkan::SwapBuffers called but VulkanRenderer is null");
|
||||||
|
|||||||
Reference in New Issue
Block a user