mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 18:13:23 +09:00
[MG_Util, MG_Test] (P7): list the no-draw depth/stencil render-pass resolve in the Vulkan POST's Known Driver Bugs as FIXED
This commit is contained in:
@@ -1171,3 +1171,55 @@ TEST(DriverBugProbes, Packed16MirrorArithmeticMatchesTheDeviceEvidence) {
|
||||
EXPECT_EQ(MirrorPacked5551(0x0047), 0x8C20);
|
||||
EXPECT_EQ(MirrorPacked5551(0x3800), 0x0007);
|
||||
}
|
||||
|
||||
// ===================== DEPTH/STENCIL RENDER-PASS RESOLVE (Vulkan) =====================
|
||||
//
|
||||
// The Vulkan table's row for MG_Backend/DirectVulkan/Renderer/WireDepthResolveProbe.h's probe: a
|
||||
// pure function of its measurement. FIXED where the shader substitute can write every aspect,
|
||||
// UNFIXABLE where a stencil resolve still has to use the render pass that writes nothing, and no
|
||||
// row at all for a clean or inconclusive measurement.
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
namespace {
|
||||
MG_Backend::DirectVulkan::WireDepthResolveProbeMeasurement DepthResolveMeasurement(Uint32 renderPassMatches,
|
||||
Uint32 shaderMatches) {
|
||||
MG_Backend::DirectVulkan::WireDepthResolveProbeMeasurement measurement;
|
||||
measurement.ran = true;
|
||||
MG_Backend::DirectVulkan::WireDepthResolveFormatReading reading;
|
||||
reading.name = "D24_UNORM_S8_UINT";
|
||||
reading.samples = 4;
|
||||
reading.ran = true;
|
||||
for (auto* aspect : {&reading.depth, &reading.stencil}) {
|
||||
aspect->measured = true;
|
||||
aspect->texels = 16;
|
||||
aspect->renderPassMatches = renderPassMatches;
|
||||
aspect->shaderRan = true;
|
||||
aspect->shaderMatches = shaderMatches;
|
||||
}
|
||||
measurement.formats.push_back(reading);
|
||||
return measurement;
|
||||
}
|
||||
} // namespace
|
||||
#endif
|
||||
|
||||
TEST(DriverBugProbes, DepthStencilResolvePassRowIsFixedWhereTheShaderSubstituteWritesEveryAspect) {
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
using MobileGL::MG_Util::SelfTest::DescribeDepthStencilResolvePassBug;
|
||||
const auto broken = DepthResolveMeasurement(0, 16);
|
||||
const auto fixed = DescribeDepthStencilResolvePassBug(broken, /*shaderStencilExport=*/true);
|
||||
ASSERT_TRUE(fixed.has_value()) << "the render pass wrote nothing while the control resolved: a bug row";
|
||||
EXPECT_EQ(fixed->verdict, DriverBugVerdict::Fixed);
|
||||
EXPECT_FALSE(fixed->name.empty());
|
||||
EXPECT_NE(fixed->detail.find("render pass 0/16"), std::string::npos) << fixed->detail;
|
||||
const auto stencilLeft = DescribeDepthStencilResolvePassBug(broken, /*shaderStencilExport=*/false);
|
||||
ASSERT_TRUE(stencilLeft.has_value());
|
||||
EXPECT_EQ(stencilLeft->verdict, DriverBugVerdict::Unfixable)
|
||||
<< "without VK_EXT_shader_stencil_export a stencil resolve still lands on the broken pass";
|
||||
EXPECT_FALSE(DescribeDepthStencilResolvePassBug(DepthResolveMeasurement(16, 16), true).has_value())
|
||||
<< "a clean render pass is no row";
|
||||
EXPECT_FALSE(DescribeDepthStencilResolvePassBug(DepthResolveMeasurement(0, 0), true).has_value())
|
||||
<< "an inconclusive probe (the control failed too) must never be reported as the bug";
|
||||
EXPECT_FALSE(DescribeDepthStencilResolvePassBug({}, true).has_value()) << "a probe that did not run is no row";
|
||||
#else
|
||||
GTEST_SKIP() << "the Vulkan table exists only in the disaggregated build";
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
|
||||
#include <Config.h>
|
||||
#include <MG_Util/Debug/Log.h>
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
#include <MG_Backend/DirectVulkan/Renderer/WireDepthResolveProbe.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
@@ -2475,4 +2478,73 @@ namespace MobileGL::MG_Util::SelfTest {
|
||||
}
|
||||
return findings;
|
||||
}
|
||||
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
Optional<DriverBugFinding> DescribeDepthStencilResolvePassBug(
|
||||
const MG_Backend::DirectVulkan::WireDepthResolveProbeMeasurement& measurement, Bool shaderStencilExport) {
|
||||
using namespace MG_Backend::DirectVulkan;
|
||||
if (EvaluateWireDepthResolveProbe(measurement) != WireDepthResolveProbeVerdict::RenderPassResolveBroken)
|
||||
return std::nullopt;
|
||||
String detail =
|
||||
"a render pass whose subpass carries a VK_KHR_depth_stencil_resolve attachment (SAMPLE_ZERO) and "
|
||||
"records no draw leaves its single-sample resolve target unwritten, while MobileGL's shader "
|
||||
"resolve pass fed the same multisample inputs resolves them correctly - measured here on 4x4 "
|
||||
"4-sample images cleared to depth 0.25 / stencil 0x5A over a depth 0.75 / stencil 0xA5 "
|
||||
"sentinel: ";
|
||||
detail += DescribeWireDepthResolveProbe(measurement);
|
||||
detail +=
|
||||
". On the split, inproc and tcp transports a multisample depth/stencil resolve "
|
||||
"(glBlitFramebuffer out of a multisample framebuffer) therefore takes the shader pass "
|
||||
"first - sample 0 fetched and written through gl_FragDepth / gl_FragStencilRefARB - and "
|
||||
"keeps the render pass only as the fallback for what the shader cannot write; the "
|
||||
"monolith transport never records that render pass";
|
||||
if (!shaderStencilExport) {
|
||||
detail +=
|
||||
". THIS DEVICE HAS NO VK_EXT_shader_stencil_export, so a fragment shader cannot write "
|
||||
"the stencil aspect: a STENCIL resolve still falls back to the render pass and reads "
|
||||
"back unwritten. Depth is repaired, stencil is not";
|
||||
}
|
||||
return DriverBugFinding{"Depth/stencil render-pass resolve writes nothing",
|
||||
shaderStencilExport ? DriverBugVerdict::Fixed : DriverBugVerdict::Unfixable,
|
||||
Move(detail)};
|
||||
}
|
||||
|
||||
namespace {
|
||||
// The same probe the Magma renderer runs at bring-up (VulkanRenderer::
|
||||
// ArmWireDepthResolveOrder), on a throwaway device carrying the renderer's extensions.
|
||||
Optional<DriverBugFinding> ProbeDepthStencilResolvePassBug(const VulkanDriverBugProbeContext& context) {
|
||||
using namespace MG_Backend::DirectVulkan;
|
||||
if (context.getInstanceProcAddr == nullptr || context.deviceExtensions == nullptr) return std::nullopt;
|
||||
Bool renderPassArmAvailable = false, shaderStencilExport = false;
|
||||
const WireDepthResolveProbeMeasurement measurement = RunWireDepthResolveProbeOnThrowawayDevice(
|
||||
context.getInstanceProcAddr, context.instance, context.physicalDevice,
|
||||
context.graphicsQueueFamilyIndex, *context.deviceExtensions, renderPassArmAvailable,
|
||||
shaderStencilExport);
|
||||
// Without the extension there is no render-pass arm and nothing to have a bug in.
|
||||
if (!renderPassArmAvailable) return std::nullopt;
|
||||
const WireDepthResolveProbeVerdict verdict = EvaluateWireDepthResolveProbe(measurement);
|
||||
MGLOG_I("Driver POST: depth/stencil resolve probe verdict=%s%s%s",
|
||||
WireDepthResolveProbeVerdictName(verdict), measurement.failureReason.empty() ? "" : " - ",
|
||||
measurement.failureReason.c_str());
|
||||
for (const WireDepthResolveFormatReading& reading : measurement.formats)
|
||||
MGLOG_I("Driver POST: depth/stencil resolve probe %s", DescribeWireDepthResolveFormat(reading).c_str());
|
||||
return DescribeDepthStencilResolvePassBug(measurement, shaderStencilExport);
|
||||
}
|
||||
|
||||
using VulkanDriverBugProbeFn = Optional<DriverBugFinding> (*)(const VulkanDriverBugProbeContext&);
|
||||
constexpr VulkanDriverBugProbeFn kVulkanDriverBugProbes[] = {
|
||||
&ProbeDepthStencilResolvePassBug,
|
||||
};
|
||||
} // namespace
|
||||
|
||||
Vector<DriverBugFinding> CollectVulkanKnownDriverBugs(const VulkanDriverBugProbeContext& context) {
|
||||
Vector<DriverBugFinding> findings;
|
||||
for (const VulkanDriverBugProbeFn probe : kVulkanDriverBugProbes) {
|
||||
if (Optional<DriverBugFinding> finding = probe(context)) {
|
||||
findings.push_back(Move(*finding));
|
||||
}
|
||||
}
|
||||
return findings;
|
||||
}
|
||||
#endif
|
||||
} // namespace MobileGL::MG_Util::SelfTest
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
#pragma once
|
||||
#include <Includes.h>
|
||||
#include <MG_Util/BackendLoaders/OpenGL/Loader.h>
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
#include <MG_Backend/DirectVulkan/Renderer/WireDepthResolveArm.h>
|
||||
#endif
|
||||
|
||||
namespace MobileGL::MG_Util::SelfTest {
|
||||
// ===================== KNOWN DRIVER BUGS =====================
|
||||
@@ -33,7 +36,8 @@ namespace MobileGL::MG_Util::SelfTest {
|
||||
//
|
||||
// ADDING A SIBLING IS ONE FUNCTION: write an `Optional<DriverBugFinding> ProbeXxx(gl)`
|
||||
// that returns nullopt when the driver is not affected, and add it to the table in
|
||||
// CollectGlesKnownDriverBugs().
|
||||
// CollectGlesKnownDriverBugs(). A Vulkan probe takes a VulkanDriverBugProbeContext instead
|
||||
// and goes in CollectVulkanKnownDriverBugs()'s table (bottom of this file).
|
||||
|
||||
// What MobileGL can do about a bug this device HAS. There is deliberately no "not
|
||||
// affected" member: a driver that passes the probe produces no finding at all, so the
|
||||
@@ -342,4 +346,35 @@ namespace MobileGL::MG_Util::SelfTest {
|
||||
// Every known driver bug this GLES driver actually has. Bugs it does not have are absent,
|
||||
// so an unaffected device renders an empty section rather than a wall of "not affected".
|
||||
Vector<DriverBugFinding> CollectGlesKnownDriverBugs(const MG_External::GLESFunctionsTable& gl);
|
||||
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
// ===================== KNOWN VULKAN DRIVER BUGS =====================
|
||||
//
|
||||
// The DirectVulkan (Magma) backend's table, under the rules above: a control per probe, an
|
||||
// inconclusive probe is never a finding, a clean one contributes nothing. A probe gets the
|
||||
// POST's throwaway instance and the physical device it picked, and creates whatever device it
|
||||
// needs itself.
|
||||
//
|
||||
// DISAGGREGATED BUILDS ONLY, for now: its one row is a defect of the Magma WIRE arm, which the
|
||||
// monolith never records, and the pull build's image must not move (P7 gate G1). A row about a
|
||||
// path the monolith takes moves this guard into the table.
|
||||
struct VulkanDriverBugProbeContext {
|
||||
PFN_vkGetInstanceProcAddr getInstanceProcAddr = nullptr;
|
||||
VkInstance instance = VK_NULL_HANDLE;
|
||||
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
|
||||
Uint32 graphicsQueueFamilyIndex = 0;
|
||||
const Vector<VkExtensionProperties>* deviceExtensions = nullptr;
|
||||
};
|
||||
|
||||
// The row for the multisample depth/stencil resolve probe
|
||||
// (MG_Backend/DirectVulkan/Renderer/WireDepthResolveProbe.h), a pure function of its
|
||||
// measurement: nullopt unless the verdict is the defect. FIXED where the shader substitute
|
||||
// can write every aspect (VK_EXT_shader_stencil_export present), UNFIXABLE otherwise - a
|
||||
// stencil resolve then still lands on the render pass that writes nothing.
|
||||
Optional<DriverBugFinding> DescribeDepthStencilResolvePassBug(
|
||||
const MG_Backend::DirectVulkan::WireDepthResolveProbeMeasurement& measurement, Bool shaderStencilExport);
|
||||
|
||||
// Every known driver bug this Vulkan driver actually has.
|
||||
Vector<DriverBugFinding> CollectVulkanKnownDriverBugs(const VulkanDriverBugProbeContext& context);
|
||||
#endif
|
||||
} // namespace MobileGL::MG_Util::SelfTest
|
||||
|
||||
@@ -3028,6 +3028,20 @@ namespace MobileGL::MG_Util::SelfTest {
|
||||
graphicsQueueFamilyIndex, deviceExtensions, features,
|
||||
vkGetPhysicalDeviceFeatures2Fn, vkGetPhysicalDeviceProperties2Fn);
|
||||
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
// The "Known Driver Bugs" section's Vulkan table (DriverBugProbes.h): each probe makes its
|
||||
// own throwaway device on the physical device picked above.
|
||||
{
|
||||
VulkanDriverBugProbeContext bugContext;
|
||||
bugContext.getInstanceProcAddr = getInstanceProcAddr;
|
||||
bugContext.instance = instance;
|
||||
bugContext.physicalDevice = physicalDevice;
|
||||
bugContext.graphicsQueueFamilyIndex = graphicsQueueFamilyIndex;
|
||||
bugContext.deviceExtensions = &deviceExtensions;
|
||||
builder.report.knownDriverBugs = CollectVulkanKnownDriverBugs(bugContext);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (HasVkExtension(deviceExtensions, VK_KHR_DRAW_INDIRECT_COUNT_EXTENSION_NAME)) {
|
||||
builder.Pass("VK_KHR_draw_indirect_count",
|
||||
"supported (count-buffer indirect draws run as single native "
|
||||
|
||||
Reference in New Issue
Block a user