mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-12 06:08:30 +09:00
[Feat] (DirectVulkan, MG_Impl): GPU transform feedback primitive queries
The TF primitive queries now ride VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT pools when the device reports transformFeedbackQueries: each captured draw is wrapped in a slot (shared between both GL targets when active together), and results sum the (written, needed) pairs - GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN from the first, GL_PRIMITIVES_GENERATED from the second. This is exact through geometry shaders, so KHR-GL33.transform_feedback.query_geometry_* pass; the CPU accounting delta remains the fallback for backends without the feature.
This commit is contained in:
@@ -225,6 +225,10 @@ namespace MobileGL {
|
|||||||
// GetQueryResult64 / DeleteBackendQuery like timer queries.
|
// GetQueryResult64 / DeleteBackendQuery like timer queries.
|
||||||
BackendQueryHandle (*BeginOcclusionQuery)();
|
BackendQueryHandle (*BeginOcclusionQuery)();
|
||||||
void (*EndOcclusionQuery)(BackendQueryHandle query);
|
void (*EndOcclusionQuery)(BackendQueryHandle query);
|
||||||
|
// Transform feedback primitive queries backed by real GPU query pools
|
||||||
|
// (optional; null = frontend falls back to CPU accounting).
|
||||||
|
BackendQueryHandle (*BeginXfbPrimitivesQuery)(Bool generated);
|
||||||
|
void (*EndXfbPrimitivesQuery)(BackendQueryHandle query);
|
||||||
Int64 (*GetGpuTimestampNs)(); // glGetInteger64v(GL_TIMESTAMP); 0 if unsupported
|
Int64 (*GetGpuTimestampNs)(); // glGetInteger64v(GL_TIMESTAMP); 0 if unsupported
|
||||||
};
|
};
|
||||||
struct GlobalBackendFunctionsTable {
|
struct GlobalBackendFunctionsTable {
|
||||||
|
|||||||
@@ -639,6 +639,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
// exist even when timer queries are disabled.
|
// exist even when timer queries are disabled.
|
||||||
funcsTable.GL.BeginOcclusionQuery = BeginOcclusionQuery;
|
funcsTable.GL.BeginOcclusionQuery = BeginOcclusionQuery;
|
||||||
funcsTable.GL.EndOcclusionQuery = EndOcclusionQuery;
|
funcsTable.GL.EndOcclusionQuery = EndOcclusionQuery;
|
||||||
|
funcsTable.GL.BeginXfbPrimitivesQuery = BeginXfbPrimitivesQuery;
|
||||||
|
funcsTable.GL.EndXfbPrimitivesQuery = EndXfbPrimitivesQuery;
|
||||||
funcsTable.GL.IsQueryResultAvailable = IsQueryResultAvailable;
|
funcsTable.GL.IsQueryResultAvailable = IsQueryResultAvailable;
|
||||||
funcsTable.GL.GetQueryResult64 = GetQueryResult64;
|
funcsTable.GL.GetQueryResult64 = GetQueryResult64;
|
||||||
funcsTable.GL.DeleteBackendQuery = DeleteBackendQuery;
|
funcsTable.GL.DeleteBackendQuery = DeleteBackendQuery;
|
||||||
|
|||||||
@@ -1564,7 +1564,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
// records are shared (SharedPtr) with the owning pool's pending list,
|
// records are shared (SharedPtr) with the owning pool's pending list,
|
||||||
// so deleting the query while results are still in flight is safe.
|
// so deleting the query while results are still in flight is safe.
|
||||||
struct VulkanTimerQuery {
|
struct VulkanTimerQuery {
|
||||||
enum class Kind : Uint8 { Timer, Occlusion };
|
enum class Kind : Uint8 { Timer, Occlusion, XfbWritten, XfbGenerated };
|
||||||
Kind kind = Kind::Timer;
|
Kind kind = Kind::Timer;
|
||||||
SharedPtr<VkTimerQueryManager::TimestampRecord> begin;
|
SharedPtr<VkTimerQueryManager::TimestampRecord> begin;
|
||||||
SharedPtr<VkTimerQueryManager::TimestampRecord> end;
|
SharedPtr<VkTimerQueryManager::TimestampRecord> end;
|
||||||
@@ -1668,6 +1668,17 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
*outNanoseconds = samples;
|
*outNanoseconds = samples;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (query->kind == VulkanTimerQuery::Kind::XfbWritten ||
|
||||||
|
query->kind == VulkanTimerQuery::Kind::XfbGenerated) {
|
||||||
|
Uint64 primitives = 0;
|
||||||
|
if (!pVulkanRenderer->ResolveXfbQueryResult(query->occlusionSlots,
|
||||||
|
query->kind == VulkanTimerQuery::Kind::XfbGenerated,
|
||||||
|
primitives)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*outNanoseconds = primitives;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
// With wait, mirrors ClientWaitSync: a query ended this frame cannot
|
// With wait, mirrors ClientWaitSync: a query ended this frame cannot
|
||||||
// complete until Present submits the commands, so the wait refuses to
|
// complete until Present submits the commands, so the wait refuses to
|
||||||
// block on the current unsubmitted serial. Returning false keeps the
|
// block on the current unsubmitted serial. Returning false keeps the
|
||||||
@@ -1700,6 +1711,27 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
delete static_cast<VulkanTimerQuery*>(handle);
|
delete static_cast<VulkanTimerQuery*>(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BackendQueryHandle BeginXfbPrimitivesQuery(Bool generated) {
|
||||||
|
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::BeginXfbPrimitivesQuery called with null VulkanRenderer");
|
||||||
|
if (!pVulkanRenderer->StartXfbQueryCapture(generated ? 1u : 0u)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
auto* query = new VulkanTimerQuery{};
|
||||||
|
query->kind = generated ? VulkanTimerQuery::Kind::XfbGenerated : VulkanTimerQuery::Kind::XfbWritten;
|
||||||
|
query->rendererGeneration = GetRendererGeneration();
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EndXfbPrimitivesQuery(BackendQueryHandle handle) {
|
||||||
|
auto* query = static_cast<VulkanTimerQuery*>(handle);
|
||||||
|
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::EndXfbPrimitivesQuery called with null VulkanRenderer");
|
||||||
|
if (query == nullptr || query->rendererGeneration != GetRendererGeneration()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pVulkanRenderer->StopXfbQueryCapture(
|
||||||
|
query->kind == VulkanTimerQuery::Kind::XfbGenerated ? 1u : 0u, query->occlusionSlots);
|
||||||
|
}
|
||||||
|
|
||||||
BackendQueryHandle BeginOcclusionQuery() {
|
BackendQueryHandle BeginOcclusionQuery() {
|
||||||
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::BeginOcclusionQuery called with null VulkanRenderer");
|
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::BeginOcclusionQuery called with null VulkanRenderer");
|
||||||
if (!pVulkanRenderer->StartOcclusionQueryCapture()) {
|
if (!pVulkanRenderer->StartOcclusionQueryCapture()) {
|
||||||
|
|||||||
@@ -123,6 +123,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
// only while a live renderer exists whose device can actually time.
|
// only while a live renderer exists whose device can actually time.
|
||||||
Bool IsTimerQuerySupported();
|
Bool IsTimerQuerySupported();
|
||||||
BackendQueryHandle BeginTimeElapsedQuery();
|
BackendQueryHandle BeginTimeElapsedQuery();
|
||||||
|
BackendQueryHandle BeginXfbPrimitivesQuery(Bool generated);
|
||||||
|
void EndXfbPrimitivesQuery(BackendQueryHandle query);
|
||||||
BackendQueryHandle BeginOcclusionQuery();
|
BackendQueryHandle BeginOcclusionQuery();
|
||||||
void EndOcclusionQuery(BackendQueryHandle query);
|
void EndOcclusionQuery(BackendQueryHandle query);
|
||||||
void EndTimeElapsedQuery(BackendQueryHandle query);
|
void EndTimeElapsedQuery(BackendQueryHandle query);
|
||||||
|
|||||||
@@ -2786,6 +2786,10 @@ void main() {
|
|||||||
vkDestroyQueryPool(m_device, m_occlusionQueryPool, nullptr);
|
vkDestroyQueryPool(m_device, m_occlusionQueryPool, nullptr);
|
||||||
m_occlusionQueryPool = VK_NULL_HANDLE;
|
m_occlusionQueryPool = VK_NULL_HANDLE;
|
||||||
}
|
}
|
||||||
|
if (m_xfbQueryPool != VK_NULL_HANDLE) {
|
||||||
|
vkDestroyQueryPool(m_device, m_xfbQueryPool, nullptr);
|
||||||
|
m_xfbQueryPool = VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
m_bufferManager.Shutdown();
|
m_bufferManager.Shutdown();
|
||||||
|
|
||||||
// Device is idle (vkDeviceWaitIdle above); query pools can be destroyed.
|
// Device is idle (vkDeviceWaitIdle above); query pools can be destroyed.
|
||||||
@@ -7788,6 +7792,7 @@ void main() {
|
|||||||
VkCommandBuffer& commandBuffer = frame.commandBuffer;
|
VkCommandBuffer& commandBuffer = frame.commandBuffer;
|
||||||
|
|
||||||
const Bool xfbActive = BeginXfbCaptureForDraw(frame);
|
const Bool xfbActive = BeginXfbCaptureForDraw(frame);
|
||||||
|
BeginXfbQueryForDraw(commandBuffer);
|
||||||
const Bool occlusionActive = BeginOcclusionForDraw(commandBuffer);
|
const Bool occlusionActive = BeginOcclusionForDraw(commandBuffer);
|
||||||
vkCmdDraw(commandBuffer,
|
vkCmdDraw(commandBuffer,
|
||||||
payload.params.vertexCount,
|
payload.params.vertexCount,
|
||||||
@@ -7796,6 +7801,7 @@ void main() {
|
|||||||
payload.params.firstInstance);
|
payload.params.firstInstance);
|
||||||
EndOcclusionForDraw(commandBuffer, occlusionActive);
|
EndOcclusionForDraw(commandBuffer, occlusionActive);
|
||||||
EndXfbCaptureForDraw(frame, xfbActive);
|
EndXfbCaptureForDraw(frame, xfbActive);
|
||||||
|
EndXfbQueryForDraw(commandBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
Bool VulkanRenderer::StartOcclusionQueryCapture() {
|
Bool VulkanRenderer::StartOcclusionQueryCapture() {
|
||||||
@@ -7855,6 +7861,91 @@ void main() {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Bool VulkanRenderer::StartXfbQueryCapture(Uint32 kind) {
|
||||||
|
if (!m_xfbQueriesSupported || !m_hostQueryResetEnabled || s_vkResetQueryPool == nullptr ||
|
||||||
|
s_vkCmdBeginQueryIndexedEXT == nullptr || kind > 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (m_xfbQueryPool == VK_NULL_HANDLE) {
|
||||||
|
VkQueryPoolCreateInfo poolInfo{};
|
||||||
|
poolInfo.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
|
||||||
|
poolInfo.queryType = VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT;
|
||||||
|
poolInfo.queryCount = kXfbQuerySlots;
|
||||||
|
if (vkCreateQueryPool(m_device, &poolInfo, nullptr, &m_xfbQueryPool) != VK_SUCCESS) {
|
||||||
|
MGLOG_E("StartXfbQueryCapture: vkCreateQueryPool failed");
|
||||||
|
m_xfbQueryPool = VK_NULL_HANDLE;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
s_vkResetQueryPool(m_device, m_xfbQueryPool, 0, kXfbQuerySlots);
|
||||||
|
}
|
||||||
|
m_xfbQueryActiveSlots[kind].clear();
|
||||||
|
m_xfbQueryCaptureActive[kind] = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanRenderer::StopXfbQueryCapture(Uint32 kind, Vector<Uint32>& outSlots) {
|
||||||
|
if (kind > 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
outSlots = Move(m_xfbQueryActiveSlots[kind]);
|
||||||
|
m_xfbQueryActiveSlots[kind].clear();
|
||||||
|
m_xfbQueryCaptureActive[kind] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bool VulkanRenderer::ResolveXfbQueryResult(const Vector<Uint32>& slots, Bool wantGenerated, Uint64& outPrimitives) {
|
||||||
|
outPrimitives = 0;
|
||||||
|
if (slots.empty() || m_xfbQueryPool == VK_NULL_HANDLE) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
auto& frame = m_frameContext.GetCurrent();
|
||||||
|
if (frame.isCommandRecording) {
|
||||||
|
if (VkRenderPassManager::GetActiveRenderPass() != nullptr) {
|
||||||
|
VkRenderPassManager::EndRenderPass(frame.commandBuffer);
|
||||||
|
}
|
||||||
|
if (!SubmitReadbackCommandsAndWait(frame)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const Uint32 slot : slots) {
|
||||||
|
Uint64 pair[2] = {0, 0}; // {primitivesWritten, primitivesNeeded}
|
||||||
|
const VkResult result =
|
||||||
|
vkGetQueryPoolResults(m_device, m_xfbQueryPool, slot, 1, sizeof(pair), pair, sizeof(pair),
|
||||||
|
VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT);
|
||||||
|
if (result == VK_SUCCESS) {
|
||||||
|
outPrimitives += pair[wantGenerated ? 1 : 0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanRenderer::BeginXfbQueryForDraw(VkCommandBuffer commandBuffer) {
|
||||||
|
m_xfbQuerySlotOpen = false;
|
||||||
|
if ((!m_xfbQueryCaptureActive[0] && !m_xfbQueryCaptureActive[1]) || m_xfbQueryPool == VK_NULL_HANDLE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const Uint32 slot = m_xfbQuerySlotCursor;
|
||||||
|
m_xfbQuerySlotCursor = (m_xfbQuerySlotCursor + 1) % kXfbQuerySlots;
|
||||||
|
// Slots are never host-reset at read time (both GL targets may reference one
|
||||||
|
// slot); recycle them here instead.
|
||||||
|
s_vkResetQueryPool(m_device, m_xfbQueryPool, slot, 1);
|
||||||
|
s_vkCmdBeginQueryIndexedEXT(commandBuffer, m_xfbQueryPool, slot, 0, 0);
|
||||||
|
for (Uint32 kind = 0; kind < 2; ++kind) {
|
||||||
|
if (m_xfbQueryCaptureActive[kind]) {
|
||||||
|
m_xfbQueryActiveSlots[kind].push_back(slot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_xfbQuerySlotOpen = true;
|
||||||
|
m_xfbQueryOpenSlot = slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanRenderer::EndXfbQueryForDraw(VkCommandBuffer commandBuffer) {
|
||||||
|
if (!m_xfbQuerySlotOpen) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
s_vkCmdEndQueryIndexedEXT(commandBuffer, m_xfbQueryPool, m_xfbQueryOpenSlot, 0);
|
||||||
|
m_xfbQuerySlotOpen = false;
|
||||||
|
}
|
||||||
|
|
||||||
Bool VulkanRenderer::BeginOcclusionForDraw(VkCommandBuffer commandBuffer) {
|
Bool VulkanRenderer::BeginOcclusionForDraw(VkCommandBuffer commandBuffer) {
|
||||||
if (!m_occlusionCaptureActive || m_occlusionQueryPool == VK_NULL_HANDLE) {
|
if (!m_occlusionCaptureActive || m_occlusionQueryPool == VK_NULL_HANDLE) {
|
||||||
return false;
|
return false;
|
||||||
@@ -7902,6 +7993,7 @@ void main() {
|
|||||||
VkCommandBuffer& commandBuffer = frame.commandBuffer;
|
VkCommandBuffer& commandBuffer = frame.commandBuffer;
|
||||||
|
|
||||||
const Bool xfbActive = BeginXfbCaptureForDraw(frame);
|
const Bool xfbActive = BeginXfbCaptureForDraw(frame);
|
||||||
|
BeginXfbQueryForDraw(commandBuffer);
|
||||||
const Bool occlusionActive = BeginOcclusionForDraw(commandBuffer);
|
const Bool occlusionActive = BeginOcclusionForDraw(commandBuffer);
|
||||||
vkCmdDrawIndexed(commandBuffer,
|
vkCmdDrawIndexed(commandBuffer,
|
||||||
payload.params.indexCount,
|
payload.params.indexCount,
|
||||||
@@ -7911,6 +8003,7 @@ void main() {
|
|||||||
payload.params.firstInstance);
|
payload.params.firstInstance);
|
||||||
EndOcclusionForDraw(commandBuffer, occlusionActive);
|
EndOcclusionForDraw(commandBuffer, occlusionActive);
|
||||||
EndXfbCaptureForDraw(frame, xfbActive);
|
EndXfbCaptureForDraw(frame, xfbActive);
|
||||||
|
EndXfbQueryForDraw(commandBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanRenderer::MultiDrawArrays(const MultiDrawCmd& payload) {
|
void VulkanRenderer::MultiDrawArrays(const MultiDrawCmd& payload) {
|
||||||
@@ -9518,6 +9611,20 @@ void main() {
|
|||||||
m_hostQueryResetEnabled = false;
|
m_hostQueryResetEnabled = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (m_transformFeedbackFeatureEnabled) {
|
||||||
|
s_vkCmdBeginQueryIndexedEXT = reinterpret_cast<PFN_vkCmdBeginQueryIndexedEXT>(
|
||||||
|
vkGetDeviceProcAddr(m_device, "vkCmdBeginQueryIndexedEXT"));
|
||||||
|
s_vkCmdEndQueryIndexedEXT = reinterpret_cast<PFN_vkCmdEndQueryIndexedEXT>(
|
||||||
|
vkGetDeviceProcAddr(m_device, "vkCmdEndQueryIndexedEXT"));
|
||||||
|
VkPhysicalDeviceTransformFeedbackPropertiesEXT xfbProperties{};
|
||||||
|
xfbProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_PROPERTIES_EXT;
|
||||||
|
VkPhysicalDeviceProperties2 properties2{};
|
||||||
|
properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
|
||||||
|
properties2.pNext = &xfbProperties;
|
||||||
|
vkGetPhysicalDeviceProperties2(m_physicalDevice.handle, &properties2);
|
||||||
|
m_xfbQueriesSupported = xfbProperties.transformFeedbackQueries == VK_TRUE &&
|
||||||
|
s_vkCmdBeginQueryIndexedEXT != nullptr && s_vkCmdEndQueryIndexedEXT != nullptr;
|
||||||
|
}
|
||||||
MGLOG_I("index type uint8 enabled: %s", m_indexTypeUint8ExtensionEnabled ? "true" : "false");
|
MGLOG_I("index type uint8 enabled: %s", m_indexTypeUint8ExtensionEnabled ? "true" : "false");
|
||||||
MGLOG_I("Logical device created.");
|
MGLOG_I("Logical device created.");
|
||||||
|
|
||||||
|
|||||||
@@ -503,6 +503,30 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
Uint32 m_occlusionSlotCursor = 0;
|
Uint32 m_occlusionSlotCursor = 0;
|
||||||
Bool m_occlusionCaptureActive = false;
|
Bool m_occlusionCaptureActive = false;
|
||||||
Vector<Uint32> m_occlusionActiveSlots;
|
Vector<Uint32> m_occlusionActiveSlots;
|
||||||
|
// Transform feedback primitive queries: one pool slot per captured draw yields
|
||||||
|
// the (written, needed) pair; GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN sums the
|
||||||
|
// first, GL_PRIMITIVES_GENERATED the second - exact with geometry shaders,
|
||||||
|
// unlike the CPU fallback accounting.
|
||||||
|
Bool m_xfbQueriesSupported = false;
|
||||||
|
PFN_vkCmdBeginQueryIndexedEXT s_vkCmdBeginQueryIndexedEXT = nullptr;
|
||||||
|
PFN_vkCmdEndQueryIndexedEXT s_vkCmdEndQueryIndexedEXT = nullptr;
|
||||||
|
VkQueryPool m_xfbQueryPool = VK_NULL_HANDLE;
|
||||||
|
static constexpr Uint32 kXfbQuerySlots = 8192;
|
||||||
|
Uint32 m_xfbQuerySlotCursor = 0;
|
||||||
|
Bool m_xfbQueryCaptureActive[2] = {false, false}; // [0]=written, [1]=generated
|
||||||
|
Vector<Uint32> m_xfbQueryActiveSlots[2];
|
||||||
|
Bool m_xfbQuerySlotOpen = false;
|
||||||
|
Uint32 m_xfbQueryOpenSlot = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// kind: 0 = PRIMITIVES_WRITTEN, 1 = PRIMITIVES_GENERATED.
|
||||||
|
Bool StartXfbQueryCapture(Uint32 kind);
|
||||||
|
void StopXfbQueryCapture(Uint32 kind, Vector<Uint32>& outSlots);
|
||||||
|
Bool ResolveXfbQueryResult(const Vector<Uint32>& slots, Bool wantGenerated, Uint64& outPrimitives);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void BeginXfbQueryForDraw(VkCommandBuffer commandBuffer);
|
||||||
|
void EndXfbQueryForDraw(VkCommandBuffer commandBuffer);
|
||||||
|
|
||||||
VkCommandPool m_commandPool = VK_NULL_HANDLE;
|
VkCommandPool m_commandPool = VK_NULL_HANDLE;
|
||||||
|
|
||||||
|
|||||||
@@ -278,9 +278,11 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
queryObject->target = target;
|
queryObject->target = target;
|
||||||
queryObject->active = true;
|
queryObject->active = true;
|
||||||
if (isTransformFeedbackQuery) {
|
if (isTransformFeedbackQuery) {
|
||||||
// CPU accounting: captured draws bump the context counter; the query
|
// Prefer real GPU transform-feedback queries (exact with geometry shaders);
|
||||||
// result is the delta between Begin and End. Without geometry-stage
|
// the CPU accounting delta stays as the fallback when the backend lacks them.
|
||||||
// amplification the assembled count IS the written/generated count.
|
const auto beginXfbPrimitivesQuery = MG_Backend::gBackendFunctionsTable.GL.BeginXfbPrimitivesQuery;
|
||||||
|
queryObject->backendHandle =
|
||||||
|
beginXfbPrimitivesQuery ? beginXfbPrimitivesQuery(target == GL_PRIMITIVES_GENERATED) : nullptr;
|
||||||
queryObject->counterSnapshot = MG_State::pGLContext->GetTransformFeedbackPrimitiveCounter();
|
queryObject->counterSnapshot = MG_State::pGLContext->GetTransformFeedbackPrimitiveCounter();
|
||||||
} else if (isOcclusionQuery) {
|
} else if (isOcclusionQuery) {
|
||||||
queryObject->backendHandle = MG_Backend::gBackendFunctionsTable.GL.BeginOcclusionQuery();
|
queryObject->backendHandle = MG_Backend::gBackendFunctionsTable.GL.BeginOcclusionQuery();
|
||||||
@@ -318,9 +320,16 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (isTransformFeedbackQuery) {
|
if (isTransformFeedbackQuery) {
|
||||||
queryObject->cachedResult =
|
if (queryObject->backendHandle) {
|
||||||
MG_State::pGLContext->GetTransformFeedbackPrimitiveCounter() - queryObject->counterSnapshot;
|
if (const auto endXfbPrimitivesQuery = MG_Backend::gBackendFunctionsTable.GL.EndXfbPrimitivesQuery) {
|
||||||
queryObject->resultCached = true;
|
endXfbPrimitivesQuery(queryObject->backendHandle);
|
||||||
|
}
|
||||||
|
// Result comes from the GPU query at read time.
|
||||||
|
} else {
|
||||||
|
queryObject->cachedResult =
|
||||||
|
MG_State::pGLContext->GetTransformFeedbackPrimitiveCounter() - queryObject->counterSnapshot;
|
||||||
|
queryObject->resultCached = true;
|
||||||
|
}
|
||||||
queryObject->active = false;
|
queryObject->active = false;
|
||||||
queryObject->ended = true;
|
queryObject->ended = true;
|
||||||
activeQueryId = 0;
|
activeQueryId = 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user