From fdd611e83d8786a0a5750c3fa65da6b73cd52300 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Tue, 22 Sep 2026 15:45:30 -0400 Subject: [PATCH] [MG_Backend, MG_Test, MG_IntegrationTest, scripts, docs] (P7): the depth mip chain is baked, and every baked header in the tree now has to match its source MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CONTRACT-P7 §5.1's (A) and (D), and (D) is what makes (A) safe to have. (A) WireDepthMipmap.{vert,frag,inc}. VK_FORMAT_FEATURE_BLIT_DST is OPTIONAL for depth formats, so a depth chain through a shader is the ordinary case and not the exotic one - the monolith arm has carried such a fallback since before the split. The wire arm had none, so a depth texture the device would not blit died on the first disjunction of `mipmap-shader-format-or-shape`, and a DEPTH|STENCIL one died a guard earlier as `depth-stencil-mipmap@P7` whether or not it could be blitted. The monolith's fallback is not reusable, which is the whole of (A): it builds a GL ProgramObject, two ShaderObjects and a GL sampler, binds through the uniform manager's global UBO and writes uSrcTexelSize as a default-block uniform - four frontend objects and a GL compiler on the server's apply thread. The baked program is the same shader with the same arithmetic on a push constant and its own descriptor set. The fragment stage is a PORT and not a rewrite: §3.2 retires a refusal by making the wire arm observably the monolith's, so the 2x2 box lands where the monolith's lands, including the half texel its ivec2(texCoord * srcTexelSize) sits off the destination centre. `depth-stencil-mipmap@P7` therefore splits. DEPTH-only retires into the baked program. DEPTH|STENCIL declines - MGLOG_E_ONCE and return, before the chain is grown, so a level this verb will not fill is not created and then recorded as defined. A decline rather than a Fatal because the monolith does no better: its fallback asserts depth-only, and an assert is nothing in the release build both arms ship as. Nothing in GL promises a stencil mip either, and this pass writes gl_FragDepth - it would leave the stencil aspect of the level it just produced undefined. (D) MOBILEGL_BAKED_INTERNAL_SHADERS. A baked header is a compiler output committed with its input beside it and nothing connecting the two: edit the source and the build stays green while the shader that runs is the one from before the edit. MG_Test's BakedInternalShadersTest recompiles all eight arrays - WireColorBlit x2, WireDepthMipmap x2, the iterationRP witness, the three probe stages - from their committed GLSL with the in-tree glslang and compares word for word. THE GATE IS THE BAKER. scripts/bake_internal_shaders.py compiles nothing; it runs this binary with MOBILEGL_BAKE_INTERNAL_SHADERS and splices the words it emits. A gate that used the glslang LIBRARY while the baker shelled out to a glslangValidator BINARY would be two compilers with no rule about which is right, and the day their defaults diverge the gate reds on a header nobody can regenerate. What the first run measured, which is the one thing nobody could know beforehand: WireColorBlitSpirv.h and all three arrays of PrimitivesGeneratedNoXfbProbeSpv.h were already fresh - the hand-run glslangValidator words reproduce exactly. The iterationRP witness was stale, as its own prose said; it is regenerated here (2142 -> 2143 words), and the witness magic 0x50323033 is still there because it is a constant in the .comp rather than a property of the words. All three provenance comments now point at the script and the gate instead of at a tool on somebody's PATH. Scenarios, on the three Magma arms: GenerateMipmapDepthWithoutNativeBlitPixels runs DEPTH_COMPONENT24 and DEPTH_COMPONENT32F under MGITEST_MAGMA_FORCE_SHADER_MIPMAP=1 and reads level one back through a real sampled attachment, asserting per half and leaving row 3 - the straddle band - unpinned, because pinning it would assert the half-texel offset rather than the mip. GenerateMipmapDepthStencilDeclinesAndKeepsTheSession takes no knob (the decline is on the aspect, before blit support is asked about) and pins that the session survives and level zero still holds what was cleared into it. Red once, three of them, executed and reverted: - a one-character edit to WireDepthMipmap.frag (no rebuild - the gate reads the source at runtime) reds the WireDepthMipmapFragment row at word 279; - with the depth arm disabled, the DepthMip entries die with Fatal{UnmigratedVerb, "Magma:mipmap-shader-format-or-shape format=125 ... aspect=0x2 ... missing=COLOR_ATTACHMENT"} - format 125 is X8_D24_UNORM_PACK32, which slice 1's diagnostic names for us; - with the Fatal restored, the decline case dies as Fatal{UnmigratedVerb, "Magma:depth-stencil-mipmap@P7"}. Gates: integration-magma-split 77 (+2), -spawn 56 (+2), -tcp 58 (+2), 0 red; ctest -L unit 2410 (+8, the gate's rows); informational integration-magma-full-split 513, 0 red, 59 skip (base 510/0/57); fatal_census green (79 abort sites, 43 words, 0 unmarked); link_ratchet --assert-monotone unchanged at 186, p7-magma 101. G1 holds and it was not obvious: the witness header is compiled into the pull build too, and its word count changed. Pull .text is still 0xa52203 and the defined-symbol name list is identical to ~/w7/p7-before/pull-syms.txt line for line (30570) - the change lands in .rodata and widens no instruction encoding. VulkanRenderer.h is edited outside this package's file list: the new pass needs four private declarations next to the colour blit's. Flagged for the integrator. --- .../DirectVulkan/Renderer/VulkanRenderer.cpp | 4 + .../DirectVulkan/Renderer/VulkanRenderer.h | 8 + .../Renderer/WireColorBlitSpirv.h | 11 +- .../Renderer/WireDepthMipmap.frag | 28 + .../DirectVulkan/Renderer/WireDepthMipmap.inc | 320 +++++++++++ .../Renderer/WireDepthMipmap.vert | 33 ++ .../Renderer/WireDepthMipmapSpirv.h | 174 ++++++ .../DirectVulkan/Renderer/WireFramebuffer.inc | 67 ++- MobileGL/MG_IntegrationTest/CMakeLists.txt | 20 + .../Scenarios/F1WireScenario.cpp | 103 ++++ .../SelfTest/BakedInternalShadersTest.cpp | 226 ++++++++ MobileGL/MG_Test/SelfTest/CMakeLists.txt | 31 ++ .../DriverPostIterationRPWitnessSpv.h | 525 +++++++++--------- .../PrimitivesGeneratedNoXfbProbeSpv.h | 14 +- docs/Disaggregated/notes/p7/magma-b.md | 101 ++++ scripts/bake_internal_shaders.py | 184 ++++++ 16 files changed, 1576 insertions(+), 273 deletions(-) create mode 100644 MobileGL/MG_Backend/DirectVulkan/Renderer/WireDepthMipmap.frag create mode 100644 MobileGL/MG_Backend/DirectVulkan/Renderer/WireDepthMipmap.inc create mode 100644 MobileGL/MG_Backend/DirectVulkan/Renderer/WireDepthMipmap.vert create mode 100644 MobileGL/MG_Backend/DirectVulkan/Renderer/WireDepthMipmapSpirv.h create mode 100644 MobileGL/MG_Test/SelfTest/BakedInternalShadersTest.cpp create mode 100644 scripts/bake_internal_shaders.py diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp index 30e1eece..351cf0d1 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp @@ -3445,6 +3445,7 @@ void main() { CollectWireObjects(m_submitCounter, true); ClearAllWireDrawPassCaches(); DestroyWireColorBlitResources(); + DestroyWireDepthMipmapResources(); #endif OnSubmitsCompletedUpTo(m_submitCounter); DestroySubmitFencePool(); @@ -7877,6 +7878,9 @@ void main() { #include "WireFramebuffer.inc" #include "WireTextureReadback.inc" #include "WireColorBlit.inc" + // AFTER WireColorBlit.inc: the depth mip reuses that file's window-Y convention and is + // written to be read beside it. + #include "WireDepthMipmap.inc" #include "WireDraw.inc" void VulkanRenderer::Clear(GLbitfield mask) { diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h index 359fae5c..1f07213b 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h @@ -500,6 +500,14 @@ namespace MobileGL::MG_Backend::DirectVulkan { struct WireColorBlitResources; WireColorBlitResources* m_wireColorBlitResources = nullptr; void DestroyWireColorBlitResources(); + // P7 wave 2-B, CONTRACT-P7 §5.1 (A): the baked depth-mip program (WireDepthMipmap.inc). + // One level of a depth chain, source and destination being two depth subresources of + // the same image; the caller loops. It owns no frontend object, which is what the + // monolith arm's GenerateDepthMipmapWithShader cannot say. + Bool GenerateWireDepthMipLevel(WireImage source, WireImage destination); + struct WireDepthMipmapResources; + WireDepthMipmapResources* m_wireDepthMipmapResources = nullptr; + void DestroyWireDepthMipmapResources(); void ReadWirePixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels); void GenerateWireMipmap(); diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/WireColorBlitSpirv.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/WireColorBlitSpirv.h index acf485d8..4101df5e 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/WireColorBlitSpirv.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/WireColorBlitSpirv.h @@ -1,6 +1,11 @@ -// Generated from WireColorBlit.vert / WireColorBlit.frag with: -// glslangValidator -V --target-env vulkan1.0 -o .spv -// Validated with spirv-val --target-env vulkan1.0. Do not edit the words by hand. +// Generated from WireColorBlit.vert / WireColorBlit.frag (Vulkan 1.0 / SPIR-V 1.0) by +// scripts/bake_internal_shaders.py, which runs MG_Test's BakedInternalShadersTest with the +// IN-TREE glslang and splices the words it emits into the arrays below. Do not edit the words +// by hand: MOBILEGL_BAKED_INTERNAL_SHADERS (the same test, run as a gate) recompiles every +// source in its table and reds on the first word that disagrees. +// +// These words were hand-run through glslangValidator before that gate existed, and the gate +// reproduces them exactly - which is the one thing nobody could have known beforehand. #pragma once static constexpr Uint32 kWireColorBlitVertexSpirv[] = { diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/WireDepthMipmap.frag b/MobileGL/MG_Backend/DirectVulkan/Renderer/WireDepthMipmap.frag new file mode 100644 index 00000000..27aebfff --- /dev/null +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/WireDepthMipmap.frag @@ -0,0 +1,28 @@ +#version 450 +// The monolith arm's kDepthMipmapFragmentShaderSource (VulkanRenderer.cpp), ported to the +// baked pipeline: the same 2x2 box of texelFetches, the same clamp, the same average, the +// same gl_FragDepth. It is a PORT and not a rewrite on purpose - CONTRACT-P7 §3.2 retires a +// refusal by making the wire arm observably the monolith's, so the two arms have to agree +// texel for texel, including where they read. +// +// uSrcTexelSize arrives on the push constant rather than in a uniform block: the wire arm +// owns no GL program, so there is no default-block uniform to write and no global UBO to +// bind. That is the whole of OQ-13's narrow half for this program. +layout(push_constant) uniform DepthMipmapRect { + vec4 srcRect; + vec4 dstRect; + int surfaceTransform; + ivec2 srcTexelSize; +} rect; +layout(set = 0, binding = 0) uniform sampler2D sourceImage; +layout(location = 0) in vec2 texCoord; + +void main() { + ivec2 srcBase = ivec2(texCoord * vec2(rect.srcTexelSize)); + ivec2 srcMax = rect.srcTexelSize - ivec2(1); + float depth0 = texelFetch(sourceImage, clamp(srcBase, ivec2(0), srcMax), 0).r; + float depth1 = texelFetch(sourceImage, clamp(srcBase + ivec2(1, 0), ivec2(0), srcMax), 0).r; + float depth2 = texelFetch(sourceImage, clamp(srcBase + ivec2(0, 1), ivec2(0), srcMax), 0).r; + float depth3 = texelFetch(sourceImage, clamp(srcBase + ivec2(1, 1), ivec2(0), srcMax), 0).r; + gl_FragDepth = 0.25 * (depth0 + depth1 + depth2 + depth3); +} diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/WireDepthMipmap.inc b/MobileGL/MG_Backend/DirectVulkan/Renderer/WireDepthMipmap.inc new file mode 100644 index 00000000..2d14a2ae --- /dev/null +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/WireDepthMipmap.inc @@ -0,0 +1,320 @@ +// Included by VulkanRenderer.cpp inside the backend namespace, after WireColorBlit.inc. +// The native depth mip owns no frontend object and never invokes the GL shader compiler. +// +// WHY THIS FILE EXISTS AT ALL. vkCmdBlitImage is the ordinary way to halve a mip, and for a +// depth format it is often not available: VK_FORMAT_FEATURE_BLIT_DST is optional for depth, +// and the monolith arm has carried a shader fallback for exactly that case since before the +// split (VulkanRenderer.cpp's GenerateDepthMipmapWithShader). The wire arm had no such +// fallback, so every depth mip the device could not blit died as +// `Magma:mipmap-shader-format-or-shape`, and a DEPTH|STENCIL texture died one guard earlier +// as `Magma:depth-stencil-mipmap@P7` whether or not it could be blitted. +// +// The monolith fallback is not reusable here and that is the whole of CONTRACT-P7 §5.1 (A): +// it builds a GL ProgramObject, two GL ShaderObjects and a GL sampler, binds through the +// uniform manager's global UBO, and writes uSrcTexelSize as a default-block uniform - four +// frontend objects and a GL compiler on the server's apply thread. The baked program below is +// the same shader with the same arithmetic, driven by a push constant and its own descriptor +// set, which is what lets the server generate a depth mip while owning nothing of the client. +#if MOBILEGL_BUILD_DISAGGREGATED +#include "WireDepthMipmapSpirv.h" + + struct VulkanRenderer::WireDepthMipmapResources { + struct Target { + VkFormat format = VK_FORMAT_UNDEFINED; + VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED; + VkRenderPass renderPass = VK_NULL_HANDLE; + VkPipeline pipeline = VK_NULL_HANDLE; + }; + VkShaderModule vertex = VK_NULL_HANDLE, fragment = VK_NULL_HANDLE; + VkDescriptorSetLayout descriptorLayout = VK_NULL_HANDLE; + VkPipelineLayout pipelineLayout = VK_NULL_HANDLE; + VkSampler nearest = VK_NULL_HANDLE; + Vector targets; + }; + + void VulkanRenderer::DestroyWireDepthMipmapResources() { + if (!m_wireDepthMipmapResources) return; + // Shutdown calls this after vkDeviceWaitIdle and after all per-submit + // descriptor pools/framebuffers/views have been collected. + auto& cache = *m_wireDepthMipmapResources; + for (const auto& target : cache.targets) { + vkDestroyPipeline(m_device, target.pipeline, nullptr); + vkDestroyRenderPass(m_device, target.renderPass, nullptr); + } + vkDestroySampler(m_device, cache.nearest, nullptr); + vkDestroyPipelineLayout(m_device, cache.pipelineLayout, nullptr); + vkDestroyDescriptorSetLayout(m_device, cache.descriptorLayout, nullptr); + vkDestroyShaderModule(m_device, cache.vertex, nullptr); + vkDestroyShaderModule(m_device, cache.fragment, nullptr); + delete m_wireDepthMipmapResources; + m_wireDepthMipmapResources = nullptr; + } + + // ONE LEVEL, source -> destination, both depth subresources of the same image. The shape + // mirrors BlitWireColorImage deliberately: the same lazily built module pair, the same + // per-format render pass/pipeline cache, the same per-submit descriptor pool, and the + // same GENERAL-on-both-sides layout rule for a same-image pass, so the two native passes + // cannot disagree about how an image is handed between them. + Bool VulkanRenderer::GenerateWireDepthMipLevel(WireImage source, WireImage destination) { + const Bool sameImage = source.image == destination.image; + if (!source.image || !destination.image) return false; + if (sameImage && source.level == destination.level && source.layer == destination.layer) + return false; + if (source.isDefault || destination.isDefault) return false; + if (source.samples != VK_SAMPLE_COUNT_1_BIT || destination.samples != VK_SAMPLE_COUNT_1_BIT) + return false; + if (source.is3D || destination.is3D) return false; + if (source.format != destination.format) return false; + // DEPTH ONLY. A combined depth/stencil image can be sampled for its depth and + // rendered as a depth attachment, but this pass writes gl_FragDepth and NOTHING + // else - the stencil aspect would come out of the render pass undefined, which is + // not what GL's glGenerateMipmap promises for the level it just produced. The + // caller declines that shape rather than producing half a level. + if (source.aspect != VK_IMAGE_ASPECT_DEPTH_BIT || + destination.aspect != VK_IMAGE_ASPECT_DEPTH_BIT) return false; + if (!source.extent.width || !source.extent.height || + !destination.extent.width || !destination.extent.height) return false; + VkFormatProperties properties{}; + vkGetPhysicalDeviceFormatProperties(m_physicalDevice.handle, source.format, &properties); + const VkFormatFeatureFlags required = VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | + VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT; + if ((properties.optimalTilingFeatures & required) != required) return false; + + // The baked block's layout, and the offsets are not a guess: the generated header + // decorates its members at 0 / 16 / 32 / 40, which is where a vec4, a vec4, an int + // and an 8-byte-aligned ivec2 land. The pad is what makes the C++ agree. + struct Constants { + float srcRect[4]; + float dstRect[4]; + Int32 surfaceTransform; + Int32 reservedForIvec2Alignment; + Int32 srcTexelSize[2]; + }; + static_assert(sizeof(Constants) == 48); + static_assert(offsetof(Constants, srcTexelSize) == 40); + + if (!m_wireDepthMipmapResources) { + m_wireDepthMipmapResources = new WireDepthMipmapResources; + auto& cache = *m_wireDepthMipmapResources; + const auto shader = [&](const Uint32* code, SizeT bytes, VkShaderModule& module) { + VkShaderModuleCreateInfo info{VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO}; + info.codeSize = bytes; + info.pCode = code; + VK_VERIFY(vkCreateShaderModule(m_device, &info, nullptr, &module)); + }; + shader(kWireDepthMipmapVertexSpirv, sizeof(kWireDepthMipmapVertexSpirv), cache.vertex); + shader(kWireDepthMipmapFragmentSpirv, sizeof(kWireDepthMipmapFragmentSpirv), cache.fragment); + const VkDescriptorSetLayoutBinding binding{0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr}; + VkDescriptorSetLayoutCreateInfo layout{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO}; + layout.bindingCount = 1; + layout.pBindings = &binding; + VK_VERIFY(vkCreateDescriptorSetLayout(m_device, &layout, nullptr, &cache.descriptorLayout)); + // BOTH STAGES ON ONE RANGE: the block is declared identically in the vertex and + // fragment sources (a push-constant block must be), the vertex stage reads the + // rects and the fragment stage reads srcTexelSize. + const VkPushConstantRange push{VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, + 0, sizeof(Constants)}; + VkPipelineLayoutCreateInfo pipelineLayout{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO}; + pipelineLayout.setLayoutCount = 1; + pipelineLayout.pSetLayouts = &cache.descriptorLayout; + pipelineLayout.pushConstantRangeCount = 1; + pipelineLayout.pPushConstantRanges = &push; + VK_VERIFY(vkCreatePipelineLayout(m_device, &pipelineLayout, nullptr, &cache.pipelineLayout)); + // NEAREST, and it is not a default: the fragment shader does its own 2x2 box with + // texelFetch, so any filtering the sampler added would be applied twice. It also + // keeps the sampler legal on a device that does not advertise linear filtering for + // depth formats, which is most of them. + VkSamplerCreateInfo sampler{VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO}; + sampler.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; + sampler.addressModeU = sampler.addressModeV = sampler.addressModeW = + VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + sampler.magFilter = sampler.minFilter = VK_FILTER_NEAREST; + sampler.maxLod = 0.0f; + VK_VERIFY(vkCreateSampler(m_device, &sampler, nullptr, &cache.nearest)); + } + auto& cache = *m_wireDepthMipmapResources; + + const VkImageLayout sourceLayout = sameImage ? VK_IMAGE_LAYOUT_GENERAL + : VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + const VkImageLayout destinationLayout = sameImage ? VK_IMAGE_LAYOUT_GENERAL + : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + WireDepthMipmapResources::Target* target = nullptr; + for (auto& entry : cache.targets) { + if (entry.format == destination.format && entry.layout == destinationLayout) { + target = &entry; + break; + } + } + if (!target) { + cache.targets.emplace_back(); + target = &cache.targets.back(); + target->format = destination.format; + target->layout = destinationLayout; + VkAttachmentDescription attachment{}; + attachment.format = destination.format; + attachment.samples = VK_SAMPLE_COUNT_1_BIT; + // DONT_CARE, not LOAD: the pass writes gl_FragDepth at every texel of the level + // it is producing, so there is nothing in the destination worth loading, and a + // level that has just been grown has no defined contents to load anyway. + attachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachment.initialLayout = attachment.finalLayout = destinationLayout; + const VkAttachmentReference depth{0, destinationLayout}; + VkSubpassDescription subpass{}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.pDepthStencilAttachment = &depth; + VkRenderPassCreateInfo pass{VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO}; + pass.attachmentCount = 1; + pass.pAttachments = &attachment; + pass.subpassCount = 1; + pass.pSubpasses = &subpass; + VK_VERIFY(vkCreateRenderPass(m_device, &pass, nullptr, &target->renderPass)); + VkPipelineShaderStageCreateInfo stages[2]{}; + for (auto& stage : stages) { + stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + stage.pName = "main"; + } + stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; + stages[0].module = cache.vertex; + stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; + stages[1].module = cache.fragment; + const VkPipelineVertexInputStateCreateInfo vertex{VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO}; + VkPipelineInputAssemblyStateCreateInfo assembly{VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO}; + assembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + VkPipelineViewportStateCreateInfo viewport{VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO}; + viewport.viewportCount = viewport.scissorCount = 1; + VkPipelineRasterizationStateCreateInfo raster{VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO}; + raster.polygonMode = VK_POLYGON_MODE_FILL; + raster.cullMode = VK_CULL_MODE_NONE; + raster.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + raster.lineWidth = 1.0f; + VkPipelineMultisampleStateCreateInfo samples{VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO}; + samples.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + // ALWAYS + write enabled: the fragment shader's gl_FragDepth is the answer, and a + // comparison against whatever DONT_CARE left behind would discard some of it. + VkPipelineDepthStencilStateCreateInfo depthStencil{VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO}; + depthStencil.depthTestEnable = VK_TRUE; + depthStencil.depthWriteEnable = VK_TRUE; + depthStencil.depthCompareOp = VK_COMPARE_OP_ALWAYS; + depthStencil.minDepthBounds = 0.0f; + depthStencil.maxDepthBounds = 1.0f; + // No colour attachment in this render pass, so no attachment states either. + VkPipelineColorBlendStateCreateInfo blend{VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO}; + const VkDynamicState dynamicStates[]{VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR}; + VkPipelineDynamicStateCreateInfo dynamic{VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO}; + dynamic.dynamicStateCount = 2; + dynamic.pDynamicStates = dynamicStates; + VkGraphicsPipelineCreateInfo pipeline{VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO}; + pipeline.stageCount = 2; + pipeline.pStages = stages; + pipeline.pVertexInputState = &vertex; + pipeline.pInputAssemblyState = &assembly; + pipeline.pViewportState = &viewport; + pipeline.pRasterizationState = &raster; + pipeline.pMultisampleState = &samples; + pipeline.pDepthStencilState = &depthStencil; + pipeline.pColorBlendState = &blend; + pipeline.pDynamicState = &dynamic; + pipeline.layout = cache.pipelineLayout; + pipeline.renderPass = target->renderPass; + VK_VERIFY(vkCreateGraphicsPipelines(m_device, VK_NULL_HANDLE, 1, &pipeline, nullptr, &target->pipeline)); + } + + m_textureManager->FlushPendingUploads(); + // Distinct mip views of the same image are legal shader input and output at once. + // GENERAL keeps the whole-image layout tracker truthful; the transition also orders + // the previous level's attachment write against this level's sample of it. + TransitionWireImage(source, sourceLayout); + if (sameImage) destination.layout = source.layout; + else TransitionWireImage(destination, destinationLayout); + const auto makeView = [&](const WireImage& image) { + VkImageViewCreateInfo view{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO}; + view.image = image.image; + view.viewType = VK_IMAGE_VIEW_TYPE_2D; + view.format = image.format; + view.subresourceRange = {VK_IMAGE_ASPECT_DEPTH_BIT, image.level, 1, image.layer, 1}; + VkImageView handle = VK_NULL_HANDLE; + VK_VERIFY(vkCreateImageView(m_device, &view, nullptr, &handle)); + return handle; + }; + const VkImageView sourceView = makeView(source), destinationView = makeView(destination); + VkFramebufferCreateInfo framebufferInfo{VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO}; + framebufferInfo.renderPass = target->renderPass; + framebufferInfo.attachmentCount = 1; + framebufferInfo.pAttachments = &destinationView; + framebufferInfo.width = destination.extent.width; + framebufferInfo.height = destination.extent.height; + framebufferInfo.layers = 1; + VkFramebuffer framebuffer = VK_NULL_HANDLE; + VK_VERIFY(vkCreateFramebuffer(m_device, &framebufferInfo, nullptr, &framebuffer)); + // Each in-flight pass owns its descriptor set, for the colour blit's reason: updating + // a cached set would rewrite the source view of an older queued pass. + VkDescriptorPool descriptorPool = VK_NULL_HANDLE; + VkDescriptorSet descriptorSet = VK_NULL_HANDLE; + const VkDescriptorPoolSize poolSize{VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1}; + VkDescriptorPoolCreateInfo pool{VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO}; + pool.maxSets = 1; + pool.poolSizeCount = 1; + pool.pPoolSizes = &poolSize; + VK_VERIFY(vkCreateDescriptorPool(m_device, &pool, nullptr, &descriptorPool)); + VkDescriptorSetAllocateInfo alloc{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO}; + alloc.descriptorPool = descriptorPool; + alloc.descriptorSetCount = 1; + alloc.pSetLayouts = &cache.descriptorLayout; + VK_VERIFY(vkAllocateDescriptorSets(m_device, &alloc, &descriptorSet)); + const VkDescriptorImageInfo sampled{cache.nearest, sourceView, sourceLayout}; + VkWriteDescriptorSet write{VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET}; + write.dstSet = descriptorSet; + write.dstBinding = 0; + write.descriptorCount = 1; + write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + write.pImageInfo = &sampled; + vkUpdateDescriptorSets(m_device, 1, &write, 0, nullptr); + + // The destination rectangle is flipped exactly as BlitWireColorImage flips it for an + // offscreen target, because it shares that file's vertex shader and therefore its + // window-Y flip. With the flip undone, destination row r samples texCoord.y + // (r + 0.5) / dstHeight, and the fragment's ivec2(texCoord * srcTexelSize) lands on + // the same source texels the monolith arm's fullscreen triangle lands on - which is + // the point: §3.2 retires a refusal by making the wire arm observably the monolith's, + // half-texel offset included. + const Constants constants{ + {0.0f, 0.0f, 1.0f, 1.0f}, + {0.0f, 1.0f, 1.0f, -1.0f}, + 0, 0, + {static_cast(source.extent.width), static_cast(source.extent.height)}}; + auto& frame = m_frameContext.GetCurrent(); + VkRenderPassBeginInfo begin{VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO}; + begin.renderPass = target->renderPass; + begin.framebuffer = framebuffer; + begin.renderArea = {{0, 0}, destination.extent}; + vkCmdBeginRenderPass(frame.commandBuffer, &begin, VK_SUBPASS_CONTENTS_INLINE); + vkCmdBindPipeline(frame.commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, target->pipeline); + const VkViewport viewport{0.0f, 0.0f, static_cast(destination.extent.width), + static_cast(destination.extent.height), 0.0f, 1.0f}; + const VkRect2D scissor{{0, 0}, destination.extent}; + vkCmdSetViewport(frame.commandBuffer, 0, 1, &viewport); + vkCmdSetScissor(frame.commandBuffer, 0, 1, &scissor); + vkCmdBindDescriptorSets(frame.commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, + cache.pipelineLayout, 0, 1, &descriptorSet, 0, nullptr); + vkCmdPushConstants(frame.commandBuffer, cache.pipelineLayout, + VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, + 0, sizeof(constants), &constants); + vkCmdDraw(frame.commandBuffer, 6, 1, 0, 0); + vkCmdEndRenderPass(frame.commandBuffer); + ResetDynamicStateShadow(); + InvalidatePipelineMemo(); + // This native pass binds its own set/layout without going through the application's + // descriptor binder. Its next fast path must rebind. + if (m_uniformManager) m_uniformManager->OnCommandBufferBoundary(); + WireRetiredObjects retired{}; + retired.framebuffers.push_back(framebuffer); + retired.imageViews = {sourceView, destinationView}; + retired.descriptorPools.push_back(descriptorPool); + RetireWireObjects(Move(retired)); + return true; + } +#endif diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/WireDepthMipmap.vert b/MobileGL/MG_Backend/DirectVulkan/Renderer/WireDepthMipmap.vert new file mode 100644 index 00000000..4c5f70ee --- /dev/null +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/WireDepthMipmap.vert @@ -0,0 +1,33 @@ +#version 450 +// Native Vulkan depth mip generation: no GL program, registry entry, or runtime transpilation. +// +// The geometry is WireColorBlit.vert's, unchanged - the same quad, the same push-constant +// rect, the same GL-bottom-left to native-image mapping - so a depth mip and a colour blit +// place their fragments identically and only the fragment stage differs. The block carries +// one member more than the colour blit's, and the fragment stage is the member's only +// reader: a push-constant block must be declared identically in every stage of one pipeline, +// so it is declared here too rather than split into a second range. +layout(push_constant) uniform DepthMipmapRect { + vec4 srcRect; + vec4 dstRect; + int surfaceTransform; + ivec2 srcTexelSize; +} rect; +layout(location = 0) out vec2 texCoord; + +void main() { + // A quad, rather than an oversized triangle, also clips partial blits to + // their destination rectangle. The Vulkan viewport covers the native image. + const vec2 corners[6] = vec2[]( + vec2(0.0, 0.0), vec2(1.0, 0.0), vec2(0.0, 1.0), + vec2(0.0, 1.0), vec2(1.0, 0.0), vec2(1.0, 1.0)); + vec2 uv = corners[gl_VertexIndex]; + vec2 p = (rect.dstRect.xy + uv * rect.dstRect.zw) * 2.0 - 1.0; + // Same GL-bottom-left to native-image mapping as the monolithic blit. + p.y = -p.y; + if (rect.surfaceTransform == 1) p = vec2(-p.y, p.x); + else if (rect.surfaceTransform == 2) p = -p; + else if (rect.surfaceTransform == 3) p = vec2(p.y, -p.x); + gl_Position = vec4(p, 0.0, 1.0); + texCoord = rect.srcRect.xy + uv * rect.srcRect.zw; +} diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/WireDepthMipmapSpirv.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/WireDepthMipmapSpirv.h new file mode 100644 index 00000000..1c43c9c3 --- /dev/null +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/WireDepthMipmapSpirv.h @@ -0,0 +1,174 @@ +// Generated from WireDepthMipmap.vert / WireDepthMipmap.frag by +// scripts/bake_internal_shaders.py, which runs MG_Test's BakedInternalShadersTest with the +// IN-TREE glslang and splices the words it emits into the arrays below. Do not edit the +// words by hand: MOBILEGL_BAKED_INTERNAL_SHADERS (the same test, run as a gate) recompiles +// every source in its table and reds on the first word that disagrees. +#pragma once + +static constexpr Uint32 kWireDepthMipmapVertexSpirv[] = { + 0x07230203u, 0x00010000u, 0x0008000bu, 0x00000071u, 0x00000000u, 0x00020011u, 0x00000001u, 0x0006000bu, + 0x00000001u, 0x4c534c47u, 0x6474732eu, 0x3035342eu, 0x00000000u, 0x0003000eu, 0x00000000u, 0x00000001u, + 0x0008000fu, 0x00000000u, 0x00000004u, 0x6e69616du, 0x00000000u, 0x00000016u, 0x0000005eu, 0x00000067u, + 0x00030003u, 0x00000002u, 0x000001c2u, 0x00040005u, 0x00000004u, 0x6e69616du, 0x00000000u, 0x00030005u, + 0x00000009u, 0x00007675u, 0x00060005u, 0x00000016u, 0x565f6c67u, 0x65747265u, 0x646e4978u, 0x00007865u, + 0x00050005u, 0x00000019u, 0x65646e69u, 0x6c626178u, 0x00000065u, 0x00030005u, 0x0000001cu, 0x00000070u, + 0x00060005u, 0x0000001fu, 0x74706544u, 0x70694d68u, 0x5270616du, 0x00746365u, 0x00050006u, 0x0000001fu, + 0x00000000u, 0x52637273u, 0x00746365u, 0x00050006u, 0x0000001fu, 0x00000001u, 0x52747364u, 0x00746365u, + 0x00080006u, 0x0000001fu, 0x00000002u, 0x66727573u, 0x54656361u, 0x736e6172u, 0x6d726f66u, 0x00000000u, + 0x00070006u, 0x0000001fu, 0x00000003u, 0x54637273u, 0x6c657865u, 0x657a6953u, 0x00000000u, 0x00040005u, + 0x00000021u, 0x74636572u, 0x00000000u, 0x00060005u, 0x0000005cu, 0x505f6c67u, 0x65567265u, 0x78657472u, + 0x00000000u, 0x00060006u, 0x0000005cu, 0x00000000u, 0x505f6c67u, 0x7469736fu, 0x006e6f69u, 0x00070006u, + 0x0000005cu, 0x00000001u, 0x505f6c67u, 0x746e696fu, 0x657a6953u, 0x00000000u, 0x00070006u, 0x0000005cu, + 0x00000002u, 0x435f6c67u, 0x4470696cu, 0x61747369u, 0x0065636eu, 0x00070006u, 0x0000005cu, 0x00000003u, + 0x435f6c67u, 0x446c6c75u, 0x61747369u, 0x0065636eu, 0x00030005u, 0x0000005eu, 0x00000000u, 0x00050005u, + 0x00000067u, 0x43786574u, 0x64726f6fu, 0x00000000u, 0x00040047u, 0x00000016u, 0x0000000bu, 0x0000002au, + 0x00030047u, 0x0000001fu, 0x00000002u, 0x00050048u, 0x0000001fu, 0x00000000u, 0x00000023u, 0x00000000u, + 0x00050048u, 0x0000001fu, 0x00000001u, 0x00000023u, 0x00000010u, 0x00050048u, 0x0000001fu, 0x00000002u, + 0x00000023u, 0x00000020u, 0x00050048u, 0x0000001fu, 0x00000003u, 0x00000023u, 0x00000028u, 0x00030047u, + 0x0000005cu, 0x00000002u, 0x00050048u, 0x0000005cu, 0x00000000u, 0x0000000bu, 0x00000000u, 0x00050048u, + 0x0000005cu, 0x00000001u, 0x0000000bu, 0x00000001u, 0x00050048u, 0x0000005cu, 0x00000002u, 0x0000000bu, + 0x00000003u, 0x00050048u, 0x0000005cu, 0x00000003u, 0x0000000bu, 0x00000004u, 0x00040047u, 0x00000067u, + 0x0000001eu, 0x00000000u, 0x00020013u, 0x00000002u, 0x00030021u, 0x00000003u, 0x00000002u, 0x00030016u, + 0x00000006u, 0x00000020u, 0x00040017u, 0x00000007u, 0x00000006u, 0x00000002u, 0x00040020u, 0x00000008u, + 0x00000007u, 0x00000007u, 0x00040015u, 0x0000000au, 0x00000020u, 0x00000000u, 0x0004002bu, 0x0000000au, + 0x0000000bu, 0x00000006u, 0x0004001cu, 0x0000000cu, 0x00000007u, 0x0000000bu, 0x0004002bu, 0x00000006u, + 0x0000000du, 0x00000000u, 0x0005002cu, 0x00000007u, 0x0000000eu, 0x0000000du, 0x0000000du, 0x0004002bu, + 0x00000006u, 0x0000000fu, 0x3f800000u, 0x0005002cu, 0x00000007u, 0x00000010u, 0x0000000fu, 0x0000000du, + 0x0005002cu, 0x00000007u, 0x00000011u, 0x0000000du, 0x0000000fu, 0x0005002cu, 0x00000007u, 0x00000012u, + 0x0000000fu, 0x0000000fu, 0x0009002cu, 0x0000000cu, 0x00000013u, 0x0000000eu, 0x00000010u, 0x00000011u, + 0x00000011u, 0x00000010u, 0x00000012u, 0x00040015u, 0x00000014u, 0x00000020u, 0x00000001u, 0x00040020u, + 0x00000015u, 0x00000001u, 0x00000014u, 0x0004003bu, 0x00000015u, 0x00000016u, 0x00000001u, 0x00040020u, + 0x00000018u, 0x00000007u, 0x0000000cu, 0x00040017u, 0x0000001du, 0x00000006u, 0x00000004u, 0x00040017u, + 0x0000001eu, 0x00000014u, 0x00000002u, 0x0006001eu, 0x0000001fu, 0x0000001du, 0x0000001du, 0x00000014u, + 0x0000001eu, 0x00040020u, 0x00000020u, 0x00000009u, 0x0000001fu, 0x0004003bu, 0x00000020u, 0x00000021u, + 0x00000009u, 0x0004002bu, 0x00000014u, 0x00000022u, 0x00000001u, 0x00040020u, 0x00000023u, 0x00000009u, + 0x0000001du, 0x0004002bu, 0x00000006u, 0x0000002du, 0x40000000u, 0x0004002bu, 0x0000000au, 0x00000031u, + 0x00000001u, 0x00040020u, 0x00000032u, 0x00000007u, 0x00000006u, 0x0004002bu, 0x00000014u, 0x00000037u, + 0x00000002u, 0x00040020u, 0x00000038u, 0x00000009u, 0x00000014u, 0x00020014u, 0x0000003bu, 0x0004002bu, + 0x0000000au, 0x00000042u, 0x00000000u, 0x0004002bu, 0x00000014u, 0x00000051u, 0x00000003u, 0x0004001cu, + 0x0000005bu, 0x00000006u, 0x00000031u, 0x0006001eu, 0x0000005cu, 0x0000001du, 0x00000006u, 0x0000005bu, + 0x0000005bu, 0x00040020u, 0x0000005du, 0x00000003u, 0x0000005cu, 0x0004003bu, 0x0000005du, 0x0000005eu, + 0x00000003u, 0x0004002bu, 0x00000014u, 0x0000005fu, 0x00000000u, 0x00040020u, 0x00000064u, 0x00000003u, + 0x0000001du, 0x00040020u, 0x00000066u, 0x00000003u, 0x00000007u, 0x0004003bu, 0x00000066u, 0x00000067u, + 0x00000003u, 0x00050036u, 0x00000002u, 0x00000004u, 0x00000000u, 0x00000003u, 0x000200f8u, 0x00000005u, + 0x0004003bu, 0x00000008u, 0x00000009u, 0x00000007u, 0x0004003bu, 0x00000018u, 0x00000019u, 0x00000007u, + 0x0004003bu, 0x00000008u, 0x0000001cu, 0x00000007u, 0x0004003du, 0x00000014u, 0x00000017u, 0x00000016u, + 0x0003003eu, 0x00000019u, 0x00000013u, 0x00050041u, 0x00000008u, 0x0000001au, 0x00000019u, 0x00000017u, + 0x0004003du, 0x00000007u, 0x0000001bu, 0x0000001au, 0x0003003eu, 0x00000009u, 0x0000001bu, 0x00050041u, + 0x00000023u, 0x00000024u, 0x00000021u, 0x00000022u, 0x0004003du, 0x0000001du, 0x00000025u, 0x00000024u, + 0x0007004fu, 0x00000007u, 0x00000026u, 0x00000025u, 0x00000025u, 0x00000000u, 0x00000001u, 0x0004003du, + 0x00000007u, 0x00000027u, 0x00000009u, 0x00050041u, 0x00000023u, 0x00000028u, 0x00000021u, 0x00000022u, + 0x0004003du, 0x0000001du, 0x00000029u, 0x00000028u, 0x0007004fu, 0x00000007u, 0x0000002au, 0x00000029u, + 0x00000029u, 0x00000002u, 0x00000003u, 0x00050085u, 0x00000007u, 0x0000002bu, 0x00000027u, 0x0000002au, + 0x00050081u, 0x00000007u, 0x0000002cu, 0x00000026u, 0x0000002bu, 0x0005008eu, 0x00000007u, 0x0000002eu, + 0x0000002cu, 0x0000002du, 0x00050050u, 0x00000007u, 0x0000002fu, 0x0000000fu, 0x0000000fu, 0x00050083u, + 0x00000007u, 0x00000030u, 0x0000002eu, 0x0000002fu, 0x0003003eu, 0x0000001cu, 0x00000030u, 0x00050041u, + 0x00000032u, 0x00000033u, 0x0000001cu, 0x00000031u, 0x0004003du, 0x00000006u, 0x00000034u, 0x00000033u, + 0x0004007fu, 0x00000006u, 0x00000035u, 0x00000034u, 0x00050041u, 0x00000032u, 0x00000036u, 0x0000001cu, + 0x00000031u, 0x0003003eu, 0x00000036u, 0x00000035u, 0x00050041u, 0x00000038u, 0x00000039u, 0x00000021u, + 0x00000037u, 0x0004003du, 0x00000014u, 0x0000003au, 0x00000039u, 0x000500aau, 0x0000003bu, 0x0000003cu, + 0x0000003au, 0x00000022u, 0x000300f7u, 0x0000003eu, 0x00000000u, 0x000400fau, 0x0000003cu, 0x0000003du, + 0x00000046u, 0x000200f8u, 0x0000003du, 0x00050041u, 0x00000032u, 0x0000003fu, 0x0000001cu, 0x00000031u, + 0x0004003du, 0x00000006u, 0x00000040u, 0x0000003fu, 0x0004007fu, 0x00000006u, 0x00000041u, 0x00000040u, + 0x00050041u, 0x00000032u, 0x00000043u, 0x0000001cu, 0x00000042u, 0x0004003du, 0x00000006u, 0x00000044u, + 0x00000043u, 0x00050050u, 0x00000007u, 0x00000045u, 0x00000041u, 0x00000044u, 0x0003003eu, 0x0000001cu, + 0x00000045u, 0x000200f9u, 0x0000003eu, 0x000200f8u, 0x00000046u, 0x00050041u, 0x00000038u, 0x00000047u, + 0x00000021u, 0x00000037u, 0x0004003du, 0x00000014u, 0x00000048u, 0x00000047u, 0x000500aau, 0x0000003bu, + 0x00000049u, 0x00000048u, 0x00000037u, 0x000300f7u, 0x0000004bu, 0x00000000u, 0x000400fau, 0x00000049u, + 0x0000004au, 0x0000004eu, 0x000200f8u, 0x0000004au, 0x0004003du, 0x00000007u, 0x0000004cu, 0x0000001cu, + 0x0004007fu, 0x00000007u, 0x0000004du, 0x0000004cu, 0x0003003eu, 0x0000001cu, 0x0000004du, 0x000200f9u, + 0x0000004bu, 0x000200f8u, 0x0000004eu, 0x00050041u, 0x00000038u, 0x0000004fu, 0x00000021u, 0x00000037u, + 0x0004003du, 0x00000014u, 0x00000050u, 0x0000004fu, 0x000500aau, 0x0000003bu, 0x00000052u, 0x00000050u, + 0x00000051u, 0x000300f7u, 0x00000054u, 0x00000000u, 0x000400fau, 0x00000052u, 0x00000053u, 0x00000054u, + 0x000200f8u, 0x00000053u, 0x00050041u, 0x00000032u, 0x00000055u, 0x0000001cu, 0x00000031u, 0x0004003du, + 0x00000006u, 0x00000056u, 0x00000055u, 0x00050041u, 0x00000032u, 0x00000057u, 0x0000001cu, 0x00000042u, + 0x0004003du, 0x00000006u, 0x00000058u, 0x00000057u, 0x0004007fu, 0x00000006u, 0x00000059u, 0x00000058u, + 0x00050050u, 0x00000007u, 0x0000005au, 0x00000056u, 0x00000059u, 0x0003003eu, 0x0000001cu, 0x0000005au, + 0x000200f9u, 0x00000054u, 0x000200f8u, 0x00000054u, 0x000200f9u, 0x0000004bu, 0x000200f8u, 0x0000004bu, + 0x000200f9u, 0x0000003eu, 0x000200f8u, 0x0000003eu, 0x0004003du, 0x00000007u, 0x00000060u, 0x0000001cu, + 0x00050051u, 0x00000006u, 0x00000061u, 0x00000060u, 0x00000000u, 0x00050051u, 0x00000006u, 0x00000062u, + 0x00000060u, 0x00000001u, 0x00070050u, 0x0000001du, 0x00000063u, 0x00000061u, 0x00000062u, 0x0000000du, + 0x0000000fu, 0x00050041u, 0x00000064u, 0x00000065u, 0x0000005eu, 0x0000005fu, 0x0003003eu, 0x00000065u, + 0x00000063u, 0x00050041u, 0x00000023u, 0x00000068u, 0x00000021u, 0x0000005fu, 0x0004003du, 0x0000001du, + 0x00000069u, 0x00000068u, 0x0007004fu, 0x00000007u, 0x0000006au, 0x00000069u, 0x00000069u, 0x00000000u, + 0x00000001u, 0x0004003du, 0x00000007u, 0x0000006bu, 0x00000009u, 0x00050041u, 0x00000023u, 0x0000006cu, + 0x00000021u, 0x0000005fu, 0x0004003du, 0x0000001du, 0x0000006du, 0x0000006cu, 0x0007004fu, 0x00000007u, + 0x0000006eu, 0x0000006du, 0x0000006du, 0x00000002u, 0x00000003u, 0x00050085u, 0x00000007u, 0x0000006fu, + 0x0000006bu, 0x0000006eu, 0x00050081u, 0x00000007u, 0x00000070u, 0x0000006au, 0x0000006fu, 0x0003003eu, + 0x00000067u, 0x00000070u, 0x000100fdu, 0x00010038u, +}; + +static constexpr Uint32 kWireDepthMipmapFragmentSpirv[] = { + 0x07230203u, 0x00010000u, 0x0008000bu, 0x00000059u, 0x00000000u, 0x00020011u, 0x00000001u, 0x0006000bu, + 0x00000001u, 0x4c534c47u, 0x6474732eu, 0x3035342eu, 0x00000000u, 0x0003000eu, 0x00000000u, 0x00000001u, + 0x0007000fu, 0x00000004u, 0x00000004u, 0x6e69616du, 0x00000000u, 0x0000000du, 0x0000004fu, 0x00030010u, + 0x00000004u, 0x00000007u, 0x00030010u, 0x00000004u, 0x0000000cu, 0x00030003u, 0x00000002u, 0x000001c2u, + 0x00040005u, 0x00000004u, 0x6e69616du, 0x00000000u, 0x00040005u, 0x00000009u, 0x42637273u, 0x00657361u, + 0x00050005u, 0x0000000du, 0x43786574u, 0x64726f6fu, 0x00000000u, 0x00060005u, 0x00000010u, 0x74706544u, + 0x70694d68u, 0x5270616du, 0x00746365u, 0x00050006u, 0x00000010u, 0x00000000u, 0x52637273u, 0x00746365u, + 0x00050006u, 0x00000010u, 0x00000001u, 0x52747364u, 0x00746365u, 0x00080006u, 0x00000010u, 0x00000002u, + 0x66727573u, 0x54656361u, 0x736e6172u, 0x6d726f66u, 0x00000000u, 0x00070006u, 0x00000010u, 0x00000003u, + 0x54637273u, 0x6c657865u, 0x657a6953u, 0x00000000u, 0x00040005u, 0x00000012u, 0x74636572u, 0x00000000u, + 0x00040005u, 0x0000001au, 0x4d637273u, 0x00007861u, 0x00040005u, 0x00000021u, 0x74706564u, 0x00003068u, + 0x00050005u, 0x00000025u, 0x72756f73u, 0x6d496563u, 0x00656761u, 0x00040005u, 0x00000031u, 0x74706564u, + 0x00003168u, 0x00040005u, 0x0000003bu, 0x74706564u, 0x00003268u, 0x00040005u, 0x00000045u, 0x74706564u, + 0x00003368u, 0x00060005u, 0x0000004fu, 0x465f6c67u, 0x44676172u, 0x68747065u, 0x00000000u, 0x00040047u, + 0x0000000du, 0x0000001eu, 0x00000000u, 0x00030047u, 0x00000010u, 0x00000002u, 0x00050048u, 0x00000010u, + 0x00000000u, 0x00000023u, 0x00000000u, 0x00050048u, 0x00000010u, 0x00000001u, 0x00000023u, 0x00000010u, + 0x00050048u, 0x00000010u, 0x00000002u, 0x00000023u, 0x00000020u, 0x00050048u, 0x00000010u, 0x00000003u, + 0x00000023u, 0x00000028u, 0x00040047u, 0x00000025u, 0x00000021u, 0x00000000u, 0x00040047u, 0x00000025u, + 0x00000022u, 0x00000000u, 0x00040047u, 0x0000004fu, 0x0000000bu, 0x00000016u, 0x00020013u, 0x00000002u, + 0x00030021u, 0x00000003u, 0x00000002u, 0x00040015u, 0x00000006u, 0x00000020u, 0x00000001u, 0x00040017u, + 0x00000007u, 0x00000006u, 0x00000002u, 0x00040020u, 0x00000008u, 0x00000007u, 0x00000007u, 0x00030016u, + 0x0000000au, 0x00000020u, 0x00040017u, 0x0000000bu, 0x0000000au, 0x00000002u, 0x00040020u, 0x0000000cu, + 0x00000001u, 0x0000000bu, 0x0004003bu, 0x0000000cu, 0x0000000du, 0x00000001u, 0x00040017u, 0x0000000fu, + 0x0000000au, 0x00000004u, 0x0006001eu, 0x00000010u, 0x0000000fu, 0x0000000fu, 0x00000006u, 0x00000007u, + 0x00040020u, 0x00000011u, 0x00000009u, 0x00000010u, 0x0004003bu, 0x00000011u, 0x00000012u, 0x00000009u, + 0x0004002bu, 0x00000006u, 0x00000013u, 0x00000003u, 0x00040020u, 0x00000014u, 0x00000009u, 0x00000007u, + 0x0004002bu, 0x00000006u, 0x0000001du, 0x00000001u, 0x0005002cu, 0x00000007u, 0x0000001eu, 0x0000001du, + 0x0000001du, 0x00040020u, 0x00000020u, 0x00000007u, 0x0000000au, 0x00090019u, 0x00000022u, 0x0000000au, + 0x00000001u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000001u, 0x00000000u, 0x0003001bu, 0x00000023u, + 0x00000022u, 0x00040020u, 0x00000024u, 0x00000000u, 0x00000023u, 0x0004003bu, 0x00000024u, 0x00000025u, + 0x00000000u, 0x0004002bu, 0x00000006u, 0x00000028u, 0x00000000u, 0x0005002cu, 0x00000007u, 0x00000029u, + 0x00000028u, 0x00000028u, 0x00040015u, 0x0000002eu, 0x00000020u, 0x00000000u, 0x0004002bu, 0x0000002eu, + 0x0000002fu, 0x00000000u, 0x0005002cu, 0x00000007u, 0x00000034u, 0x0000001du, 0x00000028u, 0x0005002cu, + 0x00000007u, 0x0000003eu, 0x00000028u, 0x0000001du, 0x00040020u, 0x0000004eu, 0x00000003u, 0x0000000au, + 0x0004003bu, 0x0000004eu, 0x0000004fu, 0x00000003u, 0x0004002bu, 0x0000000au, 0x00000050u, 0x3e800000u, + 0x00050036u, 0x00000002u, 0x00000004u, 0x00000000u, 0x00000003u, 0x000200f8u, 0x00000005u, 0x0004003bu, + 0x00000008u, 0x00000009u, 0x00000007u, 0x0004003bu, 0x00000008u, 0x0000001au, 0x00000007u, 0x0004003bu, + 0x00000020u, 0x00000021u, 0x00000007u, 0x0004003bu, 0x00000020u, 0x00000031u, 0x00000007u, 0x0004003bu, + 0x00000020u, 0x0000003bu, 0x00000007u, 0x0004003bu, 0x00000020u, 0x00000045u, 0x00000007u, 0x0004003du, + 0x0000000bu, 0x0000000eu, 0x0000000du, 0x00050041u, 0x00000014u, 0x00000015u, 0x00000012u, 0x00000013u, + 0x0004003du, 0x00000007u, 0x00000016u, 0x00000015u, 0x0004006fu, 0x0000000bu, 0x00000017u, 0x00000016u, + 0x00050085u, 0x0000000bu, 0x00000018u, 0x0000000eu, 0x00000017u, 0x0004006eu, 0x00000007u, 0x00000019u, + 0x00000018u, 0x0003003eu, 0x00000009u, 0x00000019u, 0x00050041u, 0x00000014u, 0x0000001bu, 0x00000012u, + 0x00000013u, 0x0004003du, 0x00000007u, 0x0000001cu, 0x0000001bu, 0x00050082u, 0x00000007u, 0x0000001fu, + 0x0000001cu, 0x0000001eu, 0x0003003eu, 0x0000001au, 0x0000001fu, 0x0004003du, 0x00000023u, 0x00000026u, + 0x00000025u, 0x0004003du, 0x00000007u, 0x00000027u, 0x00000009u, 0x0004003du, 0x00000007u, 0x0000002au, + 0x0000001au, 0x0008000cu, 0x00000007u, 0x0000002bu, 0x00000001u, 0x0000002du, 0x00000027u, 0x00000029u, + 0x0000002au, 0x00040064u, 0x00000022u, 0x0000002cu, 0x00000026u, 0x0007005fu, 0x0000000fu, 0x0000002du, + 0x0000002cu, 0x0000002bu, 0x00000002u, 0x00000028u, 0x00050051u, 0x0000000au, 0x00000030u, 0x0000002du, + 0x00000000u, 0x0003003eu, 0x00000021u, 0x00000030u, 0x0004003du, 0x00000023u, 0x00000032u, 0x00000025u, + 0x0004003du, 0x00000007u, 0x00000033u, 0x00000009u, 0x00050080u, 0x00000007u, 0x00000035u, 0x00000033u, + 0x00000034u, 0x0004003du, 0x00000007u, 0x00000036u, 0x0000001au, 0x0008000cu, 0x00000007u, 0x00000037u, + 0x00000001u, 0x0000002du, 0x00000035u, 0x00000029u, 0x00000036u, 0x00040064u, 0x00000022u, 0x00000038u, + 0x00000032u, 0x0007005fu, 0x0000000fu, 0x00000039u, 0x00000038u, 0x00000037u, 0x00000002u, 0x00000028u, + 0x00050051u, 0x0000000au, 0x0000003au, 0x00000039u, 0x00000000u, 0x0003003eu, 0x00000031u, 0x0000003au, + 0x0004003du, 0x00000023u, 0x0000003cu, 0x00000025u, 0x0004003du, 0x00000007u, 0x0000003du, 0x00000009u, + 0x00050080u, 0x00000007u, 0x0000003fu, 0x0000003du, 0x0000003eu, 0x0004003du, 0x00000007u, 0x00000040u, + 0x0000001au, 0x0008000cu, 0x00000007u, 0x00000041u, 0x00000001u, 0x0000002du, 0x0000003fu, 0x00000029u, + 0x00000040u, 0x00040064u, 0x00000022u, 0x00000042u, 0x0000003cu, 0x0007005fu, 0x0000000fu, 0x00000043u, + 0x00000042u, 0x00000041u, 0x00000002u, 0x00000028u, 0x00050051u, 0x0000000au, 0x00000044u, 0x00000043u, + 0x00000000u, 0x0003003eu, 0x0000003bu, 0x00000044u, 0x0004003du, 0x00000023u, 0x00000046u, 0x00000025u, + 0x0004003du, 0x00000007u, 0x00000047u, 0x00000009u, 0x00050080u, 0x00000007u, 0x00000048u, 0x00000047u, + 0x0000001eu, 0x0004003du, 0x00000007u, 0x00000049u, 0x0000001au, 0x0008000cu, 0x00000007u, 0x0000004au, + 0x00000001u, 0x0000002du, 0x00000048u, 0x00000029u, 0x00000049u, 0x00040064u, 0x00000022u, 0x0000004bu, + 0x00000046u, 0x0007005fu, 0x0000000fu, 0x0000004cu, 0x0000004bu, 0x0000004au, 0x00000002u, 0x00000028u, + 0x00050051u, 0x0000000au, 0x0000004du, 0x0000004cu, 0x00000000u, 0x0003003eu, 0x00000045u, 0x0000004du, + 0x0004003du, 0x0000000au, 0x00000051u, 0x00000021u, 0x0004003du, 0x0000000au, 0x00000052u, 0x00000031u, + 0x00050081u, 0x0000000au, 0x00000053u, 0x00000051u, 0x00000052u, 0x0004003du, 0x0000000au, 0x00000054u, + 0x0000003bu, 0x00050081u, 0x0000000au, 0x00000055u, 0x00000053u, 0x00000054u, 0x0004003du, 0x0000000au, + 0x00000056u, 0x00000045u, 0x00050081u, 0x0000000au, 0x00000057u, 0x00000055u, 0x00000056u, 0x00050085u, + 0x0000000au, 0x00000058u, 0x00000050u, 0x00000057u, 0x0003003eu, 0x0000004fu, 0x00000058u, 0x000100fdu, + 0x00010038u, +}; diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/WireFramebuffer.inc b/MobileGL/MG_Backend/DirectVulkan/Renderer/WireFramebuffer.inc index 218bbdaf..0ac2d8c3 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/WireFramebuffer.inc +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/WireFramebuffer.inc @@ -828,10 +828,26 @@ if (base >= fullLevelCount) MagmaWireFatal("mipmap-base-extent"); const Uint32 count = std::min(logicalEnd - logicalBase, fullLevelCount - base); if (count <= 1) return; + // P7 wave 2-B, CONTRACT-P7 §3.2: `depth-stencil-mipmap@P7` RETIRES FOR DEPTH AND + // DECLINES FOR DEPTH|STENCIL - and the decline comes BEFORE the chain is grown, so a + // level this verb is not going to fill is not created and then recorded as defined. + // + // The combined case is a decline rather than a Fatal because the monolith arm does no + // better: its shader fallback asserts depth-only (VulkanRenderer.cpp's GenerateMipmap, + // "shader fallback only supports depth-only textures") and an assert is nothing in the + // release build both arms ship as. Nothing in GL promises a stencil mip either - the + // pass writes gl_FragDepth and would leave the stencil aspect of the level it produced + // undefined, which is worse than leaving the whole level alone and saying so. + if (resource->aspect != VK_IMAGE_ASPECT_COLOR_BIT && resource->aspect != VK_IMAGE_ASPECT_DEPTH_BIT) { + MGLOG_E_ONCE("DirectVulkan::GenerateWireMipmap: aspect 0x%x (a combined depth/stencil or " + "stencil-only texture, format %d) has no mip generation on either arm; " + "declining and leaving levels %u..%u as they are", + static_cast(resource->aspect), static_cast(resource->format), + base + 1, base + count - 1); + return; + } if (!m_textureManager->GrowWireTextureMipChain(handle,base+count,frame.commandBuffer)) MagmaWireFatal("mipmap-growth"); - if (resource->aspect != VK_IMAGE_ASPECT_COLOR_BIT && resource->aspect != VK_IMAGE_ASPECT_DEPTH_BIT) - MagmaWireFatal("depth-stencil-mipmap@P7"); VkFormatProperties props{}; const VkFormat format = viewFormat == VK_FORMAT_UNDEFINED ? resource->format : viewFormat; vkGetPhysicalDeviceFormatProperties(m_physicalDevice.handle,format,&props); @@ -841,7 +857,52 @@ (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) { + 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 - + // VK_FORMAT_FEATURE_BLIT_DST is optional for depth and plenty of devices decline + // it. Before this package the whole branch below was colour-only, so such a + // texture reached `mipmap-shader-format-or-shape` on its very first disjunction + // while the monolith arm quietly generated the chain with a shader. + // + // 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()); + for (Uint32 level = base + 1; level < base + count; ++level) { + for (Uint32 layer = baseLayer; layer < baseLayer + layerCount; ++layer) { + WireImage source{}; + source.image = resource->image; + source.format = format; + source.aspect = VK_IMAGE_ASPECT_DEPTH_BIT; + source.layout = resource->layout; + source.trackedLayout = &resource->layout; + source.levels = resource->mipLevels; + source.level = level - 1; + source.layer = layer; + source.extent = {std::max(resource->extent.width >> (level - 1), 1u), + std::max(resource->extent.height >> (level - 1), 1u)}; + WireImage destination = source; + 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, + resource->format, props, resource->aspect, resource->viewType, + resource->usageFlags, resource->extent, resource->depth, + resource->mipLevels, baseLayer, layerCount, + resource->arrayLayers).c_str()); + } + } + } else 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. diff --git a/MobileGL/MG_IntegrationTest/CMakeLists.txt b/MobileGL/MG_IntegrationTest/CMakeLists.txt index 2718a803..51163f64 100644 --- a/MobileGL/MG_IntegrationTest/CMakeLists.txt +++ b/MobileGL/MG_IntegrationTest/CMakeLists.txt @@ -2710,6 +2710,26 @@ if (MOBILEGL_BUILD_DISAGGREGATED) ${mglItestArmProperties} ENVIRONMENT "${mglItestMagmaWireEnvironment}\;MGITEST_MAGMA_FORCE_SHADER_MIPMAP=${shaderMipTier}\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p7-shadermip${mglItestArmLogTag}-tier${shaderMipTier}.log") endforeach() + # The DEPTH half of the same knob (CONTRACT-P7 §5.1 (A)). ONE TIER, not two: the depth + # pass renders into a depth ATTACHMENT view of the destination level, which a depth + # image always has usage for - there is no "cannot be attached" shape to force, so + # tier 2 would register a second copy of tier 1's run. + gtest_discover_tests(MobileGLIntegrationTest + TEST_PREFIX "DirectVulkan.${mglItestArm}.DepthMip." + TEST_FILTER "F1WireScenario.GenerateMipmapDepthWithoutNativeBlitPixels" + DISCOVERY_TIMEOUT 30 + PROPERTIES LABELS "${mglItestMagmaLabels}" TIMEOUT ${MGL_ITEST_TIMEOUT} + ${mglItestArmProperties} + ENVIRONMENT "${mglItestMagmaWireEnvironment}\;MGITEST_MAGMA_FORCE_SHADER_MIPMAP=1\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p7-depthmip${mglItestArmLogTag}.log") + # NO KNOB on the decline: it is decided on the aspect before the blit-support question + # is asked, so the plain Fm environment reaches it on every driver. + gtest_discover_tests(MobileGLIntegrationTest + TEST_PREFIX "DirectVulkan.${mglItestArm}.Fm." + TEST_FILTER "F1WireScenario.GenerateMipmapDepthStencilDeclinesAndKeepsTheSession" + DISCOVERY_TIMEOUT 30 + PROPERTIES LABELS "${mglItestMagmaLabels}" TIMEOUT ${MGL_ITEST_TIMEOUT} + ${mglItestArmProperties} + ENVIRONMENT "${mglItestMagmaWireEnvironment}\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p7-dsmipdecline${mglItestArmLogTag}.log") endforeach() endif() diff --git a/MobileGL/MG_IntegrationTest/Scenarios/F1WireScenario.cpp b/MobileGL/MG_IntegrationTest/Scenarios/F1WireScenario.cpp index ba3d2b6e..97f400e6 100644 --- a/MobileGL/MG_IntegrationTest/Scenarios/F1WireScenario.cpp +++ b/MobileGL/MG_IntegrationTest/Scenarios/F1WireScenario.cpp @@ -560,6 +560,109 @@ TEST_F(F1WireScenario, GenerateMipmapDepthPixels) { EXPECT_NEAR(pixel, 0.375f, 0.00001f); } +// P7 wave 2-B, CONTRACT-P7 §5.1 (A): the baked depth mip program, on the two GL depth formats +// that reach it as a depth-only Vulkan aspect (DEPTH_COMPONENT24 -> X8_D24_UNORM_PACK32, +// DEPTH_COMPONENT32F -> D32_SFLOAT). GenerateMipmapDepthPixels above passes on this host +// because lavapipe blits depth natively; the knob removes that arm, which is what an Adreno +// does by itself - VK_FORMAT_FEATURE_BLIT_DST is OPTIONAL for depth formats, so a depth chain +// through a shader is the ordinary case and not the exotic one. +// +// TWO HALVES AND A BAND THAT IS NOT ASSERTED. The generated level is read back through a real +// sampled attachment, so a flat fill would prove nothing: the source is split at its middle +// row instead. The ported filter takes a 2x2 box at ivec2(texCoord * srcTexelSize), which +// lands half a texel off the destination centre - the monolith's own arithmetic, kept +// deliberately (§3.2 retires a refusal by matching that arm, defects included). Under either +// reading of that half texel, destination rows 0..2 come only from the lower half and rows +// 4..7 only from the upper half; row 3 is the straddle and is the one row this case does not +// pin, because pinning it would assert the half-texel offset rather than the mip. +TEST_F(F1WireScenario, GenerateMipmapDepthWithoutNativeBlitPixels) { + 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 blits depth " + "natively, so the baked depth mip program under test is not entered"; + for (const GLenum internalFormat : {GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT32F}) { + GLuint depth = 0; + glGenTextures(1, &depth); + glBindTexture(GL_TEXTURE_2D, depth); + glTexStorage2D(GL_TEXTURE_2D, 5, internalFormat, 16, 16); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth, 0); + glDrawBuffer(GL_NONE); + glReadBuffer(GL_NONE); + ASSERT_EQ(glCheckFramebufferStatus(GL_FRAMEBUFFER), GLenum(GL_FRAMEBUFFER_COMPLETE)) + << "format 0x" << std::hex << internalFormat; + glDepthMask(GL_TRUE); + glDisable(GL_SCISSOR_TEST); + glClearDepth(0.75); + glClear(GL_DEPTH_BUFFER_BIT); + glEnable(GL_SCISSOR_TEST); + glScissor(0, 0, 16, 8); + glClearDepth(0.25); + glClear(GL_DEPTH_BUFFER_BIT); + glDisable(GL_SCISSOR_TEST); + const auto before = PeekSplitRuntime().emitSeq; + glGenerateMipmap(GL_TEXTURE_2D); + ASSERT_GT(PeekSplitRuntime().emitSeq, before) << "F1.DepthMip.wire"; + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth, 1); + ASSERT_EQ(glCheckFramebufferStatus(GL_FRAMEBUFFER), GLenum(GL_FRAMEBUFFER_COMPLETE)); + GLfloat rows[8]{}; + glReadPixels(2, 0, 1, 8, GL_DEPTH_COMPONENT, GL_FLOAT, rows); + ASSERT_EQ(FirstGLError(), GLenum(GL_NO_ERROR)) << "F1.DepthMip.error"; + for (int row = 0; row < 8; ++row) { + if (row == 3) continue; + const GLfloat expected = row < 3 ? 0.25f : 0.75f; + EXPECT_NEAR(rows[row], expected, 0.002f) + << "F1.DepthMip.pixels tier=" << tier << " format=0x" << std::hex << internalFormat + << std::dec << " row=" << row; + } + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, 0, 0); + glDeleteTextures(1, &depth); + glBindTexture(GL_TEXTURE_2D, texture); + } + glClearDepth(1.0); +} + +// The OTHER half of `depth-stencil-mipmap@P7`, and it is a decline rather than a retirement +// (CONTRACT-P7 §3.2): a combined depth/stencil texture has no mip generation on EITHER arm - +// the monolith's shader fallback asserts depth-only, and an assert is nothing in the release +// build both arms ship as. What this case pins is that the wire arm now says so and carries +// on, rather than taking the session down: no GL error, the session still alive afterwards, +// and level zero still holding what was cleared into it. +// +// No knob: the decline is decided on the ASPECT, before the blit-support question is asked, +// so it fires on every driver. +// Red once (executed, reverted): restore the MagmaWireFatal and this case dies with +// Fatal{UnmigratedVerb, "Magma:depth-stencil-mipmap@P7"}. +TEST_F(F1WireScenario, GenerateMipmapDepthStencilDeclinesAndKeepsTheSession) { + if (!Ready()) return; + GLuint depthStencil = 0; + glGenTextures(1, &depthStencil); + glBindTexture(GL_TEXTURE_2D, depthStencil); + glTexStorage2D(GL_TEXTURE_2D, 4, GL_DEPTH24_STENCIL8, 8, 8); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, depthStencil, 0); + glDrawBuffer(GL_NONE); + glReadBuffer(GL_NONE); + ASSERT_EQ(glCheckFramebufferStatus(GL_FRAMEBUFFER), GLenum(GL_FRAMEBUFFER_COMPLETE)); + glDepthMask(GL_TRUE); + glDisable(GL_SCISSOR_TEST); + glClearDepth(0.5); + glClear(GL_DEPTH_BUFFER_BIT); + glGenerateMipmap(GL_TEXTURE_2D); + EXPECT_EQ(FirstGLError(), GLenum(GL_NO_ERROR)) << "F1.DepthStencilMip.error"; + // The session is what the retired Fatal used to take with it. A readback is a round trip + // through the server, so it answers that question and the level-zero one at once. + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, depthStencil, 0); + ASSERT_EQ(glCheckFramebufferStatus(GL_FRAMEBUFFER), GLenum(GL_FRAMEBUFFER_COMPLETE)); + GLfloat level0 = 0; + glReadPixels(1, 1, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &level0); + EXPECT_EQ(FirstGLError(), GLenum(GL_NO_ERROR)) << "F1.DepthStencilMip.readback"; + EXPECT_NEAR(level0, 0.5f, 0.002f) << "F1.DepthStencilMip.level0"; + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); + glDeleteTextures(1, &depthStencil); + glBindTexture(GL_TEXTURE_2D, texture); + glClearDepth(1.0); +} + TEST_F(F1WireScenario, GenerateMipmapHonorsMutableBaseAndMaxWithoutChangingOtherLevels) { if (!Ready()) return; const std::array colors[5] = { diff --git a/MobileGL/MG_Test/SelfTest/BakedInternalShadersTest.cpp b/MobileGL/MG_Test/SelfTest/BakedInternalShadersTest.cpp new file mode 100644 index 00000000..469a39db --- /dev/null +++ b/MobileGL/MG_Test/SelfTest/BakedInternalShadersTest.cpp @@ -0,0 +1,226 @@ +// MobileGL - MobileGL/MG_Test/SelfTest/BakedInternalShadersTest.cpp +// Copyright (c) 2026 MobileGL-Dev +// Licensed under the GNU Lesser General Public License v3.0: +// https://www.gnu.org/licenses/gpl-3.0.txt +// https://www.gnu.org/licenses/lgpl-3.0.txt +// SPDX-License-Identifier: LGPL-3.0-only +// End of Source File Header +// +// MOBILEGL_BAKED_INTERNAL_SHADERS - the freshness gate (CONTRACT-P7 §5.1 (D)). +// +// WHAT A BAKED SPIR-V HEADER ACTUALLY IS: a compiler output committed to the tree, with its +// input committed beside it and NOTHING connecting the two. Edit WireColorBlit.frag and the +// build stays green, the lane stays green, and the shader that runs is the one from before +// the edit - silently, on every device, until somebody reads the words. The tree already had +// three such pairs before this package added a fourth, and one of them (the iterationRP +// witness) was measurably stale: its own header said so in prose, which is exactly as much +// enforcement as prose provides. +// +// So every baked header is recompiled here, from its committed source, with the IN-TREE +// glslang, and compared word for word. +// +// THIS TEST IS THE BAKER, NOT A SECOND OPINION. scripts/bake_internal_shaders.py does not +// compile anything: it runs this binary with MOBILEGL_BAKE_INTERNAL_SHADERS set, takes the +// words it emits, and splices them into the headers. That direction is deliberate. A gate +// that compiles through the glslang LIBRARY while the baker shells out to a glslangValidator +// BINARY has two compilers and no rule about which one is right, and the day their defaults +// diverge - an option the CLI passes by default, a version skew between a system tool and +// the submodule - the gate reds on a header nobody can regenerate. One compiler, one truth, +// and the gate's own output is what the tree stores. +// +// SOURCE PATH: the sources live in the source tree, not next to the binary, so the build +// hands their root in as MOBILEGL_BAKED_SHADER_ROOT. A test that cannot find them FAILS +// rather than skipping: a freshness gate that silently disarms is the thing it exists +// against. + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +namespace { + +// The two Renderer headers are written to be included inside the backend namespace, after +// the project's own type aliases; they name Uint32 and declare nothing else. Including them +// here with a local alias keeps them byte-identical to what VulkanRenderer.cpp compiles - +// the alternative, a copy of the words in this file, is precisely the drift this gate is for. +using Uint32 = std::uint32_t; +#include +#include + +struct BakedShader { + const char* name; // ctest entry name, and the bake file's stem + const char* source; // repository-relative GLSL source + const char* header; // repository-relative header the words live in + const char* symbol; // the array identifier inside that header + EShLanguage stage; + glslang::EShTargetClientVersion client; + glslang::EShTargetLanguageVersion spv; + const std::uint32_t* words; + std::size_t wordCount; +}; + +#define MGL_BAKED_ROW(name, source, header, symbol, stage, client, spv, array) \ + BakedShader{name, source, header, symbol, stage, client, spv, (array), \ + sizeof(array) / sizeof((array)[0])} + +const BakedShader kBakedShaders[] = { + // The Magma wire arms' own programs: no GL program object, no registry entry, no runtime + // transpilation, so the words in the header are the only copy that exists. + MGL_BAKED_ROW("WireColorBlitVertex", + "MobileGL/MG_Backend/DirectVulkan/Renderer/WireColorBlit.vert", + "MobileGL/MG_Backend/DirectVulkan/Renderer/WireColorBlitSpirv.h", + "kWireColorBlitVertexSpirv", EShLangVertex, + glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0, + kWireColorBlitVertexSpirv), + MGL_BAKED_ROW("WireColorBlitFragment", + "MobileGL/MG_Backend/DirectVulkan/Renderer/WireColorBlit.frag", + "MobileGL/MG_Backend/DirectVulkan/Renderer/WireColorBlitSpirv.h", + "kWireColorBlitFragmentSpirv", EShLangFragment, + glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0, + kWireColorBlitFragmentSpirv), + MGL_BAKED_ROW("WireDepthMipmapVertex", + "MobileGL/MG_Backend/DirectVulkan/Renderer/WireDepthMipmap.vert", + "MobileGL/MG_Backend/DirectVulkan/Renderer/WireDepthMipmapSpirv.h", + "kWireDepthMipmapVertexSpirv", EShLangVertex, + glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0, + kWireDepthMipmapVertexSpirv), + MGL_BAKED_ROW("WireDepthMipmapFragment", + "MobileGL/MG_Backend/DirectVulkan/Renderer/WireDepthMipmap.frag", + "MobileGL/MG_Backend/DirectVulkan/Renderer/WireDepthMipmapSpirv.h", + "kWireDepthMipmapFragmentSpirv", EShLangFragment, + glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0, + kWireDepthMipmapFragmentSpirv), + // The two driver self-test probes. They are not Magma's, and they are in this table for + // the reason the contract names them: they are baked, so they can be stale, and one of + // them was. + MGL_BAKED_ROW("DriverPostIterationRPWitness", + "MobileGL/MG_Util/SelfTest/DriverPostIterationRPWitness.comp", + "MobileGL/MG_Util/SelfTest/DriverPostIterationRPWitnessSpv.h", + "kDriverPostIterationRPWitnessSpv", EShLangCompute, + glslang::EShTargetVulkan_1_1, glslang::EShTargetSpv_1_3, + MobileGL::MG_Util::SelfTest::kDriverPostIterationRPWitnessSpv), + MGL_BAKED_ROW("PrimitivesGeneratedNoXfbProbeVert", + "MobileGL/MG_Util/SelfTest/PrimitivesGeneratedNoXfbProbe.vert", + "MobileGL/MG_Util/SelfTest/PrimitivesGeneratedNoXfbProbeSpv.h", + "kPrimitivesGeneratedNoXfbProbeVertSpv", EShLangVertex, + glslang::EShTargetVulkan_1_1, glslang::EShTargetSpv_1_3, + MobileGL::MG_Util::SelfTest::kPrimitivesGeneratedNoXfbProbeVertSpv), + MGL_BAKED_ROW("PrimitivesGeneratedNoXfbProbeTesc", + "MobileGL/MG_Util/SelfTest/PrimitivesGeneratedNoXfbProbe.tesc", + "MobileGL/MG_Util/SelfTest/PrimitivesGeneratedNoXfbProbeSpv.h", + "kPrimitivesGeneratedNoXfbProbeTescSpv", EShLangTessControl, + glslang::EShTargetVulkan_1_1, glslang::EShTargetSpv_1_3, + MobileGL::MG_Util::SelfTest::kPrimitivesGeneratedNoXfbProbeTescSpv), + MGL_BAKED_ROW("PrimitivesGeneratedNoXfbProbeTese", + "MobileGL/MG_Util/SelfTest/PrimitivesGeneratedNoXfbProbe.tese", + "MobileGL/MG_Util/SelfTest/PrimitivesGeneratedNoXfbProbeSpv.h", + "kPrimitivesGeneratedNoXfbProbeTeseSpv", EShLangTessEvaluation, + glslang::EShTargetVulkan_1_1, glslang::EShTargetSpv_1_3, + MobileGL::MG_Util::SelfTest::kPrimitivesGeneratedNoXfbProbeTeseSpv), +}; + +#undef MGL_BAKED_ROW + +std::string RepositoryRoot() { +#ifdef MOBILEGL_BAKED_SHADER_ROOT + return std::string(MOBILEGL_BAKED_SHADER_ROOT); +#else + return std::string(); +#endif +} + +// glslang::InitializeProcess is process-wide and refcounted. One call for the whole binary: +// this test never runs MobileGL::Initialize, which is the other caller. +void EnsureGlslangProcess() { + static const bool ready = glslang::InitializeProcess(); + ASSERT_TRUE(ready) << "glslang::InitializeProcess failed"; +} + +class BakedInternalShaders : public ::testing::TestWithParam {}; + +TEST_P(BakedInternalShaders, HeaderMatchesItsSource) { + const BakedShader& row = GetParam(); + ASSERT_NO_FATAL_FAILURE(EnsureGlslangProcess()); + + const std::string root = RepositoryRoot(); + ASSERT_FALSE(root.empty()) + << "MOBILEGL_BAKED_SHADER_ROOT was not defined for this target: the gate cannot find " + "the GLSL sources and must not pass by default"; + const std::string sourcePath = root + "/" + row.source; + std::ifstream in(sourcePath, std::ios::binary); + ASSERT_TRUE(in.good()) << "cannot open " << sourcePath; + std::ostringstream buffer; + buffer << in.rdbuf(); + const std::string source = buffer.str(); + ASSERT_FALSE(source.empty()) << sourcePath << " is empty"; + + glslang::TShader shader(row.stage); + const char* strings[] = {source.c_str()}; + shader.setStrings(strings, 1); + shader.setEnvInput(glslang::EShSourceGlsl, row.stage, glslang::EShClientVulkan, 100); + shader.setEnvClient(glslang::EShClientVulkan, row.client); + shader.setEnvTarget(glslang::EShTargetSpv, row.spv); + const EShMessages messages = static_cast(EShMsgSpvRules | EShMsgVulkanRules); + ASSERT_TRUE(shader.parse(GetDefaultResources(), 100, false, messages)) + << row.source << " does not compile:\n" << shader.getInfoLog(); + + glslang::TProgram program; + program.addShader(&shader); + ASSERT_TRUE(program.link(messages)) << row.source << " does not link:\n" << program.getInfoLog(); + glslang::TIntermediate* intermediate = program.getIntermediate(row.stage); + ASSERT_NE(intermediate, nullptr); + + std::vector spirv; + spv::SpvBuildLogger logger; + glslang::SpvOptions options; + options.disableOptimizer = true; + glslang::GlslangToSpv(*intermediate, spirv, &logger, &options); + ASSERT_FALSE(spirv.empty()) << "GlslangToSpv produced nothing for " << row.source; + + // Bake mode writes what it computed and asserts nothing else: the script that sets this + // variable is about to overwrite the header, so a red here would only stop it doing so. + if (const char* bakeDir = std::getenv("MOBILEGL_BAKE_INTERNAL_SHADERS")) { + const std::string out = std::string(bakeDir) + "/" + row.symbol + ".words"; + std::ofstream sink(out, std::ios::binary); + ASSERT_TRUE(sink.good()) << "cannot write " << out; + sink << row.header << "\n"; + for (unsigned int word : spirv) { + char text[16]; + std::snprintf(text, sizeof(text), "0x%08x\n", word); + sink << text; + } + ASSERT_TRUE(sink.good()) << "failed writing " << out; + return; + } + + ASSERT_EQ(spirv.size(), row.wordCount) + << row.header << ": " << row.symbol << " has " << row.wordCount << " words, recompiling " + << row.source << " with the in-tree glslang produces " << spirv.size() + << ". Regenerate with scripts/bake_internal_shaders.py."; + for (std::size_t at = 0; at < spirv.size(); ++at) { + ASSERT_EQ(spirv[at], row.words[at]) + << row.header << ": " << row.symbol << " word " << at << " is stale against " + << row.source << ". Regenerate with scripts/bake_internal_shaders.py."; + } +} + +INSTANTIATE_TEST_SUITE_P(BakedShaders, BakedInternalShaders, + ::testing::ValuesIn(kBakedShaders), + [](const ::testing::TestParamInfo& info) { + return std::string(info.param.name); + }); + +} // namespace diff --git a/MobileGL/MG_Test/SelfTest/CMakeLists.txt b/MobileGL/MG_Test/SelfTest/CMakeLists.txt index 768e9179..af0cbedd 100644 --- a/MobileGL/MG_Test/SelfTest/CMakeLists.txt +++ b/MobileGL/MG_Test/SelfTest/CMakeLists.txt @@ -46,7 +46,38 @@ target_link_libraries(PrimitivesGeneratedNoXfbProbeTest PRIVATE ${LINK_LIBRARIES} ) +# MOBILEGL_BAKED_INTERNAL_SHADERS (CONTRACT-P7 §5.1 (D)): recompiles every baked SPIR-V header +# in the tree from its committed GLSL with the IN-TREE glslang and byte-compares. It is also the +# BAKER - scripts/bake_internal_shaders.py runs this binary rather than a glslangValidator of its +# own, so the gate and the generator cannot be two compilers with two answers. +# +# glslang-default-resource-limits is the standalone limits table glslangValidator itself parses +# with; MobileGL_s links glslang but builds its own limits from the live GL context, which a test +# with no context cannot do. MOBILEGL_BAKED_SHADER_ROOT is the SOURCE tree: the .vert/.frag/.comp +# inputs are not installed next to the binary and the gate fails rather than skips without them. +add_executable( + BakedInternalShadersTest + BakedInternalShadersTest.cpp +) + +target_include_directories(BakedInternalShadersTest PRIVATE + ${MGL_ROOT}/include + ${MGL_ROOT}/MobileGL +) + +target_compile_definitions(BakedInternalShadersTest PRIVATE + MOBILEGL_BAKED_SHADER_ROOT="${MGL_ROOT}" +) + +target_link_libraries(BakedInternalShadersTest PRIVATE + GTest::gtest_main + glslang + glslang-default-resource-limits + ${LINK_LIBRARIES} +) + include(GoogleTest) gtest_discover_tests(DriverPostIterationRPWitnessTest DISCOVERY_TIMEOUT 30 PROPERTIES LABELS unit) +gtest_discover_tests(BakedInternalShadersTest DISCOVERY_TIMEOUT 30 PROPERTIES LABELS unit) gtest_discover_tests(DriverBugProbesTest DISCOVERY_TIMEOUT 30 PROPERTIES LABELS unit) gtest_discover_tests(PrimitivesGeneratedNoXfbProbeTest DISCOVERY_TIMEOUT 30 PROPERTIES LABELS unit) diff --git a/MobileGL/MG_Util/SelfTest/DriverPostIterationRPWitnessSpv.h b/MobileGL/MG_Util/SelfTest/DriverPostIterationRPWitnessSpv.h index 556fdcf8..a2b91804 100644 --- a/MobileGL/MG_Util/SelfTest/DriverPostIterationRPWitnessSpv.h +++ b/MobileGL/MG_Util/SelfTest/DriverPostIterationRPWitnessSpv.h @@ -6,15 +6,18 @@ // SPDX-License-Identifier: LGPL-3.0-only // End of Source File Header // -// Generated from DriverPostIterationRPWitness.comp with: -// glslangValidator --target-env vulkan1.1 -V DriverPostIterationRPWitness.comp -// Validated with spirv-val --target-env vulkan1.1. Do not edit words by hand. +// Generated from DriverPostIterationRPWitness.comp (Vulkan 1.1 / SPIR-V 1.3) by +// scripts/bake_internal_shaders.py, which runs MG_Test's BakedInternalShadersTest with the +// IN-TREE glslang and splices the words it emits into the array below. Do not edit the words +// by hand: MOBILEGL_BAKED_INTERNAL_SHADERS (the same test, run as a gate) recompiles this +// source and reds on the first word that disagrees. // -// The stored words predate the Program203 -> IterationRP source rename, so their -// embedded OpName debug strings still spell the old identifiers; regeneration from -// the renamed source produces semantically identical code differing only in those -// strings. The witness magic stays 0x50323033 ("P203" - the trace's program id) so -// these words remain valid without regeneration. +// P7 wave 2-B regenerated these words. They had predated the Program203 -> IterationRP source +// rename, so their OpName debug strings still spelled the old identifiers, and the header said +// so in prose - which is exactly as much enforcement as prose provides: nothing recompiled the +// source, so nothing could tell that difference from a real one. The witness magic is still +// 0x50323033 ("P203" - the trace's program id), because it is a constant in the .comp and not +// a property of the words. #pragma once @@ -38,259 +41,259 @@ namespace MobileGL::MG_Util::SelfTest { 0x67627553u, 0x70756f72u, 0x6b73614du, 0x72616853u, 0x00006465u, 0x00090005u, 0x0000001du, 0x7473616cu, 0x656e614cu, 0x74697257u, 0x6f437265u, 0x53746e75u, 0x65726168u, 0x00000064u, 0x00080005u, 0x00000020u, 0x65646e69u, 0x49646578u, 0x7475706eu, 0x61746f54u, 0x6168536cu, 0x00646572u, 0x00060005u, 0x0000002au, - 0x4e5f6c67u, 0x75536d75u, 0x6f726762u, 0x00737075u, 0x00080005u, 0x00000035u, 0x676f7250u, 0x326d6172u, - 0x69573330u, 0x73656e74u, 0x74754f73u, 0x00747570u, 0x00050006u, 0x00000035u, 0x00000000u, 0x6967616du, - 0x00000063u, 0x00070006u, 0x00000035u, 0x00000001u, 0x6f706f74u, 0x79676f6cu, 0x67616c46u, 0x00000073u, - 0x00070006u, 0x00000035u, 0x00000002u, 0x536d756eu, 0x72676275u, 0x7370756fu, 0x00000000u, 0x00060006u, - 0x00000035u, 0x00000003u, 0x706f6f6cu, 0x676e654cu, 0x00006874u, 0x00080006u, 0x00000035u, 0x00000004u, - 0x6e656573u, 0x67627553u, 0x70756f72u, 0x6b73614du, 0x00000000u, 0x00060006u, 0x00000035u, 0x00000005u, - 0x656e776fu, 0x31313572u, 0x00000000u, 0x00080006u, 0x00000035u, 0x00000006u, 0x7473616cu, 0x656e614cu, - 0x74697257u, 0x6f437265u, 0x00746e75u, 0x00080006u, 0x00000035u, 0x00000007u, 0x65646e69u, 0x49646578u, - 0x7475706eu, 0x61746f54u, 0x0000006cu, 0x00060006u, 0x00000035u, 0x00000008u, 0x50776172u, 0x69666572u, - 0x00000078u, 0x00060006u, 0x00000035u, 0x00000009u, 0x6e616373u, 0x68636143u, 0x00000065u, 0x00070006u, - 0x00000035u, 0x0000000au, 0x616e6966u, 0x6576416cu, 0x65676172u, 0x00000000u, 0x00050005u, 0x00000037u, - 0x5774756fu, 0x656e7469u, 0x00007373u, 0x00050005u, 0x0000003du, 0x6f6e6163u, 0x6163696eu, 0x00004e6cu, - 0x00060005u, 0x00000050u, 0x535f6c67u, 0x72676275u, 0x4970756fu, 0x00000044u, 0x00060005u, 0x0000005eu, - 0x535f6c67u, 0x72676275u, 0x5370756fu, 0x00657a69u, 0x00080005u, 0x00000064u, 0x535f6c67u, 0x72676275u, - 0x4970756fu, 0x636f766eu, 0x6f697461u, 0x0044496eu, 0x00060005u, 0x0000006eu, 0x6f6e6163u, 0x6163696eu, - 0x6d6f446cu, 0x006e6961u, 0x00070005u, 0x00000074u, 0x6e496469u, 0x6f6e6143u, 0x6163696eu, 0x6d6f446cu, - 0x006e6961u, 0x00060005u, 0x000000acu, 0x72756f73u, 0x6f446563u, 0x6e69616du, 0x00000000u, 0x00060005u, - 0x000000b7u, 0x706d6173u, 0x754c656cu, 0x616e696du, 0x0065636eu, 0x00060005u, 0x000000c8u, 0x66657270u, - 0x75537869u, 0x6361436du, 0x00006568u, 0x00050005u, 0x000000d9u, 0x706f6f6cu, 0x676e654cu, 0x00006874u, - 0x00050005u, 0x000000edu, 0x6e616373u, 0x67617453u, 0x00000065u, 0x00040047u, 0x0000000au, 0x0000000bu, - 0x0000001du, 0x00040047u, 0x0000002au, 0x0000000bu, 0x00000026u, 0x00040047u, 0x0000002du, 0x00000006u, - 0x00000004u, 0x00040047u, 0x0000002eu, 0x00000006u, 0x00000004u, 0x00040047u, 0x00000031u, 0x00000006u, - 0x00000008u, 0x00040047u, 0x00000032u, 0x00000006u, 0x00000008u, 0x00040047u, 0x00000034u, 0x00000006u, - 0x00000100u, 0x00030047u, 0x00000035u, 0x00000002u, 0x00050048u, 0x00000035u, 0x00000000u, 0x00000023u, - 0x00000000u, 0x00050048u, 0x00000035u, 0x00000001u, 0x00000023u, 0x00000004u, 0x00050048u, 0x00000035u, - 0x00000002u, 0x00000023u, 0x00000008u, 0x00050048u, 0x00000035u, 0x00000003u, 0x00000023u, 0x0000000cu, - 0x00050048u, 0x00000035u, 0x00000004u, 0x00000023u, 0x00000010u, 0x00050048u, 0x00000035u, 0x00000005u, - 0x00000023u, 0x00000020u, 0x00050048u, 0x00000035u, 0x00000006u, 0x00000023u, 0x00000030u, 0x00050048u, - 0x00000035u, 0x00000007u, 0x00000023u, 0x000000b0u, 0x00050048u, 0x00000035u, 0x00000008u, 0x00000023u, - 0x00000130u, 0x00050048u, 0x00000035u, 0x00000009u, 0x00000023u, 0x00000230u, 0x00050048u, 0x00000035u, - 0x0000000au, 0x00000023u, 0x00000830u, 0x00040047u, 0x00000037u, 0x00000021u, 0x00000000u, 0x00040047u, - 0x00000037u, 0x00000022u, 0x00000000u, 0x00040047u, 0x00000050u, 0x0000000bu, 0x00000028u, 0x00030047u, - 0x0000005eu, 0x00000000u, 0x00040047u, 0x0000005eu, 0x0000000bu, 0x00000024u, 0x00030047u, 0x0000005fu, - 0x00000000u, 0x00030047u, 0x00000064u, 0x00000000u, 0x00040047u, 0x00000064u, 0x0000000bu, 0x00000029u, - 0x00030047u, 0x00000065u, 0x00000000u, 0x00030047u, 0x00000066u, 0x00000000u, 0x00030047u, 0x00000087u, - 0x00000000u, 0x00030047u, 0x0000008bu, 0x00000000u, 0x00030047u, 0x0000008cu, 0x00000000u, 0x00030047u, - 0x0000008du, 0x00000000u, 0x00030047u, 0x000000c0u, 0x00000000u, 0x00030047u, 0x000000c1u, 0x00000000u, - 0x00030047u, 0x000000c2u, 0x00000000u, 0x00030047u, 0x00000107u, 0x00000000u, 0x00030047u, 0x00000108u, - 0x00000000u, 0x00030047u, 0x00000109u, 0x00000000u, 0x00030047u, 0x0000012fu, 0x00000000u, 0x00030047u, - 0x00000132u, 0x00000000u, 0x00040047u, 0x00000144u, 0x0000000bu, 0x00000019u, 0x00020013u, 0x00000002u, - 0x00030021u, 0x00000003u, 0x00000002u, 0x00040015u, 0x00000006u, 0x00000020u, 0x00000000u, 0x00040020u, - 0x00000007u, 0x00000007u, 0x00000006u, 0x00040020u, 0x00000009u, 0x00000001u, 0x00000006u, 0x0004003bu, - 0x00000009u, 0x0000000au, 0x00000001u, 0x0004002bu, 0x00000006u, 0x0000000du, 0x00000000u, 0x00020014u, - 0x0000000eu, 0x00040020u, 0x00000012u, 0x00000004u, 0x00000006u, 0x0004003bu, 0x00000012u, 0x00000013u, - 0x00000004u, 0x0004003bu, 0x00000012u, 0x00000014u, 0x00000004u, 0x0004003bu, 0x00000012u, 0x00000015u, - 0x00000004u, 0x0004002bu, 0x00000006u, 0x00000017u, 0x00000020u, 0x0004001cu, 0x0000001bu, 0x00000006u, - 0x00000017u, 0x00040020u, 0x0000001cu, 0x00000004u, 0x0000001bu, 0x0004003bu, 0x0000001cu, 0x0000001du, - 0x00000004u, 0x0004003bu, 0x0000001cu, 0x00000020u, 0x00000004u, 0x0004002bu, 0x00000006u, 0x00000023u, - 0x00000001u, 0x0004002bu, 0x00000006u, 0x00000024u, 0x00000108u, 0x0004002bu, 0x00000006u, 0x00000025u, - 0x00000002u, 0x0004003bu, 0x00000009u, 0x0000002au, 0x00000001u, 0x00040017u, 0x0000002cu, 0x00000006u, - 0x00000004u, 0x0004001cu, 0x0000002du, 0x00000006u, 0x00000017u, 0x0004001cu, 0x0000002eu, 0x00000006u, - 0x00000017u, 0x00030016u, 0x0000002fu, 0x00000020u, 0x00040017u, 0x00000030u, 0x0000002fu, 0x00000002u, - 0x0004001cu, 0x00000031u, 0x00000030u, 0x00000017u, 0x0004001cu, 0x00000032u, 0x00000030u, 0x00000017u, - 0x0004002bu, 0x00000006u, 0x00000033u, 0x00000006u, 0x0004001cu, 0x00000034u, 0x00000032u, 0x00000033u, - 0x000d001eu, 0x00000035u, 0x00000006u, 0x00000006u, 0x00000006u, 0x00000006u, 0x00000006u, 0x0000002cu, - 0x0000002du, 0x0000002eu, 0x00000031u, 0x00000034u, 0x00000030u, 0x00040020u, 0x00000036u, 0x0000000cu, - 0x00000035u, 0x0004003bu, 0x00000036u, 0x00000037u, 0x0000000cu, 0x00040015u, 0x00000038u, 0x00000020u, - 0x00000001u, 0x0004002bu, 0x00000038u, 0x00000039u, 0x00000002u, 0x00040020u, 0x0000003bu, 0x0000000cu, - 0x00000006u, 0x0004003bu, 0x00000009u, 0x00000050u, 0x00000001u, 0x0004002bu, 0x00000006u, 0x0000005cu, - 0x00000004u, 0x0004003bu, 0x00000009u, 0x0000005eu, 0x00000001u, 0x0004003bu, 0x00000009u, 0x00000064u, - 0x00000001u, 0x0004002bu, 0x00000006u, 0x0000006bu, 0x00000008u, 0x00040020u, 0x0000006du, 0x00000007u, - 0x0000000eu, 0x0004002bu, 0x00000038u, 0x00000099u, 0x00000004u, 0x0004002bu, 0x00000038u, 0x000000a0u, - 0x00000006u, 0x0004002bu, 0x00000038u, 0x000000a6u, 0x00000007u, 0x00040020u, 0x000000b6u, 0x00000007u, - 0x00000030u, 0x0004002bu, 0x0000002fu, 0x000000bbu, 0x00000000u, 0x0004002bu, 0x00000006u, 0x000000beu, - 0x00000003u, 0x0004001cu, 0x000000c6u, 0x00000030u, 0x00000017u, 0x00040020u, 0x000000c7u, 0x00000004u, - 0x000000c6u, 0x0004003bu, 0x000000c7u, 0x000000c8u, 0x00000004u, 0x00040020u, 0x000000cbu, 0x00000004u, - 0x00000030u, 0x0004002bu, 0x00000038u, 0x000000d2u, 0x00000008u, 0x00040020u, 0x000000d7u, 0x0000000cu, - 0x00000030u, 0x0004002bu, 0x00000038u, 0x000000eau, 0x00000003u, 0x0004002bu, 0x00000038u, 0x00000115u, - 0x00000009u, 0x0004002bu, 0x00000038u, 0x0000011du, 0x00000001u, 0x0004002bu, 0x00000006u, 0x00000120u, - 0x000001ffu, 0x0004002bu, 0x00000038u, 0x00000124u, 0x00000000u, 0x0004002bu, 0x0000002fu, 0x00000126u, - 0x44000000u, 0x0004002bu, 0x00000038u, 0x0000012eu, 0x00000005u, 0x00040020u, 0x00000134u, 0x0000000cu, - 0x0000002cu, 0x0004002bu, 0x00000038u, 0x00000136u, 0x0000000au, 0x0004002bu, 0x00000006u, 0x00000140u, - 0x50323033u, 0x00040017u, 0x00000142u, 0x00000006u, 0x00000003u, 0x0004002bu, 0x00000006u, 0x00000143u, - 0x00000010u, 0x0006002cu, 0x00000142u, 0x00000144u, 0x00000017u, 0x00000143u, 0x00000023u, 0x00050036u, - 0x00000002u, 0x00000004u, 0x00000000u, 0x00000003u, 0x000200f8u, 0x00000005u, 0x0004003bu, 0x00000007u, - 0x00000008u, 0x00000007u, 0x0004003bu, 0x00000007u, 0x0000003du, 0x00000007u, 0x0004003bu, 0x0000006du, - 0x0000006eu, 0x00000007u, 0x0004003bu, 0x0000006du, 0x00000074u, 0x00000007u, 0x0004003bu, 0x0000006du, - 0x000000acu, 0x00000007u, 0x0004003bu, 0x000000b6u, 0x000000b7u, 0x00000007u, 0x0004003bu, 0x00000007u, - 0x000000d9u, 0x00000007u, 0x0004003bu, 0x00000007u, 0x000000edu, 0x00000007u, 0x0004003du, 0x00000006u, - 0x0000000bu, 0x0000000au, 0x0003003eu, 0x00000008u, 0x0000000bu, 0x0004003du, 0x00000006u, 0x0000000cu, - 0x00000008u, 0x000500aau, 0x0000000eu, 0x0000000fu, 0x0000000cu, 0x0000000du, 0x000300f7u, 0x00000011u, - 0x00000000u, 0x000400fau, 0x0000000fu, 0x00000010u, 0x00000011u, 0x000200f8u, 0x00000010u, 0x0003003eu, - 0x00000013u, 0x0000000du, 0x0003003eu, 0x00000014u, 0x0000000du, 0x0003003eu, 0x00000015u, 0x0000000du, - 0x000200f9u, 0x00000011u, 0x000200f8u, 0x00000011u, 0x0004003du, 0x00000006u, 0x00000016u, 0x00000008u, - 0x000500b0u, 0x0000000eu, 0x00000018u, 0x00000016u, 0x00000017u, 0x000300f7u, 0x0000001au, 0x00000000u, - 0x000400fau, 0x00000018u, 0x00000019u, 0x0000001au, 0x000200f8u, 0x00000019u, 0x0004003du, 0x00000006u, - 0x0000001eu, 0x00000008u, 0x00050041u, 0x00000012u, 0x0000001fu, 0x0000001du, 0x0000001eu, 0x0003003eu, - 0x0000001fu, 0x0000000du, 0x0004003du, 0x00000006u, 0x00000021u, 0x00000008u, 0x00050041u, 0x00000012u, - 0x00000022u, 0x00000020u, 0x00000021u, 0x0003003eu, 0x00000022u, 0x0000000du, 0x000200f9u, 0x0000001au, - 0x000200f8u, 0x0000001au, 0x000300e1u, 0x00000023u, 0x00000024u, 0x000400e0u, 0x00000025u, 0x00000025u, - 0x00000024u, 0x0004003du, 0x00000006u, 0x00000026u, 0x00000008u, 0x000500aau, 0x0000000eu, 0x00000027u, - 0x00000026u, 0x0000000du, 0x000300f7u, 0x00000029u, 0x00000000u, 0x000400fau, 0x00000027u, 0x00000028u, - 0x00000029u, 0x000200f8u, 0x00000028u, 0x0004003du, 0x00000006u, 0x0000002bu, 0x0000002au, 0x0003003eu, - 0x00000013u, 0x0000002bu, 0x0004003du, 0x00000006u, 0x0000003au, 0x0000002au, 0x00050041u, 0x0000003bu, - 0x0000003cu, 0x00000037u, 0x00000039u, 0x0003003eu, 0x0000003cu, 0x0000003au, 0x000200f9u, 0x00000029u, - 0x000200f8u, 0x00000029u, 0x000400e0u, 0x00000025u, 0x00000025u, 0x00000024u, 0x0004003du, 0x00000006u, - 0x0000003eu, 0x00000013u, 0x0003003eu, 0x0000003du, 0x0000003eu, 0x0004003du, 0x00000006u, 0x0000003fu, - 0x0000002au, 0x0004003du, 0x00000006u, 0x00000040u, 0x0000003du, 0x000500abu, 0x0000000eu, 0x00000041u, - 0x0000003fu, 0x00000040u, 0x000300f7u, 0x00000043u, 0x00000000u, 0x000400fau, 0x00000041u, 0x00000042u, - 0x00000043u, 0x000200f8u, 0x00000042u, 0x000700f1u, 0x00000006u, 0x00000044u, 0x00000014u, 0x00000023u, - 0x0000000du, 0x00000023u, 0x000200f9u, 0x00000043u, 0x000200f8u, 0x00000043u, 0x0004003du, 0x00000006u, - 0x00000045u, 0x0000002au, 0x000500b0u, 0x0000000eu, 0x00000046u, 0x00000045u, 0x00000025u, 0x000400a8u, - 0x0000000eu, 0x00000047u, 0x00000046u, 0x000300f7u, 0x00000049u, 0x00000000u, 0x000400fau, 0x00000047u, - 0x00000048u, 0x00000049u, 0x000200f8u, 0x00000048u, 0x0004003du, 0x00000006u, 0x0000004au, 0x0000002au, - 0x000500acu, 0x0000000eu, 0x0000004bu, 0x0000004au, 0x00000017u, 0x000200f9u, 0x00000049u, 0x000200f8u, - 0x00000049u, 0x000700f5u, 0x0000000eu, 0x0000004cu, 0x00000046u, 0x00000043u, 0x0000004bu, 0x00000048u, - 0x000300f7u, 0x0000004eu, 0x00000000u, 0x000400fau, 0x0000004cu, 0x0000004du, 0x0000004eu, 0x000200f8u, - 0x0000004du, 0x000700f1u, 0x00000006u, 0x0000004fu, 0x00000014u, 0x00000023u, 0x0000000du, 0x00000025u, - 0x000200f9u, 0x0000004eu, 0x000200f8u, 0x0000004eu, 0x0004003du, 0x00000006u, 0x00000051u, 0x00000050u, - 0x0004003du, 0x00000006u, 0x00000052u, 0x0000003du, 0x000500aeu, 0x0000000eu, 0x00000053u, 0x00000051u, - 0x00000052u, 0x000400a8u, 0x0000000eu, 0x00000054u, 0x00000053u, 0x000300f7u, 0x00000056u, 0x00000000u, - 0x000400fau, 0x00000054u, 0x00000055u, 0x00000056u, 0x000200f8u, 0x00000055u, 0x0004003du, 0x00000006u, - 0x00000057u, 0x00000050u, 0x000500aeu, 0x0000000eu, 0x00000058u, 0x00000057u, 0x00000017u, 0x000200f9u, - 0x00000056u, 0x000200f8u, 0x00000056u, 0x000700f5u, 0x0000000eu, 0x00000059u, 0x00000053u, 0x0000004eu, - 0x00000058u, 0x00000055u, 0x000300f7u, 0x0000005bu, 0x00000000u, 0x000400fau, 0x00000059u, 0x0000005au, - 0x0000005bu, 0x000200f8u, 0x0000005au, 0x000700f1u, 0x00000006u, 0x0000005du, 0x00000014u, 0x00000023u, - 0x0000000du, 0x0000005cu, 0x000200f9u, 0x0000005bu, 0x000200f8u, 0x0000005bu, 0x0004003du, 0x00000006u, - 0x0000005fu, 0x0000005eu, 0x000500aau, 0x0000000eu, 0x00000060u, 0x0000005fu, 0x0000000du, 0x000400a8u, - 0x0000000eu, 0x00000061u, 0x00000060u, 0x000300f7u, 0x00000063u, 0x00000000u, 0x000400fau, 0x00000061u, - 0x00000062u, 0x00000063u, 0x000200f8u, 0x00000062u, 0x0004003du, 0x00000006u, 0x00000065u, 0x00000064u, - 0x0004003du, 0x00000006u, 0x00000066u, 0x0000005eu, 0x000500aeu, 0x0000000eu, 0x00000067u, 0x00000065u, - 0x00000066u, 0x000200f9u, 0x00000063u, 0x000200f8u, 0x00000063u, 0x000700f5u, 0x0000000eu, 0x00000068u, - 0x00000060u, 0x0000005bu, 0x00000067u, 0x00000062u, 0x000300f7u, 0x0000006au, 0x00000000u, 0x000400fau, - 0x00000068u, 0x00000069u, 0x0000006au, 0x000200f8u, 0x00000069u, 0x000700f1u, 0x00000006u, 0x0000006cu, - 0x00000014u, 0x00000023u, 0x0000000du, 0x0000006bu, 0x000200f9u, 0x0000006au, 0x000200f8u, 0x0000006au, - 0x0004003du, 0x00000006u, 0x0000006fu, 0x0000003du, 0x000500aeu, 0x0000000eu, 0x00000070u, 0x0000006fu, - 0x00000025u, 0x0004003du, 0x00000006u, 0x00000071u, 0x0000003du, 0x000500b2u, 0x0000000eu, 0x00000072u, - 0x00000071u, 0x00000017u, 0x000500a7u, 0x0000000eu, 0x00000073u, 0x00000070u, 0x00000072u, 0x0003003eu, - 0x0000006eu, 0x00000073u, 0x0004003du, 0x0000000eu, 0x00000075u, 0x0000006eu, 0x000300f7u, 0x00000077u, - 0x00000000u, 0x000400fau, 0x00000075u, 0x00000076u, 0x00000077u, 0x000200f8u, 0x00000076u, 0x0004003du, - 0x00000006u, 0x00000078u, 0x00000050u, 0x0004003du, 0x00000006u, 0x00000079u, 0x0000003du, 0x000500b0u, - 0x0000000eu, 0x0000007au, 0x00000078u, 0x00000079u, 0x000200f9u, 0x00000077u, 0x000200f8u, 0x00000077u, - 0x000700f5u, 0x0000000eu, 0x0000007bu, 0x00000075u, 0x0000006au, 0x0000007au, 0x00000076u, 0x0003003eu, - 0x00000074u, 0x0000007bu, 0x0004003du, 0x0000000eu, 0x0000007cu, 0x00000074u, 0x000300f7u, 0x0000007eu, - 0x00000000u, 0x000400fau, 0x0000007cu, 0x0000007du, 0x0000007eu, 0x000200f8u, 0x0000007du, 0x0004003du, - 0x00000006u, 0x0000007fu, 0x00000050u, 0x000500c4u, 0x00000006u, 0x00000080u, 0x00000023u, 0x0000007fu, - 0x000700f1u, 0x00000006u, 0x00000081u, 0x00000015u, 0x00000023u, 0x0000000du, 0x00000080u, 0x0004003du, - 0x00000006u, 0x00000082u, 0x00000050u, 0x00050041u, 0x00000012u, 0x00000083u, 0x00000020u, 0x00000082u, - 0x0004003du, 0x00000006u, 0x00000084u, 0x00000008u, 0x00050080u, 0x00000006u, 0x00000085u, 0x00000084u, - 0x00000023u, 0x000700eau, 0x00000006u, 0x00000086u, 0x00000083u, 0x00000023u, 0x0000000du, 0x00000085u, - 0x0004003du, 0x00000006u, 0x00000087u, 0x0000005eu, 0x000500abu, 0x0000000eu, 0x00000088u, 0x00000087u, - 0x0000000du, 0x000300f7u, 0x0000008au, 0x00000000u, 0x000400fau, 0x00000088u, 0x00000089u, 0x0000008au, - 0x000200f8u, 0x00000089u, 0x0004003du, 0x00000006u, 0x0000008bu, 0x00000064u, 0x0004003du, 0x00000006u, - 0x0000008cu, 0x0000005eu, 0x00050082u, 0x00000006u, 0x0000008du, 0x0000008cu, 0x00000023u, 0x000500aau, - 0x0000000eu, 0x0000008eu, 0x0000008bu, 0x0000008du, 0x000200f9u, 0x0000008au, 0x000200f8u, 0x0000008au, - 0x000700f5u, 0x0000000eu, 0x0000008fu, 0x00000088u, 0x0000007du, 0x0000008eu, 0x00000089u, 0x000300f7u, - 0x00000091u, 0x00000000u, 0x000400fau, 0x0000008fu, 0x00000090u, 0x00000091u, 0x000200f8u, 0x00000090u, - 0x0004003du, 0x00000006u, 0x00000092u, 0x00000050u, 0x00050041u, 0x00000012u, 0x00000093u, 0x0000001du, - 0x00000092u, 0x000700eau, 0x00000006u, 0x00000094u, 0x00000093u, 0x00000023u, 0x0000000du, 0x00000023u, - 0x000200f9u, 0x00000091u, 0x000200f8u, 0x00000091u, 0x000200f9u, 0x0000007eu, 0x000200f8u, 0x0000007eu, - 0x000300e1u, 0x00000023u, 0x00000024u, 0x000400e0u, 0x00000025u, 0x00000025u, 0x00000024u, 0x0004003du, - 0x00000006u, 0x00000095u, 0x00000008u, 0x000500aau, 0x0000000eu, 0x00000096u, 0x00000095u, 0x0000000du, - 0x000300f7u, 0x00000098u, 0x00000000u, 0x000400fau, 0x00000096u, 0x00000097u, 0x00000098u, 0x000200f8u, - 0x00000097u, 0x0004003du, 0x00000006u, 0x0000009au, 0x00000015u, 0x00050041u, 0x0000003bu, 0x0000009bu, - 0x00000037u, 0x00000099u, 0x0003003eu, 0x0000009bu, 0x0000009au, 0x000200f9u, 0x00000098u, 0x000200f8u, - 0x00000098u, 0x0004003du, 0x00000006u, 0x0000009cu, 0x00000008u, 0x000500b0u, 0x0000000eu, 0x0000009du, - 0x0000009cu, 0x00000017u, 0x000300f7u, 0x0000009fu, 0x00000000u, 0x000400fau, 0x0000009du, 0x0000009eu, - 0x0000009fu, 0x000200f8u, 0x0000009eu, 0x0004003du, 0x00000006u, 0x000000a1u, 0x00000008u, 0x0004003du, - 0x00000006u, 0x000000a2u, 0x00000008u, 0x00050041u, 0x00000012u, 0x000000a3u, 0x0000001du, 0x000000a2u, - 0x0004003du, 0x00000006u, 0x000000a4u, 0x000000a3u, 0x00060041u, 0x0000003bu, 0x000000a5u, 0x00000037u, - 0x000000a0u, 0x000000a1u, 0x0003003eu, 0x000000a5u, 0x000000a4u, 0x0004003du, 0x00000006u, 0x000000a7u, - 0x00000008u, 0x0004003du, 0x00000006u, 0x000000a8u, 0x00000008u, 0x00050041u, 0x00000012u, 0x000000a9u, - 0x00000020u, 0x000000a8u, 0x0004003du, 0x00000006u, 0x000000aau, 0x000000a9u, 0x00060041u, 0x0000003bu, - 0x000000abu, 0x00000037u, 0x000000a6u, 0x000000a7u, 0x0003003eu, 0x000000abu, 0x000000aau, 0x000200f9u, - 0x0000009fu, 0x000200f8u, 0x0000009fu, 0x0004003du, 0x0000000eu, 0x000000adu, 0x0000006eu, 0x000300f7u, - 0x000000afu, 0x00000000u, 0x000400fau, 0x000000adu, 0x000000aeu, 0x000000afu, 0x000200f8u, 0x000000aeu, - 0x0004003du, 0x00000006u, 0x000000b0u, 0x00000014u, 0x000500aau, 0x0000000eu, 0x000000b1u, 0x000000b0u, - 0x0000000du, 0x000200f9u, 0x000000afu, 0x000200f8u, 0x000000afu, 0x000700f5u, 0x0000000eu, 0x000000b2u, - 0x000000adu, 0x0000009fu, 0x000000b1u, 0x000000aeu, 0x0003003eu, 0x000000acu, 0x000000b2u, 0x0004003du, - 0x0000000eu, 0x000000b3u, 0x000000acu, 0x000300f7u, 0x000000b5u, 0x00000000u, 0x000400fau, 0x000000b3u, - 0x000000b4u, 0x000000b5u, 0x000200f8u, 0x000000b4u, 0x0004003du, 0x00000006u, 0x000000b8u, 0x0000000au, - 0x00050080u, 0x00000006u, 0x000000b9u, 0x000000b8u, 0x00000023u, 0x00040070u, 0x0000002fu, 0x000000bau, - 0x000000b9u, 0x00050050u, 0x00000030u, 0x000000bcu, 0x000000bau, 0x000000bbu, 0x0003003eu, 0x000000b7u, - 0x000000bcu, 0x0004003du, 0x00000030u, 0x000000bdu, 0x000000b7u, 0x0006015eu, 0x00000030u, 0x000000bfu, - 0x000000beu, 0x00000001u, 0x000000bdu, 0x0003003eu, 0x000000b7u, 0x000000bfu, 0x0004003du, 0x00000006u, - 0x000000c0u, 0x00000064u, 0x0004003du, 0x00000006u, 0x000000c1u, 0x0000005eu, 0x00050082u, 0x00000006u, - 0x000000c2u, 0x000000c1u, 0x00000023u, 0x000500aau, 0x0000000eu, 0x000000c3u, 0x000000c0u, 0x000000c2u, - 0x000300f7u, 0x000000c5u, 0x00000000u, 0x000400fau, 0x000000c3u, 0x000000c4u, 0x000000c5u, 0x000200f8u, - 0x000000c4u, 0x0004003du, 0x00000006u, 0x000000c9u, 0x00000050u, 0x0004003du, 0x00000030u, 0x000000cau, - 0x000000b7u, 0x00050041u, 0x000000cbu, 0x000000ccu, 0x000000c8u, 0x000000c9u, 0x0003003eu, 0x000000ccu, - 0x000000cau, 0x000200f9u, 0x000000c5u, 0x000200f8u, 0x000000c5u, 0x000400e0u, 0x00000025u, 0x00000025u, - 0x00000024u, 0x0004003du, 0x00000006u, 0x000000cdu, 0x0000000au, 0x0004003du, 0x00000006u, 0x000000ceu, - 0x0000002au, 0x000500b0u, 0x0000000eu, 0x000000cfu, 0x000000cdu, 0x000000ceu, 0x000300f7u, 0x000000d1u, - 0x00000000u, 0x000400fau, 0x000000cfu, 0x000000d0u, 0x000000d1u, 0x000200f8u, 0x000000d0u, 0x0004003du, - 0x00000006u, 0x000000d3u, 0x0000000au, 0x0004003du, 0x00000006u, 0x000000d4u, 0x0000000au, 0x00050041u, - 0x000000cbu, 0x000000d5u, 0x000000c8u, 0x000000d4u, 0x0004003du, 0x00000030u, 0x000000d6u, 0x000000d5u, - 0x00060041u, 0x000000d7u, 0x000000d8u, 0x00000037u, 0x000000d2u, 0x000000d3u, 0x0003003eu, 0x000000d8u, - 0x000000d6u, 0x000200f9u, 0x000000d1u, 0x000200f8u, 0x000000d1u, 0x000400e0u, 0x00000025u, 0x00000025u, - 0x00000024u, 0x0004003du, 0x00000006u, 0x000000dau, 0x0000002au, 0x0006000cu, 0x00000038u, 0x000000dbu, - 0x00000001u, 0x0000004bu, 0x000000dau, 0x0004007cu, 0x00000006u, 0x000000dcu, 0x000000dbu, 0x0003003eu, - 0x000000d9u, 0x000000dcu, 0x0004003du, 0x00000006u, 0x000000ddu, 0x0000002au, 0x0004003du, 0x00000006u, - 0x000000deu, 0x000000d9u, 0x00050082u, 0x00000006u, 0x000000dfu, 0x000000deu, 0x00000023u, 0x000500c4u, - 0x00000006u, 0x000000e0u, 0x00000023u, 0x000000dfu, 0x00050082u, 0x00000006u, 0x000000e1u, 0x000000ddu, - 0x000000e0u, 0x000500acu, 0x0000000eu, 0x000000e2u, 0x000000e1u, 0x0000000du, 0x000600a9u, 0x00000006u, - 0x000000e3u, 0x000000e2u, 0x00000023u, 0x0000000du, 0x0004003du, 0x00000006u, 0x000000e4u, 0x000000d9u, - 0x00050080u, 0x00000006u, 0x000000e5u, 0x000000e4u, 0x000000e3u, 0x0003003eu, 0x000000d9u, 0x000000e5u, - 0x0004003du, 0x00000006u, 0x000000e6u, 0x0000000au, 0x000500aau, 0x0000000eu, 0x000000e7u, 0x000000e6u, - 0x0000000du, 0x000300f7u, 0x000000e9u, 0x00000000u, 0x000400fau, 0x000000e7u, 0x000000e8u, 0x000000e9u, - 0x000200f8u, 0x000000e8u, 0x0004003du, 0x00000006u, 0x000000ebu, 0x000000d9u, 0x00050041u, 0x0000003bu, - 0x000000ecu, 0x00000037u, 0x000000eau, 0x0003003eu, 0x000000ecu, 0x000000ebu, 0x000200f9u, 0x000000e9u, - 0x000200f8u, 0x000000e9u, 0x0003003eu, 0x000000edu, 0x0000000du, 0x000200f9u, 0x000000eeu, 0x000200f8u, - 0x000000eeu, 0x000400f6u, 0x000000f0u, 0x000000f1u, 0x00000000u, 0x000200f9u, 0x000000f2u, 0x000200f8u, - 0x000000f2u, 0x0004003du, 0x00000006u, 0x000000f3u, 0x000000edu, 0x0004003du, 0x00000006u, 0x000000f4u, - 0x000000d9u, 0x000500b0u, 0x0000000eu, 0x000000f5u, 0x000000f3u, 0x000000f4u, 0x000400fau, 0x000000f5u, - 0x000000efu, 0x000000f0u, 0x000200f8u, 0x000000efu, 0x0004003du, 0x00000006u, 0x000000f6u, 0x00000050u, - 0x0004003du, 0x00000006u, 0x000000f7u, 0x000000edu, 0x000500c4u, 0x00000006u, 0x000000f8u, 0x00000023u, - 0x000000f7u, 0x000500c7u, 0x00000006u, 0x000000f9u, 0x000000f6u, 0x000000f8u, 0x000500acu, 0x0000000eu, - 0x000000fau, 0x000000f9u, 0x0000000du, 0x000300f7u, 0x000000fcu, 0x00000000u, 0x000400fau, 0x000000fau, - 0x000000fbu, 0x000000fcu, 0x000200f8u, 0x000000fbu, 0x0004003du, 0x00000006u, 0x000000fdu, 0x00000050u, - 0x0004003du, 0x00000006u, 0x000000feu, 0x000000edu, 0x000500c2u, 0x00000006u, 0x000000ffu, 0x000000fdu, - 0x000000feu, 0x0004003du, 0x00000006u, 0x00000100u, 0x000000edu, 0x000500c4u, 0x00000006u, 0x00000101u, - 0x000000ffu, 0x00000100u, 0x00050082u, 0x00000006u, 0x00000102u, 0x00000101u, 0x00000023u, 0x00050041u, - 0x000000cbu, 0x00000103u, 0x000000c8u, 0x00000102u, 0x0004003du, 0x00000030u, 0x00000104u, 0x00000103u, - 0x0004003du, 0x00000030u, 0x00000105u, 0x000000b7u, 0x00050081u, 0x00000030u, 0x00000106u, 0x00000105u, - 0x00000104u, 0x0003003eu, 0x000000b7u, 0x00000106u, 0x0004003du, 0x00000006u, 0x00000107u, 0x00000064u, - 0x0004003du, 0x00000006u, 0x00000108u, 0x0000005eu, 0x00050082u, 0x00000006u, 0x00000109u, 0x00000108u, - 0x00000023u, 0x000500aau, 0x0000000eu, 0x0000010au, 0x00000107u, 0x00000109u, 0x000300f7u, 0x0000010cu, - 0x00000000u, 0x000400fau, 0x0000010au, 0x0000010bu, 0x0000010cu, 0x000200f8u, 0x0000010bu, 0x0004003du, - 0x00000006u, 0x0000010du, 0x00000050u, 0x0004003du, 0x00000030u, 0x0000010eu, 0x000000b7u, 0x00050041u, - 0x000000cbu, 0x0000010fu, 0x000000c8u, 0x0000010du, 0x0003003eu, 0x0000010fu, 0x0000010eu, 0x000200f9u, - 0x0000010cu, 0x000200f8u, 0x0000010cu, 0x000200f9u, 0x000000fcu, 0x000200f8u, 0x000000fcu, 0x000400e0u, - 0x00000025u, 0x00000025u, 0x00000024u, 0x0004003du, 0x00000006u, 0x00000110u, 0x0000000au, 0x0004003du, - 0x00000006u, 0x00000111u, 0x0000002au, 0x000500b0u, 0x0000000eu, 0x00000112u, 0x00000110u, 0x00000111u, - 0x000300f7u, 0x00000114u, 0x00000000u, 0x000400fau, 0x00000112u, 0x00000113u, 0x00000114u, 0x000200f8u, - 0x00000113u, 0x0004003du, 0x00000006u, 0x00000116u, 0x000000edu, 0x0004003du, 0x00000006u, 0x00000117u, - 0x0000000au, 0x0004003du, 0x00000006u, 0x00000118u, 0x0000000au, 0x00050041u, 0x000000cbu, 0x00000119u, - 0x000000c8u, 0x00000118u, 0x0004003du, 0x00000030u, 0x0000011au, 0x00000119u, 0x00070041u, 0x000000d7u, - 0x0000011bu, 0x00000037u, 0x00000115u, 0x00000116u, 0x00000117u, 0x0003003eu, 0x0000011bu, 0x0000011au, - 0x000200f9u, 0x00000114u, 0x000200f8u, 0x00000114u, 0x000400e0u, 0x00000025u, 0x00000025u, 0x00000024u, - 0x000200f9u, 0x000000f1u, 0x000200f8u, 0x000000f1u, 0x0004003du, 0x00000006u, 0x0000011cu, 0x000000edu, - 0x00050080u, 0x00000006u, 0x0000011eu, 0x0000011cu, 0x0000011du, 0x0003003eu, 0x000000edu, 0x0000011eu, - 0x000200f9u, 0x000000eeu, 0x000200f8u, 0x000000f0u, 0x0004003du, 0x00000006u, 0x0000011fu, 0x0000000au, - 0x000500aau, 0x0000000eu, 0x00000121u, 0x0000011fu, 0x00000120u, 0x000300f7u, 0x00000123u, 0x00000000u, - 0x000400fau, 0x00000121u, 0x00000122u, 0x00000123u, 0x000200f8u, 0x00000122u, 0x0004003du, 0x00000030u, - 0x00000125u, 0x000000b7u, 0x00050050u, 0x00000030u, 0x00000127u, 0x00000126u, 0x00000126u, 0x00050088u, - 0x00000030u, 0x00000128u, 0x00000125u, 0x00000127u, 0x00050041u, 0x000000cbu, 0x00000129u, 0x000000c8u, - 0x00000124u, 0x0003003eu, 0x00000129u, 0x00000128u, 0x000200f9u, 0x00000123u, 0x000200f8u, 0x00000123u, - 0x000400e0u, 0x00000025u, 0x00000025u, 0x00000024u, 0x0004003du, 0x00000006u, 0x0000012au, 0x0000000au, - 0x000500aau, 0x0000000eu, 0x0000012bu, 0x0000012au, 0x00000120u, 0x000300f7u, 0x0000012du, 0x00000000u, - 0x000400fau, 0x0000012bu, 0x0000012cu, 0x0000012du, 0x000200f8u, 0x0000012cu, 0x0004003du, 0x00000006u, - 0x0000012fu, 0x0000005eu, 0x0004003du, 0x00000006u, 0x00000130u, 0x0000002au, 0x0004003du, 0x00000006u, - 0x00000131u, 0x00000050u, 0x0004003du, 0x00000006u, 0x00000132u, 0x00000064u, 0x00070050u, 0x0000002cu, - 0x00000133u, 0x0000012fu, 0x00000130u, 0x00000131u, 0x00000132u, 0x00050041u, 0x00000134u, 0x00000135u, - 0x00000037u, 0x0000012eu, 0x0003003eu, 0x00000135u, 0x00000133u, 0x00050041u, 0x000000cbu, 0x00000137u, - 0x000000c8u, 0x00000124u, 0x0004003du, 0x00000030u, 0x00000138u, 0x00000137u, 0x00050041u, 0x000000d7u, - 0x00000139u, 0x00000037u, 0x00000136u, 0x0003003eu, 0x00000139u, 0x00000138u, 0x000200f9u, 0x0000012du, - 0x000200f8u, 0x0000012du, 0x000200f9u, 0x000000b5u, 0x000200f8u, 0x000000b5u, 0x000400e0u, 0x00000025u, - 0x00000025u, 0x00000024u, 0x0004003du, 0x00000006u, 0x0000013au, 0x00000008u, 0x000500aau, 0x0000000eu, - 0x0000013bu, 0x0000013au, 0x0000000du, 0x000300f7u, 0x0000013du, 0x00000000u, 0x000400fau, 0x0000013bu, - 0x0000013cu, 0x0000013du, 0x000200f8u, 0x0000013cu, 0x0004003du, 0x00000006u, 0x0000013eu, 0x00000014u, - 0x00050041u, 0x0000003bu, 0x0000013fu, 0x00000037u, 0x0000011du, 0x0003003eu, 0x0000013fu, 0x0000013eu, - 0x00050041u, 0x0000003bu, 0x00000141u, 0x00000037u, 0x00000124u, 0x0003003eu, 0x00000141u, 0x00000140u, - 0x000200f9u, 0x0000013du, 0x000200f8u, 0x0000013du, 0x000100fdu, 0x00010038u, + 0x4e5f6c67u, 0x75536d75u, 0x6f726762u, 0x00737075u, 0x00090005u, 0x00000035u, 0x72657449u, 0x6f697461u, + 0x5750526eu, 0x656e7469u, 0x754f7373u, 0x74757074u, 0x00000000u, 0x00050006u, 0x00000035u, 0x00000000u, + 0x6967616du, 0x00000063u, 0x00070006u, 0x00000035u, 0x00000001u, 0x6f706f74u, 0x79676f6cu, 0x67616c46u, + 0x00000073u, 0x00070006u, 0x00000035u, 0x00000002u, 0x536d756eu, 0x72676275u, 0x7370756fu, 0x00000000u, + 0x00060006u, 0x00000035u, 0x00000003u, 0x706f6f6cu, 0x676e654cu, 0x00006874u, 0x00080006u, 0x00000035u, + 0x00000004u, 0x6e656573u, 0x67627553u, 0x70756f72u, 0x6b73614du, 0x00000000u, 0x00060006u, 0x00000035u, + 0x00000005u, 0x656e776fu, 0x31313572u, 0x00000000u, 0x00080006u, 0x00000035u, 0x00000006u, 0x7473616cu, + 0x656e614cu, 0x74697257u, 0x6f437265u, 0x00746e75u, 0x00080006u, 0x00000035u, 0x00000007u, 0x65646e69u, + 0x49646578u, 0x7475706eu, 0x61746f54u, 0x0000006cu, 0x00060006u, 0x00000035u, 0x00000008u, 0x50776172u, + 0x69666572u, 0x00000078u, 0x00060006u, 0x00000035u, 0x00000009u, 0x6e616373u, 0x68636143u, 0x00000065u, + 0x00070006u, 0x00000035u, 0x0000000au, 0x616e6966u, 0x6576416cu, 0x65676172u, 0x00000000u, 0x00050005u, + 0x00000037u, 0x5774756fu, 0x656e7469u, 0x00007373u, 0x00050005u, 0x0000003du, 0x6f6e6163u, 0x6163696eu, + 0x00004e6cu, 0x00060005u, 0x00000050u, 0x535f6c67u, 0x72676275u, 0x4970756fu, 0x00000044u, 0x00060005u, + 0x0000005eu, 0x535f6c67u, 0x72676275u, 0x5370756fu, 0x00657a69u, 0x00080005u, 0x00000064u, 0x535f6c67u, + 0x72676275u, 0x4970756fu, 0x636f766eu, 0x6f697461u, 0x0044496eu, 0x00060005u, 0x0000006eu, 0x6f6e6163u, + 0x6163696eu, 0x6d6f446cu, 0x006e6961u, 0x00070005u, 0x00000074u, 0x6e496469u, 0x6f6e6143u, 0x6163696eu, + 0x6d6f446cu, 0x006e6961u, 0x00060005u, 0x000000acu, 0x72756f73u, 0x6f446563u, 0x6e69616du, 0x00000000u, + 0x00060005u, 0x000000b7u, 0x706d6173u, 0x754c656cu, 0x616e696du, 0x0065636eu, 0x00060005u, 0x000000c8u, + 0x66657270u, 0x75537869u, 0x6361436du, 0x00006568u, 0x00050005u, 0x000000d9u, 0x706f6f6cu, 0x676e654cu, + 0x00006874u, 0x00050005u, 0x000000edu, 0x6e616373u, 0x67617453u, 0x00000065u, 0x00040047u, 0x0000000au, + 0x0000000bu, 0x0000001du, 0x00040047u, 0x0000002au, 0x0000000bu, 0x00000026u, 0x00040047u, 0x0000002du, + 0x00000006u, 0x00000004u, 0x00040047u, 0x0000002eu, 0x00000006u, 0x00000004u, 0x00040047u, 0x00000031u, + 0x00000006u, 0x00000008u, 0x00040047u, 0x00000032u, 0x00000006u, 0x00000008u, 0x00040047u, 0x00000034u, + 0x00000006u, 0x00000100u, 0x00030047u, 0x00000035u, 0x00000002u, 0x00050048u, 0x00000035u, 0x00000000u, + 0x00000023u, 0x00000000u, 0x00050048u, 0x00000035u, 0x00000001u, 0x00000023u, 0x00000004u, 0x00050048u, + 0x00000035u, 0x00000002u, 0x00000023u, 0x00000008u, 0x00050048u, 0x00000035u, 0x00000003u, 0x00000023u, + 0x0000000cu, 0x00050048u, 0x00000035u, 0x00000004u, 0x00000023u, 0x00000010u, 0x00050048u, 0x00000035u, + 0x00000005u, 0x00000023u, 0x00000020u, 0x00050048u, 0x00000035u, 0x00000006u, 0x00000023u, 0x00000030u, + 0x00050048u, 0x00000035u, 0x00000007u, 0x00000023u, 0x000000b0u, 0x00050048u, 0x00000035u, 0x00000008u, + 0x00000023u, 0x00000130u, 0x00050048u, 0x00000035u, 0x00000009u, 0x00000023u, 0x00000230u, 0x00050048u, + 0x00000035u, 0x0000000au, 0x00000023u, 0x00000830u, 0x00040047u, 0x00000037u, 0x00000021u, 0x00000000u, + 0x00040047u, 0x00000037u, 0x00000022u, 0x00000000u, 0x00040047u, 0x00000050u, 0x0000000bu, 0x00000028u, + 0x00030047u, 0x0000005eu, 0x00000000u, 0x00040047u, 0x0000005eu, 0x0000000bu, 0x00000024u, 0x00030047u, + 0x0000005fu, 0x00000000u, 0x00030047u, 0x00000064u, 0x00000000u, 0x00040047u, 0x00000064u, 0x0000000bu, + 0x00000029u, 0x00030047u, 0x00000065u, 0x00000000u, 0x00030047u, 0x00000066u, 0x00000000u, 0x00030047u, + 0x00000087u, 0x00000000u, 0x00030047u, 0x0000008bu, 0x00000000u, 0x00030047u, 0x0000008cu, 0x00000000u, + 0x00030047u, 0x0000008du, 0x00000000u, 0x00030047u, 0x000000c0u, 0x00000000u, 0x00030047u, 0x000000c1u, + 0x00000000u, 0x00030047u, 0x000000c2u, 0x00000000u, 0x00030047u, 0x00000107u, 0x00000000u, 0x00030047u, + 0x00000108u, 0x00000000u, 0x00030047u, 0x00000109u, 0x00000000u, 0x00030047u, 0x0000012fu, 0x00000000u, + 0x00030047u, 0x00000132u, 0x00000000u, 0x00040047u, 0x00000144u, 0x0000000bu, 0x00000019u, 0x00020013u, + 0x00000002u, 0x00030021u, 0x00000003u, 0x00000002u, 0x00040015u, 0x00000006u, 0x00000020u, 0x00000000u, + 0x00040020u, 0x00000007u, 0x00000007u, 0x00000006u, 0x00040020u, 0x00000009u, 0x00000001u, 0x00000006u, + 0x0004003bu, 0x00000009u, 0x0000000au, 0x00000001u, 0x0004002bu, 0x00000006u, 0x0000000du, 0x00000000u, + 0x00020014u, 0x0000000eu, 0x00040020u, 0x00000012u, 0x00000004u, 0x00000006u, 0x0004003bu, 0x00000012u, + 0x00000013u, 0x00000004u, 0x0004003bu, 0x00000012u, 0x00000014u, 0x00000004u, 0x0004003bu, 0x00000012u, + 0x00000015u, 0x00000004u, 0x0004002bu, 0x00000006u, 0x00000017u, 0x00000020u, 0x0004001cu, 0x0000001bu, + 0x00000006u, 0x00000017u, 0x00040020u, 0x0000001cu, 0x00000004u, 0x0000001bu, 0x0004003bu, 0x0000001cu, + 0x0000001du, 0x00000004u, 0x0004003bu, 0x0000001cu, 0x00000020u, 0x00000004u, 0x0004002bu, 0x00000006u, + 0x00000023u, 0x00000001u, 0x0004002bu, 0x00000006u, 0x00000024u, 0x00000108u, 0x0004002bu, 0x00000006u, + 0x00000025u, 0x00000002u, 0x0004003bu, 0x00000009u, 0x0000002au, 0x00000001u, 0x00040017u, 0x0000002cu, + 0x00000006u, 0x00000004u, 0x0004001cu, 0x0000002du, 0x00000006u, 0x00000017u, 0x0004001cu, 0x0000002eu, + 0x00000006u, 0x00000017u, 0x00030016u, 0x0000002fu, 0x00000020u, 0x00040017u, 0x00000030u, 0x0000002fu, + 0x00000002u, 0x0004001cu, 0x00000031u, 0x00000030u, 0x00000017u, 0x0004001cu, 0x00000032u, 0x00000030u, + 0x00000017u, 0x0004002bu, 0x00000006u, 0x00000033u, 0x00000006u, 0x0004001cu, 0x00000034u, 0x00000032u, + 0x00000033u, 0x000d001eu, 0x00000035u, 0x00000006u, 0x00000006u, 0x00000006u, 0x00000006u, 0x00000006u, + 0x0000002cu, 0x0000002du, 0x0000002eu, 0x00000031u, 0x00000034u, 0x00000030u, 0x00040020u, 0x00000036u, + 0x0000000cu, 0x00000035u, 0x0004003bu, 0x00000036u, 0x00000037u, 0x0000000cu, 0x00040015u, 0x00000038u, + 0x00000020u, 0x00000001u, 0x0004002bu, 0x00000038u, 0x00000039u, 0x00000002u, 0x00040020u, 0x0000003bu, + 0x0000000cu, 0x00000006u, 0x0004003bu, 0x00000009u, 0x00000050u, 0x00000001u, 0x0004002bu, 0x00000006u, + 0x0000005cu, 0x00000004u, 0x0004003bu, 0x00000009u, 0x0000005eu, 0x00000001u, 0x0004003bu, 0x00000009u, + 0x00000064u, 0x00000001u, 0x0004002bu, 0x00000006u, 0x0000006bu, 0x00000008u, 0x00040020u, 0x0000006du, + 0x00000007u, 0x0000000eu, 0x0004002bu, 0x00000038u, 0x00000099u, 0x00000004u, 0x0004002bu, 0x00000038u, + 0x000000a0u, 0x00000006u, 0x0004002bu, 0x00000038u, 0x000000a6u, 0x00000007u, 0x00040020u, 0x000000b6u, + 0x00000007u, 0x00000030u, 0x0004002bu, 0x0000002fu, 0x000000bbu, 0x00000000u, 0x0004002bu, 0x00000006u, + 0x000000beu, 0x00000003u, 0x0004001cu, 0x000000c6u, 0x00000030u, 0x00000017u, 0x00040020u, 0x000000c7u, + 0x00000004u, 0x000000c6u, 0x0004003bu, 0x000000c7u, 0x000000c8u, 0x00000004u, 0x00040020u, 0x000000cbu, + 0x00000004u, 0x00000030u, 0x0004002bu, 0x00000038u, 0x000000d2u, 0x00000008u, 0x00040020u, 0x000000d7u, + 0x0000000cu, 0x00000030u, 0x0004002bu, 0x00000038u, 0x000000eau, 0x00000003u, 0x0004002bu, 0x00000038u, + 0x00000115u, 0x00000009u, 0x0004002bu, 0x00000038u, 0x0000011du, 0x00000001u, 0x0004002bu, 0x00000006u, + 0x00000120u, 0x000001ffu, 0x0004002bu, 0x00000038u, 0x00000124u, 0x00000000u, 0x0004002bu, 0x0000002fu, + 0x00000126u, 0x44000000u, 0x0004002bu, 0x00000038u, 0x0000012eu, 0x00000005u, 0x00040020u, 0x00000134u, + 0x0000000cu, 0x0000002cu, 0x0004002bu, 0x00000038u, 0x00000136u, 0x0000000au, 0x0004002bu, 0x00000006u, + 0x00000140u, 0x50323033u, 0x00040017u, 0x00000142u, 0x00000006u, 0x00000003u, 0x0004002bu, 0x00000006u, + 0x00000143u, 0x00000010u, 0x0006002cu, 0x00000142u, 0x00000144u, 0x00000017u, 0x00000143u, 0x00000023u, + 0x00050036u, 0x00000002u, 0x00000004u, 0x00000000u, 0x00000003u, 0x000200f8u, 0x00000005u, 0x0004003bu, + 0x00000007u, 0x00000008u, 0x00000007u, 0x0004003bu, 0x00000007u, 0x0000003du, 0x00000007u, 0x0004003bu, + 0x0000006du, 0x0000006eu, 0x00000007u, 0x0004003bu, 0x0000006du, 0x00000074u, 0x00000007u, 0x0004003bu, + 0x0000006du, 0x000000acu, 0x00000007u, 0x0004003bu, 0x000000b6u, 0x000000b7u, 0x00000007u, 0x0004003bu, + 0x00000007u, 0x000000d9u, 0x00000007u, 0x0004003bu, 0x00000007u, 0x000000edu, 0x00000007u, 0x0004003du, + 0x00000006u, 0x0000000bu, 0x0000000au, 0x0003003eu, 0x00000008u, 0x0000000bu, 0x0004003du, 0x00000006u, + 0x0000000cu, 0x00000008u, 0x000500aau, 0x0000000eu, 0x0000000fu, 0x0000000cu, 0x0000000du, 0x000300f7u, + 0x00000011u, 0x00000000u, 0x000400fau, 0x0000000fu, 0x00000010u, 0x00000011u, 0x000200f8u, 0x00000010u, + 0x0003003eu, 0x00000013u, 0x0000000du, 0x0003003eu, 0x00000014u, 0x0000000du, 0x0003003eu, 0x00000015u, + 0x0000000du, 0x000200f9u, 0x00000011u, 0x000200f8u, 0x00000011u, 0x0004003du, 0x00000006u, 0x00000016u, + 0x00000008u, 0x000500b0u, 0x0000000eu, 0x00000018u, 0x00000016u, 0x00000017u, 0x000300f7u, 0x0000001au, + 0x00000000u, 0x000400fau, 0x00000018u, 0x00000019u, 0x0000001au, 0x000200f8u, 0x00000019u, 0x0004003du, + 0x00000006u, 0x0000001eu, 0x00000008u, 0x00050041u, 0x00000012u, 0x0000001fu, 0x0000001du, 0x0000001eu, + 0x0003003eu, 0x0000001fu, 0x0000000du, 0x0004003du, 0x00000006u, 0x00000021u, 0x00000008u, 0x00050041u, + 0x00000012u, 0x00000022u, 0x00000020u, 0x00000021u, 0x0003003eu, 0x00000022u, 0x0000000du, 0x000200f9u, + 0x0000001au, 0x000200f8u, 0x0000001au, 0x000300e1u, 0x00000023u, 0x00000024u, 0x000400e0u, 0x00000025u, + 0x00000025u, 0x00000024u, 0x0004003du, 0x00000006u, 0x00000026u, 0x00000008u, 0x000500aau, 0x0000000eu, + 0x00000027u, 0x00000026u, 0x0000000du, 0x000300f7u, 0x00000029u, 0x00000000u, 0x000400fau, 0x00000027u, + 0x00000028u, 0x00000029u, 0x000200f8u, 0x00000028u, 0x0004003du, 0x00000006u, 0x0000002bu, 0x0000002au, + 0x0003003eu, 0x00000013u, 0x0000002bu, 0x0004003du, 0x00000006u, 0x0000003au, 0x0000002au, 0x00050041u, + 0x0000003bu, 0x0000003cu, 0x00000037u, 0x00000039u, 0x0003003eu, 0x0000003cu, 0x0000003au, 0x000200f9u, + 0x00000029u, 0x000200f8u, 0x00000029u, 0x000400e0u, 0x00000025u, 0x00000025u, 0x00000024u, 0x0004003du, + 0x00000006u, 0x0000003eu, 0x00000013u, 0x0003003eu, 0x0000003du, 0x0000003eu, 0x0004003du, 0x00000006u, + 0x0000003fu, 0x0000002au, 0x0004003du, 0x00000006u, 0x00000040u, 0x0000003du, 0x000500abu, 0x0000000eu, + 0x00000041u, 0x0000003fu, 0x00000040u, 0x000300f7u, 0x00000043u, 0x00000000u, 0x000400fau, 0x00000041u, + 0x00000042u, 0x00000043u, 0x000200f8u, 0x00000042u, 0x000700f1u, 0x00000006u, 0x00000044u, 0x00000014u, + 0x00000023u, 0x0000000du, 0x00000023u, 0x000200f9u, 0x00000043u, 0x000200f8u, 0x00000043u, 0x0004003du, + 0x00000006u, 0x00000045u, 0x0000002au, 0x000500b0u, 0x0000000eu, 0x00000046u, 0x00000045u, 0x00000025u, + 0x000400a8u, 0x0000000eu, 0x00000047u, 0x00000046u, 0x000300f7u, 0x00000049u, 0x00000000u, 0x000400fau, + 0x00000047u, 0x00000048u, 0x00000049u, 0x000200f8u, 0x00000048u, 0x0004003du, 0x00000006u, 0x0000004au, + 0x0000002au, 0x000500acu, 0x0000000eu, 0x0000004bu, 0x0000004au, 0x00000017u, 0x000200f9u, 0x00000049u, + 0x000200f8u, 0x00000049u, 0x000700f5u, 0x0000000eu, 0x0000004cu, 0x00000046u, 0x00000043u, 0x0000004bu, + 0x00000048u, 0x000300f7u, 0x0000004eu, 0x00000000u, 0x000400fau, 0x0000004cu, 0x0000004du, 0x0000004eu, + 0x000200f8u, 0x0000004du, 0x000700f1u, 0x00000006u, 0x0000004fu, 0x00000014u, 0x00000023u, 0x0000000du, + 0x00000025u, 0x000200f9u, 0x0000004eu, 0x000200f8u, 0x0000004eu, 0x0004003du, 0x00000006u, 0x00000051u, + 0x00000050u, 0x0004003du, 0x00000006u, 0x00000052u, 0x0000003du, 0x000500aeu, 0x0000000eu, 0x00000053u, + 0x00000051u, 0x00000052u, 0x000400a8u, 0x0000000eu, 0x00000054u, 0x00000053u, 0x000300f7u, 0x00000056u, + 0x00000000u, 0x000400fau, 0x00000054u, 0x00000055u, 0x00000056u, 0x000200f8u, 0x00000055u, 0x0004003du, + 0x00000006u, 0x00000057u, 0x00000050u, 0x000500aeu, 0x0000000eu, 0x00000058u, 0x00000057u, 0x00000017u, + 0x000200f9u, 0x00000056u, 0x000200f8u, 0x00000056u, 0x000700f5u, 0x0000000eu, 0x00000059u, 0x00000053u, + 0x0000004eu, 0x00000058u, 0x00000055u, 0x000300f7u, 0x0000005bu, 0x00000000u, 0x000400fau, 0x00000059u, + 0x0000005au, 0x0000005bu, 0x000200f8u, 0x0000005au, 0x000700f1u, 0x00000006u, 0x0000005du, 0x00000014u, + 0x00000023u, 0x0000000du, 0x0000005cu, 0x000200f9u, 0x0000005bu, 0x000200f8u, 0x0000005bu, 0x0004003du, + 0x00000006u, 0x0000005fu, 0x0000005eu, 0x000500aau, 0x0000000eu, 0x00000060u, 0x0000005fu, 0x0000000du, + 0x000400a8u, 0x0000000eu, 0x00000061u, 0x00000060u, 0x000300f7u, 0x00000063u, 0x00000000u, 0x000400fau, + 0x00000061u, 0x00000062u, 0x00000063u, 0x000200f8u, 0x00000062u, 0x0004003du, 0x00000006u, 0x00000065u, + 0x00000064u, 0x0004003du, 0x00000006u, 0x00000066u, 0x0000005eu, 0x000500aeu, 0x0000000eu, 0x00000067u, + 0x00000065u, 0x00000066u, 0x000200f9u, 0x00000063u, 0x000200f8u, 0x00000063u, 0x000700f5u, 0x0000000eu, + 0x00000068u, 0x00000060u, 0x0000005bu, 0x00000067u, 0x00000062u, 0x000300f7u, 0x0000006au, 0x00000000u, + 0x000400fau, 0x00000068u, 0x00000069u, 0x0000006au, 0x000200f8u, 0x00000069u, 0x000700f1u, 0x00000006u, + 0x0000006cu, 0x00000014u, 0x00000023u, 0x0000000du, 0x0000006bu, 0x000200f9u, 0x0000006au, 0x000200f8u, + 0x0000006au, 0x0004003du, 0x00000006u, 0x0000006fu, 0x0000003du, 0x000500aeu, 0x0000000eu, 0x00000070u, + 0x0000006fu, 0x00000025u, 0x0004003du, 0x00000006u, 0x00000071u, 0x0000003du, 0x000500b2u, 0x0000000eu, + 0x00000072u, 0x00000071u, 0x00000017u, 0x000500a7u, 0x0000000eu, 0x00000073u, 0x00000070u, 0x00000072u, + 0x0003003eu, 0x0000006eu, 0x00000073u, 0x0004003du, 0x0000000eu, 0x00000075u, 0x0000006eu, 0x000300f7u, + 0x00000077u, 0x00000000u, 0x000400fau, 0x00000075u, 0x00000076u, 0x00000077u, 0x000200f8u, 0x00000076u, + 0x0004003du, 0x00000006u, 0x00000078u, 0x00000050u, 0x0004003du, 0x00000006u, 0x00000079u, 0x0000003du, + 0x000500b0u, 0x0000000eu, 0x0000007au, 0x00000078u, 0x00000079u, 0x000200f9u, 0x00000077u, 0x000200f8u, + 0x00000077u, 0x000700f5u, 0x0000000eu, 0x0000007bu, 0x00000075u, 0x0000006au, 0x0000007au, 0x00000076u, + 0x0003003eu, 0x00000074u, 0x0000007bu, 0x0004003du, 0x0000000eu, 0x0000007cu, 0x00000074u, 0x000300f7u, + 0x0000007eu, 0x00000000u, 0x000400fau, 0x0000007cu, 0x0000007du, 0x0000007eu, 0x000200f8u, 0x0000007du, + 0x0004003du, 0x00000006u, 0x0000007fu, 0x00000050u, 0x000500c4u, 0x00000006u, 0x00000080u, 0x00000023u, + 0x0000007fu, 0x000700f1u, 0x00000006u, 0x00000081u, 0x00000015u, 0x00000023u, 0x0000000du, 0x00000080u, + 0x0004003du, 0x00000006u, 0x00000082u, 0x00000050u, 0x00050041u, 0x00000012u, 0x00000083u, 0x00000020u, + 0x00000082u, 0x0004003du, 0x00000006u, 0x00000084u, 0x00000008u, 0x00050080u, 0x00000006u, 0x00000085u, + 0x00000084u, 0x00000023u, 0x000700eau, 0x00000006u, 0x00000086u, 0x00000083u, 0x00000023u, 0x0000000du, + 0x00000085u, 0x0004003du, 0x00000006u, 0x00000087u, 0x0000005eu, 0x000500abu, 0x0000000eu, 0x00000088u, + 0x00000087u, 0x0000000du, 0x000300f7u, 0x0000008au, 0x00000000u, 0x000400fau, 0x00000088u, 0x00000089u, + 0x0000008au, 0x000200f8u, 0x00000089u, 0x0004003du, 0x00000006u, 0x0000008bu, 0x00000064u, 0x0004003du, + 0x00000006u, 0x0000008cu, 0x0000005eu, 0x00050082u, 0x00000006u, 0x0000008du, 0x0000008cu, 0x00000023u, + 0x000500aau, 0x0000000eu, 0x0000008eu, 0x0000008bu, 0x0000008du, 0x000200f9u, 0x0000008au, 0x000200f8u, + 0x0000008au, 0x000700f5u, 0x0000000eu, 0x0000008fu, 0x00000088u, 0x0000007du, 0x0000008eu, 0x00000089u, + 0x000300f7u, 0x00000091u, 0x00000000u, 0x000400fau, 0x0000008fu, 0x00000090u, 0x00000091u, 0x000200f8u, + 0x00000090u, 0x0004003du, 0x00000006u, 0x00000092u, 0x00000050u, 0x00050041u, 0x00000012u, 0x00000093u, + 0x0000001du, 0x00000092u, 0x000700eau, 0x00000006u, 0x00000094u, 0x00000093u, 0x00000023u, 0x0000000du, + 0x00000023u, 0x000200f9u, 0x00000091u, 0x000200f8u, 0x00000091u, 0x000200f9u, 0x0000007eu, 0x000200f8u, + 0x0000007eu, 0x000300e1u, 0x00000023u, 0x00000024u, 0x000400e0u, 0x00000025u, 0x00000025u, 0x00000024u, + 0x0004003du, 0x00000006u, 0x00000095u, 0x00000008u, 0x000500aau, 0x0000000eu, 0x00000096u, 0x00000095u, + 0x0000000du, 0x000300f7u, 0x00000098u, 0x00000000u, 0x000400fau, 0x00000096u, 0x00000097u, 0x00000098u, + 0x000200f8u, 0x00000097u, 0x0004003du, 0x00000006u, 0x0000009au, 0x00000015u, 0x00050041u, 0x0000003bu, + 0x0000009bu, 0x00000037u, 0x00000099u, 0x0003003eu, 0x0000009bu, 0x0000009au, 0x000200f9u, 0x00000098u, + 0x000200f8u, 0x00000098u, 0x0004003du, 0x00000006u, 0x0000009cu, 0x00000008u, 0x000500b0u, 0x0000000eu, + 0x0000009du, 0x0000009cu, 0x00000017u, 0x000300f7u, 0x0000009fu, 0x00000000u, 0x000400fau, 0x0000009du, + 0x0000009eu, 0x0000009fu, 0x000200f8u, 0x0000009eu, 0x0004003du, 0x00000006u, 0x000000a1u, 0x00000008u, + 0x0004003du, 0x00000006u, 0x000000a2u, 0x00000008u, 0x00050041u, 0x00000012u, 0x000000a3u, 0x0000001du, + 0x000000a2u, 0x0004003du, 0x00000006u, 0x000000a4u, 0x000000a3u, 0x00060041u, 0x0000003bu, 0x000000a5u, + 0x00000037u, 0x000000a0u, 0x000000a1u, 0x0003003eu, 0x000000a5u, 0x000000a4u, 0x0004003du, 0x00000006u, + 0x000000a7u, 0x00000008u, 0x0004003du, 0x00000006u, 0x000000a8u, 0x00000008u, 0x00050041u, 0x00000012u, + 0x000000a9u, 0x00000020u, 0x000000a8u, 0x0004003du, 0x00000006u, 0x000000aau, 0x000000a9u, 0x00060041u, + 0x0000003bu, 0x000000abu, 0x00000037u, 0x000000a6u, 0x000000a7u, 0x0003003eu, 0x000000abu, 0x000000aau, + 0x000200f9u, 0x0000009fu, 0x000200f8u, 0x0000009fu, 0x0004003du, 0x0000000eu, 0x000000adu, 0x0000006eu, + 0x000300f7u, 0x000000afu, 0x00000000u, 0x000400fau, 0x000000adu, 0x000000aeu, 0x000000afu, 0x000200f8u, + 0x000000aeu, 0x0004003du, 0x00000006u, 0x000000b0u, 0x00000014u, 0x000500aau, 0x0000000eu, 0x000000b1u, + 0x000000b0u, 0x0000000du, 0x000200f9u, 0x000000afu, 0x000200f8u, 0x000000afu, 0x000700f5u, 0x0000000eu, + 0x000000b2u, 0x000000adu, 0x0000009fu, 0x000000b1u, 0x000000aeu, 0x0003003eu, 0x000000acu, 0x000000b2u, + 0x0004003du, 0x0000000eu, 0x000000b3u, 0x000000acu, 0x000300f7u, 0x000000b5u, 0x00000000u, 0x000400fau, + 0x000000b3u, 0x000000b4u, 0x000000b5u, 0x000200f8u, 0x000000b4u, 0x0004003du, 0x00000006u, 0x000000b8u, + 0x0000000au, 0x00050080u, 0x00000006u, 0x000000b9u, 0x000000b8u, 0x00000023u, 0x00040070u, 0x0000002fu, + 0x000000bau, 0x000000b9u, 0x00050050u, 0x00000030u, 0x000000bcu, 0x000000bau, 0x000000bbu, 0x0003003eu, + 0x000000b7u, 0x000000bcu, 0x0004003du, 0x00000030u, 0x000000bdu, 0x000000b7u, 0x0006015eu, 0x00000030u, + 0x000000bfu, 0x000000beu, 0x00000001u, 0x000000bdu, 0x0003003eu, 0x000000b7u, 0x000000bfu, 0x0004003du, + 0x00000006u, 0x000000c0u, 0x00000064u, 0x0004003du, 0x00000006u, 0x000000c1u, 0x0000005eu, 0x00050082u, + 0x00000006u, 0x000000c2u, 0x000000c1u, 0x00000023u, 0x000500aau, 0x0000000eu, 0x000000c3u, 0x000000c0u, + 0x000000c2u, 0x000300f7u, 0x000000c5u, 0x00000000u, 0x000400fau, 0x000000c3u, 0x000000c4u, 0x000000c5u, + 0x000200f8u, 0x000000c4u, 0x0004003du, 0x00000006u, 0x000000c9u, 0x00000050u, 0x0004003du, 0x00000030u, + 0x000000cau, 0x000000b7u, 0x00050041u, 0x000000cbu, 0x000000ccu, 0x000000c8u, 0x000000c9u, 0x0003003eu, + 0x000000ccu, 0x000000cau, 0x000200f9u, 0x000000c5u, 0x000200f8u, 0x000000c5u, 0x000400e0u, 0x00000025u, + 0x00000025u, 0x00000024u, 0x0004003du, 0x00000006u, 0x000000cdu, 0x0000000au, 0x0004003du, 0x00000006u, + 0x000000ceu, 0x0000002au, 0x000500b0u, 0x0000000eu, 0x000000cfu, 0x000000cdu, 0x000000ceu, 0x000300f7u, + 0x000000d1u, 0x00000000u, 0x000400fau, 0x000000cfu, 0x000000d0u, 0x000000d1u, 0x000200f8u, 0x000000d0u, + 0x0004003du, 0x00000006u, 0x000000d3u, 0x0000000au, 0x0004003du, 0x00000006u, 0x000000d4u, 0x0000000au, + 0x00050041u, 0x000000cbu, 0x000000d5u, 0x000000c8u, 0x000000d4u, 0x0004003du, 0x00000030u, 0x000000d6u, + 0x000000d5u, 0x00060041u, 0x000000d7u, 0x000000d8u, 0x00000037u, 0x000000d2u, 0x000000d3u, 0x0003003eu, + 0x000000d8u, 0x000000d6u, 0x000200f9u, 0x000000d1u, 0x000200f8u, 0x000000d1u, 0x000400e0u, 0x00000025u, + 0x00000025u, 0x00000024u, 0x0004003du, 0x00000006u, 0x000000dau, 0x0000002au, 0x0006000cu, 0x00000038u, + 0x000000dbu, 0x00000001u, 0x0000004bu, 0x000000dau, 0x0004007cu, 0x00000006u, 0x000000dcu, 0x000000dbu, + 0x0003003eu, 0x000000d9u, 0x000000dcu, 0x0004003du, 0x00000006u, 0x000000ddu, 0x0000002au, 0x0004003du, + 0x00000006u, 0x000000deu, 0x000000d9u, 0x00050082u, 0x00000006u, 0x000000dfu, 0x000000deu, 0x00000023u, + 0x000500c4u, 0x00000006u, 0x000000e0u, 0x00000023u, 0x000000dfu, 0x00050082u, 0x00000006u, 0x000000e1u, + 0x000000ddu, 0x000000e0u, 0x000500acu, 0x0000000eu, 0x000000e2u, 0x000000e1u, 0x0000000du, 0x000600a9u, + 0x00000006u, 0x000000e3u, 0x000000e2u, 0x00000023u, 0x0000000du, 0x0004003du, 0x00000006u, 0x000000e4u, + 0x000000d9u, 0x00050080u, 0x00000006u, 0x000000e5u, 0x000000e4u, 0x000000e3u, 0x0003003eu, 0x000000d9u, + 0x000000e5u, 0x0004003du, 0x00000006u, 0x000000e6u, 0x0000000au, 0x000500aau, 0x0000000eu, 0x000000e7u, + 0x000000e6u, 0x0000000du, 0x000300f7u, 0x000000e9u, 0x00000000u, 0x000400fau, 0x000000e7u, 0x000000e8u, + 0x000000e9u, 0x000200f8u, 0x000000e8u, 0x0004003du, 0x00000006u, 0x000000ebu, 0x000000d9u, 0x00050041u, + 0x0000003bu, 0x000000ecu, 0x00000037u, 0x000000eau, 0x0003003eu, 0x000000ecu, 0x000000ebu, 0x000200f9u, + 0x000000e9u, 0x000200f8u, 0x000000e9u, 0x0003003eu, 0x000000edu, 0x0000000du, 0x000200f9u, 0x000000eeu, + 0x000200f8u, 0x000000eeu, 0x000400f6u, 0x000000f0u, 0x000000f1u, 0x00000000u, 0x000200f9u, 0x000000f2u, + 0x000200f8u, 0x000000f2u, 0x0004003du, 0x00000006u, 0x000000f3u, 0x000000edu, 0x0004003du, 0x00000006u, + 0x000000f4u, 0x000000d9u, 0x000500b0u, 0x0000000eu, 0x000000f5u, 0x000000f3u, 0x000000f4u, 0x000400fau, + 0x000000f5u, 0x000000efu, 0x000000f0u, 0x000200f8u, 0x000000efu, 0x0004003du, 0x00000006u, 0x000000f6u, + 0x00000050u, 0x0004003du, 0x00000006u, 0x000000f7u, 0x000000edu, 0x000500c4u, 0x00000006u, 0x000000f8u, + 0x00000023u, 0x000000f7u, 0x000500c7u, 0x00000006u, 0x000000f9u, 0x000000f6u, 0x000000f8u, 0x000500acu, + 0x0000000eu, 0x000000fau, 0x000000f9u, 0x0000000du, 0x000300f7u, 0x000000fcu, 0x00000000u, 0x000400fau, + 0x000000fau, 0x000000fbu, 0x000000fcu, 0x000200f8u, 0x000000fbu, 0x0004003du, 0x00000006u, 0x000000fdu, + 0x00000050u, 0x0004003du, 0x00000006u, 0x000000feu, 0x000000edu, 0x000500c2u, 0x00000006u, 0x000000ffu, + 0x000000fdu, 0x000000feu, 0x0004003du, 0x00000006u, 0x00000100u, 0x000000edu, 0x000500c4u, 0x00000006u, + 0x00000101u, 0x000000ffu, 0x00000100u, 0x00050082u, 0x00000006u, 0x00000102u, 0x00000101u, 0x00000023u, + 0x00050041u, 0x000000cbu, 0x00000103u, 0x000000c8u, 0x00000102u, 0x0004003du, 0x00000030u, 0x00000104u, + 0x00000103u, 0x0004003du, 0x00000030u, 0x00000105u, 0x000000b7u, 0x00050081u, 0x00000030u, 0x00000106u, + 0x00000105u, 0x00000104u, 0x0003003eu, 0x000000b7u, 0x00000106u, 0x0004003du, 0x00000006u, 0x00000107u, + 0x00000064u, 0x0004003du, 0x00000006u, 0x00000108u, 0x0000005eu, 0x00050082u, 0x00000006u, 0x00000109u, + 0x00000108u, 0x00000023u, 0x000500aau, 0x0000000eu, 0x0000010au, 0x00000107u, 0x00000109u, 0x000300f7u, + 0x0000010cu, 0x00000000u, 0x000400fau, 0x0000010au, 0x0000010bu, 0x0000010cu, 0x000200f8u, 0x0000010bu, + 0x0004003du, 0x00000006u, 0x0000010du, 0x00000050u, 0x0004003du, 0x00000030u, 0x0000010eu, 0x000000b7u, + 0x00050041u, 0x000000cbu, 0x0000010fu, 0x000000c8u, 0x0000010du, 0x0003003eu, 0x0000010fu, 0x0000010eu, + 0x000200f9u, 0x0000010cu, 0x000200f8u, 0x0000010cu, 0x000200f9u, 0x000000fcu, 0x000200f8u, 0x000000fcu, + 0x000400e0u, 0x00000025u, 0x00000025u, 0x00000024u, 0x0004003du, 0x00000006u, 0x00000110u, 0x0000000au, + 0x0004003du, 0x00000006u, 0x00000111u, 0x0000002au, 0x000500b0u, 0x0000000eu, 0x00000112u, 0x00000110u, + 0x00000111u, 0x000300f7u, 0x00000114u, 0x00000000u, 0x000400fau, 0x00000112u, 0x00000113u, 0x00000114u, + 0x000200f8u, 0x00000113u, 0x0004003du, 0x00000006u, 0x00000116u, 0x000000edu, 0x0004003du, 0x00000006u, + 0x00000117u, 0x0000000au, 0x0004003du, 0x00000006u, 0x00000118u, 0x0000000au, 0x00050041u, 0x000000cbu, + 0x00000119u, 0x000000c8u, 0x00000118u, 0x0004003du, 0x00000030u, 0x0000011au, 0x00000119u, 0x00070041u, + 0x000000d7u, 0x0000011bu, 0x00000037u, 0x00000115u, 0x00000116u, 0x00000117u, 0x0003003eu, 0x0000011bu, + 0x0000011au, 0x000200f9u, 0x00000114u, 0x000200f8u, 0x00000114u, 0x000400e0u, 0x00000025u, 0x00000025u, + 0x00000024u, 0x000200f9u, 0x000000f1u, 0x000200f8u, 0x000000f1u, 0x0004003du, 0x00000006u, 0x0000011cu, + 0x000000edu, 0x00050080u, 0x00000006u, 0x0000011eu, 0x0000011cu, 0x0000011du, 0x0003003eu, 0x000000edu, + 0x0000011eu, 0x000200f9u, 0x000000eeu, 0x000200f8u, 0x000000f0u, 0x0004003du, 0x00000006u, 0x0000011fu, + 0x0000000au, 0x000500aau, 0x0000000eu, 0x00000121u, 0x0000011fu, 0x00000120u, 0x000300f7u, 0x00000123u, + 0x00000000u, 0x000400fau, 0x00000121u, 0x00000122u, 0x00000123u, 0x000200f8u, 0x00000122u, 0x0004003du, + 0x00000030u, 0x00000125u, 0x000000b7u, 0x00050050u, 0x00000030u, 0x00000127u, 0x00000126u, 0x00000126u, + 0x00050088u, 0x00000030u, 0x00000128u, 0x00000125u, 0x00000127u, 0x00050041u, 0x000000cbu, 0x00000129u, + 0x000000c8u, 0x00000124u, 0x0003003eu, 0x00000129u, 0x00000128u, 0x000200f9u, 0x00000123u, 0x000200f8u, + 0x00000123u, 0x000400e0u, 0x00000025u, 0x00000025u, 0x00000024u, 0x0004003du, 0x00000006u, 0x0000012au, + 0x0000000au, 0x000500aau, 0x0000000eu, 0x0000012bu, 0x0000012au, 0x00000120u, 0x000300f7u, 0x0000012du, + 0x00000000u, 0x000400fau, 0x0000012bu, 0x0000012cu, 0x0000012du, 0x000200f8u, 0x0000012cu, 0x0004003du, + 0x00000006u, 0x0000012fu, 0x0000005eu, 0x0004003du, 0x00000006u, 0x00000130u, 0x0000002au, 0x0004003du, + 0x00000006u, 0x00000131u, 0x00000050u, 0x0004003du, 0x00000006u, 0x00000132u, 0x00000064u, 0x00070050u, + 0x0000002cu, 0x00000133u, 0x0000012fu, 0x00000130u, 0x00000131u, 0x00000132u, 0x00050041u, 0x00000134u, + 0x00000135u, 0x00000037u, 0x0000012eu, 0x0003003eu, 0x00000135u, 0x00000133u, 0x00050041u, 0x000000cbu, + 0x00000137u, 0x000000c8u, 0x00000124u, 0x0004003du, 0x00000030u, 0x00000138u, 0x00000137u, 0x00050041u, + 0x000000d7u, 0x00000139u, 0x00000037u, 0x00000136u, 0x0003003eu, 0x00000139u, 0x00000138u, 0x000200f9u, + 0x0000012du, 0x000200f8u, 0x0000012du, 0x000200f9u, 0x000000b5u, 0x000200f8u, 0x000000b5u, 0x000400e0u, + 0x00000025u, 0x00000025u, 0x00000024u, 0x0004003du, 0x00000006u, 0x0000013au, 0x00000008u, 0x000500aau, + 0x0000000eu, 0x0000013bu, 0x0000013au, 0x0000000du, 0x000300f7u, 0x0000013du, 0x00000000u, 0x000400fau, + 0x0000013bu, 0x0000013cu, 0x0000013du, 0x000200f8u, 0x0000013cu, 0x0004003du, 0x00000006u, 0x0000013eu, + 0x00000014u, 0x00050041u, 0x0000003bu, 0x0000013fu, 0x00000037u, 0x0000011du, 0x0003003eu, 0x0000013fu, + 0x0000013eu, 0x00050041u, 0x0000003bu, 0x00000141u, 0x00000037u, 0x00000124u, 0x0003003eu, 0x00000141u, + 0x00000140u, 0x000200f9u, 0x0000013du, 0x000200f8u, 0x0000013du, 0x000100fdu, 0x00010038u, }; inline constexpr std::size_t kDriverPostIterationRPWitnessSpvWordCount = sizeof(kDriverPostIterationRPWitnessSpv) / sizeof(kDriverPostIterationRPWitnessSpv[0]); diff --git a/MobileGL/MG_Util/SelfTest/PrimitivesGeneratedNoXfbProbeSpv.h b/MobileGL/MG_Util/SelfTest/PrimitivesGeneratedNoXfbProbeSpv.h index b3a2b522..a5cb1500 100644 --- a/MobileGL/MG_Util/SelfTest/PrimitivesGeneratedNoXfbProbeSpv.h +++ b/MobileGL/MG_Util/SelfTest/PrimitivesGeneratedNoXfbProbeSpv.h @@ -6,13 +6,15 @@ // SPDX-License-Identifier: LGPL-3.0-only // End of Source File Header -// Generated from PrimitivesGeneratedNoXfbProbe.{vert,tesc,tese} with: -// glslangValidator --target-env vulkan1.1 -V PrimitivesGeneratedNoXfbProbe. -// (SPIR-V words dumped little-endian, six per line.) +// Generated from PrimitivesGeneratedNoXfbProbe.{vert,tesc,tese} (Vulkan 1.1 / SPIR-V 1.3) by +// scripts/bake_internal_shaders.py, which runs MG_Test's BakedInternalShadersTest with the +// IN-TREE glslang and splices the words it emits into the arrays below. Do not edit the words +// by hand, and do not rely on remembering to regenerate: MOBILEGL_BAKED_INTERNAL_SHADERS (the +// same test, run as a gate) recompiles all three sources and reds on the first word that +// disagrees. Measured when that gate landed: these three arrays were already fresh. // -// Regenerate whenever a probe shader changes; nothing else in the probe depends on -// the exact binary. None of the modules carries an Xfb execution mode - that is the -// probe's subject, see PrimitivesGeneratedNoXfbProbe.cpp. +// None of the modules carries an Xfb execution mode - that is the probe's subject, see +// PrimitivesGeneratedNoXfbProbe.cpp. #pragma once #include diff --git a/docs/Disaggregated/notes/p7/magma-b.md b/docs/Disaggregated/notes/p7/magma-b.md index e9dd401a..2ac517c4 100644 --- a/docs/Disaggregated/notes/p7/magma-b.md +++ b/docs/Disaggregated/notes/p7/magma-b.md @@ -18,6 +18,7 @@ | 片 | 名 | 退役 / decline | 提交 | |---|---|---|---| | 1 | `mipmap-shader-format-or-shape` | 诊断 + 颜色 shader mip 回落(**退役**一半:可渲染格式的不可附着图像) | 见 §1 | +| 2 | `depth-stencil-mipmap@P7` + 烘焙 (A)(D) | DEPTH-only **退役**(烘焙深度 mip 程序);D\|S **decline** | 见 §2 | --- @@ -102,3 +103,103 @@ MGPipe: Fatal{UnmigratedVerb, "Magma:mipmap-shader-format-or-shape format=37 ext ### 1.5 门 `integration-magma-split` **75**(+2)、`-spawn` **54**(+2)、`-tcp` **56**(+2),三臂 0 红。 + +--- + +## 2. 片 2:深度 mip 烘焙 (A) + 新鲜度门 (D) + +### 2.1 为什么 monolith 的回落不能直接用 + +monolith 臂从拆分之前就带着一条深度 mip 的 shader 回落(`GenerateDepthMipmapWithShader`),因为 +`VK_FORMAT_FEATURE_BLIT_DST` 对深度格式**是可选的**,相当多的设备不给。wire 臂没有这条回落:深度纹理 +一旦不能原生 blit,第一个析取项 `resource->aspect != COLOR` 就把它送进 +`mipmap-shader-format-or-shape`(片 1 的诊断现在会把它指名为 `aspect=0x2 missing=COLOR_ATTACHMENT`)。 + +monolith 那条回落**不可复用**,这正是 CONTRACT-P7 §5.1 (A) 的全部内容:它要造一个 GL `ProgramObject`、 +两个 `ShaderObject` 和一个 GL sampler,经 uniform manager 的 global UBO 绑定,把 `uSrcTexelSize` 当默认块 +uniform 写——四个前端对象加一次 GL 编译器调用,落在 server 的 apply 线程上。 + +### 2.2 烘焙 (A) + +- `WireDepthMipmap.vert`:几何与 `WireColorBlit.vert` **逐字相同**(同一个 quad、同一个 push-constant + rect、同一个 GL-bottom-left → native-image 映射),只多一个成员 `ivec2 srcTexelSize`。push-constant 块 + 必须在同一管线的每个 stage 里声明一致,所以顶点阶段也声明它,而不是拆出第二个 range。 +- `WireDepthMipmap.frag`:`kDepthMipmapFragmentShaderSource` 的**移植**而非重写——同样的 2×2 + `texelFetch` 盒、同样的 clamp、同样的平均、同样的 `gl_FragDepth`。§3.2 的「退役」= 与 monolith 臂同 + 可观测,所以连它那半个 texel 的偏移(`ivec2(texCoord * srcTexelSize)` 落在目标中心偏半 texel)也照搬。 +- `WireDepthMipmap.inc`:深度附件 render pass(`loadOp = DONT_CARE`,pass 写满整级)+ 管线 + (`depthTest/Write = TRUE`、`compareOp = ALWAYS`、无颜色附件)+ 按格式/布局缓存的 target 表,形状与 + `WireColorBlit.inc` 逐条对齐(同样的懒建模块、同样的每提交描述符池、同 image 时两侧都 GENERAL)。 + sampler 用 NEAREST:frag 自己做 2×2 盒,任何采样器过滤都会被算两遍,而且多数设备不给深度格式 + linear。 +- `GenerateWireMipmap` 深度分支接入;`WireDepthMipmapSpirv.h` 由下面的门自己烘。 + +### 2.3 `depth-stencil-mipmap@P7` 拆成退役 + decline + +DEPTH-only 走上面的烘焙程序(**退役**)。D|S 合并与 stencil-only 改为 **decline**(`MGLOG_E_ONCE` + +return,§3.2),并且**在 `GrowWireTextureMipChain` 之前**就 return——否则会先长出一条这个动词不会填的 +链,随后又把它记成 defined。 + +decline 而非 Fatal 的理由:**monolith 也做不到**。它的 shader 回落自己断言 depth-only +("shader fallback only supports depth-only textures"),而断言在两条臂都发的 release 构建里等于零。GL +也没承诺 stencil mip;这条 pass 写 `gl_FragDepth`,会把它刚产出的那一级的 stencil aspect 留成未定义—— +比整级不动还糟。 + +### 2.4 新鲜度门 (D):`MOBILEGL_BAKED_INTERNAL_SHADERS` + +`MG_Test/SelfTest/BakedInternalShadersTest`,表驱动,8 行(`WireColorBlit.{vert,frag}`、 +`WireDepthMipmap.{vert,frag}`、`DriverPostIterationRPWitness.comp`、 +`PrimitivesGeneratedNoXfbProbe.{vert,tesc,tese}`),用**树内 glslang** 重编每一行并逐字比较。 + +**这个门就是烘焙器。** `scripts/bake_internal_shaders.py` 自己不编译任何东西:它用 +`MOBILEGL_BAKE_INTERNAL_SHADERS` 跑这个二进制,拿它吐出的字,splice 进头文件。方向是刻意的——门走 +glslang **库**、烘焙器 shell 出一个 glslangValidator **二进制**,就是两个编译器、没有哪个说了算;等它们 +的默认值哪天分叉,门会在一个没人能重新生成的头上变红。格式**按头保留**而不是统一:树里的头对每行几个字 +和 `u` 后缀意见不一,统一会把一个字的改动埋进整文件 diff。 + +**首轮实测(这是事先没人能知道的一件事):** + +| 头 | 结论 | +|---|---| +| `WireColorBlitSpirv.h` | **已新鲜**——手跑 glslangValidator 的字与树内 glslang 逐字相同 | +| `PrimitivesGeneratedNoXfbProbeSpv.h` | **已新鲜**(三个数组全部) | +| `DriverPostIterationRPWitnessSpv.h` | **过期**,按其自己的散文所说;本片重烘(2142 → 2143 字),witness magic `0x50323033` 仍在(它是 `.comp` 里的常量,不是字的属性) | + +三处 provenance 注释改为指向脚本与门。 + +### 2.5 场景 + +- `F1WireScenario.GenerateMipmapDepthWithoutNativeBlitPixels`(旋钮 tier 1,三臂): + `GL_DEPTH_COMPONENT24`(→ `X8_D24_UNORM_PACK32`)与 `GL_DEPTH_COMPONENT32F`(→ `D32_SFLOAT`)各一遍, + 16×16、上下两半 0.75 / 0.25,`glGenerateMipmap` 后按**采样附件**读回 level 1 的 8 行。**第 3 行不断言**: + 移植来的滤波器带半个 texel 偏移,两种读法下第 0..2 行只来自下半、第 4..7 行只来自上半,第 3 行是跨界 + 带——钉它等于钉那半个 texel 而不是钉 mip。 +- `F1WireScenario.GenerateMipmapDepthStencilDeclinesAndKeepsTheSession`(**无旋钮**,三臂): + `GL_DEPTH24_STENCIL8` 上 `glGenerateMipmap` 不报 GL 错、不带走会话(回读是一次穿到 server 的往返, + 一并回答会话还活着与 level 0 还在)。 + +### 2.6 red-once(R-16,三条,均已执行并还原) + +1. **新鲜度门**:把 `WireDepthMipmap.frag` 的 `0.25` 改成 `0.2500001`(**不需要重建**——门在运行时读源), + `WireDepthMipmapFragment` 行在**第 279 个字**变红并指名重新生成的办法;还原后 8/8 绿。 +2. **深度退役**:关掉深度分支 → `DirectVulkan.Split.DepthMip.*` 死于 + `Fatal{UnmigratedVerb, "Magma:mipmap-shader-format-or-shape format=125 extent=16x16x1 levels=5 + aspect=0x2 view=1 usage=0x27 layers=0+1/1 missing=COLOR_ATTACHMENT"}`(格式 125 = + `X8_D24_UNORM_PACK32`,诊断把它指名了)。 +3. **D|S decline**:把 `MagmaWireFatal` 放回去 → 该用例死于 + `Fatal{UnmigratedVerb, "Magma:depth-stencil-mipmap@P7"}`。 + +还原后两条 2/2 绿。 + +### 2.7 门 + +`integration-magma-split` **77**(+2)、`-spawn` **56**(+2)、`-tcp` **58**(+2),三臂 0 红; +`ctest -L unit` **2410**(+8 = 新鲜度门的八行)全绿;信息档 `integration-magma-full-split` **513**, +0 红 / 59 skip(基线 510,0 红 / 57 skip——两条新增用例在无旋钮的全量档自 skip)。 + +**G1 实测过了,而且这一条事先不是显然的**:`DriverPostIterationRPWitnessSpv.h` 是 +`MG_Util/SelfTest` 的头,pull 构建也编它,重烘改了 2142 → 2143 个字。pull `.text` 仍是 **`0xa52203`**, +`nm --defined-only` 的名字表与 `~/w7/p7-before/pull-syms.txt` 逐行相同(30570 行)——字长的变化落在 +`.rodata`,没有改动任何一条指令的编码宽度。`fatal_census` 绿(79 abort 站点 / 43 家族词 / 0 无标记, +本片**退掉一处** `Fatal{}` 用法但不动 abort 站点数);`link_ratchet --assert-monotone` 186 不变, +`p7-magma` 桶 101。 diff --git a/scripts/bake_internal_shaders.py b/scripts/bake_internal_shaders.py new file mode 100644 index 00000000..77474037 --- /dev/null +++ b/scripts/bake_internal_shaders.py @@ -0,0 +1,184 @@ +#!/usr/bin/env python3 +# MobileGL - scripts/bake_internal_shaders.py +# Copyright (c) 2026 MobileGL-Dev +# Licensed under the GNU Lesser General Public License v3.0: +# https://www.gnu.org/licenses/gpl-3.0.txt +# https://www.gnu.org/licenses/lgpl-3.0.txt +# SPDX-License-Identifier: LGPL-3.0-only +# End of Source File Header +"""Regenerate the tree's baked SPIR-V headers from their committed GLSL. + + python3 scripts/bake_internal_shaders.py --build-dir build-split + python3 scripts/bake_internal_shaders.py --build-dir build-split --check + +THIS SCRIPT DOES NOT COMPILE ANYTHING. It runs MG_Test's BakedInternalShadersTest with +MOBILEGL_BAKE_INTERNAL_SHADERS set, takes the words that binary emits, and splices them into +the headers. The compiler is therefore the IN-TREE glslang the gate itself uses - one compiler +and one answer. + +The alternative, which is what the four headers in the tree were made with before this script +existed, is a glslangValidator somebody happened to have on their PATH. That is how the +iterationRP witness ended up carrying words its own source no longer produces: nothing named +the compiler, nothing re-ran it, and the header's prose was the only record that it was stale. +A gate whose baker is a different program from the gate has the same failure one level up - the +day the CLI's defaults and the library's diverge, the gate reds on a header nobody can +regenerate, and the fix is to edit the words by hand. + +FORMATTING IS PRESERVED PER HEADER, not normalised: the headers in the tree disagree about +words per line and about the `u` suffix, and reformatting one of them would bury a one-word +change in a whole-file diff. The style is read back out of the array being replaced. + +--check exits non-zero and names the headers that would change, without writing. It is the +same verdict the ctest entry gives; this mode exists so the regeneration path can be checked +from a shell without reading gtest output. +""" + +import argparse +import os +import re +import shutil +import subprocess +import sys +import tempfile +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +TEST_TARGET = "BakedInternalShadersTest" +BAKE_ENV = "MOBILEGL_BAKE_INTERNAL_SHADERS" + +# ` 0x07230203u, 0x00010000u, ...` - the body of one baked array. +WORD_RE = re.compile(r"0x([0-9a-fA-F]{1,8})u?") + + +def find_test_binary(build_dir: Path) -> Path: + """The test executable, wherever this generator put it.""" + candidates = sorted(build_dir.rglob(TEST_TARGET)) + candidates = [c for c in candidates if c.is_file() and os.access(c, os.X_OK)] + if not candidates: + raise SystemExit( + f"{TEST_TARGET} not found under {build_dir}. Build it first:\n" + f" cmake --build {build_dir} --target {TEST_TARGET}" + ) + return candidates[0] + + +def emit_words(binary: Path, out_dir: Path) -> dict: + """Run the gate in bake mode; return {symbol: (header, [words])}.""" + env = dict(os.environ) + env[BAKE_ENV] = str(out_dir) + result = subprocess.run([str(binary)], env=env, capture_output=True, text=True) + if result.returncode != 0: + sys.stderr.write(result.stdout) + sys.stderr.write(result.stderr) + raise SystemExit(f"{TEST_TARGET} failed in bake mode (rc={result.returncode})") + baked = {} + for path in sorted(out_dir.glob("*.words")): + lines = path.read_text().splitlines() + if not lines: + raise SystemExit(f"{path} is empty") + header = lines[0].strip() + words = [int(line, 16) for line in lines[1:] if line.strip()] + baked[path.stem] = (header, words) + if not baked: + raise SystemExit(f"{TEST_TARGET} emitted nothing into {out_dir}") + return baked + + +def locate_array(text: str, symbol: str): + """(body_start, body_end, style) for `symbol`'s array body.""" + opener = re.search(re.escape(symbol) + r"\s*\[\s*\]\s*=\s*\{", text) + if not opener: + raise SystemExit(f"cannot find the array `{symbol}` to replace") + body_start = opener.end() + closer = text.find("};", body_start) + if closer < 0: + raise SystemExit("the array `" + symbol + "` has no closing `};`") + body = text[body_start:closer] + rows = [row for row in body.split("\n") if WORD_RE.search(row)] + if not rows: + raise SystemExit(f"the array `{symbol}` holds no words to learn its style from") + style = { + "indent": re.match(r"[ \t]*", rows[0]).group(0), + # The LONGEST row, not the first: a one-row array and a ragged last row would both + # teach the wrong width. + "per_line": max(len(WORD_RE.findall(row)) for row in rows), + "suffix": "u" if re.search(r"0x[0-9a-fA-F]+u", body) else "", + # The whitespace the closing brace sits behind. It is part of the SPAN being + # replaced, so a body written without it silently outdents every array it + # touches - a diff on three headers that changed no word at all. + "close_indent": re.search(r"[ \t]*$", text[:closer]).group(0), + } + return body_start, closer, style + + +def format_body(words, style) -> str: + per_line = style["per_line"] + out = ["\n"] + for at in range(0, len(words), per_line): + chunk = words[at:at + per_line] + out.append(style["indent"] + + ", ".join(f"0x{word:08x}{style['suffix']}" for word in chunk) + ",\n") + out.append(style["close_indent"]) + return "".join(out) + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter) + parser.add_argument("--build-dir", default="build-split", + help="a configured build tree that has BakedInternalShadersTest") + parser.add_argument("--build", action="store_true", + help="build the test target first") + parser.add_argument("--check", action="store_true", + help="report headers that would change and exit non-zero; write nothing") + args = parser.parse_args() + + build_dir = Path(args.build_dir) + if not build_dir.is_absolute(): + build_dir = ROOT / build_dir + if args.build: + subprocess.run(["cmake", "--build", str(build_dir), "--target", TEST_TARGET], + check=True) + + binary = find_test_binary(build_dir) + scratch = Path(tempfile.mkdtemp(prefix="mgl-bake-")) + try: + baked = emit_words(binary, scratch) + finally: + shutil.rmtree(scratch, ignore_errors=True) + + # One read/write per HEADER, not per symbol: two arrays share + # WireColorBlitSpirv.h and three share PrimitivesGeneratedNoXfbProbeSpv.h, and + # rewriting the file once per array would splice into a stale copy. + by_header = {} + for symbol, (header, words) in baked.items(): + by_header.setdefault(header, []).append((symbol, words)) + + changed = [] + for header, entries in sorted(by_header.items()): + path = ROOT / header + if not path.exists(): + raise SystemExit(f"{header} does not exist") + original = path.read_text() + text = original + for symbol, words in sorted(entries): + start, end, style = locate_array(text, symbol) + text = text[:start] + format_body(words, style) + text[end:] + if text == original: + print(f"bake: {header} is already fresh ({len(entries)} array(s))") + continue + changed.append(header) + if args.check: + print(f"bake: {header} WOULD CHANGE ({', '.join(s for s, _ in sorted(entries))})") + else: + path.write_text(text) + print(f"bake: {header} rewritten ({', '.join(s for s, _ in sorted(entries))})") + + if args.check and changed: + print(f"bake: {len(changed)} header(s) are stale against their sources", file=sys.stderr) + return 1 + return 0 + + +if __name__ == "__main__": + sys.exit(main())