mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 18:13:23 +09:00
[MG_Backend, MG_IntegrationTest, docs] (P7): the shader mip arm names the format it refused and renders through scratch when the image cannot be attached
Two Iris cases died on the Redmi (Adreno 830v2, DirectVulkan inproc) with
Fatal{UnmigratedVerb, "Magma:mipmap-shader-format-or-shape"}, deterministically and
under both run-ahead and lockstep, while the monolith arm and the whole host lane
stayed green. That one line was the entire evidence, and five disjunctions share the
name, so the one thing the device could have told us - which format, at what shape,
missing which feature bit - was the one thing it did not.
The name stays first and unprefixed, because the census, the retrace refusal tally
and every log grep key on the leading word; the driver's own answer follows it:
format enum, the view's storage format when it reinterprets, extent, levels, aspect,
view type, usage bits, layer range, and the names of the optimalTilingFeatures bits
that are ABSENT. The next device run reads a line instead of re-deriving it.
The refusal also loses half its territory. The shader arm rendered STRAIGHT INTO the
next level, which needs the image to carry COLOR_ATTACHMENT usage - and a texture
whose format the device declines as a colour attachment never gets that bit, so every
sampled-only texture without BLIT support fell into the refusal rather than into a
fallback. When the FORMAT can be rendered and only the IMAGE cannot, an owned scratch
attachment of the destination level's size takes the pass and vkCmdCopyImage moves the
mip in: a raw texel move, no conversion, texels still from GPU contents rather than a
staged shadow, and the scratch retires with the submit that consumes it - the shape the
multisample resolve arm above it already uses.
Neither shader arm had ever executed on the gate machine. lavapipe reports
BLIT_SRC|BLIT_DST for every colour format this binary can create, so GenerateWireMipmap
takes its native arm for all of them; GenerateMipmapPackedFloatPixels next door looks
like it covers the shader arm and does not, because R11F_G11F_B10F blits natively here
too. MGITEST_MAGMA_FORCE_SHADER_MIPMAP drops the native arm instead, one tier per shape
(1 = render into the level, 2 = render into scratch and copy back), read once and
compiled only into the disaggregated build.
F1WireScenario.GenerateMipmapWithoutNativeBlitPixels paints two different halves rather
than one flat colour - a pass that loses GL's row order averages to the same number
everywhere, and a single-colour assertion would call that correct - and asserts level
one per half. It is registered on the three Magma arms twice, ONE ENVIRONMENT EACH: the
knob changes how every mipmap in the process is generated, so a lane-wide flip would
silently retarget the four green mipmap entries beside it.
Red once (executed, reverted): with the scratch arm forced off, the tier-2 entry dies
with Fatal{UnmigratedVerb, "Magma:mipmap-shader-format-or-shape format=37 extent=8x8x1
levels=4 aspect=0x1 view=1 usage=0x17 layers=0+1/1 missing=DEPTH_STENCIL_ATTACHMENT"},
by name and with the shape in it; restored, the same filter is 8/8 green.
Gates: integration-magma-split 75 (+2), -spawn 54 (+2), -tcp 56 (+2), 0 red;
ctest -L unit 2402/2402; informational integration-magma-full-split 511, 0 red, 58 skip
(the new case skips where the knob is unset); fatal_census green (79 abort sites, 43
family words, 0 unmarked - no site added, the funnel is unchanged); link_ratchet
--assert-monotone unchanged at 186, p7-magma bucket 101.
This commit is contained in:
@@ -13,6 +13,80 @@
|
||||
"MGPipe: Fatal{UnmigratedVerb, \"Magma:%s\"}", detail);
|
||||
}
|
||||
|
||||
// P7 wave 2-B. THE NAME STAYS FIRST AND THE DRIVER'S ANSWER FOLLOWS IT. Two Iris cases
|
||||
// died on the Redmi (Adreno 830v2) with `Fatal{UnmigratedVerb, "Magma:mipmap-shader-
|
||||
// format-or-shape"}` and that line was the whole evidence: five disjunctions share the
|
||||
// name, so the one thing the device could have told us - WHICH format, at what shape,
|
||||
// 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.
|
||||
static String MagmaWireShapeDetail(const char* name, VkFormat format, VkFormat storageFormat,
|
||||
const VkFormatProperties& props, VkImageAspectFlags aspect,
|
||||
VkImageViewType viewType, VkImageUsageFlags usage,
|
||||
const VkExtent2D& extent, Uint32 depth, Uint32 levels,
|
||||
Uint32 baseLayer, Uint32 layerCount, Uint32 arrayLayers) {
|
||||
const auto hex = [](Uint32 value) {
|
||||
static const char* digits = "0123456789abcdef";
|
||||
String out = "0x";
|
||||
Bool leading = true;
|
||||
for (Int shift = 28; shift >= 0; shift -= 4) {
|
||||
const Uint32 nibble = (value >> shift) & 0xfu;
|
||||
if (nibble == 0 && leading && shift) continue;
|
||||
leading = false;
|
||||
out.push_back(digits[nibble]);
|
||||
}
|
||||
return out;
|
||||
};
|
||||
const VkFormatFeatureFlags absent = ~props.optimalTilingFeatures;
|
||||
String missing;
|
||||
const auto note = [&](VkFormatFeatureFlags bit, const char* word) {
|
||||
if (!(absent & bit)) return;
|
||||
if (!missing.empty()) missing += "|";
|
||||
missing += word;
|
||||
};
|
||||
note(VK_FORMAT_FEATURE_BLIT_SRC_BIT, "BLIT_SRC");
|
||||
note(VK_FORMAT_FEATURE_BLIT_DST_BIT, "BLIT_DST");
|
||||
note(VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT, "SAMPLED");
|
||||
note(VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT, "SAMPLED_LINEAR");
|
||||
note(VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT, "COLOR_ATTACHMENT");
|
||||
note(VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT, "DEPTH_STENCIL_ATTACHMENT");
|
||||
if (missing.empty()) missing = "none";
|
||||
String out = name;
|
||||
out += " format=" + std::to_string(static_cast<Int>(format));
|
||||
// A view that reinterprets its owner's format is the OTHER reason the native blit
|
||||
// arm is skipped, and it is invisible in the format number alone.
|
||||
if (storageFormat != format) out += " storage=" + std::to_string(static_cast<Int>(storageFormat));
|
||||
out += " extent=" + std::to_string(extent.width) + "x" + std::to_string(extent.height) +
|
||||
"x" + std::to_string(depth);
|
||||
out += " levels=" + std::to_string(levels);
|
||||
out += " aspect=" + hex(static_cast<Uint32>(aspect));
|
||||
out += " view=" + std::to_string(static_cast<Int>(viewType));
|
||||
out += " usage=" + hex(static_cast<Uint32>(usage));
|
||||
out += " layers=" + std::to_string(baseLayer) + "+" + std::to_string(layerCount) +
|
||||
"/" + std::to_string(arrayLayers);
|
||||
out += " missing=" + missing;
|
||||
return out;
|
||||
}
|
||||
|
||||
// THE HOST GATE CANNOT REACH THE SHADER MIP ARM ON ITS OWN. lavapipe reports
|
||||
// BLIT_SRC|BLIT_DST for every colour format this lane can create, so `nativeBlit` is
|
||||
// always true there and both shader shapes below would only ever run on the phone -
|
||||
// which is how the Adreno refusal reached a release in the first place. The knob makes
|
||||
// them reachable from a host lane and is read exactly once:
|
||||
// 1 - drop the native blit (the arm an Adreno takes for a format without BLIT_DST);
|
||||
// 2 - also hide the destination's COLOR_ATTACHMENT usage (the arm it takes for a
|
||||
// sampled-only texture, which renders through owned scratch and copies back).
|
||||
// MGITEST_, not MOBILEGL_: it is a test knob, it is not mirrored into the accepted-env
|
||||
// table, and this file is compiled only into the disaggregated build.
|
||||
static Uint32 MagmaWireForcedShaderMipmapTier() {
|
||||
static const Uint32 tier = [] {
|
||||
const char* value = std::getenv("MGITEST_MAGMA_FORCE_SHADER_MIPMAP");
|
||||
if (!value || !*value) return 0u;
|
||||
return static_cast<Uint32>(std::strtoul(value, nullptr, 10));
|
||||
}();
|
||||
return tier;
|
||||
}
|
||||
|
||||
VulkanRenderer::WireImage VulkanRenderer::ResolveWireImage(
|
||||
const MG_Pipe::MGPFramebufferState& fbo, const MG_Pipe::MGPSurface& surface,
|
||||
VkImageAspectFlags aspect, Bool isWriteTarget) {
|
||||
@@ -762,19 +836,46 @@
|
||||
const VkFormat format = viewFormat == VK_FORMAT_UNDEFINED ? resource->format : viewFormat;
|
||||
vkGetPhysicalDeviceFormatProperties(m_physicalDevice.handle,format,&props);
|
||||
const auto required = VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT;
|
||||
const Bool nativeBlit = format == resource->format && (props.optimalTilingFeatures & required) == required;
|
||||
const Uint32 forcedShaderTier = MagmaWireForcedShaderMipmapTier();
|
||||
const Bool nativeBlit = forcedShaderTier == 0 && format == resource->format &&
|
||||
(props.optimalTilingFeatures & required) == required;
|
||||
const VkFilter filter = props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT ?
|
||||
VK_FILTER_LINEAR : VK_FILTER_NEAREST;
|
||||
if (!nativeBlit) {
|
||||
// Packed floating-point color formats may support sampling and
|
||||
// rendering without BLIT_DST. Generate from actual GPU mip contents
|
||||
// through the native shader pass; no client or staged texels enter.
|
||||
if (resource->aspect != VK_IMAGE_ASPECT_COLOR_BIT ||
|
||||
resource->viewType == VK_IMAGE_VIEW_TYPE_1D || resource->viewType == VK_IMAGE_VIEW_TYPE_1D_ARRAY ||
|
||||
resource->viewType == VK_IMAGE_VIEW_TYPE_3D ||
|
||||
!(resource->usageFlags & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) ||
|
||||
baseLayer >= resource->arrayLayers || layerCount > resource->arrayLayers - baseLayer)
|
||||
MagmaWireFatal("mipmap-shader-format-or-shape");
|
||||
//
|
||||
// TWO SHADER SHAPES, not one. The arm that was here rendered STRAIGHT INTO the
|
||||
// next level, which needs the image to have been created with
|
||||
// COLOR_ATTACHMENT usage - and a texture whose format the device refuses as a
|
||||
// colour attachment never gets that bit (VkTextureManager.cpp:2156-2160), so
|
||||
// every sampled-only texture without BLIT support fell into the refusal rather
|
||||
// than into a fallback. When the FORMAT can be rendered and only the IMAGE
|
||||
// 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());
|
||||
for (Uint32 level = base + 1; level < base + count; ++level) {
|
||||
for (Uint32 layer = baseLayer; layer < baseLayer + layerCount; ++layer) {
|
||||
WireImage source{};
|
||||
@@ -792,10 +893,63 @@
|
||||
destination.level = level;
|
||||
destination.extent = {std::max(resource->extent.width >> level, 1u),
|
||||
std::max(resource->extent.height >> level, 1u)};
|
||||
if (!BlitWireColorImage(source, destination, 0, 0, source.extent.width, source.extent.height,
|
||||
0, 0, destination.extent.width, destination.extent.height,
|
||||
filter == VK_FILTER_LINEAR ? GL_LINEAR : GL_NEAREST, true))
|
||||
const GLenum blitFilter = filter == VK_FILTER_LINEAR ? GL_LINEAR : GL_NEAREST;
|
||||
if (attachable) {
|
||||
if (!BlitWireColorImage(source, destination, 0, 0, source.extent.width,
|
||||
source.extent.height, 0, 0, destination.extent.width,
|
||||
destination.extent.height, blitFilter, true))
|
||||
MagmaWireFatal("mipmap-shader-blit");
|
||||
continue;
|
||||
}
|
||||
// Owned, per level, and retired with the submit that consumes it - the
|
||||
// shape the multisample resolve above already uses. A shared scratch
|
||||
// resized here would destroy an image an earlier queued pass still reads.
|
||||
auto scratch = MakeUnique<VkTextureManager::TextureResource>();
|
||||
VkImageCreateInfo info{VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO};
|
||||
info.imageType = VK_IMAGE_TYPE_2D;
|
||||
info.format = format;
|
||||
info.extent = {destination.extent.width, destination.extent.height, 1};
|
||||
info.mipLevels = info.arrayLayers = 1;
|
||||
info.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
info.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
info.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
|
||||
VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
VmaAllocationCreateInfo allocation{};
|
||||
allocation.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE;
|
||||
allocation.requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
||||
if (vmaCreateImage(m_allocator, &info, &allocation, &scratch->image,
|
||||
&scratch->allocation, nullptr) != VK_SUCCESS)
|
||||
MagmaWireFatal("mipmap-shader-scratch-allocation");
|
||||
scratch->format = format;
|
||||
scratch->extent = destination.extent;
|
||||
scratch->mipLevels = scratch->arrayLayers = scratch->depth = 1;
|
||||
scratch->aspect = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
WireImage target{};
|
||||
target.image = scratch->image;
|
||||
target.format = format;
|
||||
target.aspect = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
target.extent = destination.extent;
|
||||
target.trackedLayout = &scratch->layout;
|
||||
if (!BlitWireColorImage(source, target, 0, 0, source.extent.width,
|
||||
source.extent.height, 0, 0, target.extent.width, target.extent.height,
|
||||
blitFilter, true))
|
||||
MagmaWireFatal("mipmap-shader-blit");
|
||||
target.layout = scratch->layout;
|
||||
TransitionWireImage(target, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
|
||||
WireImage writeBack = destination;
|
||||
writeBack.layout = resource->layout;
|
||||
TransitionWireImage(writeBack, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
VkImageCopy copy{};
|
||||
copy.srcSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1};
|
||||
copy.dstSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, level, layer, 1};
|
||||
copy.extent = {destination.extent.width, destination.extent.height, 1};
|
||||
vkCmdCopyImage(m_frameContext.GetCurrent().commandBuffer, scratch->image,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, resource->image,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©);
|
||||
WireRetiredObjects retired{};
|
||||
retired.textures.push_back(Move(scratch));
|
||||
RetireWireObjects(Move(retired));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -2684,6 +2684,32 @@ if (MOBILEGL_BUILD_DISAGGREGATED)
|
||||
${mglItestArmProperties}
|
||||
ENVIRONMENT "${mglItestMagmaWireEnvironment}\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p5f-fm${mglItestArmLogTag}-${fmCase}.log")
|
||||
endforeach()
|
||||
# ---- P7 wave 2-B: THE TWO SHADER MIP ARMS, FORCED ------------------------------------
|
||||
#
|
||||
# WHY A KNOB AND NOT A FORMAT. The refusal these two entries cover
|
||||
# (`Magma:mipmap-shader-format-or-shape`) killed two Iris cases on an Adreno 830v2 and
|
||||
# has never once fired on this host, because lavapipe reports BLIT_SRC|BLIT_DST for
|
||||
# every colour format the integration binary can create - so GenerateWireMipmap takes
|
||||
# its NATIVE arm for all of them and neither shader arm is reachable by choosing a
|
||||
# format. `GenerateMipmapPackedFloatPixels` next door looks like it covers the shader
|
||||
# arm and does not: R11F_G11F_B10F blits natively here too.
|
||||
# MGITEST_MAGMA_FORCE_SHADER_MIPMAP drops that arm instead, one tier per shape, so the
|
||||
# host lane exercises the code the device actually runs.
|
||||
#
|
||||
# ITS OWN ENTRIES RATHER THAN A LANE-WIDE FLIP, for the reason the strict-arming entry
|
||||
# gives: the knob changes how EVERY mipmap in the process is generated, so putting it in
|
||||
# the shared Fm environment would silently retarget GenerateMipmapPixels,
|
||||
# GenerateMipmapPackedFloatPixels, GenerateMipmapDepthPixels and the two base/view cases
|
||||
# as well - four green entries that would stop measuring the arm they were written for.
|
||||
foreach(shaderMipTier 1 2)
|
||||
gtest_discover_tests(MobileGLIntegrationTest
|
||||
TEST_PREFIX "DirectVulkan.${mglItestArm}.ShaderMip${shaderMipTier}."
|
||||
TEST_FILTER "F1WireScenario.GenerateMipmapWithoutNativeBlitPixels"
|
||||
DISCOVERY_TIMEOUT 30
|
||||
PROPERTIES LABELS "${mglItestMagmaLabels}" TIMEOUT ${MGL_ITEST_TIMEOUT}
|
||||
${mglItestArmProperties}
|
||||
ENVIRONMENT "${mglItestMagmaWireEnvironment}\;MGITEST_MAGMA_FORCE_SHADER_MIPMAP=${shaderMipTier}\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p7-shadermip${mglItestArmLogTag}-tier${shaderMipTier}.log")
|
||||
endforeach()
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
|
||||
@@ -481,6 +481,66 @@ TEST_F(F1WireScenario, GenerateMipmapPackedFloatPixels) {
|
||||
for (int i = 0; i < 8; ++i) EXPECT_NEAR(rows[i], generated[i], 0.01f);
|
||||
}
|
||||
|
||||
// P7 wave 2-B: THE ARM THE HOST CANNOT REACH ON ITS OWN.
|
||||
//
|
||||
// Two Iris cases (minecraft-1.21.4-fabric-iris-iterationt-in-world and its -nodsa twin) killed
|
||||
// the server apply thread on a Redmi (Adreno 830v2) with
|
||||
// MGPipe: Fatal{UnmigratedVerb, "Magma:mipmap-shader-format-or-shape"}
|
||||
// deterministically, under both run-ahead and lockstep, while the monolith arm passed and the
|
||||
// host lane stayed green. It stayed green because lavapipe reports BLIT_SRC|BLIT_DST for every
|
||||
// colour format this scenario can create: `nativeBlit` is always true here, so NEITHER shader
|
||||
// arm of GenerateWireMipmap has ever executed on the gate machine. A refusal no host lane can
|
||||
// reach is a refusal that ships.
|
||||
//
|
||||
// MGITEST_MAGMA_FORCE_SHADER_MIPMAP is what makes them reachable, and it selects which one:
|
||||
// 1 - drop the native blit only. The destination level is still a colour attachment, so the
|
||||
// pass renders straight into it (the arm an Adreno takes for a format without BLIT_DST).
|
||||
// 2 - also treat the destination as not attachable. The pass renders into owned scratch and
|
||||
// copies the mip in (the arm it takes for a texture whose image never got
|
||||
// COLOR_ATTACHMENT usage - which, before this package, was the refusal itself).
|
||||
//
|
||||
// The assertion is the ORDINARY mip result, per half: a shader mip that renders somewhere else
|
||||
// is still wrong unless the level holds what a blit would have put there, in GL's row order.
|
||||
// Red once (executed, reverted): drop the scratch arm (force `viaScratch` false) and the tier-2
|
||||
// entry dies with Fatal{UnmigratedVerb, "Magma:mipmap-shader-format-or-shape ..."} by name.
|
||||
TEST_F(F1WireScenario, GenerateMipmapWithoutNativeBlitPixels) {
|
||||
if (!Ready()) return;
|
||||
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 reports "
|
||||
"BLIT_SRC|BLIT_DST for GL_RGBA8, so the shader mip arms under test are "
|
||||
"not entered and this case would assert the native blit twice over";
|
||||
Attach(GL_RGBA8, GL_COLOR_ATTACHMENT0, 4);
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
glClearColor(0.75f, 0.25f, 0.5f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
// Two different halves, not one flat colour: a pass that loses GL's row order averages to
|
||||
// the same number everywhere and a single-colour assertion would call that correct.
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
glScissor(0, 0, 8, 4);
|
||||
glClearColor(0.25f, 0.75f, 0.5f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
const auto before = PeekSplitRuntime().emitSeq;
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
ASSERT_GT(PeekSplitRuntime().emitSeq, before) << "F1.ShaderMip.wire";
|
||||
// Level one is 4x4, and each of its rows averages two source rows from ONE half - the
|
||||
// boundary never falls inside a destination texel, so the two colours survive unmixed.
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 1);
|
||||
ASSERT_EQ(glCheckFramebufferStatus(GL_FRAMEBUFFER), GLenum(GL_FRAMEBUFFER_COMPLETE));
|
||||
GLubyte rows[4][4]{};
|
||||
glReadPixels(1, 0, 1, 4, GL_RGBA, GL_UNSIGNED_BYTE, rows);
|
||||
ASSERT_EQ(FirstGLError(), GLenum(GL_NO_ERROR)) << "F1.ShaderMip.error";
|
||||
const int bottom[4] = {64, 191, 128, 255};
|
||||
const int top[4] = {191, 64, 128, 255};
|
||||
for (int row = 0; row < 4; ++row) {
|
||||
const int* expected = row < 2 ? bottom : top;
|
||||
for (int channel = 0; channel < 4; ++channel)
|
||||
EXPECT_NEAR(rows[row][channel], expected[channel], 2)
|
||||
<< "F1.ShaderMip.pixels tier=" << tier << " row=" << row << " channel=" << channel;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(F1WireScenario, GenerateMipmapDepthPixels) {
|
||||
if (!Ready()) return;
|
||||
std::array<GLfloat, 8 * 8> source{};
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
# P7 wave 2-B:Magma blit / copy / mip 簇(分支 `p7/magma-b`)
|
||||
|
||||
> 计划见 [`PLAN-PH-P34B-P7.md`](PLAN-PH-P34B-P7.md) §1.3 与 §3 wave 2-B;规范见
|
||||
> [`CONTRACT-P7.md`](../../../../MobileGL/MG_Remote/CONTRACT-P7.md) §0(规则 I / J)、§3.2(退役 vs
|
||||
> decline 表)、§5.1 / §5.2。基线 = `feat/disaggregated` 上 `~/w7/pipe` 的头 `8652bac0`
|
||||
> (wave 0 已落地:Magma 两进程三臂、`MGPipeSessionFailHook` → `Session::Fail` 漏斗、
|
||||
> `scripts/link_ratchet.py`)。
|
||||
>
|
||||
> 主机口径:WSL Arch + lavapipe(`/usr/share/vulkan/icd.d/lvp_icd.json`,系统唯一 ICD),
|
||||
> `build-split` = Release / clang / ccache / `DISAGGREGATED=ON` `INPROC=ON` `PIPE_PUSH=ON`
|
||||
> `BUILD_INTEGRATION_TEST=ON`,ICD 与 `~/w7/pipe/build-split` 逐项相同。
|
||||
>
|
||||
> 基线门读数(本树实测):`integration-magma-split` **73**(71 PASS + 2 SKIP)、`-spawn` **52**、
|
||||
> `-tcp` **54**,三臂 0 红。
|
||||
|
||||
## 0. 每片一条提交,每片一次 red-once
|
||||
|
||||
| 片 | 名 | 退役 / decline | 提交 |
|
||||
|---|---|---|---|
|
||||
| 1 | `mipmap-shader-format-or-shape` | 诊断 + 颜色 shader mip 回落(**退役**一半:可渲染格式的不可附着图像) | 见 §1 |
|
||||
|
||||
---
|
||||
|
||||
## 1. 片 1:`mipmap-shader-format-or-shape` 的诊断与颜色回落
|
||||
|
||||
### 1.1 设备证据与它为什么主机看不见
|
||||
|
||||
`minecraft-1.21.4-fabric-iris-iterationt-in-world` 与 `-iterationt-nodsa-in-world` 在 Redmi
|
||||
(`2f7cbe2e`,Adreno 830v2,DirectVulkan inproc,`--use-pbuffer`)上确定性地让 server apply 线程死于
|
||||
|
||||
```
|
||||
MGPipe: Fatal{UnmigratedVerb, "Magma:mipmap-shader-format-or-shape"}
|
||||
```
|
||||
|
||||
run-ahead 与 lockstep 都复现;monolith 臂通过;WSL/lavapipe 全绿。
|
||||
|
||||
那一行就是全部证据,而 `WireFramebuffer.inc` 里这个名字下挂着**五个互斥的析取项**(aspect 非颜色、
|
||||
1D/1D_ARRAY/3D 视图、图像缺 `COLOR_ATTACHMENT` usage、层范围越界、格式不是采样浮点)。设备能告诉我们的
|
||||
唯一一件事——**是哪一个**——恰恰是它没告诉我们的。
|
||||
|
||||
主机看不见它,原因是结构性的:lavapipe 对本集成二进制能创建的**每一个**颜色格式都报
|
||||
`BLIT_SRC|BLIT_DST`,于是 `GenerateWireMipmap` 的 `nativeBlit` 恒真,**两条 shader 臂在门机器上从未
|
||||
执行过一次**。旁边那条看起来覆盖了 shader 臂的 `F1WireScenario.GenerateMipmapPackedFloatPixels` 也没有:
|
||||
`R11F_G11F_B10F` 在这里同样原生 blit。一条没有任何主机车道能到达的拒绝,就是一条会发版的拒绝。
|
||||
|
||||
### 1.2 做了什么
|
||||
|
||||
**(a) 诊断。** 名字仍是第一个词、不加前缀(census、retrace 拒绝普查与所有 log grep 都按首词键控),
|
||||
其后附上驱动自己的答案:格式枚举、(视图重解释时的)存储格式、extent、levels、aspect、viewType、
|
||||
usage 位、层范围,以及 `optimalTilingFeatures` 里**缺**的特征位名。实测输出形如
|
||||
|
||||
```
|
||||
Magma:mipmap-shader-format-or-shape format=37 extent=8x8x1 levels=4 aspect=0x1 view=1 \
|
||||
usage=0x17 layers=0+1/1 missing=DEPTH_STENCIL_ATTACHMENT
|
||||
```
|
||||
|
||||
下一次设备窗口不必再推导,读一行即可指名。
|
||||
|
||||
**(b) 颜色回落(退役的那一半)。** 原来的 shader 臂**直接渲染进下一级**,这要求图像创建时带
|
||||
`COLOR_ATTACHMENT` usage;而设备拒绝把某格式作为颜色附件时,`VkTextureManager` 就不会给它这个位——
|
||||
于是每一个「缺 BLIT 且只采样」的纹理都落进拒绝而不是回落。现在区分两种形状:
|
||||
|
||||
- **格式可渲染、图像可附着** → 原臂不变(直接渲染进目标级);
|
||||
- **格式可渲染、图像不可附着** → 以目标级尺寸创建自有 scratch 颜色附件,shader 渲染进 scratch,再
|
||||
`vkCmdCopyImage` 原样搬进该级。纯 texel 搬运,无格式转换,texel 仍来自 GPU 内容而非 staged 影子;
|
||||
scratch 按 multisample resolve 臂同样的形状随提交退休(共享 scratch 在此处改尺寸会毁掉更早排队的一次
|
||||
pass 仍在读的图像)。
|
||||
|
||||
仍然拒绝且现在**指名**的残余:非颜色 aspect(深度归片 2)、1D/1D_ARRAY/3D、层范围越界、非采样浮点格式、
|
||||
以及格式本身就不能作颜色附件(此时没有任何 shader 臂可走)。
|
||||
|
||||
**(c) 主机可达性(测试旋钮)。** `MGITEST_MAGMA_FORCE_SHADER_MIPMAP`,只读一次,只在
|
||||
`#if MOBILEGL_BUILD_DISAGGREGATED` 内编译,不进 `ConfigLoader` 的 accepted-env 表:
|
||||
|
||||
- `1` = 丢掉原生 blit(Adreno 对缺 `BLIT_DST` 格式走的那条臂);
|
||||
- `2` = 再把目标当作不可附着(Adreno 对只采样纹理走的那条臂,也就是本片之前的拒绝本身)。
|
||||
|
||||
### 1.3 场景与车道
|
||||
|
||||
`F1WireScenario.GenerateMipmapWithoutNativeBlitPixels`:8×8 RGBA8、上下两半不同色(一色铺满时,丢失
|
||||
GL 行序的实现会平均成同一个数,单色断言会把它判对)、`glGenerateMipmap` 后读 level 1 的四行,断言下两行
|
||||
是下半色、上两行是上半色(边界从不落在目标 texel 内部,两色不混)。旋钮未设时 `GTEST_SKIP` 并写明理由。
|
||||
|
||||
注册在 Fm 车道的三臂上,**每臂两条**(`ShaderMip1.` / `ShaderMip2.`),**各自一条 ENVIRONMENT**——
|
||||
不是车道级翻转:旋钮改变本进程内**每一次** mipmap 的生成方式,放进共享 Fm 环境会把
|
||||
`GenerateMipmapPixels`、`GenerateMipmapPackedFloatPixels`、`GenerateMipmapDepthPixels` 与两条
|
||||
base/view 用例一起改道,四条绿条目会不再测量它们被写出来测量的那条臂。
|
||||
|
||||
### 1.4 red-once(R-16,已执行并还原)
|
||||
|
||||
把 scratch 臂关掉(`viaScratch` 恒假)重建,`ctest -R ShaderMip`:
|
||||
|
||||
```
|
||||
CTEST_RC=8
|
||||
MGPipe: Fatal{UnmigratedVerb, "Magma:mipmap-shader-format-or-shape format=37 extent=8x8x1
|
||||
levels=4 aspect=0x1 view=1 usage=0x17 layers=0+1/1 missing=DEPTH_STENCIL_ATTACHMENT"}
|
||||
75% tests passed, 2 tests failed out of 8
|
||||
```
|
||||
|
||||
即 tier-2 条目**按名死亡**,且诊断把格式与形状都说出来了。还原后同一过滤器 8/8 绿。
|
||||
|
||||
### 1.5 门
|
||||
|
||||
`integration-magma-split` **75**(+2)、`-spawn` **54**(+2)、`-tcp` **56**(+2),三臂 0 红。
|
||||
Reference in New Issue
Block a user