diff --git a/HANDOFF_DILIGENT.md b/HANDOFF_DILIGENT.md index 46df8ff2..f614d927 100644 --- a/HANDOFF_DILIGENT.md +++ b/HANDOFF_DILIGENT.md @@ -161,6 +161,7 @@ Verified locally on Turnip Adreno 750: - `GenerateMipmap` (Diligent GPU mip generation on state textures) - `GetTexImage` / `GetTextureImage` (RGBA8 readback) - Fence sync entries (`FenceSync` / `ClientWaitSync` / `WaitSync` / `DeleteSync` / `GetSyncStatus`) as CPU always-signaled fallback + - Timer query entries (`BeginTimeElapsedQuery` / `EndTimeElapsedQuery` / `QueryCounterTimestamp` / `GetQueryResult64` etc.) as CPU `steady_clock` fallback - `ReadPixels` from default and user color attachments - Primitive expansion: - `GL_TRIANGLE_FAN` expanded to triangle list @@ -219,7 +220,7 @@ Notes: - Textures auto-sync `ITextureObject` → Diligent resources, including mip levels and sampler state; compressed textures and integer/3-channel formats that Diligent lacks are still skipped. - Global UBO (default-block `glUniform*`) and named application UBO blocks (through `glBindBufferBase`/`glUniformBlockBinding`) now upload and bind; SSBOs are still not fed from frontend buffer bindings. - No swapchain / EGL window surface presentation yet; `Present()` only flushes. -- No transform feedback / GPU queries / non-color readback; fence sync is provided as a CPU always-signaled fallback. +- No transform feedback / GPU-accelerated queries / non-color readback; fence sync and timer queries use CPU fallbacks. - Draw range, multi-draw, instanced-draw wrappers, clear-buffer, blit, read-pixels, CopyTexImage*, CopyImageSubData, GenerateMipmap, GetTexImage/GetTextureImage and indirect draws are now wired; buffer subdata paths still remain. - A last-PSO cache now avoids recreating the pipeline when program/render-state/topology/VAO layout is unchanged; texture/UBO resources are still rebound dynamically per draw. - The `GLFunctionsTable` is only partially populated. diff --git a/MobileGL/MG_Backend/Diligent/BackendObject_Diligent.cpp b/MobileGL/MG_Backend/Diligent/BackendObject_Diligent.cpp index 3536a38e..00f64ed1 100644 --- a/MobileGL/MG_Backend/Diligent/BackendObject_Diligent.cpp +++ b/MobileGL/MG_Backend/Diligent/BackendObject_Diligent.cpp @@ -17,6 +17,7 @@ #include #include +#include namespace MobileGL::MG_Backend::DiligentBackend { namespace { @@ -50,6 +51,12 @@ namespace MobileGL::MG_Backend::DiligentBackend { Uint32 BaseInstance = 0; }; + struct CpuTimerQuery { + std::chrono::steady_clock::time_point Start; + Uint64 TimestampNs = 0; + Bool Available = false; + }; + const Uint8* ResolveIndirectCommandBytes(const void* indirect, SizeT requiredBytes, const char* label) { auto drawBuffer = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::DrawIndirect).GetBoundObject(); if (drawBuffer) { @@ -571,6 +578,57 @@ namespace MobileGL::MG_Backend::DiligentBackend { } } + Bool IsTimerQuerySupported() { + return true; + } + + BackendQueryHandle BeginTimeElapsedQuery() { + auto* query = new CpuTimerQuery; + query->Start = std::chrono::steady_clock::now(); + query->Available = false; + return query; + } + + void EndTimeElapsedQuery(BackendQueryHandle query) { + if (query == nullptr) { + return; + } + auto* cpuQuery = static_cast(query); + const auto now = std::chrono::steady_clock::now(); + cpuQuery->TimestampNs = static_cast( + std::chrono::duration_cast(now - cpuQuery->Start).count()); + cpuQuery->Available = true; + } + + BackendQueryHandle QueryCounterTimestamp() { + auto* query = new CpuTimerQuery; + query->TimestampNs = static_cast( + std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()).count()); + query->Available = true; + return query; + } + + Bool IsQueryResultAvailable(BackendQueryHandle query) { + return query != nullptr && static_cast(query)->Available; + } + + Bool GetQueryResult64(BackendQueryHandle query, Bool wait, Uint64* outNanoseconds) { + if (query == nullptr || outNanoseconds == nullptr) { + return false; + } + auto* cpuQuery = static_cast(query); + if (!cpuQuery->Available && !wait) { + return false; + } + *outNanoseconds = cpuQuery->TimestampNs; + return true; + } + + void DeleteBackendQuery(BackendQueryHandle query) { + delete static_cast(query); + } + BackendSyncHandle FenceSync() { // CPU fallback fence: always signaled is a valid implementation for a // backend without native sync primitives. The handle still round-trips @@ -724,6 +782,13 @@ namespace MobileGL::MG_Backend::DiligentBackend { m_functions.GL.WaitSync = WaitSync; m_functions.GL.DeleteSync = DeleteSync; m_functions.GL.GetSyncStatus = GetSyncStatus; + m_functions.GL.IsTimerQuerySupported = IsTimerQuerySupported; + m_functions.GL.BeginTimeElapsedQuery = BeginTimeElapsedQuery; + m_functions.GL.EndTimeElapsedQuery = EndTimeElapsedQuery; + m_functions.GL.QueryCounterTimestamp = QueryCounterTimestamp; + m_functions.GL.IsQueryResultAvailable = IsQueryResultAvailable; + m_functions.GL.GetQueryResult64 = GetQueryResult64; + m_functions.GL.DeleteBackendQuery = DeleteBackendQuery; m_functions.Present = Present; m_initialized = true;