diff --git a/.gitignore b/.gitignore index 496ad15..e1ab186 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /build/ +/build-x64/ *.log *.user *.suo diff --git a/src/draw_ids.cpp b/src/draw_ids.cpp index db2d77f..a58c8e4 100644 --- a/src/draw_ids.cpp +++ b/src/draw_ids.cpp @@ -2448,6 +2448,120 @@ struct DrawIdRecord::PsState { ID3D11SamplerState* smp[kPsSmp]; }; +enum : uint32_t { + kShadowIl = 1u << 0, + kShadowVb = 1u << 1, + kShadowIb = 1u << 2, + kShadowTopo = 1u << 3, + kShadowVs = 1u << 4, + kShadowCb = 1u << 5, + kShadowSrv = 1u << 6, + kShadowSmp = 1u << 7, + kShadowRs = 1u << 8, + kShadowVp = 1u << 9, + kShadowSc = 1u << 10, +}; + +struct DrawShadowState { + uint32_t bits = 0; + uint32_t vbMask = 0, cbMask = 0, srvMask = 0, smpMask = 0; + ID3D11InputLayout* il = nullptr; + ID3D11Buffer* vb[DrawIdRecord::kVb] = {}; UINT vbStride[DrawIdRecord::kVb] = {}, vbOff[DrawIdRecord::kVb] = {}; + ID3D11Buffer* ib = nullptr; DXGI_FORMAT ibFmt = DXGI_FORMAT_UNKNOWN; UINT ibOff = 0; + D3D11_PRIMITIVE_TOPOLOGY topo = D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED; + ID3D11VertexShader* vs = nullptr; + ID3D11Buffer* cb[DrawIdRecord::kVsCb] = {}; UINT cbFirst[DrawIdRecord::kVsCb] = {}, cbNum[DrawIdRecord::kVsCb] = {}; + ID3D11ShaderResourceView* srv[DrawIdRecord::kVsSrv] = {}; + ID3D11SamplerState* smp[DrawIdRecord::kVsSmp] = {}; + ID3D11RasterizerState* rs = nullptr; + UINT nVp = 0, nSc = 0; + D3D11_VIEWPORT vp[DrawIdRecord::kVp] = {}; + D3D11_RECT sc[DrawIdRecord::kSc] = {}; +}; + +DrawShadowState g_drawShadow; +constexpr uint32_t kShadowVbFull = (1u << DrawIdRecord::kVb) - 1u; +constexpr uint32_t kShadowCbFull = (1u << DrawIdRecord::kVsCb) - 1u; +constexpr uint32_t kShadowSrvFull = (1u << DrawIdRecord::kVsSrv) - 1u; +constexpr uint32_t kShadowSmpFull = (1u << DrawIdRecord::kVsSmp) - 1u; +DrawIdRecord::ShadowWindow g_shadowWin; +enum : int { kShIl = 0, kShVb, kShIb, kShTopo, kShVs, kShCb, kShSrv, kShSmp, kShRs, kShVp, kShSc }; + +template void AddRefShadow(T* p) { if (p) p->AddRef(); } + +uint32_t DrawIdRecord::ShadowCapture(State* out) { + uint32_t got = 0; + if (g_drawShadow.bits & kShadowIl) { + out->il = g_drawShadow.il; AddRefShadow(out->il); got |= kShadowIl; + } + if (g_drawShadow.vbMask == kShadowVbFull) { + memcpy(out->vb, g_drawShadow.vb, sizeof(out->vb)); + memcpy(out->vbStride, g_drawShadow.vbStride, sizeof(out->vbStride)); + memcpy(out->vbOff, g_drawShadow.vbOff, sizeof(out->vbOff)); + for (auto* p : out->vb) AddRefShadow(p); + got |= kShadowVb; + } + if (g_drawShadow.bits & kShadowIb) { + out->ib = g_drawShadow.ib; out->ibFmt = g_drawShadow.ibFmt; out->ibOff = g_drawShadow.ibOff; + AddRefShadow(out->ib); got |= kShadowIb; + } + if (g_drawShadow.bits & kShadowTopo) { out->topo = g_drawShadow.topo; got |= kShadowTopo; } + if (g_drawShadow.bits & kShadowVs) { + out->vs = g_drawShadow.vs; AddRefShadow(out->vs); got |= kShadowVs; + } + if (g_drawShadow.cbMask == kShadowCbFull) { + memcpy(out->cb, g_drawShadow.cb, sizeof(out->cb)); + memcpy(out->cbFirst, g_drawShadow.cbFirst, sizeof(out->cbFirst)); + memcpy(out->cbNum, g_drawShadow.cbNum, sizeof(out->cbNum)); + for (auto* p : out->cb) AddRefShadow(p); + got |= kShadowCb; + } + if (g_drawShadow.srvMask == kShadowSrvFull) { + memcpy(out->srv, g_drawShadow.srv, sizeof(out->srv)); + for (auto* p : out->srv) AddRefShadow(p); + got |= kShadowSrv; + } + if (g_drawShadow.smpMask == kShadowSmpFull) { + memcpy(out->smp, g_drawShadow.smp, sizeof(out->smp)); + for (auto* p : out->smp) AddRefShadow(p); + got |= kShadowSmp; + } + if (g_drawShadow.bits & kShadowRs) { + out->rs = g_drawShadow.rs; AddRefShadow(out->rs); got |= kShadowRs; + } + if (g_drawShadow.bits & kShadowVp) { + out->nVp = g_drawShadow.nVp; + memcpy(out->vp, g_drawShadow.vp, sizeof(out->vp)); + got |= kShadowVp; + } + if (g_drawShadow.bits & kShadowSc) { + out->nSc = g_drawShadow.nSc; + memcpy(out->sc, g_drawShadow.sc, sizeof(out->sc)); + got |= kShadowSc; + } + return got; +} + +void DrawIdRecord::ShadowSeed(const State& s) { + g_drawShadow.il = s.il; g_drawShadow.bits |= kShadowIl; + memcpy(g_drawShadow.vb, s.vb, sizeof(g_drawShadow.vb)); + memcpy(g_drawShadow.vbStride, s.vbStride, sizeof(g_drawShadow.vbStride)); + memcpy(g_drawShadow.vbOff, s.vbOff, sizeof(g_drawShadow.vbOff)); + g_drawShadow.vbMask = kShadowVbFull; + g_drawShadow.ib = s.ib; g_drawShadow.ibFmt = s.ibFmt; g_drawShadow.ibOff = s.ibOff; g_drawShadow.bits |= kShadowIb; + g_drawShadow.topo = s.topo; g_drawShadow.bits |= kShadowTopo; + g_drawShadow.vs = s.vs; g_drawShadow.bits |= kShadowVs; + memcpy(g_drawShadow.cb, s.cb, sizeof(g_drawShadow.cb)); + memcpy(g_drawShadow.cbFirst, s.cbFirst, sizeof(g_drawShadow.cbFirst)); + memcpy(g_drawShadow.cbNum, s.cbNum, sizeof(g_drawShadow.cbNum)); + g_drawShadow.cbMask = kShadowCbFull; + memcpy(g_drawShadow.srv, s.srv, sizeof(g_drawShadow.srv)); g_drawShadow.srvMask = kShadowSrvFull; + memcpy(g_drawShadow.smp, s.smp, sizeof(g_drawShadow.smp)); g_drawShadow.smpMask = kShadowSmpFull; + g_drawShadow.rs = s.rs; g_drawShadow.bits |= kShadowRs; + g_drawShadow.nVp = s.nVp; memcpy(g_drawShadow.vp, s.vp, sizeof(g_drawShadow.vp)); g_drawShadow.bits |= kShadowVp; + g_drawShadow.nSc = s.nSc; memcpy(g_drawShadow.sc, s.sc, sizeof(g_drawShadow.sc)); g_drawShadow.bits |= kShadowSc; +} + void DrawIdRecord::ReleasePs(int from, int to) { if (!m_ps) return; for (int i = from; i < to; ++i) { @@ -2510,6 +2624,115 @@ void DrawIdRecord::Reset() { m_srvNext = 0; } +DrawIdRecord::ShadowWindow DrawIdRecord::TakeShadowWindow() { + ShadowWindow out = g_shadowWin; + g_shadowWin = ShadowWindow{}; + return out; +} + +void DrawIdRecord::ShadowInvalidate() { + g_drawShadow = DrawShadowState{}; +} + +void DrawIdRecord::NoteInputLayout(ID3D11InputLayout* il) { + g_drawShadow.il = il; + g_drawShadow.bits |= kShadowIl; +} + +void DrawIdRecord::NoteVertexBuffers(UINT start, UINT num, ID3D11Buffer* const* vb, const UINT* stride, const UINT* off) { + if (!vb || !stride || !off || start >= (UINT)kVb) return; + if (num > (UINT)kVb - start) num = (UINT)kVb - start; + for (UINT i = 0; i < num; ++i) { + const UINT slot = start + i; + g_drawShadow.vb[slot] = vb[i]; + g_drawShadow.vbStride[slot] = stride[i]; + g_drawShadow.vbOff[slot] = off[i]; + g_drawShadow.vbMask |= 1u << slot; + } +} + +void DrawIdRecord::NoteIndexBuffer(ID3D11Buffer* ib, DXGI_FORMAT fmt, UINT off) { + g_drawShadow.ib = ib; g_drawShadow.ibFmt = fmt; g_drawShadow.ibOff = off; + g_drawShadow.bits |= kShadowIb; +} + +void DrawIdRecord::NotePrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY topo) { + g_drawShadow.topo = topo; + g_drawShadow.bits |= kShadowTopo; +} + +void DrawIdRecord::NoteVsShader(ID3D11VertexShader* vs) { + g_drawShadow.vs = vs; + g_drawShadow.bits |= kShadowVs; +} + +void DrawIdRecord::NoteVsConstantBuffers(UINT start, UINT num, ID3D11Buffer* const* cb) { + if (start >= (UINT)kVsCb) return; + if (num > (UINT)kVsCb - start) num = (UINT)kVsCb - start; + for (UINT i = 0; i < num; ++i) { + const UINT slot = start + i; + ID3D11Buffer* const buf = cb ? cb[i] : nullptr; + g_drawShadow.cb[slot] = buf; + g_drawShadow.cbFirst[slot] = 0u; + g_drawShadow.cbNum[slot] = buf ? 4096u : 0u; + g_drawShadow.cbMask |= 1u << slot; + } +} + +void DrawIdRecord::NoteVsConstantBuffers1(UINT start, UINT num, ID3D11Buffer* const* cb, const UINT* first, const UINT* count) { + if (start >= (UINT)kVsCb) return; + if (num > (UINT)kVsCb - start) num = (UINT)kVsCb - start; + for (UINT i = 0; i < num; ++i) { + const UINT slot = start + i; + ID3D11Buffer* const buf = cb ? cb[i] : nullptr; + g_drawShadow.cb[slot] = buf; + g_drawShadow.cbFirst[slot] = buf && first ? first[i] : 0u; + g_drawShadow.cbNum[slot] = buf ? (count ? count[i] : 4096u) : 0u; + g_drawShadow.cbMask |= 1u << slot; + } +} + +void DrawIdRecord::NoteVsShaderResources(UINT start, UINT num, ID3D11ShaderResourceView* const* srv) { + if (!srv || start >= (UINT)kVsSrv) return; + if (num > (UINT)kVsSrv - start) num = (UINT)kVsSrv - start; + for (UINT i = 0; i < num; ++i) { + const UINT slot = start + i; + g_drawShadow.srv[slot] = srv[i]; + g_drawShadow.srvMask |= 1u << slot; + } +} + +void DrawIdRecord::NoteVsSamplers(UINT start, UINT num, ID3D11SamplerState* const* smp) { + if (!smp || start >= (UINT)kVsSmp) return; + if (num > (UINT)kVsSmp - start) num = (UINT)kVsSmp - start; + for (UINT i = 0; i < num; ++i) { + const UINT slot = start + i; + g_drawShadow.smp[slot] = smp[i]; + g_drawShadow.smpMask |= 1u << slot; + } +} + +void DrawIdRecord::NoteRsState(ID3D11RasterizerState* rs) { + g_drawShadow.rs = rs; + g_drawShadow.bits |= kShadowRs; +} + +void DrawIdRecord::NoteViewports(UINT n, const D3D11_VIEWPORT* vp) { + if (n > (UINT)kVp) n = (UINT)kVp; + g_drawShadow.nVp = n; + if (n) memcpy(g_drawShadow.vp, vp, sizeof(D3D11_VIEWPORT) * n); + if (n < (UINT)kVp) memset(g_drawShadow.vp + n, 0, sizeof(D3D11_VIEWPORT) * ((UINT)kVp - n)); + g_drawShadow.bits |= kShadowVp; +} + +void DrawIdRecord::NoteScissorRects(UINT n, const D3D11_RECT* sc) { + if (n > (UINT)kSc) n = (UINT)kSc; + g_drawShadow.nSc = n; + if (n) memcpy(g_drawShadow.sc, sc, sizeof(D3D11_RECT) * n); + if (n < (UINT)kSc) memset(g_drawShadow.sc + n, 0, sizeof(D3D11_RECT) * ((UINT)kSc - n)); + g_drawShadow.bits |= kShadowSc; +} + void DrawIdRecord::Shutdown() { Reset(); m_mirrorSrv.Reset(); m_mirror.Reset(); m_mirrorBytes = 0; @@ -2652,29 +2875,52 @@ bool DrawIdRecord::Record(ID3D11DeviceContext1* ctx, UINT ic, UINT inst, UINT si memset(m_st, 0, sizeof(State) * kMax); } State& s = m_st[m_n]; - ctx->IAGetInputLayout(&s.il); - ctx->IAGetVertexBuffers(0, kVb, s.vb, s.vbStride, s.vbOff); - ctx->IAGetIndexBuffer(&s.ib, &s.ibFmt, &s.ibOff); - ctx->IAGetPrimitiveTopology(&s.topo); - ctx->VSGetShader(&s.vs, nullptr, nullptr); - ctx->VSGetConstantBuffers1(0, kVsCb, s.cb, s.cbFirst, s.cbNum); - ctx->VSGetShaderResources(0, kVsSrv, s.srv); - ctx->VSGetSamplers(0, kVsSmp, s.smp); - ctx->RSGetState(&s.rs); - { + memset(&s, 0, sizeof(s)); + const uint32_t shadow = ShadowCapture(&s); + ++g_shadowWin.calls; + auto markShadow = [&](int field, bool hit) { + if (hit) ++g_shadowWin.shadow[field]; + else ++g_shadowWin.fallback[field]; + }; + markShadow(kShIl, (shadow & kShadowIl) != 0); + markShadow(kShVb, (shadow & kShadowVb) != 0); + markShadow(kShIb, (shadow & kShadowIb) != 0); + markShadow(kShTopo, (shadow & kShadowTopo) != 0); + markShadow(kShVs, (shadow & kShadowVs) != 0); + markShadow(kShCb, (shadow & kShadowCb) != 0); + markShadow(kShSrv, (shadow & kShadowSrv) != 0); + markShadow(kShSmp, (shadow & kShadowSmp) != 0); + markShadow(kShRs, (shadow & kShadowRs) != 0); + markShadow(kShVp, (shadow & kShadowVp) != 0); + markShadow(kShSc, (shadow & kShadowSc) != 0); + if (!(shadow & kShadowIl)) ctx->IAGetInputLayout(&s.il); + if (!(shadow & kShadowVb)) ctx->IAGetVertexBuffers(0, kVb, s.vb, s.vbStride, s.vbOff); + if (!(shadow & kShadowIb)) ctx->IAGetIndexBuffer(&s.ib, &s.ibFmt, &s.ibOff); + if (!(shadow & kShadowTopo)) ctx->IAGetPrimitiveTopology(&s.topo); + if (!(shadow & kShadowVs)) ctx->VSGetShader(&s.vs, nullptr, nullptr); + if (!(shadow & kShadowCb)) ctx->VSGetConstantBuffers1(0, kVsCb, s.cb, s.cbFirst, s.cbNum); + if (!(shadow & kShadowSrv)) ctx->VSGetShaderResources(0, kVsSrv, s.srv); + if (!(shadow & kShadowSmp)) ctx->VSGetSamplers(0, kVsSmp, s.smp); + if (!(shadow & kShadowRs)) ctx->RSGetState(&s.rs); + if (!(shadow & kShadowVp) || !(shadow & kShadowSc)) { D3D11_VIEWPORT vps[D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE]; UINT nv = D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE; - ctx->RSGetViewports(&nv, vps); - if (nv > (UINT)kVp) nv = kVp; - s.nVp = nv; - memcpy(s.vp, vps, sizeof(D3D11_VIEWPORT) * nv); + if (!(shadow & kShadowVp)) { + ctx->RSGetViewports(&nv, vps); + if (nv > (UINT)kVp) nv = kVp; + s.nVp = nv; + memcpy(s.vp, vps, sizeof(D3D11_VIEWPORT) * nv); + } D3D11_RECT scs[D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE]; UINT ns = D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE; - ctx->RSGetScissorRects(&ns, scs); - if (ns > (UINT)kSc) ns = kSc; - s.nSc = ns; - memcpy(s.sc, scs, sizeof(D3D11_RECT) * ns); + if (!(shadow & kShadowSc)) { + ctx->RSGetScissorRects(&ns, scs); + if (ns > (UINT)kSc) ns = kSc; + s.nSc = ns; + memcpy(s.sc, scs, sizeof(D3D11_RECT) * ns); + } } + ShadowSeed(s); s.ic = ic; s.inst = inst; s.si = si; s.sinst = sinst; s.bv = bv; s.instanced = instanced; // v0.10.0 phase 10: the pixel-shader state of a forward draw (the batched forward-depth re-draw needs the game's alpha) if (m_capPs && !m_ps && m_n == 0) { @@ -3420,6 +3666,8 @@ float DrawIdMv::SnapPx() { return g_snapPx; } DrawIdMv::DrawIdMv() {} DrawIdMv::~DrawIdMv() { + PrepWait(); + PrepStopThread(); delete[] m_prevFull; delete[] m_prevLoose; delete[] m_prevFlags; delete[] m_curFull; delete[] m_curLoose; delete[] m_curFlags; delete[] m_ent; delete[] m_orph; @@ -3434,6 +3682,51 @@ DrawIdMv::~DrawIdMv() { delete[] m_dump; } +bool DrawIdMv::PrepEnsureThread() { + if (m_prepThread.joinable()) return true; + if (m_prepThreadFailed) return false; + try { + m_prepStop = false; + m_prepJobReady = false; + m_prepJobRunning = false; + m_prepThread = std::thread([this]() { PrepWorkerLoop(); }); + return true; + } catch (...) { + m_prepThreadFailed = true; + Log("MV draw-ids: prepare worker thread create failed -- prepare stays on the render thread"); + return false; + } +} + +void DrawIdMv::PrepStopThread() { + { + std::lock_guard lock(m_prepMutex); + m_prepStop = true; + m_prepKickCv.notify_one(); + } + if (m_prepThread.joinable()) m_prepThread.join(); + m_prepStop = false; + m_prepJobReady = false; + m_prepJobRunning = false; + m_prepWorkN = 0; +} + +void DrawIdMv::PrepWorkerLoop() { + std::unique_lock lock(m_prepMutex); + for (;;) { + m_prepKickCv.wait(lock, [this]() { return m_prepStop || m_prepJobReady; }); + if (m_prepStop) break; + const uint32_t ne = m_prepWorkN; + m_prepJobReady = false; + m_prepJobRunning = true; + lock.unlock(); + PrepWorker(ne); + lock.lock(); + m_prepJobRunning = false; + m_prepDoneCv.notify_all(); + } +} + bool DrawIdMv::Init(ID3D11Device* dev) { if (m_ready) return true; if (m_failed || !dev) return false; @@ -3849,6 +4142,362 @@ void DrawIdMv::Poll(ID3D11DeviceContext* ctx) { } } +void DrawIdMv::PrepWorker(uint32_t ne) { + auto byKey = [](const Ent& a, const Ent& b) { return a.key < b.key || (a.key == b.key && a.sideIdx < b.sideIdx); }; + constexpr uint32_t kGroupCap = 1024; + const int64_t tFull = Qpc(); + std::sort(m_ent, m_ent + ne, byKey); + m_nGroups = 0; m_nList = 0; + uint32_t no = 0; + m_prepBigGroupLast = 0; + auto emit = [&](const Ent* e, uint32_t a, uint32_t b, bool loose) { + uint32_t c = 0, p = 0; + for (uint32_t q = a; q < b; ++q) { if (e[q].sideIdx & 0x80000000u) ++p; else ++c; } + if (!c || !p) return false; + if (c > kGroupCap || p > kGroupCap) { + m_prepBigGroupLast += c; + for (uint32_t q = a; q < b; ++q) if (!(e[q].sideIdx & 0x80000000u)) m_curBig[e[q].sideIdx] = 1; + return true; + } + uint32_t* g = &m_groupData[m_nGroups * 4u]; + g[0] = m_nList; g[1] = c; + uint32_t r = 0; + for (uint32_t q = a; q < b; ++q) { + if (e[q].sideIdx & 0x80000000u) continue; + const uint32_t i = e[q].sideIdx; + m_listData[m_nList++] = i; + m_curGroup[i] = m_nGroups; m_curRank[i] = r++; m_curLooseG[i] = loose ? 1u : 0u; + } + g[2] = m_nList; g[3] = p; + r = 0; + for (uint32_t q = a; q < b; ++q) { + if (!(e[q].sideIdx & 0x80000000u)) continue; + const uint32_t j = e[q].sideIdx & 0x7FFFFFFFu; + m_listData[m_nList++] = j; + m_prevGroup[j] = m_nGroups; m_prevRank[j] = r++; + } + ++m_nGroups; + return true; + }; + for (uint32_t a = 0; a < ne;) { + uint32_t b = a + 1; + while (b < ne && m_ent[b].key == m_ent[a].key) ++b; + if (!emit(m_ent, a, b, false)) { + for (uint32_t q = a; q < b; ++q) { + const uint32_t si = m_ent[q].sideIdx; + const uint64_t lk = (si & 0x80000000u) ? m_prevLoose[si & 0x7FFFFFFFu] : m_curLoose[si]; + m_orph[no++] = { lk, si }; + } + } + a = b; + } + m_prepFullTicksLast = (uint64_t)(Qpc() - tFull); + const int64_t tLoose = Qpc(); + std::sort(m_orph, m_orph + no, byKey); + for (uint32_t a = 0; a < no;) { + uint32_t b = a + 1; + while (b < no && m_orph[b].key == m_orph[a].key) ++b; + emit(m_orph, a, b, true); + a = b; + } + m_prepLooseTicksLast = (uint64_t)(Qpc() - tLoose); +} + +void DrawIdMv::PrepWait() { + std::unique_lock lock(m_prepMutex); + if (!m_prepThread.joinable()) return; + m_prepDoneCv.wait(lock, [this]() { return !m_prepJobReady && !m_prepJobRunning; }); +} + +bool DrawIdMv::PrepareBegin(ID3D11DeviceContext* ctx, const DrawIdRecord* rec, uint32_t fullW, uint32_t fullH, + const DrawIdRecord* fwd, float nearM, bool freshMedoid, uint32_t noMedoid, bool idsBound) { + PrepWait(); + m_prepPending = false; + m_prepN = 0; m_prepNG = 0; m_prepNF = 0; + if (m_dumpPrep) { m_dumpPrep = false; m_dumpArm = true; } + if (!ctx || !m_ready) return false; + const int64_t tPoll = Qpc(); + Poll(ctx); + m_stats.pollTicks += (uint64_t)(Qpc() - tPoll); + const int64_t t0 = Qpc(); + if (!rec || !rec->Usable()) { + m_prevN = 0; + if (!m_quiet) StaticClear(kStatClrRecord); + return false; + } + const uint32_t nG = (uint32_t)rec->Count(); + m_prepStatN = 0; + if (!m_quiet && g_statFrames > 0u) { + if (!m_prepStatKey) { + m_prepStatKey = new (std::nothrow) uint64_t[kMax]; + m_rbStatKey = new (std::nothrow) uint64_t[(size_t)kRb * kMax]; + if (!m_prepStatKey || !m_rbStatKey) { delete[] m_prepStatKey; delete[] m_rbStatKey; m_prepStatKey = m_rbStatKey = nullptr; } + } + if (m_prepStatKey && !m_statReg) + for (DrawIdMv*& u : g_statUnit) if (!u) { u = this; m_statReg = true; break; } + if (m_prepStatKey) { + const uint8_t notCand = DrawIdRecord::kFInstanced | DrawIdRecord::kFNoMvp | DrawIdRecord::kFCabin; + for (uint32_t i = 0; i < nG; ++i) m_prepStatKey[i] = (rec->Flags((int)i) & notCand) ? 0ull : rec->FullKey((int)i); + m_prepStatN = nG; + } + } + uint32_t nF = 0; + if (fwd && fwd->Count() > 0) { + if (fwd->Usable() && fwd->Segments() > 0 && fwd->PendingReplay() == 0 && !fwd->ReplayFailed() && + fwd->IdBase() == nG && nG < (uint32_t)kMax) { + nF = (uint32_t)fwd->Count(); + if (nF > (uint32_t)kMax - nG) nF = (uint32_t)kMax - nG; + } else ++m_stats.fwdDropped; + } + const uint32_t n = nG + nF; + auto src = [&](uint32_t i) -> const DrawIdRecord* { return i < nG ? rec : fwd; }; + auto idx = [&](uint32_t i) -> int { return i < nG ? (int)i : (int)(i - nG); }; + const uint32_t m = m_prevN; + const uint8_t kNoPair = DrawIdRecord::kFInstanced | DrawIdRecord::kFNoMvp; + uint32_t elig = 0, ne = 0; + m_prepInstN = 0; + for (uint32_t i = 0; i < n; ++i) { + if (g_instReplay && i < nG && (rec->Flags((int)i) & kNoPair) && m_prepInstN < (uint32_t)kRbInst) { + m_prepInstIdx[m_prepInstN] = i; m_prepInstKey[m_prepInstN] = rec->ShapeKey((int)i); ++m_prepInstN; + } + m_curFull[i] = src(i)->FullKey(idx(i)); + m_curLoose[i] = src(i)->LooseKey(idx(i)); + m_curFlags[i] = (uint8_t)(src(i)->Flags(idx(i)) | (i >= nG ? DrawIdRecord::kFFwd : 0)); + m_curGroup[i] = kNone; m_curRank[i] = 0; m_curLooseG[i] = 0; m_curBig[i] = 0; + if (m_curFlags[i] & kNoPair) continue; + ++elig; + m_ent[ne++] = { m_curFull[i], i }; + } + for (uint32_t j = 0; j < m; ++j) { + m_prevGroup[j] = kNone; m_prevRank[j] = 0; + if (m_prevFlags[j] & kNoPair) continue; + m_ent[ne++] = { m_prevFull[j], 0x80000000u | j }; + } + const uint64_t scanTicks = (uint64_t)(Qpc() - t0); + m_stats.prepScanTicks += scanTicks; + m_prepRec = rec; m_prepFwd = fwd; + m_prepReqW = fullW; m_prepReqH = fullH; + m_prepNearM = nearM; m_prepFreshMedoid = freshMedoid; m_prepNoMedoid = noMedoid; + m_prepIdsBoundReq = idsBound; + m_prepCpuBaseTicks = scanTicks; + m_prepFullTicksLast = 0; m_prepLooseTicksLast = 0; m_prepBigGroupLast = 0; + m_prepN = n; m_prepNG = nG; m_prepNF = nF; m_prepElig = elig; m_prepHist = m != 0; + m_prepPending = true; + if (PrepEnsureThread()) { + std::lock_guard lock(m_prepMutex); + m_prepWorkN = ne; + m_prepJobReady = true; + m_prepKickCv.notify_one(); + } else { + PrepWorker(ne); + } + return true; +} + +uint32_t DrawIdMv::PrepareEnd(ID3D11DeviceContext* ctx) { + if (!m_prepPending || !ctx || !m_ready) return 0; + const int64_t t0 = Qpc(); + const int64_t tWait = Qpc(); + PrepWait(); + m_stats.prepWaitTicks += (uint64_t)(Qpc() - tWait); + m_prepPending = false; + m_stats.prepFullTicks += m_prepFullTicksLast; + m_stats.prepLooseTicks += m_prepLooseTicksLast; + m_stats.bigGroup += m_prepBigGroupLast; + const DrawIdRecord* rec = m_prepRec; + const DrawIdRecord* fwd = m_prepFwd; + const uint32_t fullW = m_prepReqW, fullH = m_prepReqH; + const float nearM = m_prepNearM; + const bool freshMedoid = m_prepFreshMedoid; + const uint32_t noMedoid = m_prepNoMedoid; + const bool idsBound = m_prepIdsBoundReq; + const uint32_t n = m_prepN, nG = m_prepNG, nF = m_prepNF, m = m_prevN, elig = m_prepElig; + const uint8_t kNoPair = DrawIdRecord::kFInstanced | DrawIdRecord::kFNoMvp; + auto src = [&](uint32_t i) -> const DrawIdRecord* { return i < nG ? rec : fwd; }; + auto idx = [&](uint32_t i) -> int { return i < nG ? (int)i : (int)(i - nG); }; + if (!rec || !rec->Usable()) { m_prevN = 0; return 0; } + if (!m_prepVs) { + m_prepVs = new (std::nothrow) const void*[kMax]; + m_prepHint = new (std::nothrow) uint8_t[kMax]; + m_rbVs = new (std::nothrow) const void*[(size_t)kRb * kMax]; + m_rbHint = new (std::nothrow) uint8_t[(size_t)kRb * kMax]; + if (!m_prepVs || !m_prepHint || !m_rbVs || !m_rbHint) { + delete[] m_prepVs; delete[] m_prepHint; delete[] m_rbVs; delete[] m_rbHint; + m_prepVs = nullptr; m_prepHint = nullptr; m_rbVs = nullptr; m_rbHint = nullptr; + } + } + const bool layOn = !LayoutOff() && m_prepVs; + const int64_t tUpload = Qpc(); + D3D11_MAPPED_SUBRESOURCE mp{}; + if (FAILED(ctx->Map(m_tab.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mp))) { m_prevN = 0; return 0; } + { + uint32_t* t = (uint32_t*)mp.pData; + uint64_t looseN = 0, noGroup = 0; + const void* lastVs = nullptr; + uint32_t lastHint = 0; + for (uint32_t i = 0; i < n; ++i) { + const uint8_t fl = m_curFlags[i]; + uint32_t f = 0; + if (fl & DrawIdRecord::kFInstanced) f |= 1u; + if (fl & DrawIdRecord::kFNoMvp) f |= 2u; + if (fl & DrawIdRecord::kFCabin) f |= 4u; + if (m_curLooseG[i]) { f |= 256u; ++looseN; } + if (fl & DrawIdRecord::kFFwd) f |= 512u; + if (layOn) { + const void* vs = (fl & kNoPair) ? nullptr : src(i)->Vs(idx(i)); + if (vs != lastVs) { + const VsLay* le = VsLayFind(vs, false); + lastHint = (le && le->row >= 4u && le->row <= 8u) ? le->row : 0u; + lastVs = vs; + } + const uint32_t hint = vs ? lastHint : 0u; + const UINT rows = src(i)->MirrorRows(idx(i)); + f |= ((rows < 15u ? rows : 15u) << 16) | (hint << 20); + m_prepVs[i] = vs; m_prepHint[i] = (uint8_t)hint; + } + if (!(fl & kNoPair) && m_curGroup[i] == kNone) ++noGroup; + const float vmin = src(i)->VpMin(idx(i)), vmax = src(i)->VpMax(idx(i)); + t[i * 8 + 0] = src(i)->MirrorOff(idx(i)); + t[i * 8 + 1] = m_curGroup[i]; + t[i * 8 + 2] = f; + t[i * 8 + 3] = m_curRank[i]; + memcpy(&t[i * 8 + 4], &vmin, 4); + memcpy(&t[i * 8 + 5], &vmax, 4); + t[i * 8 + 6] = (src(i)->IndexCount(idx(i)) & 0xFFFFFFu) | ((src(i)->InstanceCount(idx(i)) < 255u ? src(i)->InstanceCount(idx(i)) : 255u) << 24); + t[i * 8 + 7] = src(i)->VpJitterPacked(idx(i)); + } + m_stats.looseDraws += looseN; + m_stats.noGroup += m != 0 ? noGroup : 0; + } + ctx->Unmap(m_tab.Get(), 0); + if (m) { + if (FAILED(ctx->Map(m_ptab.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mp))) { m_prevN = 0; return 0; } + uint32_t* t = (uint32_t*)mp.pData; + for (uint32_t j = 0; j < m; ++j) { t[j * 4 + 0] = m_prevGroup[j]; t[j * 4 + 1] = m_prevFlags[j]; t[j * 4 + 2] = m_prevRank[j]; t[j * 4 + 3] = 0; } + ctx->Unmap(m_ptab.Get(), 0); + } + if (m_nGroups) { + if (FAILED(ctx->Map(m_groups.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mp))) { m_prevN = 0; return 0; } + memcpy(mp.pData, m_groupData, (size_t)m_nGroups * 16u); + ctx->Unmap(m_groups.Get(), 0); + if (FAILED(ctx->Map(m_lists.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mp))) { m_prevN = 0; return 0; } + memcpy(mp.pData, m_listData, (size_t)m_nList * 4u); + ctx->Unmap(m_lists.Get(), 0); + } + uint32_t cand[kCandWorld + kCandCabin] = {}; + uint32_t candW = 0, candC = 0; + if (m && m_nGroups) { + uint32_t* tmp = m_candTmp; + for (int L = 0; L < 2; ++L) { + uint32_t ne2 = 0; + for (uint32_t i = 0; i < nG; ++i) { + const uint8_t fl = m_curFlags[i]; + if ((fl & kNoPair) || m_curGroup[i] == kNone) continue; + if (((fl & DrawIdRecord::kFCabin) != 0) != (L == 1)) continue; + tmp[ne2++] = i; + } + const uint32_t capC = g_consCands / 2u < (uint32_t)kCandCabin ? g_consCands / 2u : (uint32_t)kCandCabin; + const uint32_t cap = L == 0 ? g_consCands : capC; + const uint32_t take = ne2 < cap ? ne2 : cap; + for (uint32_t j = 0; j < take; ++j) cand[(L == 0 ? 0 : candW) + j] = tmp[(uint32_t)(((uint64_t)j * ne2) / take)]; + if (L == 0) candW = take; else candC = take; + } + } + if (FAILED(ctx->Map(m_cb.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mp))) { m_prevN = 0; return 0; } + { + const uint32_t counts[4] = { n, m, m_nGroups, nG }; + const float params[4] = { g_maxM, g_snapPx, (float)fullW, (float)fullH }; + const float params2[4] = { g_trackM, g_snapPx > 0.05f ? g_snapPx : 0.05f, nearM, g_attachM }; + const uint32_t cons[4] = { candW, candC, (uint32_t)kConsMin, + (g_consensus ? 1u : 0u) | (freshMedoid ? 2u : 0u) | (g_twin ? 4u : 0u) | ((noMedoid & 3u) << 3) }; + memcpy(mp.pData, counts, sizeof(counts)); + memcpy((uint8_t*)mp.pData + 16, params, sizeof(params)); + memcpy((uint8_t*)mp.pData + 32, params2, sizeof(params2)); + memcpy((uint8_t*)mp.pData + 48, cons, sizeof(cons)); + memcpy((uint8_t*)mp.pData + 64, cand, sizeof(cand)); + const float par[4] = { (g_parentMut & 2) ? 1e30f : kParDepthTol, kParMinVotes, kParMinShare, kParVoteCap }; + const uint32_t parU[4] = { (g_parent ? 1u : 0u) | ((g_parentMut & 1) ? 2u : 0u) | (g_instancedVote ? 4u : 0u) | + (g_voteRes > kParStride ? 8u : 0u) | (rec->StaticSkipped() > 0 ? 16u : 0u), + g_voteRes, g_parReach ? g_parReach : kParReachPx, kParMarchBudget }; + memcpy((uint8_t*)mp.pData + 64 + sizeof(cand), par, sizeof(par)); + memcpy((uint8_t*)mp.pData + 64 + sizeof(cand) + 16, parU, sizeof(parU)); + const float par2[4] = { (g_parentMut & 2) ? 1e30f : kParEdgeTol, kParFineSamples, + g_marchCap ? (float)g_marchCap * (5.0f / 6.0f) : 0.0f, + g_marchCap ? (float)g_marchCap * (1.0f / 6.0f) : 0.0f }; + memcpy((uint8_t*)mp.pData + 64 + sizeof(cand) + 32, par2, sizeof(par2)); + uint32_t clip[4] = { 0u, 0u, fullW, fullH }; + if (rec->Clipped()) { + const D3D11_RECT r = rec->ClipRect(); + const uint32_t x0 = r.left > 0 ? (uint32_t)r.left : 0u, y0 = r.top > 0 ? (uint32_t)r.top : 0u; + const uint32_t x1 = r.right > 0 ? (uint32_t)r.right : 0u, y1 = r.bottom > 0 ? (uint32_t)r.bottom : 0u; + clip[0] = (x0 < fullW ? x0 : fullW) & ~3u; clip[1] = (y0 < fullH ? y0 : fullH) & ~3u; + clip[2] = x1 < fullW ? x1 : fullW; clip[3] = y1 < fullH ? y1 : fullH; + if (clip[2] < clip[0]) clip[2] = clip[0]; + if (clip[3] < clip[1]) clip[3] = clip[1]; + } + const uint32_t fwdMode[4] = { (nF && fwd) ? fwd->Shift() : 0u, g_consMinCabin, g_cabinSmall, 0u }; + const bool pickOn = m && m_nGroups && (candW + candC) && g_consensus; + const bool twinOn = g_twin && m, attachOn = g_attachM > 0.0f && m; + const bool parentOn = g_parent && m && idsBound && fullW && fullH; + ID3D11Device* devC = nullptr; + if (parentOn && g_voteCompact && m_csParCollect && m_csParMarch && !m_parListUav && !m_parListFailed) ctx->GetDevice(&devC); + if (devC) { EnsureParList(devC); devC->Release(); } + const bool compact = parentOn && g_voteCompact && m_parListUav && m_marchArgsUav; + const uint32_t fold = (pickOn ? 1u : 0u) | ((twinOn || attachOn) ? 2u : 0u) | (parentOn ? 4u : 0u) | (twinOn ? 8u : 0u) | + (attachOn ? 16u : 0u) | (compact ? 32u : 0u); + m_prepCompact = compact; + const uint32_t S = g_voteRes, cw = clip[2] - clip[0], chh = clip[3] - clip[1]; + m_prepGrid = ((cw + S - 1u) / S + 7u) / 8u * (((chh + S - 1u) / S + 7u) / 8u); + uint32_t epsBits = 0; + memcpy(&epsBits, &g_statEps, 4); + const uint32_t holdBits = ((parentOn && m_uhold[0] && m_uholdValid[1 - m_ping]) ? 1u : 0u) | + ((g_parentMut & 4) ? 2u : 0u) | ((g_parentMut & 8) ? 4u : 0u) | ((g_parentMut & 16) ? 8u : 0u) | + ((g_parentMut & 32) ? 16u : 0u) | ((g_parentMut & 64) ? 32u : 0u); + const uint32_t foldU[4] = { fold, g_parMaxListed, epsBits, holdBits }; + memcpy((uint8_t*)mp.pData + 64 + sizeof(cand) + 48, clip, sizeof(clip)); + memcpy((uint8_t*)mp.pData + 64 + sizeof(cand) + 64, fwdMode, sizeof(fwdMode)); + memcpy((uint8_t*)mp.pData + 64 + sizeof(cand) + 80, foldU, sizeof(foldU)); + memcpy(m_prepClip, clip, sizeof(clip)); + m_prepFold = fold; + m_prepIds = idsBound; + } + ctx->Unmap(m_cb.Get(), 0); + m_stats.prepUploadTicks += (uint64_t)(Qpc() - tUpload); + m_prepW = fullW; m_prepH = fullH; + if (m_dumpArm && !m_dumpPending && m_dump) { + m_dumpArm = false; m_dumpPrep = true; m_dumpN = 0; m_dumpSkipped = 0; + m_dumpNG = nG; m_dumpNAll = n; m_dumpM = m; m_dumpW = fullW; m_dumpH = fullH; m_dumpHist = m != 0; + for (uint32_t i = 0; i < n; ++i) { + const DrawIdRecord* r = src(i); + const int k = idx(i); + const UINT ic = r->IndexCount(k); + if (m_dumpN >= (uint32_t)kMax) { ++m_dumpSkipped; continue; } + DumpEnt& d = m_dump[m_dumpN++]; + d.big = m_curBig[i]; d.i = i; d.ic = ic; r->CbWindow(k, &d.cbFirst, &d.cbNum); + d.mirrorOff = r->MirrorOff(k); d.group = m_curGroup[i]; + d.gCur = d.group != kNone ? m_groupData[d.group * 4u + 1u] : 0u; + d.gPrev = d.group != kNone ? m_groupData[d.group * 4u + 3u] : 0u; + d.full = m_curFull[i]; d.loose = m_curLoose[i]; + d.flags = (uint8_t)(m_curFlags[i] | (m_curLooseG[i] ? 0x80 : 0)); + d.ps = r->Ps(k); d.psInfo = DrawIdRecord::PsInfo(d.ps); + d.vpMin = r->VpMin(k); d.vpMax = r->VpMax(k); d.inst = r->InstanceCount(k); + d.vs = r->Vs(k); d.rows = (uint8_t)r->MirrorRows(k); d.hint = layOn ? m_prepHint[i] : 0u; + } + } + m_prepLay = layOn; + m_prepCandW = candW; m_prepCandC = candC; m_prepElig = elig; m_prepHist = m != 0; + if (noMedoid & 1u) ++m_stats.noMedoidFrames[0]; + if (noMedoid & 2u) ++m_stats.noMedoidFrames[1]; + ++m_stats.frames; + m_stats.draws += n; + m_stats.fwdDraws += nF; + m_stats.instanced += (uint64_t)rec->Instanced(); + m_stats.noMvp += (uint64_t)rec->NoMvp(); + if (m) ++m_stats.histFrames; + m_stats.cpuTicks += m_prepCpuBaseTicks + (uint64_t)(Qpc() - t0); + return n; +} + uint32_t DrawIdMv::Prepare(ID3D11DeviceContext* ctx, const DrawIdRecord* rec, uint32_t fullW, uint32_t fullH, const DrawIdRecord* fwd, float nearM, bool freshMedoid, uint32_t noMedoid, bool idsBound) { m_prepN = 0; m_prepNG = 0; m_prepNF = 0; diff --git a/src/draw_ids.h b/src/draw_ids.h index 4f186bb..798a4f8 100644 --- a/src/draw_ids.h +++ b/src/draw_ids.h @@ -102,7 +102,10 @@ #include #include +#include #include +#include +#include // Non-blocking GPU timestamp span (same pattern as SceneDlaa's SnapTimer): Begin / End around GPU work, polled with // DONOTFLUSH; Take() returns the window average since the last Take. @@ -289,9 +292,29 @@ class DrawIdRecord { const void* Ps(int i) const; enum : uint8_t { kPsScanned = 1, kPsDiscard = 2, kPsDepth = 4 }; typedef uint8_t (*PsInfoFn)(const void* ps); + enum : int { kShadowFieldCount = 11 }; + struct ShadowWindow { + uint64_t calls = 0; + uint64_t shadow[kShadowFieldCount] = {}; + uint64_t fallback[kShadowFieldCount] = {}; + }; static void NoteGamePs(const void* ps) { s_gamePs = ps; } static void SetPsInfoFn(PsInfoFn fn) { s_psInfo = fn; } static uint8_t PsInfo(const void* ps) { return s_psInfo ? s_psInfo(ps) : 0; } + static ShadowWindow TakeShadowWindow(); + static void ShadowInvalidate(); + static void NoteInputLayout(ID3D11InputLayout* il); + static void NoteVertexBuffers(UINT start, UINT num, ID3D11Buffer* const* vb, const UINT* stride, const UINT* off); + static void NoteIndexBuffer(ID3D11Buffer* ib, DXGI_FORMAT fmt, UINT off); + static void NotePrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY topo); + static void NoteVsShader(ID3D11VertexShader* vs); + static void NoteVsConstantBuffers(UINT start, UINT num, ID3D11Buffer* const* cb); + static void NoteVsConstantBuffers1(UINT start, UINT num, ID3D11Buffer* const* cb, const UINT* first, const UINT* count); + static void NoteVsShaderResources(UINT start, UINT num, ID3D11ShaderResourceView* const* srv); + static void NoteVsSamplers(UINT start, UINT num, ID3D11SamplerState* const* smp); + static void NoteRsState(ID3D11RasterizerState* rs); + static void NoteViewports(UINT n, const D3D11_VIEWPORT* vp); + static void NoteScissorRects(UINT n, const D3D11_RECT* sc); ID3D11ShaderResourceView* MirrorSrv() const { return m_mirrorSrv.Get(); } bool Usable() const { return m_n > 0 && !m_flushFail && !FlushPending() && m_mirrorSrv; } int Instanced() const { return m_nInst; } @@ -334,6 +357,8 @@ class DrawIdRecord { // v0.10.0 phase 22: copies cb0 rows 0 .. min(want, the ring's end) / 16 - 1 of the window at rowBase (row 4..7 must exist); // returns the mirror byte offset of row 4 (or 0xFFFFFFFF) and *rows = the rows mirrored from row 0 UINT AddRing(ID3D11Buffer* cb, UINT rowBase, UINT wantBytes, UINT* rows); + uint32_t ShadowCapture(State* out); + void ShadowSeed(const State& s); bool FlushRing(ID3D11DeviceContext* ctx, int k); bool EnsureMirror(ID3D11DeviceContext* ctx, UINT need); void ReleaseRange(int from, int to); @@ -482,6 +507,10 @@ class DrawIdMv { // ConsHealthy): pass A wrote nothing for it, so a consensus found here also writes that layer's info rows (world: R_ego = // the camera R, InvRow3 from the winning draw); no consensus = the solve buffer keeps the last frame's R of that layer. // v0.10.0 phase 9: `idsBound` = Dispatch will get the pass's draw-id target (the rigid-parent bit of the folded dispatch) + bool PrepareBegin(ID3D11DeviceContext* ctx, const DrawIdRecord* rec, uint32_t fullW, uint32_t fullH, + const DrawIdRecord* fwd = nullptr, float nearM = 30.0f, bool freshMedoid = true, + uint32_t noMedoid = 0, bool idsBound = true); + uint32_t PrepareEnd(ID3D11DeviceContext* ctx); uint32_t Prepare(ID3D11DeviceContext* ctx, const DrawIdRecord* rec, uint32_t fullW, uint32_t fullH, const DrawIdRecord* fwd = nullptr, float nearM = 30.0f, bool freshMedoid = true, uint32_t noMedoid = 0, bool idsBound = true); @@ -604,7 +633,13 @@ class DrawIdMv { uint64_t trackRejects = 0; // mover verdicts of ambiguous groups without a consistent track uint64_t worldPx = 0, idPx = 0, moverPx = 0, unpairedPx = 0; // pass B, 1-in-64 grid uint64_t lowPairFrames = 0; // landed frames with history whose paired share was < 50 % - uint64_t cpuTicks = 0; // QPC ticks in Prepare + uint64_t cpuTicks = 0; // QPC ticks in Prepare visible on the render thread + uint64_t pollTicks = 0; // QPC ticks in Poll (all callers) + uint64_t prepScanTicks = 0; // QPC ticks in Prepare before the key sorts (record scan / setup) + uint64_t prepWaitTicks = 0; // QPC ticks spent waiting for the worker in PrepareEnd + uint64_t prepFullTicks = 0; // QPC ticks in Prepare full-key sort + grouping + uint64_t prepLooseTicks = 0; // QPC ticks in Prepare loose-key sort + regrouping + uint64_t prepUploadTicks = 0; // QPC ticks in Prepare upload maps / cbuffer fill // v0.10.0 phase 3: consensus camera R per layer (0 world, 1 cabin; read back with the diagnostics, frames that landed) uint64_t consFrames[2] = {}, consFallback[2] = {}, consCluster[2] = {}; uint64_t consFallCluster[2] = {}; // v0.10.0 phase 18: the largest agreeing cluster in the fallback frames (sum) @@ -706,6 +741,11 @@ class DrawIdMv { private: void Poll(ID3D11DeviceContext* ctx); + bool PrepEnsureThread(); + void PrepStopThread(); + void PrepWorkerLoop(); + void PrepWorker(uint32_t ne); + void PrepWait(); bool m_ready = false, m_failed = false; Microsoft::WRL::ComPtr m_csGather, m_csMatch, m_csResolve; Microsoft::WRL::ComPtr m_csPair, m_csVote, m_csPick; // v0.10.0 phase 3 (consensus camera R) @@ -772,6 +812,26 @@ class DrawIdMv { bool m_prepLay = false; // m_prepVs / m_prepHint hold the prepared pass (layout detection on) bool m_statReg = false; // registered for PollMain (the destructor unregisters) uint64_t m_rbCtr = 0; + const DrawIdRecord* m_prepRec = nullptr; + const DrawIdRecord* m_prepFwd = nullptr; + uint32_t m_prepReqW = 0, m_prepReqH = 0; + float m_prepNearM = 30.0f; + uint32_t m_prepNoMedoid = 0; + bool m_prepFreshMedoid = true; + bool m_prepPending = false; + bool m_prepIdsBoundReq = false; + bool m_prepThreadFailed = false; + bool m_prepJobReady = false; + bool m_prepJobRunning = false; + bool m_prepStop = false; + uint32_t m_prepWorkN = 0; + uint64_t m_prepCpuBaseTicks = 0; + uint64_t m_prepFullTicksLast = 0, m_prepLooseTicksLast = 0; + uint64_t m_prepBigGroupLast = 0; + std::thread m_prepThread; + std::mutex m_prepMutex; + std::condition_variable m_prepKickCv; + std::condition_variable m_prepDoneCv; // previous committed pass of this eye (CPU keys) uint32_t m_prevN = 0; uint64_t* m_prevFull = nullptr; diff --git a/src/inject.cpp b/src/inject.cpp index 77d0364..41c61ff 100644 --- a/src/inject.cpp +++ b/src/inject.cpp @@ -1660,9 +1660,31 @@ HRESULT STDMETHODCALLTYPE hkPresent(IDXGISwapChain* sc, UINT sync, UINT flags) { // ---- scene detection + DLAA ------------------------------------------------ typedef void (STDMETHODCALLTYPE* RSSetViewports_t)(ID3D11DeviceContext*, UINT, const D3D11_VIEWPORT*); +typedef void (STDMETHODCALLTYPE* IASetInputLayout_t)(ID3D11DeviceContext*, ID3D11InputLayout*); +typedef void (STDMETHODCALLTYPE* IASetVertexBuffers_t)(ID3D11DeviceContext*, UINT, UINT, ID3D11Buffer* const*, const UINT*, const UINT*); +typedef void (STDMETHODCALLTYPE* IASetIndexBuffer_t)(ID3D11DeviceContext*, ID3D11Buffer*, DXGI_FORMAT, UINT); +typedef void (STDMETHODCALLTYPE* IASetPrimitiveTopology_t)(ID3D11DeviceContext*, D3D11_PRIMITIVE_TOPOLOGY); +typedef void (STDMETHODCALLTYPE* VSSetConstantBuffers_t)(ID3D11DeviceContext*, UINT, UINT, ID3D11Buffer* const*); +typedef void (STDMETHODCALLTYPE* VSSetConstantBuffers1_t)(ID3D11DeviceContext1*, UINT, UINT, ID3D11Buffer* const*, const UINT*, const UINT*); +typedef void (STDMETHODCALLTYPE* VSSetShader_t)(ID3D11DeviceContext*, ID3D11VertexShader*, ID3D11ClassInstance* const*, UINT); +typedef void (STDMETHODCALLTYPE* VSSetShaderResources_t)(ID3D11DeviceContext*, UINT, UINT, ID3D11ShaderResourceView* const*); +typedef void (STDMETHODCALLTYPE* VSSetSamplers_t)(ID3D11DeviceContext*, UINT, UINT, ID3D11SamplerState* const*); +typedef void (STDMETHODCALLTYPE* RSSetState_t)(ID3D11DeviceContext*, ID3D11RasterizerState*); +typedef void (STDMETHODCALLTYPE* RSSetScissorRects_t)(ID3D11DeviceContext*, UINT, const D3D11_RECT*); typedef void (STDMETHODCALLTYPE* Draw_t)(ID3D11DeviceContext*, UINT, UINT); typedef void (STDMETHODCALLTYPE* OMSetRenderTargets_t)(ID3D11DeviceContext*, UINT, ID3D11RenderTargetView* const*, ID3D11DepthStencilView*); +IASetInputLayout_t oIASetInputLayout = nullptr; +IASetVertexBuffers_t oIASetVertexBuffers = nullptr; +IASetIndexBuffer_t oIASetIndexBuffer = nullptr; +IASetPrimitiveTopology_t oIASetPrimitiveTopology = nullptr; +VSSetConstantBuffers_t oVSSetConstantBuffers = nullptr; +VSSetConstantBuffers1_t oVSSetConstantBuffers1 = nullptr; +VSSetShader_t oVSSetShader = nullptr; +VSSetShaderResources_t oVSSetShaderResources = nullptr; +VSSetSamplers_t oVSSetSamplers = nullptr; +RSSetState_t oRSSetState = nullptr; +RSSetScissorRects_t oRSSetScissorRects = nullptr; Draw_t oDraw = nullptr; OMSetRenderTargets_t oOMSetRenderTargets = nullptr; RSSetViewports_t oRSSetViewports = nullptr; @@ -7082,12 +7104,99 @@ void ReconcileViewports(ID3D11DeviceContext* ctx, bool gameJustSet) { v[0].TopLeftY += sy; if (over) { v[0].Width += 1.0f; v[0].Height += 1.0f; } oRSSetViewports(ctx, g_gameVpN, v); +#ifdef WITH_DLAA + DrawIdRecord::NoteViewports(g_gameVpN, v); +#endif } else { oRSSetViewports(ctx, g_gameVpN, g_gameVp); +#ifdef WITH_DLAA + DrawIdRecord::NoteViewports(g_gameVpN, g_gameVp); +#endif } g_vpShifted = shift; g_vpShiftX = sx; g_vpShiftY = sy; g_vpOver = over; } +void STDMETHODCALLTYPE hkIASetInputLayout(ID3D11DeviceContext* ctx, ID3D11InputLayout* il) { +#ifdef WITH_DLAA + if (!t_inDlaa && IsGameCtx(ctx)) DrawIdRecord::NoteInputLayout(il); +#endif + oIASetInputLayout(ctx, il); +} + +void STDMETHODCALLTYPE hkIASetVertexBuffers(ID3D11DeviceContext* ctx, UINT start, UINT num, ID3D11Buffer* const* vb, + const UINT* stride, const UINT* off) { +#ifdef WITH_DLAA + if (!t_inDlaa && IsGameCtx(ctx)) DrawIdRecord::NoteVertexBuffers(start, num, vb, stride, off); +#endif + oIASetVertexBuffers(ctx, start, num, vb, stride, off); +} + +void STDMETHODCALLTYPE hkIASetIndexBuffer(ID3D11DeviceContext* ctx, ID3D11Buffer* ib, DXGI_FORMAT fmt, UINT off) { +#ifdef WITH_DLAA + if (!t_inDlaa && IsGameCtx(ctx)) DrawIdRecord::NoteIndexBuffer(ib, fmt, off); +#endif + oIASetIndexBuffer(ctx, ib, fmt, off); +} + +void STDMETHODCALLTYPE hkIASetPrimitiveTopology(ID3D11DeviceContext* ctx, D3D11_PRIMITIVE_TOPOLOGY topo) { +#ifdef WITH_DLAA + if (!t_inDlaa && IsGameCtx(ctx)) DrawIdRecord::NotePrimitiveTopology(topo); +#endif + oIASetPrimitiveTopology(ctx, topo); +} + +void STDMETHODCALLTYPE hkVSSetShader(ID3D11DeviceContext* ctx, ID3D11VertexShader* vs, + ID3D11ClassInstance* const* ci, UINT n) { +#ifdef WITH_DLAA + if (!t_inDlaa && IsGameCtx(ctx)) DrawIdRecord::NoteVsShader(vs); +#endif + oVSSetShader(ctx, vs, ci, n); +} + +void STDMETHODCALLTYPE hkVSSetConstantBuffers(ID3D11DeviceContext* ctx, UINT start, UINT num, ID3D11Buffer* const* cb) { +#ifdef WITH_DLAA + if (!t_inDlaa && IsGameCtx(ctx)) DrawIdRecord::NoteVsConstantBuffers(start, num, cb); +#endif + oVSSetConstantBuffers(ctx, start, num, cb); +} + +void STDMETHODCALLTYPE hkVSSetConstantBuffers1(ID3D11DeviceContext1* ctx, UINT start, UINT num, + ID3D11Buffer* const* cb, const UINT* first, const UINT* count) { +#ifdef WITH_DLAA + if (!t_inDlaa && IsGameCtx1(ctx)) DrawIdRecord::NoteVsConstantBuffers1(start, num, cb, first, count); +#endif + oVSSetConstantBuffers1(ctx, start, num, cb, first, count); +} + +void STDMETHODCALLTYPE hkVSSetShaderResources(ID3D11DeviceContext* ctx, UINT start, UINT num, + ID3D11ShaderResourceView* const* srv) { +#ifdef WITH_DLAA + if (!t_inDlaa && IsGameCtx(ctx)) DrawIdRecord::NoteVsShaderResources(start, num, srv); +#endif + oVSSetShaderResources(ctx, start, num, srv); +} + +void STDMETHODCALLTYPE hkVSSetSamplers(ID3D11DeviceContext* ctx, UINT start, UINT num, ID3D11SamplerState* const* smp) { +#ifdef WITH_DLAA + if (!t_inDlaa && IsGameCtx(ctx)) DrawIdRecord::NoteVsSamplers(start, num, smp); +#endif + oVSSetSamplers(ctx, start, num, smp); +} + +void STDMETHODCALLTYPE hkRSSetState(ID3D11DeviceContext* ctx, ID3D11RasterizerState* rs) { +#ifdef WITH_DLAA + if (!t_inDlaa && IsGameCtx(ctx)) DrawIdRecord::NoteRsState(rs); +#endif + oRSSetState(ctx, rs); +} + +void STDMETHODCALLTYPE hkRSSetScissorRects(ID3D11DeviceContext* ctx, UINT n, const D3D11_RECT* sc) { +#ifdef WITH_DLAA + if (!t_inDlaa && IsGameCtx(ctx) && sc) DrawIdRecord::NoteScissorRects(n, sc); +#endif + oRSSetScissorRects(ctx, n, sc); +} + void STDMETHODCALLTYPE hkRSSetViewports(ID3D11DeviceContext* ctx, UINT n, const D3D11_VIEWPORT* vps) { if (Ofxr::FromOfxr(_ReturnAddress())) { Ofxr::NotePassed(); oRSSetViewports(ctx, n, vps); return; } // v0.10.0 OFXR Bridge: the layer's own D3D11 work on the game context is not ours to track TraceVp(ctx, n, vps); @@ -11755,6 +11864,13 @@ void DidStatsTag(char* buf, size_t cap, uint64_t passes) { const double frd = fr > 0 ? fr : 1.0; const double rb = s.rbFrames > s0.rbFrames ? (double)(s.rbFrames - s0.rbFrames) : 0.0; const double rbd = rb > 0 ? rb : 1.0; + const double pollCpu = (double)(s.pollTicks - s0.pollTicks) * 1000.0 / qf / frd; + const double prepScanCpu = (double)(s.prepScanTicks - s0.prepScanTicks) * 1000.0 / qf / frd; + const double prepWaitCpu = (double)(s.prepWaitTicks - s0.prepWaitTicks) * 1000.0 / qf / frd; + const double prepFullCpu = (double)(s.prepFullTicks - s0.prepFullTicks) * 1000.0 / qf / frd; + const double prepLooseCpu = (double)(s.prepLooseTicks - s0.prepLooseTicks) * 1000.0 / qf / frd; + const double prepUploadCpu = (double)(s.prepUploadTicks - s0.prepUploadTicks) * 1000.0 / qf / frd; + const DrawIdRecord::ShadowWindow sh = DrawIdRecord::TakeShadowWindow(); const uint64_t stD[6] = { 0, s.st[1] - s0.st[1], s.st[2] - s0.st[2], s.st[3] - s0.st[3], s.st[4] - s0.st[4], s.st[5] - s0.st[5] }; const uint64_t elig = stD[1] + stD[2] + stD[3]; @@ -11766,7 +11882,7 @@ void DidStatsTag(char* buf, size_t cap, uint64_t passes) { "pairing (eye 0, %.0f frames, %.0f read back): paired %.1f %% of %.0f draws/frame (movers %.1f, static %.1f, " "unpaired %.1f, instanced %.1f, no-MVP %.1f), via the pointer-free key %.1f draws/frame (%.1f paired), no " "partner group %.1f/frame, oversized groups %.1f/frame, unconfirmed tracks %.1f/frame, frames < 50 %% paired %llu, " - "CPU %.3f ms/frame, GPU " + "CPU %.3f ms/frame (poll %.3f, scan %.3f, wait %.3f, full %.3f, loose %.3f, upload %.3f), GPU " "%.3f ms avg / %.3f max | pass B GPU %.3f ms avg / %.3f max | id coverage %.1f %% of world px (movers %.2f %%, " "unpaired %.2f %%)", g_didOn ? "ACTIVE" : "inactive", g_didHooked.load() ? "instanced hooked" : "instanced NOT hooked", @@ -11782,9 +11898,23 @@ void DidStatsTag(char* buf, size_t cap, uint64_t passes) { (double)(s.loosePaired - s0.loosePaired) / rbd, (double)(s.noGroup - s0.noGroup) / frd, (double)(s.bigGroup - s0.bigGroup) / frd, (double)(s.trackRejects - s0.trackRejects) / rbd, (unsigned long long)(s.lowPairFrames - s0.lowPairFrames), - (double)(s.cpuTicks - s0.cpuTicks) * 1000.0 / qf / frd, pAvg, pMax, bAvg, bMax, + (double)(s.cpuTicks - s0.cpuTicks) * 1000.0 / qf / frd, pollCpu, prepScanCpu, prepWaitCpu, prepFullCpu, prepLooseCpu, + prepUploadCpu, pAvg, pMax, bAvg, bMax, 100.0 * (double)(s.idPx - s0.idPx) / wpxd, 100.0 * (double)(s.moverPx - s0.moverPx) / wpxd, 100.0 * (double)(s.unpairedPx - s0.unpairedPx) / wpxd); + if (sh.calls > 0) { + static const char* const kName[DrawIdRecord::kShadowFieldCount] = { + "il", "vb", "ib", "topo", "vs", "cb", "srv", "smp", "rs", "vp", "sc" + }; + char sbuf[512]; + int off = snprintf(sbuf, sizeof(sbuf), "MV draw-ids shadow @blit: Record shadow hit %% over %llu draw(s):", + (unsigned long long)sh.calls); + for (int i = 0; i < DrawIdRecord::kShadowFieldCount && off > 0 && off < (int)sizeof(sbuf) - 24; ++i) { + const double pct = 100.0 * (double)sh.shadow[i] / (double)sh.calls; + off += snprintf(sbuf + off, sizeof(sbuf) - (size_t)off, " %s %.0f", kName[i], pct); + } + Log("%s", sbuf); + } // v0.10.0 phase 3: consensus camera R (eye 0) + forward-pass ids { const size_t used = strlen(buf); @@ -16349,9 +16479,15 @@ bool UpdateGameContext(IDXGISwapChain* sc, uint64_t n, void* caller) { g_gameDev = dev; g_candDev = nullptr; g_candPresents = 0; if (first) { +#ifdef WITH_DLAA + DrawIdRecord::ShadowInvalidate(); +#endif Log("game render context = %p (device %p, ID3D11DeviceContext1 %p, Present #%llu tid=%lu)", (void*)ic, (void*)dev, (void*)ic1, (unsigned long long)n, GetCurrentThreadId()); } else { +#ifdef WITH_DLAA + DrawIdRecord::ShadowInvalidate(); +#endif if (g_cCtxChanges < 5) { ++g_cCtxChanges; Log("game render context changed %p -> %p (device %p -> %p, ID3D11DeviceContext1 %p, Present #%llu tid=%lu)", @@ -16556,6 +16692,9 @@ void AdoptFromPresentLayer(ID3D11DeviceContext* ctx, const PlEvidence& ev, const g_plDraws = 0; g_plGameTid.store(tid, std::memory_order_relaxed); // every frame boundary runs on this thread from now on FrameEndReset(); // the rule's state was the presenting context's until now +#ifdef WITH_DLAA + DrawIdRecord::ShadowInvalidate(); +#endif g_plActiveQpc.store(now, std::memory_order_relaxed); g_plAdopted.store(true, std::memory_order_relaxed); g_plActive.store(true, std::memory_order_release); // hkPresent: count + log only from now on @@ -17884,6 +18023,28 @@ DWORD WINAPI SetupThread(LPVOID) { reinterpret_cast(&oOMSetRenderTargets)); HookFn("RSSetViewports", cvtbl[44], reinterpret_cast(&hkRSSetViewports), reinterpret_cast(&oRSSetViewports)); + #ifdef WITH_DLAA + HookFn("IASetInputLayout", cvtbl[17], reinterpret_cast(&hkIASetInputLayout), + reinterpret_cast(&oIASetInputLayout)); + HookFn("IASetVertexBuffers", cvtbl[18], reinterpret_cast(&hkIASetVertexBuffers), + reinterpret_cast(&oIASetVertexBuffers)); + HookFn("IASetIndexBuffer", cvtbl[19], reinterpret_cast(&hkIASetIndexBuffer), + reinterpret_cast(&oIASetIndexBuffer)); + HookFn("IASetPrimitiveTopology", cvtbl[24], reinterpret_cast(&hkIASetPrimitiveTopology), + reinterpret_cast(&oIASetPrimitiveTopology)); + HookFn("VSSetConstantBuffers", cvtbl[7], reinterpret_cast(&hkVSSetConstantBuffers), + reinterpret_cast(&oVSSetConstantBuffers)); + HookFn("VSSetShader", cvtbl[11], reinterpret_cast(&hkVSSetShader), + reinterpret_cast(&oVSSetShader)); + HookFn("VSSetShaderResources", cvtbl[25], reinterpret_cast(&hkVSSetShaderResources), + reinterpret_cast(&oVSSetShaderResources)); + HookFn("VSSetSamplers", cvtbl[26], reinterpret_cast(&hkVSSetSamplers), + reinterpret_cast(&oVSSetSamplers)); + HookFn("RSSetState", cvtbl[43], reinterpret_cast(&hkRSSetState), + reinterpret_cast(&oRSSetState)); + HookFn("RSSetScissorRects", cvtbl[45], reinterpret_cast(&hkRSSetScissorRects), + reinterpret_cast(&oRSSetScissorRects)); + #endif HookFn("DrawIndexed", cvtbl[12], reinterpret_cast(&hkDrawIndexed), reinterpret_cast(&oDrawIndexed)); // v0.9.0 texture LOD bias: PSSetSamplers = 10. g_lodHooked gates every use of oPSSetSamplers (LodFrameUpdate). @@ -17936,6 +18097,8 @@ DWORD WINAPI SetupThread(LPVOID) { reinterpret_cast(&oDiscardResource)); HookFn("DiscardView", c1vtbl[118], reinterpret_cast(&hkDiscardView), reinterpret_cast(&oDiscardView)); + HookFn("VSSetConstantBuffers1", c1vtbl[119], reinterpret_cast(&hkVSSetConstantBuffers1), + reinterpret_cast(&oVSSetConstantBuffers1)); HookFn("DiscardView1", c1vtbl[133], reinterpret_cast(&hkDiscardView1), reinterpret_cast(&oDiscardView1)); ctx1->Release(); diff --git a/src/motion_vectors.cpp b/src/motion_vectors.cpp index 5e9a0a5..f47ee48 100644 --- a/src/motion_vectors.cpp +++ b/src/motion_vectors.cpp @@ -1534,10 +1534,11 @@ bool CameraMv::Generate(ID3D11DeviceContext* ctx, uint32_t w, uint32_t h, uint32 // v0.9.0 (sets m_objMask for WriteDims; 0 = off). R_world = the last good frame's camera motion, not this frame's: // r2: no "moving" verdicts from it (camOk false -- while the camera turns every static draw would deviate); the ids' // own R (MVP_prev * inverse(MVP_cur)) does not depend on it and is still computed. - ObjPrepare(ctx, objs, fullW, fullH, false); // v0.10.0: per-draw pairs need no camera solve of this frame (R = MVP_prev * inverse(MVP_cur)); the camera R only // predicts duplicates' partners and decides "static", so the last good one is fine here. - const uint32_t didPrep = DidPrepare(ctx, did, didIdSrv, fullW, fullH, didFwd, false); // no fresh medoid + const bool didPrepBegun = DidPrepareBegin(ctx, did, didIdSrv, fullW, fullH, didFwd, false); // no fresh medoid + ObjPrepare(ctx, objs, fullW, fullH, false); + const uint32_t didPrep = didPrepBegun ? DidPrepareEnd(ctx, didIdSrv) : 0; if (!WriteDims(ctx, w, h, x0, y0, fullW, fullH)) { // nothing dispatched: same contract as below ObjNoRun("Generate (reuse path): the dims cbuffer could not be mapped", objs); // v0.9.0 r8 if (candBad) m_prevStale = true; else Commit(ctx, cur); @@ -1731,11 +1732,14 @@ bool CameraMv::Generate(ID3D11DeviceContext* ctx, uint32_t w, uint32_t h, uint32 ctx->Unmap(m_cbPairs.Get(), 0); // v0.9.0 (sets m_objMask for WriteDims; 0 = off). r9: a world miss is a pairing failure for the objects too (mask // hold) + bool didPrepBegun = false; + if ((noMed & 1u) == 0u) + didPrepBegun = DidPrepareBegin(ctx, did, didIdSrv, fullW, fullH, didFwd, !fs.missHeld, noMed); ObjPrepare(ctx, objs, fullW, fullH, !fs.worldMiss, fs.worldMiss, missWhy); // v0.10.0 per-draw MVs: CPU grouping + uploads (sets m_didN for WriteDims); never a history reset for them // (phase 3: + the forward record; pass A writes a fresh medoid unless the world miss is held) const uint32_t didPrep = (noMed & 1u) ? didPrepEarly - : DidPrepare(ctx, did, didIdSrv, fullW, fullH, didFwd, !fs.missHeld, noMed); + : (didPrepBegun ? DidPrepareEnd(ctx, didIdSrv) : 0u); if (!WriteDims(ctx, w, h, x0, y0, fullW, fullH)) { ObjNoRun("Generate: the dims cbuffer could not be mapped", objs); // v0.9.0 r8 diagnostic m_didN = 0; @@ -1788,28 +1792,42 @@ bool CameraMv::Generate(ID3D11DeviceContext* ctx, uint32_t w, uint32_t h, uint32 // v0.10.0: the pass's draw record -> this eye's DrawIdMv (created lazily: a failure leaves per-draw MVs off, the camera // path is untouched). m_didN = the draw count when pass B can use the ids (the pass's draw-id target is complete); the // record still becomes the eye's history without it. Returns the prepared draw count (0 = no dispatch / no commit). -uint32_t CameraMv::DidPrepare(ID3D11DeviceContext* ctx, const DrawIdRecord* did, ID3D11ShaderResourceView* idSrv, - uint32_t fullW, uint32_t fullH, const DrawIdRecord* didFwd, bool freshMedoid, uint32_t noMedoid) { +bool CameraMv::DidPrepareBegin(ID3D11DeviceContext* ctx, const DrawIdRecord* did, ID3D11ShaderResourceView* idSrv, + uint32_t fullW, uint32_t fullH, const DrawIdRecord* didFwd, bool freshMedoid, + uint32_t noMedoid) { m_didN = 0; m_didNG = 0; m_didIdSrv = nullptr; m_didStaticGate = false; // v0.10.0 phase 14 - if (!did || !ctx) { m_did.Forget(); return 0; } + m_didPrepPending = false; + if (!did || !ctx) { m_did.Forget(); return false; } if (!m_did.Ready() && !m_did.Init(m_dev.Get())) { if (!m_didInitTried && ShaderCache::Done()) { m_didInitTried = true; Log("MV draw-ids: the pairing unit could not be created -- per-draw motion vectors off for this unit"); } m_did.Forget(); - return 0; + return false; } - // v0.10.0 phase 3: forward draws only with the ids (idSrv): without the target their pairing would be pointless - const uint32_t n = m_did.Prepare(ctx, did, fullW, fullH, idSrv ? didFwd : nullptr, m_nearReject, freshMedoid, noMedoid, - idSrv != nullptr); // v0.10.0 phase 9: the folded dispatch's rigid-parent bit - if (n && idSrv) { m_didN = n; m_didNG = m_did.PreparedG(); m_didIdSrv = idSrv; m_didStaticGate = did->StaticGated(); } + m_didPrepPending = m_did.PrepareBegin(ctx, did, fullW, fullH, idSrv ? didFwd : nullptr, m_nearReject, freshMedoid, + noMedoid, idSrv != nullptr); + return m_didPrepPending; +} + +uint32_t CameraMv::DidPrepareEnd(ID3D11DeviceContext* ctx, ID3D11ShaderResourceView* idSrv) { + if (!m_didPrepPending) return 0; + m_didPrepPending = false; + const uint32_t n = m_did.PrepareEnd(ctx); + if (n && idSrv) { m_didN = n; m_didNG = m_did.PreparedG(); m_didIdSrv = idSrv; m_didStaticGate = m_didN != 0; } return n; } +uint32_t CameraMv::DidPrepare(ID3D11DeviceContext* ctx, const DrawIdRecord* did, ID3D11ShaderResourceView* idSrv, + uint32_t fullW, uint32_t fullH, const DrawIdRecord* didFwd, bool freshMedoid, uint32_t noMedoid) { + if (!DidPrepareBegin(ctx, did, idSrv, fullW, fullH, didFwd, freshMedoid, noMedoid)) return 0; + return DidPrepareEnd(ctx, idSrv); +} + // =========================================================================================================== // v0.9.0 per-object motion vectors (see ObjRecord / ObjIds in motion_vectors.h and docs/DLAA_INTEGRATION.md) // =========================================================================================================== diff --git a/src/motion_vectors.h b/src/motion_vectors.h index 5574e3a..5ceeaa1 100644 --- a/src/motion_vectors.h +++ b/src/motion_vectors.h @@ -925,8 +925,13 @@ class CameraMv { // static gate (FwdMode.z: the Ctrl+F6 view's id-0 olive) uint32_t m_dimsX0 = 0, m_dimsY0 = 0, m_dimsW = 0, m_dimsH = 0; // v0.10.0 phase 21: pass B's crop (WriteDims; the probe) uint32_t m_probePacked = 0; // v0.10.0 phase 21: FwdMode.w of this pass B (0 = no probe) + bool m_didPrepPending = false; // DrawIdMv::PrepareBegin ran, DrawIdMv::PrepareEnd pending // prepares the record (CPU grouping + uploads); sets m_didN when pass B can use the ids. Returns the draws prepared. // v0.10.0 phase 3: + the forward record and whether pass A wrote this frame's medoid (the consensus statistic). + bool DidPrepareBegin(ID3D11DeviceContext* ctx, const DrawIdRecord* did, ID3D11ShaderResourceView* idSrv, + uint32_t fullW, uint32_t fullH, const DrawIdRecord* didFwd, bool freshMedoid, + uint32_t noMedoid = 0); + uint32_t DidPrepareEnd(ID3D11DeviceContext* ctx, ID3D11ShaderResourceView* idSrv); uint32_t DidPrepare(ID3D11DeviceContext* ctx, const DrawIdRecord* did, ID3D11ShaderResourceView* idSrv, uint32_t fullW, uint32_t fullH, const DrawIdRecord* didFwd, bool freshMedoid, uint32_t noMedoid = 0); };