mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
[Magma] consume wire-owned transform feedback capture spans
This commit is contained in:
@@ -11904,10 +11904,6 @@ void main() {
|
||||
if (MGB_CTX->IsTransformFeedbackPaused()) {
|
||||
return false;
|
||||
}
|
||||
const auto& program = MGB_CTX->GetTransformFeedbackProgram();
|
||||
if (!program || program->GetTransformFeedbackVaryingCount() == 0) {
|
||||
return false;
|
||||
}
|
||||
// The bound pipeline's last pre-rasterization stage has to have been declared with Xfb
|
||||
// (VUID-vkCmdBeginTransformFeedbackEXT-None-04128). Everything above this line reads GL
|
||||
// state, which cannot answer that: a program can be built as a capture variant and still
|
||||
@@ -11921,24 +11917,19 @@ void main() {
|
||||
"undefined behaviour rather than a capture");
|
||||
return false;
|
||||
}
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
if (MG_Config::Transport != MG_Config::TransportMode::Monolith)
|
||||
return BeginWireXfbCaptureForDraw(frame);
|
||||
#endif
|
||||
const auto& program = MGB_CTX->GetTransformFeedbackProgram();
|
||||
if (!program || program->GetTransformFeedbackVaryingCount() == 0) {
|
||||
return false;
|
||||
}
|
||||
const SizeT bufferCount = std::min<SizeT>(program->GetTransformFeedbackBufferCount(), 4);
|
||||
if (bufferCount == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_xfbCounterBuffer.IsValid()) {
|
||||
if (!m_xfbCounterBuffer.Create({
|
||||
.allocator = m_allocator,
|
||||
.size = 16 * kXfbCounterObjectSlots,
|
||||
.usage = VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_COUNTER_BUFFER_BIT_EXT |
|
||||
VK_BUFFER_USAGE_TRANSFER_DST_BIT,
|
||||
.memoryUsage = VMA_MEMORY_USAGE_AUTO,
|
||||
})) {
|
||||
MGLOG_E_ONCE("BeginXfbCaptureForDraw: failed to create the counter buffer");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
VkBuffer buffers[4] = {};
|
||||
VkDeviceSize offsets[4] = {};
|
||||
VkDeviceSize sizes[4] = {};
|
||||
@@ -11976,8 +11967,31 @@ void main() {
|
||||
sizes[i] = rangeSize;
|
||||
}
|
||||
|
||||
s_vkCmdBindTransformFeedbackBuffersEXT(frame.commandBuffer, 0, static_cast<Uint32>(bufferCount), buffers,
|
||||
offsets, sizes);
|
||||
return BeginXfbCaptureWithBuffers(frame, static_cast<Uint32>(bufferCount), buffers, offsets, sizes);
|
||||
}
|
||||
|
||||
Bool VulkanRenderer::BeginXfbCaptureWithBuffers(FrameContext::FrameData& frame, Uint32 bufferCount,
|
||||
const VkBuffer* buffers, const VkDeviceSize* offsets, const VkDeviceSize* sizes) {
|
||||
if (!m_xfbCounterBuffer.IsValid()) {
|
||||
if (!m_xfbCounterBuffer.Create({
|
||||
.allocator = m_allocator,
|
||||
.size = 16 * kXfbCounterObjectSlots,
|
||||
.usage = VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_COUNTER_BUFFER_BIT_EXT |
|
||||
VK_BUFFER_USAGE_TRANSFER_DST_BIT,
|
||||
.memoryUsage = VMA_MEMORY_USAGE_AUTO,
|
||||
})) {
|
||||
MGLOG_E_ONCE("BeginXfbCaptureForDraw: failed to create the counter buffer");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
m_currentDrawXfbBufferCount = bufferCount;
|
||||
m_currentDrawXfbBufferMask = 0;
|
||||
for (Uint32 i = 0; i < bufferCount; ++i) {
|
||||
if (buffers[i] == VK_NULL_HANDLE) continue;
|
||||
m_currentDrawXfbBufferMask |= 1u << i;
|
||||
s_vkCmdBindTransformFeedbackBuffersEXT(frame.commandBuffer, i, 1, buffers + i,
|
||||
offsets + i, sizes + i);
|
||||
}
|
||||
|
||||
const Uint32 counterSlot = CurrentXfbCounterSlot();
|
||||
const Uint64 generation = MGB_CTX->GetTransformFeedbackGeneration();
|
||||
@@ -11987,6 +12001,7 @@ void main() {
|
||||
VkBuffer counterBuffers[4] = {};
|
||||
VkDeviceSize counterOffsets[4] = {};
|
||||
for (SizeT i = 0; i < bufferCount; ++i) {
|
||||
if (!(m_currentDrawXfbBufferMask & (1u << i))) continue;
|
||||
counterBuffers[i] = m_xfbCounterBuffer.GetHandle();
|
||||
counterOffsets[i] = static_cast<VkDeviceSize>(counterSlot) * 16 + static_cast<VkDeviceSize>(i) * 4;
|
||||
}
|
||||
@@ -12003,12 +12018,14 @@ void main() {
|
||||
if (!began) {
|
||||
return;
|
||||
}
|
||||
const auto& program = MGB_CTX->GetTransformFeedbackProgram();
|
||||
const SizeT bufferCount = program ? std::min<SizeT>(program->GetTransformFeedbackBufferCount(), 4) : 0;
|
||||
// Exactly the targets used by this draw's Begin, independent of frontend
|
||||
// objects and of whether the span came from the wire or monolith state.
|
||||
const Uint32 bufferCount = m_currentDrawXfbBufferCount;
|
||||
const Uint32 counterSlot = CurrentXfbCounterSlot();
|
||||
VkBuffer counterBuffers[4] = {};
|
||||
VkDeviceSize counterOffsets[4] = {};
|
||||
for (SizeT i = 0; i < bufferCount; ++i) {
|
||||
if (!(m_currentDrawXfbBufferMask & (1u << i))) continue;
|
||||
counterBuffers[i] = m_xfbCounterBuffer.GetHandle();
|
||||
counterOffsets[i] = static_cast<VkDeviceSize>(counterSlot) * 16 + static_cast<VkDeviceSize>(i) * 4;
|
||||
}
|
||||
|
||||
@@ -806,6 +806,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// Wraps a recorded draw with BeginTransformFeedbackEXT/EndTransformFeedbackEXT
|
||||
// when GL transform feedback is active; binds capture buffers on demand.
|
||||
Bool BeginXfbCaptureForDraw(FrameContext::FrameData& frame);
|
||||
Bool BeginXfbCaptureWithBuffers(FrameContext::FrameData& frame, Uint32 count,
|
||||
const VkBuffer* buffers, const VkDeviceSize* offsets,
|
||||
const VkDeviceSize* sizes);
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
Bool BeginWireXfbCaptureForDraw(FrameContext::FrameData& frame);
|
||||
#endif
|
||||
Uint32 m_currentDrawXfbBufferCount = 0;
|
||||
Uint32 m_currentDrawXfbBufferMask = 0;
|
||||
void EndXfbCaptureForDraw(FrameContext::FrameData& frame, Bool began);
|
||||
// Makes the captured bytes visible to whatever reads them next. Deferred rather than
|
||||
// recorded next to the capture, because the capturing draw runs inside a render pass
|
||||
|
||||
@@ -16,6 +16,47 @@
|
||||
std::abort();
|
||||
}
|
||||
|
||||
static const MG_Pipe::MGPStreamOutputBegin& WireXfbSpan() {
|
||||
const auto& state = MG_Pipe::MGPipeApplier();
|
||||
const auto found = state.StreamOutputSpans.find(state.BoundStreamOutputLifetimeId);
|
||||
if (found == state.StreamOutputSpans.end()) MagmaWireFatal("xfb-span-record");
|
||||
return found->second;
|
||||
}
|
||||
|
||||
Bool VulkanRenderer::BeginWireXfbCaptureForDraw(FrameContext::FrameData& frame) {
|
||||
const auto& span = WireXfbSpan();
|
||||
const auto program = WireProgramSource(span.CaptureProgram);
|
||||
const Uint32 count = static_cast<Uint32>(std::min<SizeT>(program.GetTransformFeedbackBufferCount(), 4));
|
||||
if (!count || !program.GetTransformFeedbackVaryingCount()) return false;
|
||||
VkBuffer buffers[4]{};
|
||||
VkDeviceSize offsets[4]{};
|
||||
VkDeviceSize sizes[4]{};
|
||||
Bool hasTarget = false;
|
||||
for (Uint32 i = 0; i < count; ++i) {
|
||||
// A leading gl_NextBuffer leaves a hole in the binding indices. No
|
||||
// output names that zero-stride target, and GL need not bind a buffer.
|
||||
if (!program.GetTransformFeedbackStride(i)) continue;
|
||||
const auto& target = span.Targets[i];
|
||||
BufferSlice slice{};
|
||||
if (MG_Pipe::MGPipeHandleIsNull(target.Res) ||
|
||||
!m_bufferManager.AcquireWireSlice(BufferKind::Vertex, target.Res, slice))
|
||||
MagmaWireFatal("xfb-buffer-resource");
|
||||
if (target.Offset >= slice.size) return false;
|
||||
const Uint64 size = target.Size == 0 || target.Size == MG_Pipe::kMGPipeWholeBuffer
|
||||
? slice.size - target.Offset : target.Size;
|
||||
if (!size || size > slice.size - target.Offset) return false;
|
||||
buffers[i] = slice.buffer;
|
||||
offsets[i] = slice.offset + target.Offset;
|
||||
sizes[i] = size;
|
||||
hasTarget = true;
|
||||
// The server owns the storage. Publish dirtiness through the existing
|
||||
// reverse channel so CPU readback does not reuse the client's old shadow.
|
||||
m_bufferManager.MarkWireBufferGpuWritten(target.Res, target.Offset, size);
|
||||
}
|
||||
if (!hasTarget) return false;
|
||||
return BeginXfbCaptureWithBuffers(frame, count, buffers, offsets, sizes);
|
||||
}
|
||||
|
||||
static Bool ConvertWireVertexStream(const MGPVertexAttribWire& attr,
|
||||
VertexInputStateFactory::VertexStreamConversion conversion, const Uint8* source,
|
||||
SizeT stride, SizeT count, Vector<Uint8>& output) {
|
||||
@@ -159,8 +200,9 @@
|
||||
auto& state = MG_Pipe::MGPipeApplier();
|
||||
const auto program = WireProgramSource(state.BoundShaderCso);
|
||||
if (!program.GetLinkStatus() || !program.GetSpirvStatus()) return false;
|
||||
if (aspects & DrawSetupAspect::IndirectDrawBuffer || MGB_CTX->IsTransformFeedbackActive())
|
||||
if (aspects & DrawSetupAspect::IndirectDrawBuffer)
|
||||
WireBufferLegacyFatal();
|
||||
MakeXfbWritesVisible();
|
||||
const auto* fbo = state.DrawFramebuffer();
|
||||
if (!fbo) MagmaWireFatal("draw-framebuffer-record");
|
||||
RetireWireDrawPass();
|
||||
@@ -170,6 +212,9 @@
|
||||
|
||||
ProgramFactory::CompileOptionFlags flags =
|
||||
ProgramFactory::CompileOptionFlags(GetBaseTransformFlagsRaw(fbo->IsDefault));
|
||||
if (m_transformFeedbackFeatureEnabled && MGB_CTX->IsTransformFeedbackActive() &&
|
||||
program.GetTransformFeedbackVaryingCount() > 0)
|
||||
flags |= ProgramFactory::CompileOptionBit::XfbCapture;
|
||||
const auto* programObject = &m_programFactory->GetOrCreateProgram(program, flags);
|
||||
if (!(aspects & DrawSetupAspect::IndexBuffer) && programObject->readsBaseVertexBuiltin) {
|
||||
flags |= ProgramFactory::CompileOptionBit::ZeroBaseVertex;
|
||||
@@ -177,6 +222,7 @@
|
||||
}
|
||||
if (programObject->pointSizeCapabilityUnsupported) return false;
|
||||
const auto& programObj = *programObject;
|
||||
m_currentDrawXfbCaptureDeclined = programObj.xfbCaptureDeclined;
|
||||
if (!m_uniformManager->PrepareWireTextureResources(program, programObj)) return false;
|
||||
// Lazy image uploads/promotions can submit the old recording. Resolve
|
||||
// ALL attachments before descriptor binding captures the current cmd.
|
||||
|
||||
Reference in New Issue
Block a user