mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-18 00:58:30 +09:00
[Test] (MG_Test, MG_IntegrationTest, P5b): pin i1 seven class-B slots and the served compute program, and arm 24 inproc lane entries whose results only exist if the records crossed
This commit is contained in:
@@ -1935,6 +1935,55 @@ if (MOBILEGL_BUILD_DISAGGREGATED)
|
|||||||
ENVIRONMENT "${MGL_ITEST_GLES_SPLIT_ENVIRONMENT}"
|
ENVIRONMENT "${MGL_ITEST_GLES_SPLIT_ENVIRONMENT}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# ---- P5b package i1 (MG_Remote/CONTRACT-P5B.md §2 i1) --------------------------------
|
||||||
|
#
|
||||||
|
# THE LANE CASE THAT PROVES THE RECORDS CROSSED, and it is a RESULT and not a probe (R-16:
|
||||||
|
# a probe may not arm against a stub). Each of these four scenarios asserts a value that
|
||||||
|
# only exists if the verb ran on the apply thread - the texel a compute shader stored
|
||||||
|
# through an image unit, the word glCopyImageSubData moved, the counter an atomic add left
|
||||||
|
# behind, the block a program pipeline rebound - so a sink that DECLINED, or a client that
|
||||||
|
# fell through to the driver, is red by the number it reads back and not by a tally.
|
||||||
|
#
|
||||||
|
# Before i1 every one of these aborted at the client's Fatal{UnmigratedVerb} on the first
|
||||||
|
# glBindImageTexture / glDispatchCompute / glCopyImageSubData / glShaderStorageBlockBinding;
|
||||||
|
# all four are green under inproc now (the census: 13 + 6 + 11 + 3 DirectGLES entries).
|
||||||
|
# RED ONCE BY DOING X: return false from ServerVerbSink::OnLaunchGrid before the
|
||||||
|
# gl.DispatchCompute call and UnboundImageDescriptor / AtomicCounter / ProgramPipeline all
|
||||||
|
# read back their initial values.
|
||||||
|
#
|
||||||
|
# THE FILTERS ARE NARROWED TO THE CASES THAT ACTUALLY EMIT, and that is ScenarioFixture's
|
||||||
|
# rule rather than a convenience: an armed `DirectGLES.Split.` case must move the client
|
||||||
|
# encoder's record ordinal (ScenarioFixture.h:86), because a lane whose workload produced no
|
||||||
|
# record is the "resolved the transport and then fell through to the driver" shape that
|
||||||
|
# every pixel assertion is blind to. ProgramPipelineScenario's other nine cases are pure
|
||||||
|
# name/state cases that draw nothing, so the whole-scenario filter would arm them and they
|
||||||
|
# would be red for having nothing to say. The two named here are the ones that were
|
||||||
|
# Fatal{UnmigratedVerb, "ShaderStorageBlockBinding"} on the c0b head.
|
||||||
|
#
|
||||||
|
# One block per scenario, following the three above: a `:`-separated multi-pattern
|
||||||
|
# TEST_FILTER is unproven through gtest_discover_tests' flat PROPERTIES forwarding.
|
||||||
|
set(MGL_SPLIT_I1_SCENARIOS
|
||||||
|
UnboundImageDescriptorScenario # bind_shader_image (72) + launch_grid (60), 13 cases
|
||||||
|
CopyImageLayeredScenario # resource_copy_region (53), 6 cases
|
||||||
|
AtomicCounterScenario # launch_grid (60) + memory_barrier (61), 3 cases
|
||||||
|
ProgramPipelineScenario) # set_storage_block_binding (75), the 2 storage-block cases
|
||||||
|
set(MGL_SPLIT_I1_FILTER_UnboundImageDescriptorScenario "UnboundImageDescriptorScenario.*")
|
||||||
|
set(MGL_SPLIT_I1_FILTER_CopyImageLayeredScenario "CopyImageLayeredScenario.*")
|
||||||
|
set(MGL_SPLIT_I1_FILTER_AtomicCounterScenario "AtomicCounterScenario.*")
|
||||||
|
set(MGL_SPLIT_I1_FILTER_ProgramPipelineScenario "ProgramPipelineScenario.*StorageBlock*")
|
||||||
|
foreach(mglItestI1Scenario IN LISTS MGL_SPLIT_I1_SCENARIOS)
|
||||||
|
gtest_discover_tests(MobileGLIntegrationTest
|
||||||
|
TEST_PREFIX "DirectGLES.Split."
|
||||||
|
TEST_LIST "MGL_SPLIT_I1_${mglItestI1Scenario}_TESTS"
|
||||||
|
TEST_FILTER "${MGL_SPLIT_I1_FILTER_${mglItestI1Scenario}}"
|
||||||
|
DISCOVERY_TIMEOUT 30
|
||||||
|
PROPERTIES
|
||||||
|
LABELS "integration-gpu\;integration-split"
|
||||||
|
TIMEOUT ${MGL_ITEST_TIMEOUT}
|
||||||
|
ENVIRONMENT "${MGL_ITEST_GLES_SPLIT_ENVIRONMENT}"
|
||||||
|
)
|
||||||
|
endforeach()
|
||||||
|
|
||||||
# The split half of the counting pair. MGITEST_PERSISTENT_MAP_ARM=emulated is R-6: under split
|
# The split half of the counting pair. MGITEST_PERSISTENT_MAP_ARM=emulated is R-6: under split
|
||||||
# the adopt tier is pinned at T2, the resource owner declines every acquisition and the client
|
# the adopt tier is pinned at T2, the resource owner declines every acquisition and the client
|
||||||
# pushes the mapping's dirty blocks - so pmap must be non-zero and mpr must be the monolith
|
# pushes the mapping's dirty blocks - so pmap must be non-zero and mpr must be the monolith
|
||||||
|
|||||||
@@ -11,4 +11,13 @@ foreach(scenario @MGL_SPLIT_SMALL_RING_SCENARIOS@)
|
|||||||
"MOBILEGL_LOG_FILE_PATH=@CMAKE_CURRENT_BINARY_DIR@/split-logs/${entry}.log")
|
"MOBILEGL_LOG_FILE_PATH=@CMAKE_CURRENT_BINARY_DIR@/split-logs/${entry}.log")
|
||||||
endforeach()
|
endforeach()
|
||||||
endforeach()
|
endforeach()
|
||||||
|
# P5b package i1's four scenarios, the same shape as the small-ring loop above: every
|
||||||
|
# DirectGLES.Split. entry owns exactly one absolute log path and shares it with nobody, which
|
||||||
|
# is what SplitLogPaths.PrivateAndDistinct checks over the discovered set.
|
||||||
|
foreach(scenario @MGL_SPLIT_I1_SCENARIOS@)
|
||||||
|
foreach(entry IN LISTS MGL_SPLIT_I1_${scenario}_TESTS)
|
||||||
|
set_tests_properties("${entry}" PROPERTIES ENVIRONMENT
|
||||||
|
"MOBILEGL_LOG_FILE_PATH=@CMAKE_CURRENT_BINARY_DIR@/split-logs/${entry}.log")
|
||||||
|
endforeach()
|
||||||
|
endforeach()
|
||||||
# PersistentMapArm retains its existing private path and RESOURCE_LOCK: b1 reads it.
|
# PersistentMapArm retains its existing private path and RESOURCE_LOCK: b1 reads it.
|
||||||
|
|||||||
@@ -254,13 +254,23 @@ TEST_F(FieldOwnershipTest, TheReducedPathsUnmigratedFieldsAreAllAccountedFor) {
|
|||||||
EXPECT_EQ(MGPipeFieldOwnershipOf(MGPipeInputField::GetPixelStoreParameters, 1u),
|
EXPECT_EQ(MGPipeFieldOwnershipOf(MGPipeInputField::GetPixelStoreParameters, 1u),
|
||||||
MGPipeFieldOwnership::kFatal);
|
MGPipeFieldOwnership::kFatal);
|
||||||
|
|
||||||
// The three off the reduced path, each for its own checkable reason.
|
// The two off the reduced path, each for its own checkable reason. THREE UNTIL P5b: the
|
||||||
|
// third was GetProgramForDispatch, FATAL because "there is no compute on the reduced path",
|
||||||
|
// and package i1 is what put compute on the path (CONTRACT-P5B.md §6.9). It is asserted
|
||||||
|
// below in its new class rather than deleted from this case, because a field that quietly
|
||||||
|
// left the FATAL list is exactly what this case exists to catch.
|
||||||
EXPECT_EQ(MGPipeFieldOwnershipOf(MGPipeInputField::GetBoundTransformFeedbackName),
|
EXPECT_EQ(MGPipeFieldOwnershipOf(MGPipeInputField::GetBoundTransformFeedbackName),
|
||||||
MGPipeFieldOwnership::kFatal);
|
MGPipeFieldOwnership::kFatal);
|
||||||
EXPECT_EQ(MGPipeFieldOwnershipOf(MGPipeInputField::GetTransformFeedbackPausedPrimitiveCounter),
|
EXPECT_EQ(MGPipeFieldOwnershipOf(MGPipeInputField::GetTransformFeedbackPausedPrimitiveCounter),
|
||||||
MGPipeFieldOwnership::kFatal);
|
MGPipeFieldOwnership::kFatal);
|
||||||
|
// P5b i1: launch_grid (60) crosses, the backend's PrepareForCompute pulls the compute
|
||||||
|
// program inside it (DirectGLES.cpp:5779), and the field takes GetProgramForDraw's class
|
||||||
|
// and its retiring phases - so it is a measured DEBT now, not a defect.
|
||||||
EXPECT_EQ(MGPipeFieldOwnershipOf(MGPipeInputField::GetProgramForDispatch),
|
EXPECT_EQ(MGPipeFieldOwnershipOf(MGPipeInputField::GetProgramForDispatch),
|
||||||
MGPipeFieldOwnership::kFatal);
|
MGPipeFieldOwnership::kBarrierPulled);
|
||||||
|
EXPECT_EQ(MGPipeFieldOwnershipOf(MGPipeInputField::GetProgramForDispatch),
|
||||||
|
MGPipeFieldOwnershipOf(MGPipeInputField::GetProgramForDraw))
|
||||||
|
<< "GetProgramForDispatch is GetProgramForDraw's twin and must share its class";
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(FieldOwnershipTest, TheSevenStickyForwardsAgreeWithTheirFieldRows) {
|
TEST_F(FieldOwnershipTest, TheSevenStickyForwardsAgreeWithTheirFieldRows) {
|
||||||
@@ -516,17 +526,43 @@ TEST_F(FieldOwnershipTest, StrictErrorsAlsoPromotesTheStickyForwards) {
|
|||||||
|
|
||||||
// A FATAL-class read aborts whatever the knob says: no carrier, and the reduced path never
|
// A FATAL-class read aborts whatever the knob says: no carrier, and the reduced path never
|
||||||
// reads it, so it is a real defect rather than a debt.
|
// reads it, so it is a real defect rather than a debt.
|
||||||
|
// P5b i1: the exemplar MOVED. This case used GetProgramForDispatch, which is BARRIER-PULLED
|
||||||
|
// from i1 on (a debt the server serves, not an abort), so it would now assert that a served
|
||||||
|
// read aborts - green for the wrong reason at best. GetTransformFeedbackPausedPrimitiveCounter
|
||||||
|
// is the same statement with a field that is still FATAL: reachable only from class kQuery,
|
||||||
|
// which the reduced path never enters.
|
||||||
|
// RED ONCE BY DOING X: put GetProgramForDispatch back in the FATAL block of FieldOwnership.def
|
||||||
|
// and TheFieldOwnershipTableIsTheContractsTableRow's new kBarrierPulled expectation goes red by
|
||||||
|
// name; swap the field below for GetProgramForDispatch and THIS case goes red instead, because
|
||||||
|
// a barrier-pulled read under a stamp does not abort.
|
||||||
TEST_F(FieldOwnershipTest, AFatalClassReadAbortsEvenWithoutStrictErrors) {
|
TEST_F(FieldOwnershipTest, AFatalClassReadAbortsEvenWithoutStrictErrors) {
|
||||||
const ChildResult r = RunInChild([] {
|
const ChildResult r = RunInChild([] {
|
||||||
MGPipeServerStampVerbBoundary(MGPipeVerb::DrawArrays);
|
MGPipeServerStampVerbBoundary(MGPipeVerb::DrawArrays);
|
||||||
(void)gPipeInputs.GetProgramForDispatch();
|
(void)gPipeInputs.GetTransformFeedbackPausedPrimitiveCounter();
|
||||||
});
|
});
|
||||||
ASSERT_TRUE(DiedOfAbort(r)) << DescribeStatus(r) << "\n" << r.Log;
|
ASSERT_TRUE(DiedOfAbort(r)) << DescribeStatus(r) << "\n" << r.Log;
|
||||||
EXPECT_NE(r.Log.find("Fatal{UnmigratedPipeInput, \"GetProgramForDispatch@DrawArrays\"}"),
|
EXPECT_NE(r.Log.find("Fatal{UnmigratedPipeInput, "
|
||||||
|
"\"GetTransformFeedbackPausedPrimitiveCounter@DrawArrays\"}"),
|
||||||
std::string::npos)
|
std::string::npos)
|
||||||
<< r.Log;
|
<< r.Log;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// And the field that LEFT the FATAL class is served rather than fatal, under the verb that made
|
||||||
|
// it reachable. This is i1's half of the §6.9 grant made checkable: a dispatch stamp plus a read
|
||||||
|
// of the compute program must NOT abort, which is precisely the statement "compute is on the
|
||||||
|
// path now". RED ONCE BY DOING X: revert the FieldOwnership.def row to FATAL and this child
|
||||||
|
// aborts with Fatal{UnmigratedPipeInput, "GetProgramForDispatch@DispatchCompute"}.
|
||||||
|
TEST_F(FieldOwnershipTest, TheComputeProgramIsServedUnderADispatchStampFromP5bOn) {
|
||||||
|
const ChildResult r = RunInChild([] {
|
||||||
|
MGPipeServerStampVerbBoundary(MGPipeVerb::DispatchCompute);
|
||||||
|
(void)gPipeInputs.GetProgramForDispatch();
|
||||||
|
MGPipeServerClearVerbBoundary();
|
||||||
|
});
|
||||||
|
EXPECT_TRUE(ExitedWith(r, 0)) << DescribeStatus(r) << "\n" << r.Log;
|
||||||
|
EXPECT_EQ(r.Log.find("Fatal{UnmigratedPipeInput, \"GetProgramForDispatch"), std::string::npos)
|
||||||
|
<< r.Log;
|
||||||
|
}
|
||||||
|
|
||||||
// The argument-keyed row: the pack half is answerable and the unpack half is not, and the
|
// The argument-keyed row: the pack half is answerable and the unpack half is not, and the
|
||||||
// difference is the accessor's own argument rather than a second field id. Splitting the field
|
// difference is the accessor's own argument rather than a second field id. Splitting the field
|
||||||
// into two ids would have made the unpack half BARRIER-PULLED - silently served - which is
|
// into two ids would have made the unpack half BARRIER-PULLED - silently served - which is
|
||||||
|
|||||||
@@ -171,12 +171,21 @@ namespace {
|
|||||||
// =====================================================================================
|
// =====================================================================================
|
||||||
|
|
||||||
TEST(RemoteEmitTable, TheThreeClassesPartitionAllSeventyOneSlots) {
|
TEST(RemoteEmitTable, TheThreeClassesPartitionAllSeventyOneSlots) {
|
||||||
// CONTRACT-P5.md §7: 2 answered locally + 5 emitted + 64 Fatal. Read from the functions the
|
// CONTRACT-P5.md §7: 2 answered locally + 5 emitted + 64 Fatal at the P5b contract commit.
|
||||||
// table itself reports with - which is also what t1's arming condition reads - rather than
|
// Read from the functions the table itself reports with - which is also what t1's arming
|
||||||
// recomputed here, so a table that lost an emitter cannot look like one that never had it.
|
// condition reads - rather than recomputed here, so a table that lost an emitter cannot look
|
||||||
|
// like one that never had it.
|
||||||
|
//
|
||||||
|
// P5b package i1 (CONTRACT-P5B.md §2 i1) flipped SEVEN slots C -> B: BindImageTexture,
|
||||||
|
// DispatchCompute, DispatchComputeIndirect, MemoryBarrier, MemoryBarrierByRegion,
|
||||||
|
// CopyImageSubData, ShaderStorageBlockBinding. So the two numbers that move are 5 -> 12 and
|
||||||
|
// 64 -> 57, and the SUM below is the invariant that does not move whichever package lands
|
||||||
|
// next. RED ONCE BY DOING X: comment out `table.GL.MemoryBarrier = &EmitMemoryBarrier;` in
|
||||||
|
// BuildRemoteEmitTable and this case stays green while NoSlotIsNull goes red - which is why
|
||||||
|
// the per-package count is asserted here and the null walk is a separate case.
|
||||||
EXPECT_EQ(LocallyAnsweredSlotCount(), 2u);
|
EXPECT_EQ(LocallyAnsweredSlotCount(), 2u);
|
||||||
EXPECT_EQ(ImplementedVerbCount(), 5u);
|
EXPECT_EQ(ImplementedVerbCount(), 12u);
|
||||||
EXPECT_EQ(UnmigratedSlotCount(), 64u);
|
EXPECT_EQ(UnmigratedSlotCount(), 57u);
|
||||||
EXPECT_EQ(LocallyAnsweredSlotCount() + ImplementedVerbCount() + UnmigratedSlotCount(),
|
EXPECT_EQ(LocallyAnsweredSlotCount() + ImplementedVerbCount() + UnmigratedSlotCount(),
|
||||||
kRemoteEmitSlotCount);
|
kRemoteEmitSlotCount);
|
||||||
}
|
}
|
||||||
@@ -257,6 +266,49 @@ TEST(RemoteEmitTable, SetSwapIntervalIsClassCAndSaysSo) {
|
|||||||
EXPECT_NE(r.Log.find("Fatal{UnmigratedVerb, \"SetSwapInterval\"}"), std::string::npos) << r.Log;
|
EXPECT_NE(r.Log.find("Fatal{UnmigratedVerb, \"SetSwapInterval\"}"), std::string::npos) << r.Log;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif // MGTEST_HAVE_FORK
|
||||||
|
|
||||||
|
// ---- P5b package i1 (MG_Remote/CONTRACT-P5B.md §2 i1) -------------------------------------
|
||||||
|
|
||||||
|
TEST(RemoteEmitTable, TheSevenI1SlotsAreClassBAndAreNotTheFatalThunk) {
|
||||||
|
// The census's five measured slots plus the two companions that share their rows. Named
|
||||||
|
// rather than counted, so a table that flipped a DIFFERENT seven is red here and not only
|
||||||
|
// in the arithmetic. The comparison is against a slot that is still class C: a flipped slot
|
||||||
|
// and an unflipped one cannot be the same pointer, which is what a forgotten class-B
|
||||||
|
// assignment would look like (class C is assigned FIRST in BuildRemoteEmitTable precisely so
|
||||||
|
// that the mistake is loud rather than null).
|
||||||
|
const MG_Backend::GlobalBackendFunctionsTable& table = RemoteEmitTable();
|
||||||
|
const void* fatal = reinterpret_cast<const void*>(table.GL.GetTexImage); // wave-3 tail, class C
|
||||||
|
ASSERT_NE(fatal, nullptr);
|
||||||
|
const void* const i1[] = {
|
||||||
|
reinterpret_cast<const void*>(table.GL.BindImageTexture),
|
||||||
|
reinterpret_cast<const void*>(table.GL.DispatchCompute),
|
||||||
|
reinterpret_cast<const void*>(table.GL.DispatchComputeIndirect),
|
||||||
|
reinterpret_cast<const void*>(table.GL.MemoryBarrier),
|
||||||
|
reinterpret_cast<const void*>(table.GL.MemoryBarrierByRegion),
|
||||||
|
reinterpret_cast<const void*>(table.GL.CopyImageSubData),
|
||||||
|
reinterpret_cast<const void*>(table.GL.ShaderStorageBlockBinding),
|
||||||
|
};
|
||||||
|
static const char* const kNames[] = {"BindImageTexture", "DispatchCompute",
|
||||||
|
"DispatchComputeIndirect", "MemoryBarrier",
|
||||||
|
"MemoryBarrierByRegion", "CopyImageSubData",
|
||||||
|
"ShaderStorageBlockBinding"};
|
||||||
|
for (SizeT i = 0; i < sizeof(i1) / sizeof(i1[0]); ++i) {
|
||||||
|
EXPECT_NE(i1[i], nullptr) << kNames[i] << " is null";
|
||||||
|
EXPECT_NE(i1[i], fatal) << kNames[i]
|
||||||
|
<< " still points at an UnmigratedVerbFatal thunk; i1 flipped it "
|
||||||
|
"to class B";
|
||||||
|
}
|
||||||
|
// The two barrier slots and the two dispatch slots share a WIRE ROW but not an emitter: the
|
||||||
|
// discriminant (ByRegion / IsIndirect) is set by the emitter, so one thunk for both would
|
||||||
|
// carry the wrong one.
|
||||||
|
EXPECT_NE(i1[3], i1[4]) << "MemoryBarrier and MemoryBarrierByRegion share memory_barrier (61) "
|
||||||
|
"but must set opposite ByRegion values";
|
||||||
|
EXPECT_NE(i1[1], i1[2]) << "DispatchCompute and DispatchComputeIndirect share launch_grid (60) "
|
||||||
|
"but must set opposite IsIndirect values";
|
||||||
|
}
|
||||||
|
|
||||||
|
#if MGTEST_HAVE_FORK
|
||||||
TEST(RemoteEmitTable, AClassBSlotWithNoSessionAbortsRatherThanFallingThrough) {
|
TEST(RemoteEmitTable, AClassBSlotWithNoSessionAbortsRatherThanFallingThrough) {
|
||||||
// The other half of "no slot may fall through to the driver". With no ClientSession the
|
// The other half of "no slot may fall through to the driver". With no ClientSession the
|
||||||
// emitter has nowhere to put the record, and the one thing it may not do is return quietly:
|
// emitter has nowhere to put the record, and the one thing it may not do is return quietly:
|
||||||
@@ -265,6 +317,53 @@ TEST(RemoteEmitTable, AClassBSlotWithNoSessionAbortsRatherThanFallingThrough) {
|
|||||||
ASSERT_TRUE(DiedOfAbort(r)) << DescribeStatus(r) << "\n" << r.Log;
|
ASSERT_TRUE(DiedOfAbort(r)) << DescribeStatus(r) << "\n" << r.Log;
|
||||||
EXPECT_NE(r.Log.find("Fatal{NoClientSession, \"Clear\"}"), std::string::npos) << r.Log;
|
EXPECT_NE(r.Log.find("Fatal{NoClientSession, \"Clear\"}"), std::string::npos) << r.Log;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(RemoteEmitTable, EachI1SlotReachesRequireSessionUnderItsOwnName) {
|
||||||
|
// The behavioural half: an i1 slot is class B, so with no ClientSession it reaches
|
||||||
|
// RequireSession and aborts Fatal{NoClientSession, "<slot>"} - NOT Fatal{UnmigratedVerb}
|
||||||
|
// (which would mean the flip never happened) and NOT quietly (which is the split lane
|
||||||
|
// running monolith and going green, R-4). The name in the message is the slot's own, which
|
||||||
|
// is the half a single case could not state.
|
||||||
|
//
|
||||||
|
// RED ONCE BY DOING X: put `X(MemoryBarrier, void, (GLbitfield))` back in
|
||||||
|
// MGR_UNMIGRATED_I1_SLOTS and drop `table.GL.MemoryBarrier = &EmitMemoryBarrier;` - the
|
||||||
|
// MemoryBarrier arm below then finds Fatal{UnmigratedVerb, "MemoryBarrier"} instead.
|
||||||
|
const ChildResult barrier = RunInChild([] { RemoteEmitTable().GL.MemoryBarrier(0x2000); });
|
||||||
|
ASSERT_TRUE(DiedOfAbort(barrier)) << DescribeStatus(barrier) << "\n" << barrier.Log;
|
||||||
|
EXPECT_NE(barrier.Log.find("Fatal{NoClientSession, \"MemoryBarrier\"}"), std::string::npos)
|
||||||
|
<< barrier.Log;
|
||||||
|
EXPECT_EQ(barrier.Log.find("Fatal{UnmigratedVerb"), std::string::npos)
|
||||||
|
<< "MemoryBarrier is class B from P5b i1 on:\n"
|
||||||
|
<< barrier.Log;
|
||||||
|
|
||||||
|
const ChildResult dispatch = RunInChild([] { RemoteEmitTable().GL.DispatchCompute(1, 1, 1); });
|
||||||
|
ASSERT_TRUE(DiedOfAbort(dispatch)) << DescribeStatus(dispatch) << "\n" << dispatch.Log;
|
||||||
|
EXPECT_NE(dispatch.Log.find("Fatal{NoClientSession, \"DispatchCompute\"}"), std::string::npos)
|
||||||
|
<< dispatch.Log;
|
||||||
|
|
||||||
|
const ChildResult bind = RunInChild(
|
||||||
|
[] { RemoteEmitTable().GL.BindImageTexture(0, 1, 0, GL_FALSE, 0, 0x88BA, 0x8058); });
|
||||||
|
ASSERT_TRUE(DiedOfAbort(bind)) << DescribeStatus(bind) << "\n" << bind.Log;
|
||||||
|
EXPECT_NE(bind.Log.find("Fatal{NoClientSession, \"BindImageTexture\"}"), std::string::npos)
|
||||||
|
<< bind.Log;
|
||||||
|
|
||||||
|
const ChildResult ssbo = RunInChild(
|
||||||
|
[] { RemoteEmitTable().GL.ShaderStorageBlockBinding(1, "Blk", 2); });
|
||||||
|
ASSERT_TRUE(DiedOfAbort(ssbo)) << DescribeStatus(ssbo) << "\n" << ssbo.Log;
|
||||||
|
EXPECT_NE(ssbo.Log.find("Fatal{NoClientSession, \"ShaderStorageBlockBinding\"}"),
|
||||||
|
std::string::npos)
|
||||||
|
<< ssbo.Log;
|
||||||
|
|
||||||
|
const ChildResult copy = RunInChild([] {
|
||||||
|
const MG_Backend::CopyImageEndpoint src{};
|
||||||
|
const MG_Backend::CopyImageEndpoint dst{};
|
||||||
|
RemoteEmitTable().GL.CopyImageSubData(src, 0x0DE1, 0, 0, 0, 0, dst, 0x0DE1, 0, 0, 0, 0, 1,
|
||||||
|
1, 1);
|
||||||
|
});
|
||||||
|
ASSERT_TRUE(DiedOfAbort(copy)) << DescribeStatus(copy) << "\n" << copy.Log;
|
||||||
|
EXPECT_NE(copy.Log.find("Fatal{NoClientSession, \"CopyImageSubData\"}"), std::string::npos)
|
||||||
|
<< copy.Log;
|
||||||
|
}
|
||||||
#endif // MGTEST_HAVE_FORK
|
#endif // MGTEST_HAVE_FORK
|
||||||
|
|
||||||
// =====================================================================================
|
// =====================================================================================
|
||||||
|
|||||||
Reference in New Issue
Block a user