#include "cm/collisionqueryjobmanager.h" #include "cm/jobs/polygonmodel/polygonmodel.h" #include #include #include #include #include #include namespace { struct pcQuerySlot_t { queryResults_t primary; queryResults_t secondary; bool hasSecondary; }; struct pcQueryMemory_t { void* data; std::size_t size; }; } // namespace struct idQueryData { idQueryData(); ~idQueryData(); void AdvanceSubModelQueryFirstSubmittedIndex(std::uint64_t newIndex); void StartFrame(); int UpdateQueryDataStats(queryDataStats_t* stats, int& numStats, int maxStats, queryDataStats_t& totalStats); void EndFrame(); void Clear(); void ReleaseQueryOnlyData(); modelQuery_t* AllocModelQuery(); subModelQuery_t* AllocSubModelQuery(); queryParms_t* AllocQueryParms(); slideMoveState_t* AllocSlideMoveState(); queryResults_t* AllocIntermediateResults(unsigned int numResults, unsigned int resultSize); queryResults_t* AllocFinalResult(unsigned int totalSize); std::vector outstanding; std::vector modelQueries; std::vector subModelQueries; std::vector queryParms; std::vector slideMoveStates; std::vector intermediateResults; std::vector finalResults; bool started; int failedModelQuery; int failedSubModelQuery; int failedQueryParms; int failedIntermediateResults; int failedFinalResults; int failedSlideMoveState; int failedMergeResults; }; timings_t::timings_t() : min(UINT_MAX), max(0), total(0), count(0) { } idQueryData::idQueryData() : started(false), failedModelQuery(0), failedSubModelQuery(0), failedQueryParms(0), failedIntermediateResults(0), failedFinalResults(0), failedSlideMoveState(0), failedMergeResults(0) { } idQueryData::~idQueryData() { Clear(); } void idQueryData::AdvanceSubModelQueryFirstSubmittedIndex( const std::uint64_t newIndex) { const std::size_t count = newIndex >= subModelQueries.size() ? subModelQueries.size() : static_cast(newIndex); for (std::size_t index = 0; index < count; ++index) { subModelQuery_t* const query = subModelQueries[index]; if (query->subModel != nullptr && query->subModelData != nullptr) { ReleaseSubModelData(*query->subModel, query->subModelData); query->subModelData = nullptr; } delete query; } subModelQueries.erase(subModelQueries.begin(), subModelQueries.begin() + count); } void idQueryData::StartFrame() { started = true; } int idQueryData::UpdateQueryDataStats(queryDataStats_t* const stats, int& numStats, const int maxStats, queryDataStats_t& totalStats) { totalStats.numCollisionQueries = 0; totalStats.numModelQueries = 0; totalStats.numSubModelQueries = 0; totalStats.queryDataSize = 0; totalStats.finalResultSize = 0; for (int index = 0; index < numStats; ++index) { stats[index].numCollisionQueries = 0; stats[index].numModelQueries = 0; stats[index].numSubModelQueries = 0; stats[index].queryDataSize = 0; stats[index].finalResultSize = 0; } for (const modelQuery_t* const query : modelQueries) { const char* const userName = query->userName != nullptr ? query->userName : "*Unknown*"; int statIndex = 0; while (statIndex < numStats && stats[statIndex].userName != userName) { ++statIndex; } if (statIndex == numStats) { if (numStats >= maxStats || stats == nullptr) { continue; } std::memset(&stats[statIndex], 0, sizeof(stats[statIndex])); stats[statIndex].userName = userName; ++numStats; } queryDataStats_t& entry = stats[statIndex]; const int resultSize = query->type >= TRACE_CONTACTS_UNI_DIR ? 992 : 192; entry.numCollisionQueries += query->nextOnQuery == nullptr ? 1 : 0; ++entry.numModelQueries; entry.numSubModelQueries += query->numSubModelQueries; entry.queryDataSize += (std::max)(1, query->numSubModelQueries) * resultSize; entry.finalResultSize += query->finalResultsPtr != nullptr ? resultSize : 0; } for (int index = 0; index < numStats; ++index) { queryDataStats_t& entry = stats[index]; entry.maxCollisionQueries = (std::max)(entry.maxCollisionQueries, entry.numCollisionQueries); entry.maxModelQueries = (std::max)(entry.maxModelQueries, entry.numModelQueries); entry.maxSubModelQueries = (std::max)(entry.maxSubModelQueries, entry.numSubModelQueries); entry.maxQueryDataSize = (std::max)(entry.maxQueryDataSize, entry.queryDataSize); entry.maxFinalResultSize = (std::max)(entry.maxFinalResultSize, entry.finalResultSize); totalStats.numCollisionQueries += entry.numCollisionQueries; totalStats.numModelQueries += entry.numModelQueries; totalStats.numSubModelQueries += entry.numSubModelQueries; totalStats.queryDataSize += entry.queryDataSize; totalStats.finalResultSize += entry.finalResultSize; } totalStats.maxCollisionQueries = (std::max)( totalStats.maxCollisionQueries, totalStats.numCollisionQueries); totalStats.maxModelQueries = (std::max)(totalStats.maxModelQueries, totalStats.numModelQueries); totalStats.maxSubModelQueries = (std::max)( totalStats.maxSubModelQueries, totalStats.numSubModelQueries); totalStats.maxQueryDataSize = (std::max)(totalStats.maxQueryDataSize, totalStats.queryDataSize); totalStats.maxFinalResultSize = (std::max)( totalStats.maxFinalResultSize, totalStats.finalResultSize); return numStats; } void idQueryData::EndFrame() { started = false; } void idQueryData::Clear() { ReleaseQueryOnlyData(); for (pcQuerySlot_t* const slot : outstanding) { delete slot; } outstanding.clear(); for (const pcQueryMemory_t& block : finalResults) { _aligned_free(block.data); } finalResults.clear(); started = false; failedModelQuery = 0; failedSubModelQuery = 0; failedQueryParms = 0; failedIntermediateResults = 0; failedFinalResults = 0; failedSlideMoveState = 0; failedMergeResults = 0; } void idQueryData::ReleaseQueryOnlyData() { AdvanceSubModelQueryFirstSubmittedIndex(subModelQueries.size()); for (modelQuery_t* const query : modelQueries) { delete query; } modelQueries.clear(); for (queryParms_t* const parms : queryParms) { delete parms; } queryParms.clear(); for (slideMoveState_t* const state : slideMoveStates) { delete state; } slideMoveStates.clear(); for (const pcQueryMemory_t& block : intermediateResults) { _aligned_free(block.data); } intermediateResults.clear(); } modelQuery_t* idQueryData::AllocModelQuery() { if (modelQueries.size() >= 2048) { ++failedModelQuery; return nullptr; } modelQuery_t* const query = new (std::nothrow) modelQuery_t{}; if (query == nullptr) { ++failedModelQuery; return nullptr; } modelQueries.push_back(query); return query; } subModelQuery_t* idQueryData::AllocSubModelQuery() { if (subModelQueries.size() >= 4096) { ++failedSubModelQuery; return nullptr; } subModelQuery_t* const query = new (std::nothrow) subModelQuery_t{}; if (query == nullptr) { ++failedSubModelQuery; return nullptr; } subModelQueries.push_back(query); return query; } queryParms_t* idQueryData::AllocQueryParms() { if (queryParms.size() >= 2048) { ++failedQueryParms; return nullptr; } queryParms_t* const parms = new (std::nothrow) queryParms_t{}; if (parms == nullptr) { ++failedQueryParms; return nullptr; } queryParms.push_back(parms); return parms; } slideMoveState_t* idQueryData::AllocSlideMoveState() { if (slideMoveStates.size() >= 128) { ++failedSlideMoveState; return nullptr; } slideMoveState_t* const state = new (std::nothrow) slideMoveState_t{}; if (state == nullptr) { ++failedSlideMoveState; return nullptr; } slideMoveStates.push_back(state); return state; } queryResults_t* idQueryData::AllocIntermediateResults( const unsigned int numResults, const unsigned int resultSize) { constexpr std::size_t capacity = 1024 * 1024; if (numResults == 0 || resultSize == 0 || numResults > capacity / resultSize) { const std::uint64_t requested = static_cast(numResults) * resultSize; failedIntermediateResults += static_cast((std::min)(requested, static_cast(INT_MAX))); return nullptr; } const std::size_t size = static_cast(numResults) * resultSize; std::size_t allocated = 0; for (const pcQueryMemory_t& block : intermediateResults) { allocated += block.size; } if (size > capacity - (std::min)(allocated, capacity)) { failedIntermediateResults += static_cast((std::min)(size, static_cast(INT_MAX))); return nullptr; } void* const memory = _aligned_malloc(size, 128); if (memory == nullptr) { failedIntermediateResults += static_cast((std::min)(size, static_cast(INT_MAX))); return nullptr; } std::memset(memory, 0, size); intermediateResults.push_back({memory, size}); return static_cast(memory); } queryResults_t* idQueryData::AllocFinalResult( const unsigned int totalSize) { std::size_t allocated = 0; for (const pcQueryMemory_t& block : finalResults) { allocated += block.size; } if (totalSize == 0 || totalSize > 512 * 1024 - (std::min)(allocated, static_cast(512 * 1024))) { failedFinalResults += static_cast(totalSize); return nullptr; } void* const memory = _aligned_malloc(totalSize, 128); if (memory == nullptr) { failedFinalResults += static_cast(totalSize); return nullptr; } std::memset(memory, 0, totalSize); finalResults.push_back({memory, totalSize}); return static_cast(memory); } namespace { pcQuerySlot_t* Slot(const idCollisionQuery& query) { return reinterpret_cast( static_cast(query.offset)); } pcQuerySlot_t* OwnedSlot(const idQueryData* const data, const idCollisionQuery& query) { pcQuerySlot_t* const slot = Slot(query); if (data == nullptr || slot == nullptr) { return nullptr; } return std::find(data->outstanding.begin(), data->outstanding.end(), slot) != data->outstanding.end() ? slot : nullptr; } idCollisionQuery MakeQuery(idQueryData* const data, pcQuerySlot_t* slot) { if (data != nullptr) { data->outstanding.push_back(slot); } idCollisionQuery query; query.offset = static_cast( reinterpret_cast(slot)); return query; } void InitResult(queryResults_t& result, const traceType_t type, const idVec3& end, const idMat3& axis) { std::memset(&result, 0, sizeof(result)); result.query.type = type; result.query.status = QUERY_STATUS_SUCCESS; result.query.done = 1; if (type == TRACE_CONTACTS_UNI_DIR || type == TRACE_CONTACTS_OMNI_DIR) { reinterpret_cast(result.data)->numContacts = 0; } else if (type == TRACE_CLIP) { reinterpret_cast(result.data)->numVerts = 0; reinterpret_cast(result.data)->numIndices = 0; } else { trace_t& trace = *reinterpret_cast(result.data); std::memset(&trace, 0, sizeof(trace)); trace.fraction = 1.0f; trace.endpos = end; trace.endAxis = axis; } } idCollisionQuery SubmitSimple(idQueryData* const data, const traceType_t type, const idVec3& start, const idVec3& end, const idRotation* rotation, const idVec3& direction, const float depth, const idTraceModel** const trms, const int numTrms, const idMat3& trmAxis, const int contentMask, const idPositionedCollisionModel* const models, const int numModels) { pcQuerySlot_t* const slot = new pcQuerySlot_t; slot->hasSecondary = false; InitResult(slot->primary, type, end, trmAxis); std::vector mergeResults; if (models != nullptr && numModels > 0) { for (int modelNumber = 0; modelNumber < numModels; ++modelNumber) { const idPositionedCollisionModel& positioned = models[modelNumber]; const idCollisionModelLocal* const local = dynamic_cast(positioned.model); if (local == nullptr) { continue; } const int traceModelCount = (std::max)(1, numTrms); for (int trmNumber = 0; trmNumber < traceModelCount; ++trmNumber) { const idTraceModel* const trm = trms != nullptr && numTrms > 0 ? trms[trmNumber] : nullptr; queryParms_t parms{}; parms.type = type; parms.autoMerge = true; parms.testQuery = type == TRACE_TRANSLATION || type == TRACE_ROTATION; parms.start = start; parms.end = end; if (rotation != nullptr) { parms.rotationOrigin = rotation->origin; parms.rotationAxis = rotation->vec; parms.rotationAngle = rotation->angle; } parms.dir = direction; parms.depth = depth; parms.trmAxis = trmAxis; parms.contentMask = contentMask; parms.modelOrigin = positioned.modelOrigin; parms.modelAxis = positioned.modelAxis; parms.modelEntityNum = positioned.modelEntityNum; parms.modelPhysicsId = positioned.modelPhysicsId; parms.modelBodyId = positioned.modelBodyId; parms.modelContentsOverride = positioned.modelContentsOverride; parms.selfId = 0; queryResults_t modelResult{}; modelResult.query.status = QUERY_STATUS_PENDING; idTraceWork work; work.Init(); if (local->modelType == CM_POLYGONMODEL) { idCollisionQueryExecute::ExecutePolygonModelQuery(&work, modelResult, &parms, trm, &local->polygonModel); } else if (local->modelType == CM_SPHEREMODEL && local->sphereModel != nullptr) { idCollisionQueryExecute::ExecuteSphereModelQuery( modelResult, &parms, positioned.modelJoints, local->sphereModel); } mergeResults.push_back(modelResult); } } } if (!mergeResults.empty()) { idCollisionDetectionMerge::MergeQueryResults(&slot->primary, sizeof(queryResults_t), type, mergeResults.data(), static_cast(mergeResults.size()), nullptr, DEPENDENCY_NONE, nullptr, nullptr); slot->primary.query.done = 1; slot->primary.query.type = type; if (slot->primary.query.status == QUERY_STATUS_PENDING) { slot->primary.query.status = QUERY_STATUS_SUCCESS; } } return MakeQuery(data, slot); } void DestroyQuery(idQueryData* const data, idCollisionQuery& query) { pcQuerySlot_t* const slot = OwnedSlot(data, query); if (data != nullptr && slot != nullptr) { const auto found = std::find(data->outstanding.begin(), data->outstanding.end(), slot); data->outstanding.erase(found); delete slot; } query.offset = 0; } } // namespace idCollisionQueryJobManager::idCollisionQueryJobManager() : queryData(nullptr), dummyQueryResults(nullptr), queryFrameNumber(0), stalledFrameNumber(0), firstWaitTime(0), numJobGroups(0), jobGroups(nullptr), jobList(nullptr) { std::memset(&failedQuery, 0, sizeof(failedQuery)); } idCollisionQueryJobManager::~idCollisionQueryJobManager() { Shutdown(); } void idCollisionQueryJobManager::Init() { Shutdown(); queryData = new idQueryData; dummyQueryResults = new queryResults_t; std::memset(dummyQueryResults, 0, sizeof(*dummyQueryResults)); dummyQueryResults->query.done = 1; dummyQueryResults->query.merged = 1; dummyQueryResults->query.status = QUERY_STATUS_SUCCESS; queryFrameNumber = 0; stalledFrameNumber = 0; firstWaitTime = 0; } void idCollisionQueryJobManager::Shutdown() { if (queryData != nullptr) { delete queryData; queryData = nullptr; } delete dummyQueryResults; dummyQueryResults = nullptr; delete[] jobGroups; jobGroups = nullptr; numJobGroups = 0; jobList = nullptr; } void idCollisionQueryJobManager::WaitForAllQueries() { // Queries execute synchronously on PC. This preserves the recovered wait // contract while avoiding the Xenon SPU/parallel-job dependency. if (queryData != nullptr) { queryData->ReleaseQueryOnlyData(); } } idCollisionQuery idCollisionQueryJobManager::SubmitTranslationQuery( const idVec3& start, const idVec3& end, const idBounds&, const idTraceModel** trms, const int numTrms, const idMat3& trmAxis, const int contentMask, const idPositionedCollisionModel* models, const int numModels, const char*) { return SubmitSimple(queryData, TRACE_TRANSLATION, start, end, nullptr, idVec3(0, 0, 0), 0.0f, trms, numTrms, trmAxis, contentMask, models, numModels); } idCollisionQuery idCollisionQueryJobManager::SubmitLocalTranslationQuery( idCollisionQuery localSpace, const idVec3& start, const idVec3& end, const idBounds& bounds, const idTraceModel** trms, const int numTrms, const idMat3& trmAxis, const int contentMask, const idPositionedCollisionModel* models, const int numModels, const char* userName) { pcQuerySlot_t* const localSlot = OwnedSlot(queryData, localSpace); if (localSlot == nullptr) { return SubmitTranslationQuery(start, end, bounds, trms, numTrms, trmAxis, contentMask, models, numModels, userName); } queryParms_t source{}; source.start = start; source.end = end; source.trmAxis = trmAxis; queryParms_t transformed; idCollisionQueryExecute::SetupDependentParms(&transformed, &source, &localSlot->primary, DEPENDENCY_LOCAL_SPACE, &localSlot->primary, nullptr); return SubmitTranslationQuery(transformed.start, transformed.end, bounds, trms, numTrms, transformed.trmAxis, contentMask, models, numModels, userName); } idCollisionQuery idCollisionQueryJobManager::SubmitRotationQuery( const idVec3& start, const idRotation& rotation, const idBounds&, const idTraceModel** trms, const int numTrms, const idMat3& trmAxis, const int contentMask, const idPositionedCollisionModel* models, const int numModels, const char*) { return SubmitSimple(queryData, TRACE_ROTATION, start, start, &rotation, idVec3(0, 0, 0), 0.0f, trms, numTrms, trmAxis, contentMask, models, numModels); } idCollisionQuery idCollisionQueryJobManager::SubmitMotionQuery( const idVec3& start, const idVec3& end, const idRotation& rotation, const idBounds& bounds, const idTraceModel** trms, const int numTrms, const idMat3& trmAxis, const int contentMask, const idPositionedCollisionModel* models, const int numModels, const char* userName) { idCollisionQuery translation = SubmitTranslationQuery(start, end, bounds, trms, numTrms, trmAxis, contentMask, models, numModels, userName); idCollisionQuery rotationQuery = SubmitRotationQuery(start, rotation, bounds, trms, numTrms, trmAxis, contentMask, models, numModels, userName); pcQuerySlot_t* const finalSlot = new pcQuerySlot_t; finalSlot->hasSecondary = false; InitResult(finalSlot->primary, TRACE_TRANSLATION, end, trmAxis); idCollisionDetectionMerge::MergeMotionResults( reinterpret_cast(finalSlot->primary.data), reinterpret_cast(Slot(rotationQuery)->primary.data), reinterpret_cast(Slot(translation)->primary.data)); DestroyQuery(queryData, translation); DestroyQuery(queryData, rotationQuery); return MakeQuery(queryData, finalSlot); } idCollisionQuery idCollisionQueryJobManager::SubmitMotionContactsQuery( const idVec3& start, const idVec3& end, const idRotation& rotation, const float depth, const idBounds& bounds, const idTraceModel** trms, const int numTrms, const idMat3& trmAxis, const int contentMask, const idPositionedCollisionModel* models, const int numModels, const char* userName) { idCollisionQuery motion = SubmitMotionQuery(start, end, rotation, bounds, trms, numTrms, trmAxis, contentMask, models, numModels, userName); pcQuerySlot_t* const slot = Slot(motion); const trace_t& trace = *reinterpret_cast(slot->primary.data); idCollisionQuery contacts = SubmitContactsQuery(trace.endpos, idVec3(0, 0, 0), depth, bounds, trms, numTrms, trace.endAxis, contentMask, models, numModels, userName); slot->secondary = Slot(contacts)->primary; slot->hasSecondary = true; DestroyQuery(queryData, contacts); return motion; } idCollisionQuery idCollisionQueryJobManager::SubmitContentsQuery( const idVec3& start, const idBounds&, const idTraceModel** trms, const int numTrms, const idMat3& trmAxis, const int contentMask, const idPositionedCollisionModel* models, const int numModels, const char*) { const traceType_t type = trms == nullptr || numTrms == 0 ? TRACE_CONTENTS_POINT : TRACE_CONTENTS; return SubmitSimple(queryData, type, start, start, nullptr, idVec3(0, 0, 0), 0.0f, trms, numTrms, trmAxis, contentMask, models, numModels); } idCollisionQuery idCollisionQueryJobManager::SubmitLocalContentsQuery( idCollisionQuery localSpace, const idVec3& start, const idBounds& bounds, const idTraceModel** trms, const int numTrms, const idMat3& trmAxis, const int contentMask, const idPositionedCollisionModel* models, const int numModels, const char* userName) { pcQuerySlot_t* const localSlot = OwnedSlot(queryData, localSpace); idVec3 transformedStart = start; idMat3 transformedAxis = trmAxis; if (localSlot != nullptr) { queryParms_t source{}; source.start = source.end = start; source.trmAxis = trmAxis; queryParms_t transformed; idCollisionQueryExecute::SetupDependentParms(&transformed, &source, &localSlot->primary, DEPENDENCY_LOCAL_SPACE, &localSlot->primary, nullptr); transformedStart = transformed.start; transformedAxis = transformed.trmAxis; } return SubmitContentsQuery(transformedStart, bounds, trms, numTrms, transformedAxis, contentMask, models, numModels, userName); } idCollisionQuery idCollisionQueryJobManager::SubmitContactsQuery( const idVec3& start, const idVec3& direction, const float depth, const idBounds&, const idTraceModel** trms, const int numTrms, const idMat3& trmAxis, const int contentMask, const idPositionedCollisionModel* models, const int numModels, const char*) { const traceType_t type = direction.LengthSqr() > 1.0e-12f ? TRACE_CONTACTS_UNI_DIR : TRACE_CONTACTS_OMNI_DIR; return SubmitSimple(queryData, type, start, start, nullptr, direction, depth, trms, numTrms, trmAxis, contentMask, models, numModels); } idCollisionQuery idCollisionQueryJobManager::SubmitClipQuery( const idVec3& start, const idBounds&, const idTraceModel** trms, const int numTrms, const idMat3& trmAxis, const int contentMask, const idPositionedCollisionModel* models, const int numModels, const char*) { return SubmitSimple(queryData, TRACE_CLIP, start, start, nullptr, idVec3(0, 0, 0), 0.0f, trms, numTrms, trmAxis, contentMask, models, numModels); } idCollisionQuery idCollisionQueryJobManager::SubmitStepMoveQuery( const idVec3& start, const idVec3& end, const idVec3& downNormal, const float stepUp, const float stepDown, const idBounds& bounds, const idTraceModel** trms, const int numTrms, const idMat3& trmAxis, const int contentMask, const idPositionedCollisionModel* models, const int numModels, const char* userName) { const auto runTranslation = [&](const idVec3& from, const idVec3& to) { idCollisionQuery query = SubmitTranslationQuery(from, to, bounds, trms, numTrms, trmAxis, contentMask, models, numModels, userName); trace_t trace = *reinterpret_cast( Slot(query)->primary.data); DestroyQuery(queryData, query); return trace; }; const trace_t forward1 = runTranslation(start, end); const idVec3 stepUpEnd = forward1.endpos - downNormal * stepUp; const trace_t up = runTranslation(forward1.endpos, stepUpEnd); const idVec3 remaining = (end - start) * (1.0f - forward1.fraction); const trace_t forward2 = runTranslation(up.endpos, up.endpos + remaining); const float climbed = stepUp * up.fraction; const trace_t down = runTranslation(forward2.endpos, forward2.endpos + downNormal * (climbed + stepDown)); pcQuerySlot_t* const slot = new pcQuerySlot_t; slot->hasSecondary = false; InitResult(slot->primary, TRACE_TRANSLATION, end, trmAxis); idCollisionDetectionMerge::MergeStepMoveResults( reinterpret_cast(slot->primary.data), &down, &forward2, &forward1, false); slot->primary.query.merged = 1; return MakeQuery(queryData, slot); } idCollisionQuery idCollisionQueryJobManager::SubmitStepMoveContactsQuery( const idVec3& start, const idVec3& end, const idVec3& downNormal, const float stepUp, const float stepDown, const idBounds& bounds, const idTraceModel** trms, const int numTrms, const idMat3& trmAxis, const int contentMask, const idPositionedCollisionModel* models, const int numModels, const char* userName) { idCollisionQuery query = SubmitStepMoveQuery(start, end, downNormal, stepUp, stepDown, bounds, trms, numTrms, trmAxis, contentMask, models, numModels, userName); pcQuerySlot_t* const slot = Slot(query); const trace_t& trace = *reinterpret_cast(slot->primary.data); idCollisionQuery contacts = SubmitContactsQuery(trace.endpos, downNormal, 0.5f, bounds, trms, numTrms, trace.endAxis, contentMask, models, numModels, userName); slot->secondary = Slot(contacts)->primary; slot->hasSecondary = true; DestroyQuery(queryData, contacts); return query; } idCollisionQuery idCollisionQueryJobManager::SubmitSlideMoveQuery( const idVec3& start, const idVec3& velocity, const idVec3& gravityVector, const float stepUp, const float stepDown, const idBounds& bounds, const idTraceModel** trms, const int numTrms, const idMat3& trmAxis, const int contentMask, const idPositionedCollisionModel* models, const int numModels, const char* userName) { slideMoveState_t state{}; idCollisionDetectionMerge::InitSlideMoveState(&state, velocity, gravityVector); trace_t trace{}; trace.fraction = 1.0f; trace.endpos = start; trace.endAxis = trmAxis; idVec3 current = start; idVec3 downDirection(0.0f, 0.0f, -1.0f); const float gravityLength = gravityVector.Length(); if (gravityLength > 1.0e-6f) { downDirection = gravityVector * (1.0f / gravityLength); } for (int iteration = 0; iteration < 4 && state.fractionRemaining > 0.0f; ++iteration) { const idVec3 target = current + state.velocity * state.fractionRemaining; idCollisionQuery step = SubmitStepMoveQuery(current, target, downDirection, stepUp, stepDown, bounds, trms, numTrms, trmAxis, contentMask, models, numModels, userName); trace = *reinterpret_cast(Slot(step)->primary.data); DestroyQuery(queryData, step); current = trace.endpos; if (idCollisionDetectionMerge::UpdateSlideMoveState(&state, &trace)) { break; } } trace.endpos = current; idCollisionDetectionMerge::FinishSlideMoveState(&state, &trace); pcQuerySlot_t* const slot = new pcQuerySlot_t; slot->hasSecondary = false; InitResult(slot->primary, TRACE_TRANSLATION, current, trmAxis); *reinterpret_cast(slot->primary.data) = trace; slot->primary.query.merged = 1; return MakeQuery(queryData, slot); } idCollisionQuery idCollisionQueryJobManager::SubmitSlideMoveContactsQuery( const idVec3& start, const idVec3& velocity, const idVec3& gravityVector, const float stepUp, const float stepDown, const idBounds& bounds, const idTraceModel** trms, const int numTrms, const idMat3& trmAxis, const int contentMask, const idPositionedCollisionModel* models, const int numModels, const char* userName) { idCollisionQuery query = SubmitSlideMoveQuery(start, velocity, gravityVector, stepUp, stepDown, bounds, trms, numTrms, trmAxis, contentMask, models, numModels, userName); pcQuerySlot_t* const slot = Slot(query); const trace_t& trace = *reinterpret_cast(slot->primary.data); idVec3 contactDirection(0.0f, 0.0f, -1.0f); const float gravityLength = gravityVector.Length(); if (gravityLength > 1.0e-6f) { contactDirection = gravityVector * (1.0f / gravityLength); } idCollisionQuery contacts = SubmitContactsQuery(trace.endpos, contactDirection, 0.5f, bounds, trms, numTrms, trace.endAxis, contentMask, models, numModels, userName); slot->secondary = Slot(contacts)->primary; slot->hasSecondary = true; DestroyQuery(queryData, contacts); return query; } bool idCollisionQueryJobManager::GetRotationResult(idTraceWork*, trace_t* const result, idCollisionQuery& query, const bool peek) { pcQuerySlot_t* const slot = OwnedSlot(queryData, query); if (slot == nullptr || result == nullptr) { return false; } *result = *reinterpret_cast(slot->primary.data); if (!peek) { DestroyQuery(queryData, query); } return true; } bool idCollisionQueryJobManager::GetTraceResult(trace_t* const result, idCollisionQuery& query, const bool peek) { pcQuerySlot_t* const slot = OwnedSlot(queryData, query); if (slot == nullptr || result == nullptr) { return false; } *result = *reinterpret_cast(slot->primary.data); if (!peek) { DestroyQuery(queryData, query); } return true; } bool idCollisionQueryJobManager::GetContactsResult( contactsResult_t* const result, idCollisionQuery& query, const bool peek) { pcQuerySlot_t* const slot = OwnedSlot(queryData, query); if (slot == nullptr || result == nullptr) { return false; } result->CopyFrom(*reinterpret_cast( slot->primary.data)); if (!peek) { DestroyQuery(queryData, query); } return true; } bool idCollisionQueryJobManager::GetMotionContactsResult(idTraceWork*, trace_t* const result, contactsResult_t* const contacts, idCollisionQuery& query, const bool peek) { pcQuerySlot_t* const slot = OwnedSlot(queryData, query); if (slot == nullptr) { return false; } if (result != nullptr) { *result = *reinterpret_cast(slot->primary.data); } if (contacts != nullptr) { if (slot->hasSecondary) { contacts->CopyFrom(*reinterpret_cast( slot->secondary.data)); } else { contacts->numContacts = 0; } } if (!peek) { DestroyQuery(queryData, query); } return true; } bool idCollisionQueryJobManager::GetClipResult(idTraceWork*, clipResult_t* const result, idCollisionQuery& query, const bool peek) { pcQuerySlot_t* const slot = OwnedSlot(queryData, query); if (slot == nullptr || result == nullptr) { return false; } *result = *reinterpret_cast(slot->primary.data); if (!peek) { DestroyQuery(queryData, query); } return true; } bool idCollisionQueryJobManager::GetSlideMoveContactsResult(idTraceWork* tw, trace_t* result, contactsResult_t* contacts, idCollisionQuery& query, const bool peek) { return GetMotionContactsResult(tw, result, contacts, query, peek); } void idCollisionQueryJobManager::ShowDebugInfo() { // Profiling accumulators are retained in the recovered layout. Console // presentation belongs to the later renderer/console integration. } void idCollisionQueryJobManager::StartFrame() { if (queryData != nullptr) { queryData->StartFrame(); } } void idCollisionQueryJobManager::EndFrame() { if (queryData != nullptr) { queryData->EndFrame(); } } void idCollisionQueryJobManager::SubmitQueries() { if (queryData != nullptr) { queryData->ReleaseQueryOnlyData(); } ++queryFrameNumber; // Synchronous PC submissions are complete before this fence is reached. } void CollisionMergeJob(modelQuery_t* const query) { if (query == nullptr || query->finalResultsPtr == nullptr) { return; } idCollisionDetectionMerge::MergeQueryResults(query->finalResultsPtr, query->resultSize, query->type, query->mergeResults, query->numMergeResults, query->slideMoveState, query->dependencyType, query->dependency1, query->dependency2); query->finalResultsPtr->query.mergeThreadId = 0; query->finalResultsPtr->query.merged = 1; } void CollisionExecuteJob(modelQuery_t* const query) { if (query == nullptr || query->parms == nullptr || query->resultsPtr == nullptr) { return; } queryParms_t parms{}; idCollisionQueryExecute::SetupDependentParms(&parms, query->parms, query->modelPosition, query->dependencyType, query->dependency1, query->dependency2); if (query->modelType == CM_POLYGONMODEL && query->polygonModel != nullptr) { idTraceWork work; work.Init(); idCollisionQueryExecute::ExecutePolygonModelQuery(&work, *query->resultsPtr, &parms, query->trm, query->polygonModel); } else if (query->modelType == CM_SPHEREMODEL && query->sphereModel != nullptr) { idCollisionQueryExecute::ExecuteSphereModelQuery(*query->resultsPtr, &parms, query->modelJoints, query->sphereModel); } query->resultsPtr->query.executeThreadId = 0; query->resultsPtr->query.done = 1; } bool idCollisionQueryJobManager::WaitForQueryResults( queryResults_t* const results) { return results != nullptr && results->query.done != 0; } modelQuery_t* idCollisionQueryJobManager::AllocModelQuery( const idPositionedCollisionModel& model) { if (queryData == nullptr || model.model == nullptr) { return nullptr; } const idCollisionModelLocal* const local = dynamic_cast(model.model); if (local == nullptr) { return nullptr; } modelQuery_t* const query = queryData->AllocModelQuery(); queryParms_t* const parms = queryData->AllocQueryParms(); if (query == nullptr || parms == nullptr) { return nullptr; } query->parms = parms; query->modelType = local->modelType; query->polygonModel = &local->polygonModel; query->sphereModel = local->sphereModel; query->modelJoints = model.modelJoints; query->resultsPtr = dummyQueryResults; query->finalResultsPtr = dummyQueryResults; query->mergeResults = dummyQueryResults; query->executePriority = 1023; query->mergePriority = 1023; query->frameNumber = queryFrameNumber; query->parms->modelOrigin = model.modelOrigin; query->parms->modelAxis = model.modelAxis; query->parms->modelEntityNum = model.modelEntityNum; query->parms->modelPhysicsId = model.modelPhysicsId; query->parms->modelBodyId = model.modelBodyId; query->parms->modelContentsOverride = model.modelContentsOverride; return query; } idCollisionQuery idCollisionQueryJobManager::AllocFinishedFinalResult( const traceType_t firstType, const traceType_t, const idVec3& endPosition, const idMat3& endAxis, const char*) { pcQuerySlot_t* const slot = new pcQuerySlot_t; slot->hasSecondary = false; InitResult(slot->primary, firstType, endPosition, endAxis); slot->primary.query.merged = 1; return MakeQuery(queryData, slot); } idCollisionQuery idCollisionQueryJobManager::AddModelQueryToMergeList( modelQuery_t* const modelQuery, const int mergePriority, slideMoveState_t* const slideMoveState) { if (modelQuery == nullptr) { return idCollisionQuery{0}; } pcQuerySlot_t* const slot = new pcQuerySlot_t; slot->hasSecondary = false; InitResult(slot->primary, modelQuery->type, modelQuery->parms != nullptr ? modelQuery->parms->end : idVec3(0.0f, 0.0f, 0.0f), modelQuery->parms != nullptr ? modelQuery->parms->trmAxis : idMat3()); modelQuery->mergePriority = mergePriority; modelQuery->slideMoveState = slideMoveState; modelQuery->finalResultsPtr = &slot->primary; CollisionMergeJob(modelQuery); return MakeQuery(queryData, slot); } void idCollisionQueryJobManager::AddModelQueryToExecuteList( modelQuery_t* const modelQuery, const idCollisionQuery modelPosition, const idCollisionQuery dependency1, const idCollisionQuery dependency2, const dependencyType_t dependencyType, const idVec3& globalStart, const idVec3& globalEnd, const idBounds&, int& executePriority) { if (modelQuery == nullptr) { return; } pcQuerySlot_t* const modelPositionSlot = OwnedSlot(queryData, modelPosition); pcQuerySlot_t* const dependency1Slot = OwnedSlot(queryData, dependency1); pcQuerySlot_t* const dependency2Slot = OwnedSlot(queryData, dependency2); modelQuery->modelPosition = modelPositionSlot != nullptr ? &modelPositionSlot->primary : nullptr; modelQuery->dependency1 = dependency1Slot != nullptr ? &dependency1Slot->primary : nullptr; modelQuery->dependency2 = dependency2Slot != nullptr ? &dependency2Slot->primary : nullptr; modelQuery->dependencyType = dependencyType; modelQuery->executePriority = executePriority++; if (modelQuery->parms != nullptr) { modelQuery->parms->start = globalStart; modelQuery->parms->end = globalEnd; } CollisionExecuteJob(modelQuery); } void idCollisionQueryJobManager::CreateMergeJobs(idParallelJobList*, modelQuery_t* modelQuery) { for (; modelQuery != nullptr; modelQuery = modelQuery->nextInMergeList) { if (modelQuery->mergePriority != 1023) { CollisionMergeJob(modelQuery); } } } void idCollisionQueryJobManager::CreateExecuteJobs(idParallelJobList*, modelQuery_t* modelQuery) { for (; modelQuery != nullptr; modelQuery = modelQuery->nextInExecuteList) { if (modelQuery->executePriority != 1023) { CollisionExecuteJob(modelQuery); } } }