mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
[Merge] (P8): integrate package F - reclassify what our client cannot send: the multi-draw latches and Magma client-vertex-array become ProtocolCorruption, Magma mip shader-shape becomes a pre-growth named decline, unpublished cap bits and the index-mirror sentinel marked reserved
This commit is contained in:
@@ -104,3 +104,9 @@ MGL_WIRE_DECLINE(ShapeCreateImageFailed)
|
||||
// Appended by the B3 review round: the preserve-copy arm of a reallocation was the one exit in
|
||||
// SyncWireTextureShape still leaving no trace at all.
|
||||
MGL_WIRE_DECLINE(ShapePreserveCopyFailed)
|
||||
|
||||
// ---- VulkanRenderer::GenerateWireMipmap (P8-F) -----------------------------------------
|
||||
// `mipmap-shader-format-or-shape` was a Fatal on this arm and a decline on the monolith arm; it
|
||||
// is the monolith's decline now. Counted once per refused glGenerateMipmap, whose chain is left
|
||||
// as it was.
|
||||
MGL_WIRE_DECLINE(MipmapShaderFormatOrShape)
|
||||
|
||||
@@ -294,7 +294,14 @@
|
||||
const Uint32 location = input.bindingAttributeLocations[binding];
|
||||
const auto& attr = vao.Attributes[location];
|
||||
const auto& resource = state.VertexBuffers[location];
|
||||
if (MG_Pipe::MGPipeHandleIsNull(resource.Res)) MagmaWireFatal("client-vertex-array@P8");
|
||||
// P8-F: A RECORD INVARIANT, NOT AN UNMIGRATED VERB (it carried a P8 phase tag).
|
||||
// The client turns every enabled client-memory array into an owned buffer before the
|
||||
// draw record is built, or refuses the draw itself (MG_Impl/Pipe/OwnedDrawInputs.h),
|
||||
// so an attribute the program reads with no buffer handle is a foreign peer's record.
|
||||
if (MG_Pipe::MGPipeHandleIsNull(resource.Res))
|
||||
MG_Pipe::MGPipeSessionFail(MG_Pipe::MGPipeFatalFamily::ProtocolCorruption,
|
||||
"MGPipe: Fatal{ProtocolCorruption, \"Magma:client-vertex-array\"} location=%u: an "
|
||||
"attribute the program reads carries no buffer handle", location);
|
||||
BufferSlice slice{};
|
||||
if (!m_bufferManager.AcquireWireSlice(BufferKind::Vertex, resource.Res, slice))
|
||||
MagmaWireFatal("vertex-buffer-resource");
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
// missing WHICH feature bit - was the one thing it did not. The census, the retrace
|
||||
// refusal tally and every log grep key on the leading word, so it is unchanged and
|
||||
// unprefixed; everything after the first space is for the reader of the next device run.
|
||||
// P8-F: the refusal is a named decline now (WireDeclines.def MipmapShaderFormatOrShape);
|
||||
// the line it logs keeps this word and this detail.
|
||||
static String MagmaWireShapeDetail(const char* name, VkFormat format, VkFormat storageFormat,
|
||||
const VkFormatProperties& props, VkImageAspectFlags aspect,
|
||||
VkImageViewType viewType, VkImageUsageFlags usage,
|
||||
@@ -1400,8 +1402,10 @@
|
||||
base + 1, base + count - 1);
|
||||
return;
|
||||
}
|
||||
if (!m_textureManager->GrowWireTextureMipChain(handle,base+count,frame.commandBuffer))
|
||||
MagmaWireFatal("mipmap-growth");
|
||||
// P8-F: WHICH ARM TAKES THIS CHAIN IS DECIDED BEFORE THE CHAIN IS GROWN, for the
|
||||
// depth-stencil decline's reason above. Nothing here depends on the growth: the format,
|
||||
// its features, the knob tier, the aspect, view type, usage and layer window are the
|
||||
// same before and after it (GrowWireTextureMipChain carries every one of them over).
|
||||
VkFormatProperties props{};
|
||||
const VkFormat format = viewFormat == VK_FORMAT_UNDEFINED ? resource->format : viewFormat;
|
||||
vkGetPhysicalDeviceFormatProperties(m_physicalDevice.handle,format,&props);
|
||||
@@ -1411,6 +1415,50 @@
|
||||
(props.optimalTilingFeatures & required) == required;
|
||||
const VkFilter filter = props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT ?
|
||||
VK_FILTER_LINEAR : VK_FILTER_NEAREST;
|
||||
const Bool layersInRange = baseLayer < resource->arrayLayers &&
|
||||
layerCount <= resource->arrayLayers - baseLayer;
|
||||
// The depth arm's shape: the layer window, and what WireDepthMipmap.inc's pass refuses
|
||||
// for an image this function hands it - a format that does not both sample and attach
|
||||
// as depth (its other refusals, 3D / multisample / a default image, are never built here).
|
||||
const auto depthPassFeatures =
|
||||
VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT;
|
||||
const Bool depthArmTakesShape =
|
||||
layersInRange && (props.optimalTilingFeatures & depthPassFeatures) == depthPassFeatures;
|
||||
// The colour arm's shape (the two shader shapes are explained at the colour arm below).
|
||||
const Bool colour = resource->aspect == VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
const Bool planar = resource->viewType != VK_IMAGE_VIEW_TYPE_1D &&
|
||||
resource->viewType != VK_IMAGE_VIEW_TYPE_1D_ARRAY &&
|
||||
resource->viewType != VK_IMAGE_VIEW_TYPE_3D;
|
||||
// The shader arm samples through a sampler2D and writes a vec4, which is what
|
||||
// BlitWireColorImage's own float check enforces; naming it here rather than letting the
|
||||
// blit return false is what puts the reason in the decline line.
|
||||
const Bool sampledFloat = vkuFormatIsSampledFloat(format);
|
||||
const Bool renderableFormat =
|
||||
(props.optimalTilingFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT) != 0;
|
||||
const Bool attachable = forcedShaderTier < 2 &&
|
||||
(resource->usageFlags & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) != 0;
|
||||
const Bool viaScratch = !attachable && renderableFormat &&
|
||||
(resource->usageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT) != 0;
|
||||
const Bool colourArmTakesShape =
|
||||
colour && planar && layersInRange && sampledFloat && (attachable || viaScratch);
|
||||
// P8-F: `mipmap-shader-format-or-shape` IS A NAMED DECLINE, NOT A FATAL (rule I (a)).
|
||||
// The monolith arm has always answered this chain with a decline (VulkanRenderer.cpp's
|
||||
// GenerateMipmap: a format without blit support is skipped and the chain left unwritten),
|
||||
// and a shape no shader arm takes is a coverage gap in this backend, not a broken record.
|
||||
// The leading word stays, so every device-log grep keyed on it still finds the line.
|
||||
const Bool shaderArmTakesShape =
|
||||
resource->aspect == VK_IMAGE_ASPECT_DEPTH_BIT ? depthArmTakesShape : colourArmTakesShape;
|
||||
if (!nativeBlit && !shaderArmTakesShape) {
|
||||
const String detail = MagmaWireShapeDetail("mipmap-shader-format-or-shape", format,
|
||||
resource->format, props, resource->aspect, resource->viewType, resource->usageFlags,
|
||||
resource->extent, resource->depth, resource->mipLevels, baseLayer, layerCount,
|
||||
resource->arrayLayers);
|
||||
MGL_WIRE_DECLINE_AT(MipmapShaderFormatOrShape, "%s; no mip arm takes this shape, levels %u..%u left as they are",
|
||||
detail.c_str(), base + 1, base + count - 1);
|
||||
return;
|
||||
}
|
||||
if (!m_textureManager->GrowWireTextureMipChain(handle,base+count,frame.commandBuffer))
|
||||
MagmaWireFatal("mipmap-growth");
|
||||
if (!nativeBlit && resource->aspect == VK_IMAGE_ASPECT_DEPTH_BIT) {
|
||||
// P7 wave 2-B, CONTRACT-P7 §5.1 (A): THE DEPTH ARM, and it is first because a
|
||||
// depth format without BLIT_DST is the ordinary case, not the exotic one -
|
||||
@@ -1422,12 +1470,7 @@
|
||||
// The per-level pass is WireDepthMipmap.inc's, which is the monolith's fragment
|
||||
// shader with the monolith's arithmetic on a baked pipeline that owns no frontend
|
||||
// object - §3.2's "observably the monolith's" without the GL program the monolith
|
||||
// needs to get there.
|
||||
if (baseLayer >= resource->arrayLayers || layerCount > resource->arrayLayers - baseLayer)
|
||||
MagmaWireFatal(MagmaWireShapeDetail("mipmap-shader-format-or-shape", format,
|
||||
resource->format, props, resource->aspect, resource->viewType,
|
||||
resource->usageFlags, resource->extent, resource->depth, resource->mipLevels,
|
||||
baseLayer, layerCount, resource->arrayLayers).c_str());
|
||||
// needs to get there. Its layer window and format were checked before the growth.
|
||||
for (Uint32 level = base + 1; level < base + count; ++level) {
|
||||
for (Uint32 layer = baseLayer; layer < baseLayer + layerCount; ++layer) {
|
||||
WireImage source{};
|
||||
@@ -1445,15 +1488,19 @@
|
||||
destination.level = level;
|
||||
destination.extent = {std::max(resource->extent.width >> level, 1u),
|
||||
std::max(resource->extent.height >> level, 1u)};
|
||||
// The pass reports the SHAPE it cannot take (1D, 3D, multisample, a
|
||||
// format without sampled or depth-attachment support), so the refusal
|
||||
// keeps its name and gains the reason.
|
||||
if (!GenerateWireDepthMipLevel(source, destination))
|
||||
MagmaWireFatal(MagmaWireShapeDetail("mipmap-shader-format-or-shape", format,
|
||||
// The pass reports the SHAPE it cannot take. depthArmTakesShape above asks
|
||||
// its questions first, so this is reached only if the two lists drift apart
|
||||
// - and then it is the same named decline (P8-F), not a session's end. It
|
||||
// can only refuse the first level: the shape does not change down the chain.
|
||||
if (!GenerateWireDepthMipLevel(source, destination)) {
|
||||
const String detail = MagmaWireShapeDetail("mipmap-shader-format-or-shape", format,
|
||||
resource->format, props, resource->aspect, resource->viewType,
|
||||
resource->usageFlags, resource->extent, resource->depth,
|
||||
resource->mipLevels, baseLayer, layerCount,
|
||||
resource->arrayLayers).c_str());
|
||||
resource->mipLevels, baseLayer, layerCount, resource->arrayLayers);
|
||||
MGL_WIRE_DECLINE_AT(MipmapShaderFormatOrShape, "%s; the depth pass refused level %u",
|
||||
detail.c_str(), level);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (!nativeBlit) {
|
||||
@@ -1470,27 +1517,9 @@
|
||||
// cannot, an owned scratch attachment of the destination level's size renders
|
||||
// the mip and vkCmdCopyImage moves it in: a raw texel move, no conversion, and
|
||||
// the texels still come from GPU contents rather than from a staged shadow.
|
||||
const Bool colour = resource->aspect == VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
const Bool planar = resource->viewType != VK_IMAGE_VIEW_TYPE_1D &&
|
||||
resource->viewType != VK_IMAGE_VIEW_TYPE_1D_ARRAY &&
|
||||
resource->viewType != VK_IMAGE_VIEW_TYPE_3D;
|
||||
const Bool layersInRange = baseLayer < resource->arrayLayers &&
|
||||
layerCount <= resource->arrayLayers - baseLayer;
|
||||
// The shader arm samples through a sampler2D and writes a vec4, which is what
|
||||
// BlitWireColorImage's own float check enforces; naming it here rather than
|
||||
// letting the blit return false is what puts the reason in the fatal line.
|
||||
const Bool sampledFloat = vkuFormatIsSampledFloat(format);
|
||||
const Bool renderableFormat =
|
||||
(props.optimalTilingFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT) != 0;
|
||||
const Bool attachable = forcedShaderTier < 2 &&
|
||||
(resource->usageFlags & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) != 0;
|
||||
const Bool viaScratch = !attachable && renderableFormat &&
|
||||
(resource->usageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT) != 0;
|
||||
if (!colour || !planar || !layersInRange || !sampledFloat || (!attachable && !viaScratch))
|
||||
MagmaWireFatal(MagmaWireShapeDetail("mipmap-shader-format-or-shape", format,
|
||||
resource->format, props, resource->aspect, resource->viewType,
|
||||
resource->usageFlags, resource->extent, resource->depth, resource->mipLevels,
|
||||
baseLayer, layerCount, resource->arrayLayers).c_str());
|
||||
//
|
||||
// Which of the two (`attachable` or `viaScratch`), and whether either takes the
|
||||
// shape at all, was decided before the growth (colourArmTakesShape, P8-F).
|
||||
for (Uint32 level = base + 1; level < base + count; ++level) {
|
||||
for (Uint32 layer = baseLayer; layer < baseLayer + layerCount; ++layer) {
|
||||
WireImage source{};
|
||||
|
||||
@@ -186,9 +186,9 @@ namespace MobileGL::MG_Backend {
|
||||
// ZERO IS THE EXPLICIT ANSWER FOR P5, not an omission (ServerSession.h's block):
|
||||
// every optional capability bit belongs to the package that owns its question, and
|
||||
// withholding one leaves the legacy path running, which is the safe direction.
|
||||
// kCapNeedsHostIndexBytes and kCapNeedsHostUboBytes must be 0 for the whole of P5
|
||||
// by ruling - they are the only two things that ask for an MGHostSpan, and 0 is
|
||||
// what keeps every one of them out of the first IPC frame (contract table 0).
|
||||
// kCapNeedsHostIndexBytes, kCapNeedsHostUboBytes and kCapViewportArray are RESERVED
|
||||
// (P8-F) and never set here: the first two are the only things that ask for an
|
||||
// MGHostSpan, and 0 keeps every one of them off the wire (contract table 0).
|
||||
//
|
||||
// P5b t2 (CONTRACT-P5B.md §6.5) PUBLISHES THE ONE BIT P5b ADDS, and this is the
|
||||
// only place that can: the question kCapBackendOwnsXfbCapture answers is "does the
|
||||
|
||||
@@ -247,10 +247,10 @@ namespace MobileGL::MG_Pipe {
|
||||
for (Uint32 w = 0; w < kMGPipeShaderBufferWritableMaskWords; ++w) {
|
||||
header.WritableMask[w] = writableMask[w];
|
||||
}
|
||||
// HostSpanCount IS 0 ALWAYS on Espryt, and that is a ruling rather than an omission:
|
||||
// the second var-tail exists for kCapNeedsHostUboBytes (Magma's named-UBO ring) and
|
||||
// that bit is 0 for the whole of P5 (CONTRACT-P5.md table 0). The codec's host-span
|
||||
// honesty pass is the guard that says so out loud if a backend ever publishes it.
|
||||
// HostSpanCount IS 0 ALWAYS, and that is a ruling rather than an omission: the
|
||||
// second var-tail exists for kCapNeedsHostUboBytes (a Magma named-UBO ring that was
|
||||
// never built) and that bit is RESERVED (P8-F): no server publishes it. The codec's
|
||||
// host-span honesty pass is the guard that says so out loud if a peer sends one.
|
||||
header.HostSpanCount = 0;
|
||||
header.ContentHash = hash;
|
||||
MGPipeRouteSetShaderBuffers(header, m_entries.data());
|
||||
|
||||
@@ -263,8 +263,8 @@ namespace MobileGL::MG_Pipe {
|
||||
if (vao) {
|
||||
if (const auto& bound = vao->GetIndexBufferBindingSlot().GetBoundObject()) {
|
||||
m_lastIndex.Res = MGPipeSlots().Acquire(MGPipeKind::Buffer, bound->GetLifetimeId());
|
||||
// The ELEMENT_ARRAY bit, and it is the one the split path keys on
|
||||
// (kCapNeedsHostIndexBytes -> restart rewriting, multi-draw flattening).
|
||||
// The INDEX|ELEMENT_ARRAY bind bits. ELEMENT_ARRAY's D-B7 reader
|
||||
// (kCapNeedsHostIndexBytes) is RESERVED (P8-F) and was never built.
|
||||
// Noted at every draw for RefreshBindMask's reason: an EBO defined through
|
||||
// DSA and unbound before its last respecify would otherwise never publish
|
||||
// it, and getting that bit wrong is invisible in monolith.
|
||||
|
||||
@@ -3354,6 +3354,26 @@ if (MOBILEGL_BUILD_DISAGGREGATED)
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/Harness/split_log_paths.py"
|
||||
check "${CMAKE_CTEST_COMMAND}" "${CMAKE_BINARY_DIR}")
|
||||
set_tests_properties(SplitLogPaths.PrivateAndDistinct PROPERTIES LABELS "integration-split")
|
||||
|
||||
# P8-F: `Magma:mipmap-shader-format-or-shape` is a named DECLINE on the wire arm now
|
||||
# (WireDeclines.def MipmapShaderFormatOrShape), as the monolith arm's GenerateMipmap already
|
||||
# declines. The shape is a 3D chain with the native blit forced off - neither shader mip arm
|
||||
# takes it - and the knob is read BY THE SERVER, so split + spawn only (the ShaderMip entries'
|
||||
# tcp note below). Its own `.ShaderMipDecline.` tail is in MAGMA_SERVER_ENV_KNOB_NO_TCP
|
||||
# (scripts/ci/spawn_lane_parity.py: one tail per knob case). The Fm lane's flavour plus the arm.
|
||||
foreach(mglItestArm IN ITEMS Split Spawn)
|
||||
mgl_itest_magma_arm_shape(${mglItestArm})
|
||||
mgl_itest_join_environment(mglItestP8fMipEnvironment
|
||||
"MOBILEGL_BACKEND_TYPE=DirectVulkan" "MOBILEGL_ITEST_REQUIRE_GPU=1" "MGITEST_SPLIT_LANE=1"
|
||||
${MGL_ITEST_CAPABILITY_ENV} ${MGL_ITEST_VULKAN_ENV} ${mglItestArmTransportEnv})
|
||||
gtest_discover_tests(MobileGLIntegrationTest
|
||||
TEST_PREFIX "DirectVulkan.${mglItestArm}.ShaderMipDecline."
|
||||
TEST_FILTER "F1WireScenario.GenerateMipmapWithoutAShaderArmShapeDeclinesAndKeepsTheSession"
|
||||
DISCOVERY_TIMEOUT 30
|
||||
PROPERTIES LABELS "${mglItestMagmaLabels}" TIMEOUT ${MGL_ITEST_TIMEOUT}
|
||||
${mglItestArmProperties}
|
||||
ENVIRONMENT "${mglItestP8fMipEnvironment}\;MGITEST_MAGMA_FORCE_SHADER_MIPMAP=1\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p8f-shadermip-decline${mglItestArmLogTag}.log")
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
# f1: split-only source keeps the monolith registry unchanged.
|
||||
|
||||
@@ -675,6 +675,65 @@ TEST_F(F1WireScenario, GenerateMipmapDepthStencilDeclinesAndKeepsTheSession) {
|
||||
glClearDepth(1.0);
|
||||
}
|
||||
|
||||
// P8-F: `Magma:mipmap-shader-format-or-shape` IS A NAMED DECLINE ON THE WIRE ARM NOW, which is
|
||||
// the monolith arm's answer for a chain it cannot build (VulkanRenderer.cpp's GenerateMipmap
|
||||
// declines a format without blit support and leaves the chain unwritten). It used to end the
|
||||
// session. A 3D texture is a shape neither wire shader arm takes (the colour pass samples a
|
||||
// sampler2D), so with the native blit forced off (MGITEST_MAGMA_FORCE_SHADER_MIPMAP) it is what
|
||||
// reaches the refusal on this host - lavapipe blits a 3D RGBA8 chain natively otherwise.
|
||||
//
|
||||
// What this case pins: no GL error, the session still answering (two readback round trips), level
|
||||
// 0 untouched, and level 1 still holding the application's own texels - the decline wrote nothing.
|
||||
// Magma only, and knob-gated, like GenerateMipmapWithoutNativeBlitPixels above.
|
||||
// Red once (executed, reverted): restore the MagmaWireFatal and this case dies with
|
||||
// Fatal{UnmigratedVerb, "Magma:mipmap-shader-format-or-shape ..."}.
|
||||
TEST_F(F1WireScenario, GenerateMipmapWithoutAShaderArmShapeDeclinesAndKeepsTheSession) {
|
||||
if (!Ready()) return;
|
||||
if (Gl().BackendName() != "DirectVulkan")
|
||||
GTEST_SKIP() << "Magma wire-arm decline (P8-F, mipmap-shader-format-or-shape): the knob and "
|
||||
"the shader mip arms it forces are Magma's; Espryt ("
|
||||
<< Gl().BackendName() << ") generates this chain itself";
|
||||
const std::string tier = SplitLane::MarkerValue("MGITEST_MAGMA_FORCE_SHADER_MIPMAP");
|
||||
if (tier.empty())
|
||||
GTEST_SKIP() << "MGITEST_MAGMA_FORCE_SHADER_MIPMAP is unset: this driver blits a 3D RGBA8 "
|
||||
"chain natively, so the decline under test is not entered";
|
||||
GLuint volume = 0;
|
||||
glGenTextures(1, &volume);
|
||||
glBindTexture(GL_TEXTURE_3D, volume);
|
||||
glTexStorage3D(GL_TEXTURE_3D, 3, GL_RGBA8, 4, 4, 4);
|
||||
const std::array<GLubyte, 4> base{200, 40, 80, 255};
|
||||
const std::array<GLubyte, 4> sentinel{10, 220, 30, 255};
|
||||
const auto fill = [](int side, const std::array<GLubyte, 4>& texel) {
|
||||
std::vector<GLubyte> pixels(static_cast<size_t>(side * side * side * 4));
|
||||
for (size_t at = 0; at < pixels.size(); at += 4) std::copy(texel.begin(), texel.end(), pixels.begin() + at);
|
||||
return pixels;
|
||||
};
|
||||
const auto level0 = fill(4, base);
|
||||
const auto level1 = fill(2, sentinel);
|
||||
glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, 4, 4, 4, GL_RGBA, GL_UNSIGNED_BYTE, level0.data());
|
||||
glTexSubImage3D(GL_TEXTURE_3D, 1, 0, 0, 0, 2, 2, 2, GL_RGBA, GL_UNSIGNED_BYTE, level1.data());
|
||||
ASSERT_EQ(FirstGLError(), GLenum(GL_NO_ERROR)) << "F1.ShaderMipDecline.setup";
|
||||
const auto before = PeekSplitRuntime().emitSeq;
|
||||
glGenerateMipmap(GL_TEXTURE_3D);
|
||||
ASSERT_GT(PeekSplitRuntime().emitSeq, before) << "F1.ShaderMipDecline.wire";
|
||||
EXPECT_EQ(FirstGLError(), GLenum(GL_NO_ERROR)) << "F1.ShaderMipDecline.error";
|
||||
for (const int level : {0, 1}) {
|
||||
const int side = 4 >> level;
|
||||
std::vector<GLubyte> pixels(static_cast<size_t>(side * side * side * 4), 0x5a);
|
||||
glGetTexImage(GL_TEXTURE_3D, level, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
|
||||
ASSERT_EQ(FirstGLError(), GLenum(GL_NO_ERROR)) << "F1.ShaderMipDecline.readback level " << level;
|
||||
const std::array<GLubyte, 4>& expected = level == 0 ? base : sentinel;
|
||||
size_t wrong = 0;
|
||||
for (size_t at = 0; at < pixels.size(); at += 4)
|
||||
if (!std::equal(expected.begin(), expected.end(), pixels.begin() + at)) ++wrong;
|
||||
EXPECT_EQ(wrong, 0u) << "F1.ShaderMipDecline.level" << level << " tier=" << tier
|
||||
<< ": a declined mip must leave every level as the application wrote it";
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_3D, 0);
|
||||
glDeleteTextures(1, &volume);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
}
|
||||
|
||||
TEST_F(F1WireScenario, GenerateMipmapHonorsMutableBaseAndMaxWithoutChangingOtherLevels) {
|
||||
if (!Ready()) return;
|
||||
const std::array<GLubyte, 4> colors[5] = {
|
||||
|
||||
@@ -13,16 +13,16 @@
|
||||
//
|
||||
// Monolith: Ptr addresses the frontend shadow or the application's own memory and the
|
||||
// accessor is one predictable branch. Split: Ptr is null and the bytes live in a staging
|
||||
// segment named by Seg/Offset, or - for the index bytes a server-side primitive-restart
|
||||
// rewrite or multi-draw flattening consumes - in the server's own index host mirror, which
|
||||
// costs no wire traffic at all (D-B7).
|
||||
// segment named by Seg/Offset. D-B7's third shape - index bytes a server-side restart
|
||||
// rewrite reads out of its own index host mirror, at no wire cost - was never built; its
|
||||
// sentinel below is RESERVED (P8-F).
|
||||
namespace MobileGL::MG_Pipe {
|
||||
// Seg sentinels. Anything else is a real SEG_STAGE id assigned by the transport.
|
||||
inline constexpr Uint32 kMGHostSpanSegNone = 0;
|
||||
// "The bytes are already on your side": the server reads them out of the index host
|
||||
// mirror it maintains for every resource created with the ELEMENT_ARRAY bind bit while
|
||||
// kCapNeedsHostIndexBytes is set. When the mirror is over budget the tracker degrades
|
||||
// to per-draw staging and counts the bytes in index-bytes-shipped.
|
||||
// RESERVED (P8-F). It was to mean "the bytes are already on your side", read out of an
|
||||
// index host mirror armed by kCapNeedsHostIndexBytes. Neither was built (client indices
|
||||
// travel as owned element buffers; the cap is reserved), so no encoder writes this value
|
||||
// and SegmentTable::Resolve answers it "unknown". Kept so the value is never reused.
|
||||
inline constexpr Uint32 kMGHostSpanSegFromServerIndexMirror = 0xFFFFFFFFu;
|
||||
|
||||
struct MGHostSpan {
|
||||
|
||||
@@ -107,19 +107,19 @@ namespace MobileGL::MG_Pipe {
|
||||
// flattening - is not expressible as a capability (D-B7).
|
||||
enum MGPCapBit : Uint64 {
|
||||
kCapNone = 0,
|
||||
kCapViewportArray = 1ull << 0,
|
||||
kCapViewportArray = 1ull << 0, // RESERVED (P8-F): no producer, no consumer; never renumber
|
||||
kCapFloat64VertexAttrib = 1ull << 1,
|
||||
kCapResidentSubData = 1ull << 2,
|
||||
kCapCpuXfbPrimitiveAccounting = 1ull << 3,
|
||||
kCapTimerQuery = 1ull << 4,
|
||||
kCapOcclusionQuery = 1ull << 5,
|
||||
kCapXfbPrimitivesQuery = 1ull << 6,
|
||||
// The server rewrites restart indices / flattens multi-draws itself and therefore
|
||||
// needs the index bytes on its side: under split this arms the index host mirror
|
||||
// (D-B7).
|
||||
// RESERVED (P8-F): never published, never read. The index host mirror it would arm
|
||||
// (D-B7) was not built: client indices travel as owned element buffers and the server
|
||||
// reads its own staged bytes. The bit position is wire format: never reuse/renumber it.
|
||||
kCapNeedsHostIndexBytes = 1ull << 7,
|
||||
// The server packs named uniform blocks into its own ring and therefore needs the
|
||||
// host bytes of a set_shader_buffers(Uniform) range (D-B8).
|
||||
// RESERVED (P8-F): never published, never read - the named-UBO host span (D-B8) has
|
||||
// no producer. The bit position is wire format: never reuse or renumber it.
|
||||
kCapNeedsHostUboBytes = 1ull << 8,
|
||||
// P5b t2 (CONTRACT-P5B.md §6.5), the one cap bit P5b adds. The SERVER's backend owns
|
||||
// the transform-feedback capture, i.e. its own table registers EndTransformFeedback.
|
||||
@@ -312,11 +312,11 @@ namespace MobileGL::MG_Pipe {
|
||||
kMGPipeBindDepthStencil = 1u << 8,
|
||||
kMGPipeBindStreamOutput = 1u << 9,
|
||||
kMGPipeBindAtomic = 1u << 10,
|
||||
// THE D-B7 SWITCH. With kCapNeedsHostIndexBytes set the server mirrors this
|
||||
// resource's bytes so it can rewrite restart indices and flatten multi-draws
|
||||
// (ARCHITECTURE.md 10.3). Getting it wrong is invisible in monolith and silently
|
||||
// disables both under split, which is why it is set from a table rather than from a
|
||||
// special case at the emission site.
|
||||
// Set beside INDEX on every element-array buffer, from a table (ResourceTracker.h).
|
||||
// It was THE D-B7 SWITCH: with kCapNeedsHostIndexBytes the server would mirror this
|
||||
// resource's bytes for restart rewriting and multi-draw flattening (ARCHITECTURE.md
|
||||
// 10.3). That cap is RESERVED (P8-F) and the mirror was never built, so no reader
|
||||
// consumes this bit today; it stays on the wire, at this position.
|
||||
kMGPipeBindElementArray = 1u << 11,
|
||||
};
|
||||
|
||||
@@ -331,8 +331,8 @@ namespace MobileGL::MG_Pipe {
|
||||
Uint8 StorageKind; // == TextureStorageType (Mipmap | Buffer)
|
||||
// MGPipeBindBit, above: VERTEX|INDEX|CONSTANT|SHADER_BUFFER|INDIRECT|SAMPLER|
|
||||
// SHADER_IMAGE|RENDER_TARGET|DEPTH_STENCIL|STREAM_OUTPUT|ATOMIC|ELEMENT_ARRAY. The
|
||||
// ELEMENT_ARRAY bit is the D-B7 switch: with kCapNeedsHostIndexBytes set the server
|
||||
// mirrors this resource.
|
||||
// ELEMENT_ARRAY bit was the D-B7 switch; kCapNeedsHostIndexBytes is RESERVED (P8-F)
|
||||
// and no server mirrors this resource.
|
||||
Uint16 BindMask;
|
||||
Uint32 InternalFormat; // already resolved to an uncompressed fallback by the client
|
||||
Uint32 Width, Height, Depth;
|
||||
@@ -992,11 +992,11 @@ namespace MobileGL::MG_Pipe {
|
||||
MGP_ASSERT_POD(MGPShaderImages, 16);
|
||||
|
||||
// One bound buffer range: 24 bytes, no inline host span. The named-UBO host bytes a
|
||||
// backend needs under kCapNeedsHostUboBytes (D-B8) travel as an OPTIONAL second var-tail,
|
||||
// MGHostSpan[HostSpanCount] behind the ranges, announced by MGPShaderBuffers below. An
|
||||
// inline span would have cost every SSBO, atomic-counter and XFB range 32 dead bytes, and
|
||||
// D-B8 says not to freeze that payload's shape before the stage-ubo-named counter has
|
||||
// produced numbers.
|
||||
// backend would need under kCapNeedsHostUboBytes (D-B8; that cap is RESERVED since P8-F
|
||||
// and nothing sends the tail) travel as an OPTIONAL second var-tail, MGHostSpan[HostSpanCount]
|
||||
// behind the ranges, announced by MGPShaderBuffers below. An inline span would have cost
|
||||
// every SSBO, atomic-counter and XFB range 32 dead bytes, and D-B8 says not to freeze that
|
||||
// payload's shape before the stage-ubo-named counter has produced numbers.
|
||||
struct MGPBufferRange {
|
||||
MGPipeHandle Res;
|
||||
Uint64 Offset;
|
||||
@@ -1005,8 +1005,8 @@ namespace MobileGL::MG_Pipe {
|
||||
MGP_ASSERT_POD(MGPBufferRange, 24);
|
||||
|
||||
// Var-tail header: MGPBufferRange[Count], then MGHostSpan[HostSpanCount]. HostSpanCount is
|
||||
// 0, or Count for the Uniform class under kCapNeedsHostUboBytes (a range with nothing to
|
||||
// ship carries an empty span, so the two arrays stay index-aligned).
|
||||
// 0, or Count for the Uniform class under kCapNeedsHostUboBytes (RESERVED, P8-F: 0 today;
|
||||
// a range with nothing to ship carries an empty span, so the two arrays stay aligned).
|
||||
// MGPShaderBuffers::Class's three values, and the ONLY spelling of them (P5e,
|
||||
// MG_Remote/CONTRACT-P5E.md §1). The payload's comment has named them since P4a and
|
||||
// nothing numbered them, so the emitter and the applier were one literal each away from
|
||||
|
||||
@@ -44,9 +44,9 @@ namespace MobileGL::MG_Remote {
|
||||
// (MGPipeTypes.h:108-124) and has no per-family bit at all, so "read the CallMask" was
|
||||
// not yet an implementable instruction. It is now:
|
||||
//
|
||||
// bits 0..8 MGPCapBit, unchanged. kCapNeedsHostIndexBytes and kCapNeedsHostUboBytes
|
||||
// are BOTH ZERO in P5 by ruling (table 0), which is what keeps every
|
||||
// MGHostSpan out of the first IPC frame.
|
||||
// bits 0..8 MGPCapBit, unchanged. kCapViewportArray (0), kCapNeedsHostIndexBytes (7)
|
||||
// and kCapNeedsHostUboBytes (8) are RESERVED (P8-F): never published, and
|
||||
// their positions are never reused, which keeps every MGHostSpan off the wire.
|
||||
// bits 9..31 reserved for further MGPCapBits.
|
||||
// bits 32..47 THE CONSUMER MASK: bit (32 + n) means "the server has a consumer for
|
||||
// MGPipe subsystem bit n" - i.e. the server's own subsystem mask, shifted.
|
||||
|
||||
@@ -251,7 +251,7 @@ namespace MobileGL::MG_Remote::Client {
|
||||
// P5e (sb, CONTRACT-P5E.md §5.6): the indexed buffer binding points, one record per
|
||||
// class. The generic tail wrapper carries the FIRST tail only, which is the whole of
|
||||
// the record on Espryt - the optional MGHostSpan tail exists for kCapNeedsHostUboBytes
|
||||
// and that bit is 0 for the whole of P5.
|
||||
// and that bit is RESERVED (P8-F): no server publishes it.
|
||||
MGP_WIRE_TAIL(SetShaderBuffers, MGPShaderBuffers, MGPBufferRange)
|
||||
MGP_WIRE_TAIL(SetVertexAttribDefaults, MGPVertexAttribDefaults, MGPAttribValue)
|
||||
|
||||
|
||||
@@ -766,17 +766,20 @@ namespace MobileGL::MG_Remote::Server {
|
||||
return LandReadbackInBuffer(record, bytes.data(), bpp);
|
||||
}
|
||||
|
||||
// P5b's server-side stub shape (CONTRACT-P5B.md): the same line the client's class-C table
|
||||
// raises (EmitTables.cpp UnmigratedVerbFatal) and the same family the census greps, so a
|
||||
// slot flipped on the client ahead of its server half aborts BY NAME on the apply thread
|
||||
// rather than rendering nothing. Named "(server sink)" in the message so a log reader can
|
||||
// tell which half is missing.
|
||||
// P8-F: A PROTOCOL GUARD, NOT AN UNMIGRATED VERB. This was P5b's server-side stub shape
|
||||
// (`Fatal{UnmigratedVerb, ...}`, "ServerVerbSink has no body for it yet"), but no package is
|
||||
// going to give these two multi-draw shapes a body, because our client can no longer send
|
||||
// either: client index arrays become one owned element buffer before the record is built
|
||||
// (EmitTables.cpp's multi-draw arm, OwnedDrawInputs.h clears kDrawHasUserIndices), and no GL
|
||||
// entry point produces an instanced multi-draw. A record that carries one came from a
|
||||
// foreign peer, so the family is the one every other malformed-record check uses.
|
||||
//
|
||||
// PH-1 (3): the shape is the PEER's (a multi-draw record it chose to send), so in an armed
|
||||
// session child the refusal latches and the verb returns false; unarmed it still dies.
|
||||
static Bool ServerUnmigratedVerbLatch(const char* slot) {
|
||||
return SessionLatch(MGFatalFamily::UnmigratedVerb, "MGPipe: Fatal{UnmigratedVerb, \"%s\"} (server sink: the record crossed and "
|
||||
"ServerVerbSink has no body for it yet - CONTRACT-P5B.md names the package)",
|
||||
static Bool MultiDrawShapeLatch(const char* slot) {
|
||||
return SessionLatch(MGFatalFamily::ProtocolCorruption, "MGPipe: Fatal{ProtocolCorruption, \"%s\"} a multi-draw "
|
||||
"record with a shape this protocol does not carry: client indices travel as an owned "
|
||||
"element buffer and no GL entry point produces an instanced multi-draw",
|
||||
slot);
|
||||
}
|
||||
|
||||
@@ -802,10 +805,10 @@ namespace MobileGL::MG_Remote::Server {
|
||||
// of 1 and no base instance is the plain draw it is equivalent to, and a count of 0 must
|
||||
// NOT collapse onto the plain draw (it draws nothing, the plain draw would draw once).
|
||||
//
|
||||
// What is still refused by name (the census's own grep family): a multi-draw that arrived
|
||||
// with a span (the client refuses "MultiDrawElements+CLIENT_INDICES" first; P8's
|
||||
// HostResolve.cpp flattens it) and a multi-draw that claims instancing (no GL entry point
|
||||
// produces one; the client never sends it).
|
||||
// What is still refused by name, as protocol corruption (P8-F): a multi-draw that arrived
|
||||
// with a span (our client stages client indices into an owned element buffer and never sets
|
||||
// kDrawHasUserIndices) and a multi-draw that claims instancing (no GL entry point produces
|
||||
// one; the client never sends it). Only a foreign peer reaches either.
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
namespace {
|
||||
// P5e (tx2), CONTRACT-P5E §5.3 / ruling 19 (ID-95, A8 closed). THE TWO UNIT WINDOWS MUST
|
||||
@@ -970,12 +973,12 @@ namespace MobileGL::MG_Remote::Server {
|
||||
// ---- the multi-draws: the two arrays rebuilt from the ranges (rule C) --------------
|
||||
if (info.NumDraws != 1) {
|
||||
if (userIndices != nullptr) {
|
||||
return ServerUnmigratedVerbLatch(info.IndexSize == 0 ? "MultiDrawArrays+CLIENT_INDICES"
|
||||
: "MultiDrawElements+CLIENT_INDICES");
|
||||
return MultiDrawShapeLatch(info.IndexSize == 0 ? "MultiDrawArrays+CLIENT_INDICES"
|
||||
: "MultiDrawElements+CLIENT_INDICES");
|
||||
}
|
||||
if (instanced) {
|
||||
return ServerUnmigratedVerbLatch(info.IndexSize == 0 ? "MultiDrawArrays+INSTANCED"
|
||||
: "MultiDrawElements+INSTANCED");
|
||||
return MultiDrawShapeLatch(info.IndexSize == 0 ? "MultiDrawArrays+INSTANCED"
|
||||
: "MultiDrawElements+INSTANCED");
|
||||
}
|
||||
const auto n = static_cast<SizeT>(info.NumDraws);
|
||||
m_multiCounts.resize(n);
|
||||
|
||||
@@ -162,9 +162,9 @@ namespace MobileGL::MG_Remote::Server {
|
||||
// that owns the question (kCapTimerQuery to the query family, kCapResidentSubData to
|
||||
// b1, and so on). `SetCapabilityBits(0)` is a legitimate and explicit answer - "this
|
||||
// server offers no optional capability" - and is the right call while those packages
|
||||
// land. kCapNeedsHostIndexBytes / kCapNeedsHostUboBytes must stay 0 for the whole of
|
||||
// P5 by ruling (CONTRACT-P5 table 0): they are the only two things that ask for an
|
||||
// MGHostSpan, and 0 is what keeps every one of them out of the first IPC frame.
|
||||
// land. kCapNeedsHostIndexBytes / kCapNeedsHostUboBytes / kCapViewportArray are RESERVED
|
||||
// (P8-F; CONTRACT-P5 table 0 first held the two host-byte bits at 0): the first two are
|
||||
// the only things that ask for an MGHostSpan, and 0 keeps every one of them off the wire.
|
||||
void SetCapabilityBits(Uint64 capBits);
|
||||
|
||||
// BITS 32..47, THE CONSUMER MASK (R-8 / C-4): which MGPipe subsystems this server has
|
||||
|
||||
@@ -456,8 +456,9 @@ namespace MobileGL::MG_Remote::Wire {
|
||||
}
|
||||
|
||||
const void* SegmentTable::Resolve(Uint32 seg, Uint64 offset, Uint64 size) const {
|
||||
// 0 is "no segment", ALWAYS (table 0), and 0xFFFFFFFF is P8's index-mirror sentinel
|
||||
// which this phase does not resolve. Both land here as "unknown", and the CALLER
|
||||
// 0 is "no segment", ALWAYS (table 0), and 0xFFFFFFFF is the RESERVED index-mirror
|
||||
// sentinel (P8-F: the mirror was never built, so nothing resolves it and no client of
|
||||
// ours sends it). Both land here as "unknown", and the CALLER
|
||||
// escalates to Fatal - a unit case has to be able to exercise this arithmetic without
|
||||
// dying.
|
||||
if (seg == kSegNone || seg > static_cast<Uint32>(kSegAdopt)) {
|
||||
@@ -2121,7 +2122,7 @@ namespace MobileGL::MG_Remote::Wire {
|
||||
// entire point is that the server stops walking the client's binding-point table.
|
||||
//
|
||||
// THE HOST-SPAN PASS STILL RUNS FIRST, and it runs whether or not the record is
|
||||
// applied: kCapNeedsHostUboBytes is 0 for the whole of P5 (table 0), so the second
|
||||
// applied: kCapNeedsHostUboBytes is RESERVED (P8-F, never published), so the second
|
||||
// tail is always absent here, and this pass is what says so out loud if it ever is
|
||||
// not. A span that was going to be refused must be refused BEFORE the applier has
|
||||
// stored the window it rides with.
|
||||
|
||||
@@ -148,9 +148,9 @@ namespace MobileGL::MG_Remote::Wire {
|
||||
// declared. Fatal on an absent blob, then CheckBlobIsHonest on a present one.
|
||||
Bool RequireDeclaredBlob(MG_Pipe::MGPWireOp op, const MG_Pipe::MGPBlobRef& blob,
|
||||
const SegmentTable& segments);
|
||||
// R-2.3 arms 1 and 3 for MGHostSpan. P5's reduced path should produce ZERO host spans
|
||||
// (kCapNeedsHostIndexBytes / kCapNeedsHostUboBytes are both 0 in P5, table 0), so this
|
||||
// firing at all is a finding, not just a corruption check.
|
||||
// R-2.3 arms 1 and 3 for MGHostSpan. Our client produces ZERO host spans
|
||||
// (kCapNeedsHostIndexBytes / kCapNeedsHostUboBytes are RESERVED since P8-F, never set), so
|
||||
// this firing at all is a finding, not just a corruption check.
|
||||
//
|
||||
// IT CANNOT DO ARM 4 - it has no segment table - so a span that names a real segment and a
|
||||
// run PAST THE END OF IT passes this function. Use the overload below on any path that has
|
||||
|
||||
@@ -1159,8 +1159,10 @@ namespace {
|
||||
p.FrameSerial = 0;
|
||||
return Forge(c, r, P::MGPWireOp::Present, Bytes(p));
|
||||
}},
|
||||
// P8-F: ProtocolCorruption, no longer UnmigratedVerb - our client cannot send this shape
|
||||
// (nor +CLIENT_INDICES, the same site), so only a foreign peer like this one reaches it.
|
||||
{"MultiDrawInstanced", "PipeApplier.cpp", "MultiDraw*+INSTANCED", Outcome::Latched,
|
||||
"Fatal{UnmigratedVerb, \"MultiDrawArrays+INSTANCED\"}", false,
|
||||
"Fatal{ProtocolCorruption, \"MultiDrawArrays+INSTANCED\"}", false,
|
||||
[](Client::ClientSession& c, PeerReport& r) {
|
||||
P::MGPDrawInfo info{};
|
||||
info.Mode = GL_TRIANGLES;
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
# P8-F 重分类与死闩清理
|
||||
|
||||
> 包说明:`~/w7/notes/p8/BRIEF-F.md`(树外)。基线 `8d3e2317`,分支 `p8/f`。我们自己的 client 行为不变:四项都是它发不出的形状,或只改注释。
|
||||
|
||||
## 结论
|
||||
|
||||
| 项 | 改动 | 位置 | 之前 | 之后 |
|
||||
|---|---|---|---|---|
|
||||
| 1 | multi-draw 的两个 server 闩改族 | `PipeApplier.cpp:779-784`、`:976-981` | `Fatal{UnmigratedVerb, "MultiDraw{Arrays,Elements}+{CLIENT_INDICES,INSTANCED}"}` | `Fatal{ProtocolCorruption, <同名>}`,仍按名闩住外来 peer |
|
||||
| 2 | `Magma:client-vertex-array` 去掉 P8 阶段标签,改为协议不变量 | `WireDraw.inc:297-304` | `MagmaWireFatal`(UnmigratedVerb 族,名字带 `@P8`) | `MGPipeSessionFail(ProtocolCorruption)`,名字 `Magma:client-vertex-array` 加 location |
|
||||
| 3 | Magma 生成 mip 的"无着色器臂形状"改为具名拒绝 | `WireFramebuffer.inc:1405-1459`(判定)、`:1456`、`:1500`(两个拒绝点) | wire 臂三处 Fatal(旧 `:1427`、`:1452`、`:1489-1493`) | `WireDeclines.def:112` 的 `MipmapShaderFormatOrShape`;判定在 `GrowWireTextureMipChain`(`:1460`)之前,拒绝时不增长 mip 链 |
|
||||
| 4 | `kCapViewportArray`、`kCapNeedsHostIndexBytes`、`kCapNeedsHostUboBytes` 与 index-mirror 哨兵标为保留位 | `MGPipeTypes.h:110`、`:117-123`、`:315-320`、`:332-335`、`:994-999`、`:1007-1009`;`MGPipeHostSpan.h:16-26`;`VertexInputEmit.h:266-267`;`ShaderBufferEmit.h:250-253`;`MG_Backend/Init.cpp:189-191` | 注释说"P5 期间为 0"或"arms the index host mirror" | 注释说 RESERVED、从不发布、位不复用不重编号;只改注释 |
|
||||
|
||||
## 各项依据
|
||||
|
||||
| 项 | 事实 | 出处 |
|
||||
|---|---|---|
|
||||
| 1 | client 把 multi-draw 的 client 索引拼成一个自有 EBO | `EmitTables.cpp:688-709` |
|
||||
| 1 | 自有 EBO 生效时清掉 `kDrawHasUserIndices` | `OwnedDrawInputs.h:45-57` |
|
||||
| 1 | multi-draw 计划的实例数恒为 1、base instance 恒为 0 | `EmitTables.cpp:686` |
|
||||
| 1 | 四个名共用一个 `SessionLatch` 格式串,`PeerLatchTest` 的一行覆盖整组 | `PipeApplier.cpp:779-784`、`PeerLatchTest.cpp:1162-1165` |
|
||||
| 1 | 族选 `ProtocolCorruption`:它的桶就是"peer 发了协议不承载的东西" | `FatalFamilies.def:26-29` |
|
||||
| 2 | client 把每个启用的 client 内存数组转成自有 buffer,指针为空就拒绝整个 draw | `OwnedDrawInputs.h:102-131` |
|
||||
| 2 | `fatal_census.py` 只数 `Fatal{Word` 族词与 abort 点,不按 `@P` 字面计数 | `fatal_census.py:165-167` |
|
||||
| 2 | 普查前后:79 个 abort 点、20 个文件、45 个族词,均不变;不重写基线 | 本包门禁读数 |
|
||||
| 2 | 树内 `@P8` 字面:1(`WireDraw.inc:297`,基线)→ 0 | `git grep '@P8' -- MobileGL scripts tools` |
|
||||
| 3 | monolith 臂对无 BLIT 的格式一直是拒绝(跳过、链不写) | `VulkanRenderer.cpp:12225-12229` |
|
||||
| 3 | 增长前后格式、特性、knob 档位、aspect、view 类型、usage、层窗口都不变 | `VkTextureManager.cpp:2884-2995` |
|
||||
| 3 | 深度 pass 对本函数交给它的图像只会因"格式不能同时采样和作深度附件"拒绝 | `WireDepthMipmap.inc:58-81` |
|
||||
| 3 | 深度 pass 若仍拒绝(两份清单漂移),走同一行拒绝,不结束会话 | `WireFramebuffer.inc:1491-1502` |
|
||||
| 4 | 生产者 0:唯一的 `SetCapabilityBits` 只置 XFB、query、resident、run-ahead 位 | `MG_Backend/Init.cpp:202-255` |
|
||||
| 4 | 消费者 0:`kCapViewportArray` 在树内只有枚举定义;`kMGPipeBindElementArray` 只在 bind mask 表里被置 | `MGPipeTypes.h:110`、`ResourceTracker.h:95` |
|
||||
| 4 | `0xFFFFFFFF` 哨兵无编码方;`SegmentTable::Resolve` 把它当未知段 | `MGPipeHostSpan.h:26`、`PipeWireCodec.cpp:458-465` |
|
||||
| 4 | pull 构建只改注释,逐文件行数不变;G1 `added=0 removed=0`,`.text` 与基线同为 `a5ba13` | 本包门禁读数 |
|
||||
|
||||
## 新门与 red-once
|
||||
|
||||
| 项 | 门 | 变异 | 载体 | 观测到的红 |
|
||||
|---|---|---|---|---|
|
||||
| 1 | `PeerLatchTest` 行 `MultiDrawInstanced` 期望 `Fatal{ProtocolCorruption, "MultiDrawArrays+INSTANCED"}` | 闩的族改回 `UnmigratedVerb` | `FuzzArm2/PeerLatchSite.*/MultiDrawInstanced`、`PeerLatch.SiteMap` | 两例红:首行 `Fatal{UnmigratedVerb, ...}` 不含期望标记;站点图报该站点未映射 |
|
||||
| 3 | `F1WireScenario.GenerateMipmapWithoutAShaderArmShapeDeclinesAndKeepsTheSession`(3D RGBA8,`MGITEST_MAGMA_FORCE_SHADER_MIPMAP=1`) | 拒绝改回 `MagmaWireFatal` | `DirectVulkan.{Split,Spawn}.ShaderMipDecline.` 两例(当时前缀为 `.ShaderMip1.`,后改名,见下) | 两例 `Subprocess aborted`,server 日志 `Fatal{UnmigratedVerb, "Magma:mipmap-shader-format-or-shape format=37 extent=4x4x4 ..."}` |
|
||||
| 3 | 同上(前提) | 无(新用例落在未改的基线代码上) | 同上 | 同样两例 abort:前提成立 |
|
||||
| 3 | `spawn_lane_parity.py` 的 `.ShaderMipDecline.` 例外行 | 删掉该行 | `spawn_lane_parity.py build-split` | rc 1:`the tcp arm does not match the split arm: missing=['DirectVulkan.ShaderMipDecline.F1WireScenario.GenerateMipmapWithoutAShaderArmShapeDeclinesAndKeepsTheSession']` |
|
||||
| 2 | 普查读数 | — | `fatal_census.py` | 无变化:该脚本不计阶段标签(见上) |
|
||||
|
||||
- 新用例只登记 split + spawn:knob 由 server 读,tcp fixture 带不过去。登记在 `MobileGL/MG_IntegrationTest/CMakeLists.txt:3358-3376`,前缀 `.ShaderMipDecline.`。
|
||||
- 该尾自成一行加进 `MAGMA_SERVER_ENV_KNOB_NO_TCP`(`spawn_lane_parity.py:141-143`):脚本要求每个尾恰好命中一个用例,借用 `.ShaderMip1.` 会命中两个,门禁时 parity 报 1 个错,已改。
|
||||
- Espryt 不涉及第 2、3 项(Magma 专属);新用例在 DirectGLES 上按后端名跳过。第 1 项在后端无关的 applier 里。
|
||||
|
||||
## 发现但未修
|
||||
|
||||
| 发现 | 证据 | 影响 |
|
||||
|---|---|---|
|
||||
| Magma wire 原生 blit 生成 3D mip 时把 depth 当层数传给 `vkCmdBlitImage`(layerCount=4,图像 arrayLayers=1) | `WireFramebuffer.inc:1612`、`VkTextureManager.cpp:2738-2741`;验证层报 `VUID-vkCmdBlitImage-srcImage-00240`、`-srcSubresource-01707`、`-dstSubresource-01708`(临时探针,未提交) | 只在 split;lavapipe 容忍;其他驱动上 3D 生成 mip 行为未定义 |
|
||||
| 深度/模板生成 mip 的拒绝只记日志,不计入 `WireDeclineTally` | `WireFramebuffer.inc:1397-1404` | `MGWIRE-DECLINES` 看不到这类拒绝 |
|
||||
| `GenerateWireDepthMipLevel` 的注释说会拒绝 1D,代码只拒绝 `is3D`,而调用方从不设置它 | `WireDepthMipmap.inc:66`、`WireFramebuffer.inc:1495` | 无原生 blit 的 1D 深度链会进入 pass(推断,未测) |
|
||||
|
||||
- monolith 缺陷:无。
|
||||
@@ -138,7 +138,9 @@ MAGMA_SERVER_ENV_KNOB_NO_TCP = (".ShaderMip1.", ".ShaderMip2.", ".DepthMip.",
|
||||
".Reclaim.MagmaWireReclaimScenario.ADrawAfterTheEarlyReclaimFollowsTheNewStoreNotTheMemoizedHandle",
|
||||
".Reclaim.MagmaWireReclaimScenario.DescriptorSetsRewindInsideOneLongFrameAfterTheirSubmitRetires") + tuple(
|
||||
f".PrimGenReroute.PrimitivesGeneratedNoXfbScenario.{case}" for case in PRIMGEN_REROUTE_CASES) + tuple(
|
||||
f".SplitRecording64.OversizedRecordingSplitScenario.{case}" for case in SPLIT_RECORDING_CASES)
|
||||
f".SplitRecording64.OversizedRecordingSplitScenario.{case}" for case in SPLIT_RECORDING_CASES) + (
|
||||
# P8-F: the mip shader-shape decline, forced by MGITEST_MAGMA_FORCE_SHADER_MIPMAP (server-read).
|
||||
".ShaderMipDecline.",)
|
||||
|
||||
|
||||
# THE MIRROR SHAPE - entries that exist on tcp ALONE, by construction. Each tail must match at least
|
||||
|
||||
Reference in New Issue
Block a user