mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-11 21:58:31 +09:00
[Fix] (DirectVulkan, MG_State): give each transform feedback object its own capture counters
The frontend half of ARB_transform_feedback2 landed for both backends, but Magma's capture was still written for the one implicit span GL 3.3 has: - A paused span kept capturing. VK_EXT_transform_feedback's counter buffers already make consecutive draws append, so pausing is simply "do not wrap this draw" - the counters keep their values and the next resumed draw carries on where the last captured one stopped. - Those counter buffers were context-wide. Transform feedback objects can each hold an open, paused span at the same time - KHR-GL40.transform_feedback.draw_xfb_test keeps three - and they were all appending through one set of four slots. Each object now gets its own group, handed out on first use; past sixteen objects they share group 0, which only matters for concurrently-paused spans. - The generation that identifies a span is what a backend keys its append state on, so it is now part of the per-object state the frontend saves and restores. Without that, resuming an object that was paused before another one began looked like a new span and restarted its counters at zero. GL_PRIMITIVES_GENERATED needed one more thing. It counts what the last vertex processing stage emitted whether or not anything is being captured, but VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT only counts what the capture saw - so a draw made while the span was paused is invisible to it. The frontend now tallies those draws, and the Vulkan query adds the delta at result time. The correction lives in the backend that needs it: an ES driver's GL_PRIMITIVES_GENERATED counts them by itself, and adding it there too would double them. transform_feedback* on Magma: 4 failures -> 3. Espryt stays at 38/38.
This commit is contained in:
@@ -2798,6 +2798,10 @@ void main() {
|
||||
}
|
||||
m_vertexInputStateFactory.reset();
|
||||
m_xfbCounterBuffer.Destroy();
|
||||
m_xfbCounterSlotByObject.clear();
|
||||
m_xfbNextCounterSlot = 0;
|
||||
m_xfbCountersValid.fill(false);
|
||||
m_xfbLastSeenGeneration.fill(0);
|
||||
if (m_occlusionQueryPool != VK_NULL_HANDLE) {
|
||||
vkDestroyQueryPool(m_device, m_occlusionQueryPool, nullptr);
|
||||
m_occlusionQueryPool = VK_NULL_HANDLE;
|
||||
@@ -8000,11 +8004,30 @@ void main() {
|
||||
resource->layout = finalLayout;
|
||||
}
|
||||
|
||||
Uint32 VulkanRenderer::CurrentXfbCounterSlot() {
|
||||
const Uint name = MG_State::pGLContext->GetBoundTransformFeedbackName();
|
||||
const auto it = m_xfbCounterSlotByObject.find(name);
|
||||
if (it != m_xfbCounterSlotByObject.end()) {
|
||||
return it->second;
|
||||
}
|
||||
// Past the tracked set every object shares slot group 0. Only concurrently-paused
|
||||
// spans need distinct groups, and applications do not keep sixteen of those open.
|
||||
const Uint32 slot = m_xfbNextCounterSlot < kXfbCounterObjectSlots ? m_xfbNextCounterSlot++ : 0;
|
||||
m_xfbCounterSlotByObject[name] = slot;
|
||||
return slot;
|
||||
}
|
||||
|
||||
Bool VulkanRenderer::BeginXfbCaptureForDraw(FrameContext::FrameData& frame) {
|
||||
if (!m_transformFeedbackFeatureEnabled || MG_State::pGLContext == nullptr ||
|
||||
!MG_State::pGLContext->IsTransformFeedbackActive()) {
|
||||
return false;
|
||||
}
|
||||
// A paused span captures nothing, and the counter buffers keep their values, so the
|
||||
// next resumed draw appends exactly where the last captured one stopped - which is
|
||||
// what pause/resume means (ARB_transform_feedback2).
|
||||
if (MG_State::pGLContext->IsTransformFeedbackPaused()) {
|
||||
return false;
|
||||
}
|
||||
const auto& program = MG_State::pGLContext->GetTransformFeedbackProgram();
|
||||
if (!program || program->GetTransformFeedbackVaryingCount() == 0) {
|
||||
return false;
|
||||
@@ -8017,7 +8040,7 @@ void main() {
|
||||
if (!m_xfbCounterBuffer.IsValid()) {
|
||||
if (!m_xfbCounterBuffer.Create({
|
||||
.allocator = m_allocator,
|
||||
.size = 16,
|
||||
.size = 16 * kXfbCounterObjectSlots,
|
||||
.usage = VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_COUNTER_BUFFER_BIT_EXT |
|
||||
VK_BUFFER_USAGE_TRANSFER_DST_BIT,
|
||||
.memoryUsage = VMA_MEMORY_USAGE_AUTO,
|
||||
@@ -8058,15 +8081,16 @@ void main() {
|
||||
s_vkCmdBindTransformFeedbackBuffersEXT(frame.commandBuffer, 0, static_cast<Uint32>(bufferCount), buffers,
|
||||
offsets, sizes);
|
||||
|
||||
const Uint32 counterSlot = CurrentXfbCounterSlot();
|
||||
const Uint64 generation = MG_State::pGLContext->GetTransformFeedbackGeneration();
|
||||
const Bool resume = m_xfbCountersValid && m_xfbLastSeenGeneration == generation;
|
||||
m_xfbLastSeenGeneration = generation;
|
||||
const Bool resume = m_xfbCountersValid[counterSlot] && m_xfbLastSeenGeneration[counterSlot] == generation;
|
||||
m_xfbLastSeenGeneration[counterSlot] = generation;
|
||||
|
||||
VkBuffer counterBuffers[4] = {};
|
||||
VkDeviceSize counterOffsets[4] = {};
|
||||
for (SizeT i = 0; i < bufferCount; ++i) {
|
||||
counterBuffers[i] = m_xfbCounterBuffer.GetHandle();
|
||||
counterOffsets[i] = static_cast<VkDeviceSize>(i) * 4;
|
||||
counterOffsets[i] = static_cast<VkDeviceSize>(counterSlot) * 16 + static_cast<VkDeviceSize>(i) * 4;
|
||||
}
|
||||
if (resume) {
|
||||
s_vkCmdBeginTransformFeedbackEXT(frame.commandBuffer, 0, static_cast<Uint32>(bufferCount),
|
||||
@@ -8083,15 +8107,16 @@ void main() {
|
||||
}
|
||||
const auto& program = MG_State::pGLContext->GetTransformFeedbackProgram();
|
||||
const SizeT bufferCount = program ? std::min<SizeT>(program->GetTransformFeedbackBufferCount(), 4) : 0;
|
||||
const Uint32 counterSlot = CurrentXfbCounterSlot();
|
||||
VkBuffer counterBuffers[4] = {};
|
||||
VkDeviceSize counterOffsets[4] = {};
|
||||
for (SizeT i = 0; i < bufferCount; ++i) {
|
||||
counterBuffers[i] = m_xfbCounterBuffer.GetHandle();
|
||||
counterOffsets[i] = static_cast<VkDeviceSize>(i) * 4;
|
||||
counterOffsets[i] = static_cast<VkDeviceSize>(counterSlot) * 16 + static_cast<VkDeviceSize>(i) * 4;
|
||||
}
|
||||
s_vkCmdEndTransformFeedbackEXT(frame.commandBuffer, 0, static_cast<Uint32>(bufferCount), counterBuffers,
|
||||
counterOffsets);
|
||||
m_xfbCountersValid = true;
|
||||
m_xfbCountersValid[counterSlot] = true;
|
||||
}
|
||||
|
||||
void VulkanRenderer::DrawArrays(const DrawCmd& payload) {
|
||||
|
||||
@@ -498,12 +498,19 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
static inline PFN_vkCmdBeginTransformFeedbackEXT s_vkCmdBeginTransformFeedbackEXT = nullptr;
|
||||
static inline PFN_vkCmdEndTransformFeedbackEXT s_vkCmdEndTransformFeedbackEXT = nullptr;
|
||||
// Counter buffers (one 4-byte slot per capture binding) let consecutive
|
||||
// draws within one glBeginTransformFeedback append GL-style.
|
||||
// draws within one glBeginTransformFeedback append GL-style. Transform feedback
|
||||
// objects can each hold an open, paused span at the same time, so the counters are
|
||||
// per object: one group of four slots each, handed out on first use.
|
||||
static constexpr SizeT kXfbCounterObjectSlots = 16;
|
||||
VkBufferObject m_xfbCounterBuffer;
|
||||
// Non-zero while inside a GL Begin/End with at least one captured draw
|
||||
// recorded; selects counter-buffer resume on the next captured draw.
|
||||
Bool m_xfbCountersValid = false;
|
||||
Uint64 m_xfbLastSeenGeneration = 0;
|
||||
UnorderedMap<Uint, Uint32> m_xfbCounterSlotByObject;
|
||||
Uint32 m_xfbNextCounterSlot = 0;
|
||||
// Set for a slot once a captured draw has been recorded into its span; selects
|
||||
// counter-buffer resume on the next captured draw of the same span.
|
||||
Array<Bool, kXfbCounterObjectSlots> m_xfbCountersValid{};
|
||||
Array<Uint64, kXfbCounterObjectSlots> m_xfbLastSeenGeneration{};
|
||||
// Counter slot group of the bound transform feedback object.
|
||||
Uint32 CurrentXfbCounterSlot();
|
||||
// Wraps a recorded draw with BeginTransformFeedbackEXT/EndTransformFeedbackEXT
|
||||
// when GL transform feedback is active; binds capture buffers on demand.
|
||||
Bool BeginXfbCaptureForDraw(FrameContext::FrameData& frame);
|
||||
|
||||
Reference in New Issue
Block a user