mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-16 06:23:23 +09:00
[MG_Impl, MG_Remote] (Disaggregated): own client vertex and index data before draw emission
This commit is contained in:
@@ -53,10 +53,12 @@
|
||||
// set_index_buffer / the buffer's own constructor did that), for MGPDrawInfo::IndexResource and
|
||||
// the two indirect-buffer handles.
|
||||
#include <MG_Impl/Pipe/ResourceTracker.h>
|
||||
#include <MG_Impl/Pipe/OwnedDrawInputs.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
|
||||
#include "WireTables.h"
|
||||
#include <MG_Impl/Pipe/FramebufferEmit.h>
|
||||
@@ -214,18 +216,6 @@ namespace MobileGL::MG_Remote::Client {
|
||||
return *session;
|
||||
}
|
||||
|
||||
// The two hooks b1 wrote and deliberately left with no caller, because the call site is
|
||||
// this file's. ORDER: the push first (it produces resource_subdata records that must
|
||||
// precede the verb on SEG_CMD), then the mark walk, then the verb record. The deferred
|
||||
// destroy drain rides the same boundary: it replays, on this GL thread, the death
|
||||
// announcements whose last SharedPtr dropped on the apply thread (PipeMutation.h's
|
||||
// deferred destroy queue), and its records must also precede this verb.
|
||||
void BeforeDrawVerb() {
|
||||
PersistentMapTracker::Instance().PushDrawConsumers();
|
||||
MG_Pipe::MGPipeDrainDeferredDestroys();
|
||||
MarkGpuWritesForDraw();
|
||||
}
|
||||
|
||||
// A verb that reads buffers but starts no shader: clear, blit, readback, present. The
|
||||
// push still has to run - a coherent map is read by the GPU on any of them - but there
|
||||
// is no shader that could write one, so no mark walk.
|
||||
@@ -383,11 +373,12 @@ namespace MobileGL::MG_Remote::Client {
|
||||
nullptr, 0, nullptr, 0, nullptr);
|
||||
}
|
||||
|
||||
// P5e (vi), ID-82: declared here because the REDUCED DrawArrays path below predates the
|
||||
// nineteen-slot block that defines them, and it needs the same two answers. One
|
||||
// definition each, further down, beside the rest of the draw plan.
|
||||
// All draw entry points share the owned-input preparation and emission below.
|
||||
[[noreturn]] void RefuseDrawByName(const char* slot, const char* qualifier);
|
||||
Bool RunAheadWouldSkipTheWait(ClientSession& session);
|
||||
void EmitDrawRecord(const char* slot, MG_Pipe::MGPDrawInfo& info,
|
||||
const MG_Pipe::MGPDrawRange* ranges, Uint32 numDraws,
|
||||
const void* clientIndices, Uint64 clientIndexBytes,
|
||||
const MG_Pipe::MGPDrawIndirect* indirect);
|
||||
|
||||
// Does the bound VAO fetch any ENABLED attribute out of the application's own memory.
|
||||
// The test is EmitVertexBuffers' own (`attrib.Enabled && !attrib.Buffer` is exactly what
|
||||
@@ -405,28 +396,7 @@ namespace MobileGL::MG_Remote::Client {
|
||||
}
|
||||
|
||||
void EmitDrawArrays(GLenum mode, GLint first, GLsizei count) {
|
||||
ClientSession& session = RequireSession("DrawArrays");
|
||||
|
||||
// P5e (vi): the same refusal the nineteen-slot block raises in EmitDrawRecord, and
|
||||
// this is the entry point the contract names by name - "DrawArrays+CLIENT_ARRAYS".
|
||||
// The probe runs only on this path's own draw, before the hooks, and it is inert
|
||||
// under lockstep (see RunAheadWouldSkipTheWait).
|
||||
const Bool clientArrays = BoundVaoHasClientVertexArrays(MG_State::pGLContext.get());
|
||||
if (clientArrays && RunAheadWouldSkipTheWait(session)) {
|
||||
RefuseDrawByName("DrawArrays", "CLIENT_ARRAYS");
|
||||
}
|
||||
|
||||
BeforeDrawVerb();
|
||||
|
||||
if (g_dropDrawEmission) {
|
||||
// E2's LOAD-BEARING negative control. Same shape as the clear drop and the same
|
||||
// rule: everything above still ran - the persistent-map push and the GPU-write
|
||||
// mark walk both happened - so the ONLY difference from the live arm is that
|
||||
// this frame's geometry never crossed the ring. A lane that still matches its
|
||||
// golden with this armed did not get its picture from the wire.
|
||||
++g_droppedDrawEmissions;
|
||||
return;
|
||||
}
|
||||
|
||||
MG_Pipe::MGPDrawInfo info{};
|
||||
info.Mode = static_cast<Uint32>(mode);
|
||||
@@ -450,8 +420,7 @@ namespace MobileGL::MG_Remote::Client {
|
||||
// payload and Fatals on a disagreement, on THIS side - so a NumDraws that drifted
|
||||
// from the tail is a producer-side abort rather than a corrupt stream a peer has to
|
||||
// diagnose.
|
||||
session.EmitAndWait(MG_Pipe::MGPWireOp::DrawVbo, &info, sizeof(info), &range,
|
||||
sizeof(range), nullptr, 0, nullptr);
|
||||
EmitDrawRecord("DrawArrays", info, &range, 1, nullptr, 0, nullptr);
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
@@ -467,11 +436,8 @@ namespace MobileGL::MG_Remote::Client {
|
||||
// ID-18), honours the E2 draw-drop control for every draw record and not only the P5
|
||||
// one, stages a client index array into SEG_STAGE when there is one, and emits.
|
||||
//
|
||||
// WHAT IS REFUSED BY NAME, ID-57's shape (Fatal{UnmigratedVerb, "<slot>+<QUALIFIER>"}),
|
||||
// never rendered wrong and never fallen through to the driver (R-4):
|
||||
// +CLIENT_INDICES a glMultiDrawElements* with no element buffer bound: `indices[i]`
|
||||
// are drawcount separate client pointers and one span names one run;
|
||||
// P8's HostResolve.cpp flattens it. No measured workload has one.
|
||||
// Client arrays and client indices become owned ordinary buffer resources.
|
||||
// Remaining invalid/unrepresentable draw shapes are named before encoding:
|
||||
// +CLIENT_COMMANDS an indirect draw with no GL_DRAW_INDIRECT_BUFFER bound: `indirect`
|
||||
// would be a host pointer, which rule B forbids on the wire.
|
||||
// +UNBOUND_PARAMETER an *IndirectCount with no GL_PARAMETER_BUFFER (the frontend has
|
||||
@@ -481,27 +447,6 @@ namespace MobileGL::MG_Remote::Client {
|
||||
// indices (or past 2^32 of them): the record spells Start in
|
||||
// indices, and rounding would draw from the wrong element.
|
||||
|
||||
// P5e (vi), ID-82 / CONTRACT-P5E §5.1: "would this session publish a draw and move on".
|
||||
//
|
||||
// IT IS THE CONJUNCTION MGPipeTypes.h STATES FOR RunAheadArmed(), spelled here rather
|
||||
// than called, and that is a SEAM, not a duplication: ClientSession::RunAheadArmed() is
|
||||
// package ra's to land (it latches the same three at the first caps adoption after Start
|
||||
// and may afterwards only turn OFF). Until it exists this is the same answer computed
|
||||
// from the same three inputs, and it is inert for the whole phase because the third one
|
||||
// - the server's kCapRunAheadApply - is not published until the integration commit
|
||||
// (kMGPipeP5eRunAheadReady). WHEN ra LANDS, THIS BODY BECOMES `session.RunAheadArmed()`:
|
||||
// one latch, one answer, and no way for the refusal to disagree with the wait rule about
|
||||
// which arm the session is on.
|
||||
Bool RunAheadWouldSkipTheWait(ClientSession& session) {
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
return session.BarrierArmed() && MG_Config::Ipc.RunAhead != 0 &&
|
||||
session.Caps().HasCap(MG_Pipe::kCapRunAheadApply);
|
||||
#else
|
||||
(void)session;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
[[noreturn]] void RefuseDrawByName(const char* slot, const char* qualifier) {
|
||||
char name[96];
|
||||
std::snprintf(name, sizeof(name), "%s+%s", slot, qualifier);
|
||||
@@ -555,25 +500,22 @@ namespace MobileGL::MG_Remote::Client {
|
||||
const void* clientIndices, Uint64 clientIndexBytes,
|
||||
const MG_Pipe::MGPDrawIndirect* indirect) {
|
||||
ClientSession& session = RequireSession(slot);
|
||||
|
||||
// P5e (vi), ID-82 / ruling 2, BEFORE the pre-verb hooks and before the record: a
|
||||
// draw that fetches vertices out of the application's own memory has no wire form,
|
||||
// and under run-ahead the server would dereference a client pointer into memory
|
||||
// this thread is already past. It is refused BY NAME on the GL thread, in the
|
||||
// shape ID-57 gives every unmigrated draw shape, rather than rendered wrong (R-4).
|
||||
//
|
||||
// Under LOCKSTEP it is not refused and nothing here changes: the client is parked
|
||||
// in WaitForApplied for exactly this record, so the server's upload reads memory
|
||||
// that is not moving - which is ruling 4's "barriered records keep P5C's
|
||||
// semantics", and is why this is a run-ahead gate and not a transport gate.
|
||||
// Staging the bytes as a per-attribute {BindingIndex, MGHostSpan} tail, shaped like
|
||||
// kDrawHasUserIndices, is P8's and retires the refusal with it.
|
||||
if ((info.Flags & static_cast<Uint8>(MG_Pipe::kDrawClientArrays)) != 0 &&
|
||||
RunAheadWouldSkipTheWait(session)) {
|
||||
RefuseDrawByName(slot, "CLIENT_ARRAYS");
|
||||
PersistentMapTracker::Instance().PushDrawConsumers();
|
||||
MG_Pipe::MGPipeDrainDeferredDestroys();
|
||||
// Snapshot before marking THIS draw's potential GPU writes: resolving
|
||||
// a GPU-produced EBO here must not clear its pending mark for this draw.
|
||||
UniquePtr<MG_Pipe::MGPipeOwnedDrawInputs> ownedInputs;
|
||||
if ((info.Flags & MG_Pipe::kDrawClientArrays) != 0 || clientIndexBytes != 0) {
|
||||
if (MG_State::pGLContext == nullptr) RefuseDrawByName(slot, "NO_CONTEXT");
|
||||
ownedInputs = MakeUnique<MG_Pipe::MGPipeOwnedDrawInputs>(*MG_State::pGLContext);
|
||||
if (!ownedInputs->Prepare(info, ranges, numDraws, clientIndices, clientIndexBytes, indirect)) {
|
||||
MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation,
|
||||
MakeUnique<GenericErrorInfo>("MG_Remote/Client", slot,
|
||||
"the client vertex/index fetch range cannot be represented by its storage"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
BeforeDrawVerb();
|
||||
MarkGpuWritesForDraw();
|
||||
|
||||
if (g_dropDrawEmission) {
|
||||
// E2's negative control covers EVERY draw record, not only DrawArrays: the
|
||||
@@ -587,22 +529,7 @@ namespace MobileGL::MG_Remote::Client {
|
||||
Wire::WireTail tails[2] = {{ranges, static_cast<Uint64>(numDraws) * sizeof(MG_Pipe::MGPDrawRange)},
|
||||
{nullptr, 0}};
|
||||
Uint32 tailCount = 1;
|
||||
MG_Pipe::MGHostSpan span{};
|
||||
if (clientIndices != nullptr && clientIndexBytes != 0) {
|
||||
// The P8 resolve-on-client rule, applied: the bytes exist on the client only,
|
||||
// so the client stages them whole and the record names the run - Ptr = nullptr,
|
||||
// Seg = SEG_STAGE (rule B). The encoder runs all four honesty arms on the span
|
||||
// before it is published, and the sink resolves it through MGPipeHostBytes.
|
||||
const MG_Pipe::MGPBlobRef staged =
|
||||
session.Encoder().StageBytes(clientIndices, clientIndexBytes);
|
||||
span.Ptr = nullptr;
|
||||
span.Seg = staged.Seg;
|
||||
span.Offset = staged.Offset;
|
||||
span.Size = staged.Size;
|
||||
info.Flags |= MG_Pipe::kDrawHasUserIndices;
|
||||
tails[1] = {&span, sizeof(span)};
|
||||
tailCount = 2;
|
||||
} else if (indirect != nullptr) {
|
||||
if (indirect != nullptr) {
|
||||
info.Flags |= MG_Pipe::kDrawIsIndirect;
|
||||
tails[1] = {indirect, sizeof(*indirect)};
|
||||
tailCount = 2;
|
||||
@@ -735,17 +662,31 @@ namespace MobileGL::MG_Remote::Client {
|
||||
const RemoteDrawBindings bindings = ReadDrawBindings();
|
||||
const Uint8 indexSize = RemoteIndexSizeFor(type);
|
||||
if (indexSize == 0) RefuseDrawByName(slot, "INDEX_TYPE");
|
||||
if (!bindings.ElementBufferBound) RefuseDrawByName(slot, "CLIENT_INDICES");
|
||||
const auto n = static_cast<Uint32>(drawcount);
|
||||
MG_Pipe::MGPDrawInfo info = PlanDrawInfo(mode, indexSize, 1, 0, n, bindings);
|
||||
Vector<MG_Pipe::MGPDrawRange>& ranges = MultiDrawScratch(n);
|
||||
Vector<Uint8> ownedIndices;
|
||||
for (Uint32 i = 0; i < n; ++i) {
|
||||
if (!PlanDrawRange(bindings, indexSize, indices[i], count[i],
|
||||
basevertex != nullptr ? basevertex[i] : 0, ranges[i])) {
|
||||
RefuseDrawByName(slot, "INDEX_OFFSET");
|
||||
}
|
||||
if (!bindings.ElementBufferBound) {
|
||||
const Uint64 byteCount = static_cast<Uint64>(ranges[i].Count) * indexSize;
|
||||
if (byteCount > std::numeric_limits<Uint32>::max() - ownedIndices.size() ||
|
||||
(byteCount != 0 && indices[i] == nullptr)) {
|
||||
MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation,
|
||||
MakeUnique<GenericErrorInfo>("MG_Remote/Client", slot, "invalid client index span"));
|
||||
return;
|
||||
}
|
||||
ranges[i].Start = static_cast<Uint32>(ownedIndices.size() / indexSize);
|
||||
const SizeT at = ownedIndices.size();
|
||||
ownedIndices.resize(at + static_cast<SizeT>(byteCount));
|
||||
if (byteCount != 0) std::memcpy(ownedIndices.data() + at, indices[i], byteCount);
|
||||
}
|
||||
}
|
||||
EmitDrawRecord(slot, info, ranges.data(), n, nullptr, 0, nullptr);
|
||||
EmitDrawRecord(slot, info, ranges.data(), n, ownedIndices.empty() ? nullptr : ownedIndices.data(),
|
||||
ownedIndices.size(), nullptr);
|
||||
}
|
||||
void EmitMultiDrawElements(GLenum mode, const GLsizei* count, GLenum type,
|
||||
const GLvoid* const* indices, GLsizei drawcount) {
|
||||
|
||||
Reference in New Issue
Block a user