From aaecb404754aaed6da0877ff8182b5110fbe5149 Mon Sep 17 00:00:00 2001 From: Utkarsh Dalal Date: Sat, 15 Aug 2026 10:18:48 +0530 Subject: [PATCH 1/3] Android: single-device framegen, pacing overhaul, gen8 mipmaps replacement Reworks the Android path on top of 87f1b93: - Single-device framegen: the graph adopts the game's VkInstance/VkDevice (initializeExternal/createContextFromImages) instead of a private device sharing AHBs across ICDs, which scrambled memory when the game ran on a different driver than the system one. Barriers route through a sync1 compat path; required features are merged into the game's device create. - Real fence sync on the input copy (Mini::Fence) replacing a no-op submit; repeated timeouts fall back to passthrough presents. - Base fps limiter (fps_limit conf key) phase-locked to the display vsync grid published by the app (vsync.txt); generated presents spaced at baseInterval/multiplier with late-slot skip; pause/resume no longer poisons the pacing EWMA (deltas over 250 ms reset it). - Clean-room mipmaps replacement shader (shaders/mipmaps_replacement.comp, embedded at build time), default on. Mesa's experimental Adreno gen8 backend miscompiles the DLL's translated mipmaps pass (storage writes stop landing after startup, freezing the flow chain); the replacement computes the same luma pyramid without the fragile single-dispatch TGSM design. orig_mipmaps=true restores the DLL translation; a raw .spv next to conf.toml overrides either. - Diagnostics: stats.txt (measured fps + per-stage timing), debug_dump (frame/flow-chain pixel dumps), debug_inputs passthrough modes, adopted- device capability log. - Config: hot-apply for fps_limit and debug keys without swapchain recreation; experimental_present_mode conf key (fifo/mailbox/immediate). --- .gitignore | 3 +- framegen/include/core/device.hpp | 15 + framegen/include/core/image.hpp | 17 + framegen/include/core/instance.hpp | 10 + framegen/public/lsfg_3_1.hpp | 44 + framegen/public/lsfg_3_1p.hpp | 36 + framegen/src/common/utils.cpp | 23 + framegen/src/core/device.cpp | 58 +- framegen/src/core/image.cpp | 40 + framegen/src/core/instance.cpp | 11 + framegen/v3.1_include/v3_1/context.hpp | 5 + framegen/v3.1_src/context.cpp | 45 + framegen/v3.1_src/lsfg.cpp | 42 + framegen/v3.1p_include/v3_1p/context.hpp | 11 + framegen/v3.1p_src/context.cpp | 55 ++ framegen/v3.1p_src/lsfg.cpp | 51 ++ include/config/config.hpp | 15 + include/context.hpp | 51 ++ include/layer.hpp | 83 ++ include/mini/commandbuffer.hpp | 4 +- include/mini/fence.hpp | 58 ++ include/mini/image.hpp | 11 + shaders/mipmaps_replacement.comp | 54 ++ src/config/config.cpp | 7 + src/context.cpp | 801 +++++++++++++++-- src/extract/mipmaps_replacement_spv.h | 1028 ++++++++++++++++++++++ src/hooks.cpp | 104 ++- src/layer.cpp | 136 +++ src/mini/commandbuffer.cpp | 5 +- src/mini/fence.cpp | 38 + src/mini/image.cpp | 79 +- 31 files changed, 2851 insertions(+), 89 deletions(-) create mode 100644 include/mini/fence.hpp create mode 100644 shaders/mipmaps_replacement.comp create mode 100644 src/extract/mipmaps_replacement_spv.h create mode 100644 src/mini/fence.cpp diff --git a/.gitignore b/.gitignore index f55198b1..f058853a 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,5 @@ /.cache /.ccls -build-android/ \ No newline at end of file +build-android/ +/build-android-* diff --git a/framegen/include/core/device.hpp b/framegen/include/core/device.hpp index a1f8992d..65025aaa 100644 --- a/framegen/include/core/device.hpp +++ b/framegen/include/core/device.hpp @@ -28,6 +28,21 @@ namespace LSFG::Core { /// Device(const Instance& instance, uint64_t deviceUUID); + /// + /// Adopt an externally owned device. The caller keeps ownership of + /// every handle; the instance must have been adopted with a custom + /// loader entry point first. Barriers use the sync1 compat path since + /// the caller's device features are unknown. + /// + /// @param instance Adopted instance (loader already initialized). + /// @param physical Physical device the external device was created on. + /// @param external Device owned by the caller. + /// @param queueFamilyIdx Queue family of the queue below. + /// @param queue Queue all framegen work is submitted to. + /// + Device(const Instance& instance, VkPhysicalDevice physical, + VkDevice external, uint32_t queueFamilyIdx, VkQueue queue); + /// Get the Vulkan handle. [[nodiscard]] auto handle() const { return *this->device; } /// Get the physical device associated with this logical device. diff --git a/framegen/include/core/image.hpp b/framegen/include/core/image.hpp index f8ae7b6e..cbe91510 100644 --- a/framegen/include/core/image.hpp +++ b/framegen/include/core/image.hpp @@ -77,6 +77,23 @@ namespace LSFG::Core { AHardwareBuffer* ahb); #endif + /// + /// Wrap a caller-owned VkImage living on the same device (single-device + /// mode). No memory is allocated or imported; only a view is created. + /// The caller must keep the image alive and hand it over in GENERAL + /// layout between presents. + /// + /// @param device Vulkan device the image belongs to. + /// @param externalImage Caller-owned image handle. + /// @param extent Extent of the image in pixels. + /// @param format Vulkan format of the image + /// @param aspectFlags Aspect flags for the image view + /// + /// @throws LSFG::vulkan_error if view creation fails. + /// + Image(const Core::Device& device, VkImage externalImage, + VkExtent2D extent, VkFormat format, VkImageAspectFlags aspectFlags); + /// Get the Vulkan handle. [[nodiscard]] auto handle() const { return *this->image; } /// Get the Vulkan device memory handle. diff --git a/framegen/include/core/instance.hpp b/framegen/include/core/instance.hpp index 24cba8a2..6aba27da 100644 --- a/framegen/include/core/instance.hpp +++ b/framegen/include/core/instance.hpp @@ -20,6 +20,16 @@ namespace LSFG::Core { /// Instance(); + /// + /// Adopt an externally owned instance. Routes all Vulkan calls through + /// the given loader entry point (e.g. a layer's next-in-chain + /// vkGetInstanceProcAddr) and never destroys the instance. + /// + /// @param gipa Loader entry point to resolve Vulkan functions with. + /// @param external Instance owned by the caller. + /// + Instance(PFN_vkGetInstanceProcAddr gipa, VkInstance external); + /// Get the Vulkan handle. [[nodiscard]] auto handle() const { return this->instance ? *this->instance : VK_NULL_HANDLE; } diff --git a/framegen/public/lsfg_3_1.hpp b/framegen/public/lsfg_3_1.hpp index 09777a7b..df9b5c96 100644 --- a/framegen/public/lsfg_3_1.hpp +++ b/framegen/public/lsfg_3_1.hpp @@ -29,6 +29,33 @@ namespace LSFG_3_1 { bool isHdr, float flowScale, uint64_t generationCount, const std::function(const std::string&)>& loader); + /// + /// Initialize the LSFG library on an externally owned device (single-device + /// mode). All framegen work runs on the caller's device and queue, routed + /// through the given loader entry point; no second VkInstance/VkDevice is + /// created. The caller must ensure the device was created with the features + /// the shader chain needs (storage-image formats, read/write without + /// format, and shaderFloat16 or vulkanMemoryModel depending on the shader + /// path). + /// + /// @param gipa Loader entry point (e.g. a layer's next vkGetInstanceProcAddr). + /// @param externalInstance Caller-owned instance. + /// @param physicalDevice Physical device of the external device. + /// @param externalDevice Caller-owned device. + /// @param queueFamilyIdx Queue family of the queue below. + /// @param queue Queue all framegen work is submitted to. + /// @param isHdr Whether the images are in HDR format. + /// @param flowScale Internal flow scale factor. + /// @param generationCount Number of frames to generate. + /// @param loader Function to load shader source code by name. + /// + __attribute__((visibility("default"))) + void initializeExternal(PFN_vkGetInstanceProcAddr gipa, + VkInstance externalInstance, VkPhysicalDevice physicalDevice, + VkDevice externalDevice, uint32_t queueFamilyIdx, VkQueue queue, + bool isHdr, float flowScale, uint64_t generationCount, + const std::function(const std::string&)>& loader); + /// /// Create a new LSFG context on a swapchain. /// @@ -68,6 +95,23 @@ namespace LSFG_3_1 { VkExtent2D extent, VkFormat format); #endif + /// + /// Single-device variant: wrap caller-owned VkImages living on the adopted + /// device. Only valid after initializeExternal. The caller keeps ownership + /// of the images and must hand them over in GENERAL layout. + /// + /// @param in0 First input image. + /// @param in1 Second input image. + /// @param outN Output images, one per generated frame. + /// @param extent Image dimensions. + /// @param format Vulkan format of all images. + /// @return Unique context identifier. + /// + __attribute__((visibility("default"))) + int32_t createContextFromImages( + VkImage in0, VkImage in1, const std::vector& outN, + VkExtent2D extent, VkFormat format); + /// /// Present a context. /// diff --git a/framegen/public/lsfg_3_1p.hpp b/framegen/public/lsfg_3_1p.hpp index 7b0ba752..2ddac5c3 100644 --- a/framegen/public/lsfg_3_1p.hpp +++ b/framegen/public/lsfg_3_1p.hpp @@ -29,6 +29,18 @@ namespace LSFG_3_1P { bool isHdr, float flowScale, uint64_t generationCount, const std::function(const std::string&)>& loader); + /// + /// Initialize the LSFG library on an externally owned device (single-device + /// mode). All framegen work runs on the caller's device and queue; no + /// second VkInstance/VkDevice is created. See LSFG_3_1::initializeExternal. + /// + __attribute__((visibility("default"))) + void initializeExternal(PFN_vkGetInstanceProcAddr gipa, + VkInstance externalInstance, VkPhysicalDevice physicalDevice, + VkDevice externalDevice, uint32_t queueFamilyIdx, VkQueue queue, + bool isHdr, float flowScale, uint64_t generationCount, + const std::function(const std::string&)>& loader); + /// /// Create a new LSFG context on a swapchain. /// @@ -55,6 +67,13 @@ namespace LSFG_3_1P { VkExtent2D extent, VkFormat format); #endif + /// Single-device variant: wrap caller-owned VkImages living on the adopted + /// device. Only valid after initializeExternal. + __attribute__((visibility("default"))) + int32_t createContextFromImages( + VkImage in0, VkImage in1, const std::vector& outN, + VkExtent2D extent, VkFormat format); + /// /// Present a context. /// @@ -85,6 +104,23 @@ namespace LSFG_3_1P { /// Block until framegen's internal Vulkan device is idle. See LSFG_3_1::waitIdle. __attribute__((visibility("default"))) void waitIdle(); + + /// Debug handle to an internal image of the framegen graph. + struct DebugImage { + const char* name; + VkImage image; + VkExtent2D extent; + VkFormat format; + }; + + /// + /// Get handles to key internal images (flow chain) for pixel dumping. + /// The images live on the graph's device in GENERAL layout. + /// + /// @param id Unique identifier of the context. + /// + __attribute__((visibility("default"))) + std::vector debugInternalImages(int32_t id); #endif } diff --git a/framegen/src/common/utils.cpp b/framegen/src/common/utils.cpp index 6436ae40..78273fe0 100644 --- a/framegen/src/common/utils.cpp +++ b/framegen/src/common/utils.cpp @@ -321,6 +321,29 @@ void Utils::clearImage(const Core::Device& device, Core::Image& image, bool whit &clearColor, 1, &subresourceRange); + const VkImageMemoryBarrier2 toGeneral{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, + .srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, + .dstStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + .dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT, + .oldLayout = image.getLayout(), + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .image = image.handle(), + .subresourceRange = { + .aspectMask = image.getAspectFlags(), + .levelCount = 1, + .layerCount = 1 + } + }; + const VkDependencyInfo toGeneralDep = { + .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, + .imageMemoryBarrierCount = 1, + .pImageMemoryBarriers = &toGeneral + }; + image.setLayout(VK_IMAGE_LAYOUT_GENERAL); + Utils::cmdPipelineBarrier2(cmdBuf.handle(), &toGeneralDep); + cmdBuf.end(); cmdBuf.submit(device.getComputeQueue(), fence); diff --git a/framegen/src/core/device.cpp b/framegen/src/core/device.cpp index bf3d6b08..7e4ac319 100644 --- a/framegen/src/core/device.cpp +++ b/framegen/src/core/device.cpp @@ -5,6 +5,7 @@ #include "core/image.hpp" #include "core/instance.hpp" #include "common/exception.hpp" +#include "common/utils.hpp" #include #include @@ -534,6 +535,61 @@ Device::Device(const Instance& instance, uint64_t deviceUUID) { if (!this->nullDescriptorSupported) { this->fallbackDescriptorImage = std::make_shared(*this, VkExtent2D{1, 1}, VK_FORMAT_R8G8B8A8_UNORM, - VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT); + VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT + | VK_IMAGE_USAGE_TRANSFER_DST_BIT); + // The fallback substitutes for nullDescriptor bindings, whose reads + // must return zeros. Its memory is undefined at creation — some + // drivers hand back zero pages, others garbage. + Utils::clearImage(*this, *this->fallbackDescriptorImage); } } + +Device::Device(const Instance& instance, VkPhysicalDevice physical, + VkDevice external, uint32_t queueFamilyIdx, VkQueue queue) { + (void)instance; // loader entry points already installed by the adopted Instance + LSFG_FRAMEGEN_LOGI("Entering Device::Device (adopted) — %s", kFramegenBuildStamp); + + volkLoadDevice(external); + + // The host application owns this device and picked its features; sync2 + // entry points can resolve even when the feature was never enabled, and + // calling them would be invalid. Force every barrier through the sync1 + // compat path, which is unconditionally legal. + vkCmdPipelineBarrier2 = nullptr; + LSFG_FRAMEGEN_LOGI( + "Adopted external device (queueFamily=%u); barriers via sync1 compat path", + queueFamilyIdx); + + if (vkGetPhysicalDeviceFeatures != nullptr && vkGetPhysicalDeviceFormatProperties != nullptr) { + VkPhysicalDeviceFeatures probed{}; + vkGetPhysicalDeviceFeatures(physical, &probed); + VkFormatProperties r8{}; + vkGetPhysicalDeviceFormatProperties(physical, VK_FORMAT_R8_UNORM, &r8); + VkFormatProperties rgba16f{}; + vkGetPhysicalDeviceFormatProperties(physical, VK_FORMAT_R16G16B16A16_SFLOAT, &rgba16f); + LSFG_FRAMEGEN_LOGI( + "Adopted-device caps: extFormats=%d readWoFmt=%d writeWoFmt=%d " + "r8unorm.optimal=0x%x rgba16f.optimal=0x%x (STORAGE_IMAGE=0x%x)", + (int)probed.shaderStorageImageExtendedFormats, + (int)probed.shaderStorageImageReadWithoutFormat, + (int)probed.shaderStorageImageWriteWithoutFormat, + r8.optimalTilingFeatures, rgba16f.optimalTilingFeatures, + VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT); + } + + this->computeQueue = queue; + this->computeFamilyIdx = queueFamilyIdx; + this->physicalDevice = physical; + // Unknown whether robustness2/nullDescriptor was enabled — assume not and + // use the fallback image for optional bindings. + this->nullDescriptorSupported = false; + this->device = std::shared_ptr( + new VkDevice(external), + [](VkDevice* device) { delete device; } + ); + this->fallbackDescriptorImage = std::make_shared(*this, + VkExtent2D{1, 1}, VK_FORMAT_R8G8B8A8_UNORM, + VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT + | VK_IMAGE_USAGE_TRANSFER_DST_BIT); + Utils::clearImage(*this, *this->fallbackDescriptorImage); +} diff --git a/framegen/src/core/image.cpp b/framegen/src/core/image.cpp index e558e009..ab1f9954 100644 --- a/framegen/src/core/image.cpp +++ b/framegen/src/core/image.cpp @@ -380,3 +380,43 @@ Image::Image(const Core::Device& device, VkExtent2D extent, VkFormat format, } #endif // __ANDROID__ + +Image::Image(const Core::Device& device, VkImage externalImage, + VkExtent2D extent, VkFormat format, VkImageAspectFlags aspectFlags) + : extent(extent), format(format), aspectFlags(aspectFlags) { + const VkImageViewCreateInfo viewDesc{ + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .image = externalImage, + .viewType = VK_IMAGE_VIEW_TYPE_2D, + .format = format, + .components = { + .r = VK_COMPONENT_SWIZZLE_IDENTITY, + .g = VK_COMPONENT_SWIZZLE_IDENTITY, + .b = VK_COMPONENT_SWIZZLE_IDENTITY, + .a = VK_COMPONENT_SWIZZLE_IDENTITY, + }, + .subresourceRange = { + .aspectMask = aspectFlags, + .levelCount = 1, + .layerCount = 1, + }, + }; + VkImageView viewHandle{}; + auto res = vkCreateImageView(device.handle(), &viewDesc, nullptr, &viewHandle); + if (res != VK_SUCCESS || viewHandle == VK_NULL_HANDLE) + throw LSFG::vulkan_error(res, "Failed to create view for wrapped image"); + + // Wrapped images are handed over in GENERAL layout, same convention as + // the AHB path. The image and its memory stay caller-owned. + this->layout = std::make_shared(VK_IMAGE_LAYOUT_GENERAL); + this->image = std::shared_ptr( + new VkImage(externalImage), + [](VkImage* img) { delete img; } + ); + this->view = std::shared_ptr( + new VkImageView(viewHandle), + [dev = device.handle()](VkImageView* imgView) { + vkDestroyImageView(dev, *imgView, nullptr); + } + ); +} diff --git a/framegen/src/core/instance.cpp b/framegen/src/core/instance.cpp index 981e6346..d2198e2e 100644 --- a/framegen/src/core/instance.cpp +++ b/framegen/src/core/instance.cpp @@ -47,3 +47,14 @@ Instance::Instance() { } ); } + +Instance::Instance(PFN_vkGetInstanceProcAddr gipa, VkInstance external) { + volkInitializeCustom(gipa); + volkLoadInstance(external); + + // non-owning: the caller controls the instance lifetime + this->instance = std::shared_ptr( + new VkInstance(external), + [](VkInstance* instance) { delete instance; } + ); +} diff --git a/framegen/v3.1_include/v3_1/context.hpp b/framegen/v3.1_include/v3_1/context.hpp index dd7a670c..d450651d 100644 --- a/framegen/v3.1_include/v3_1/context.hpp +++ b/framegen/v3.1_include/v3_1/context.hpp @@ -55,6 +55,11 @@ namespace LSFG_3_1 { VkExtent2D extent, VkFormat format); #endif + /// Single-device variant wrapping caller-owned VkImages. + Context(Vulkan& vk, + VkImage in0, VkImage in1, const std::vector& outN, + VkExtent2D extent, VkFormat format); + /// /// Present on the context. /// diff --git a/framegen/v3.1_src/context.cpp b/framegen/v3.1_src/context.cpp index 4283d599..b9d3fa43 100644 --- a/framegen/v3.1_src/context.cpp +++ b/framegen/v3.1_src/context.cpp @@ -286,3 +286,48 @@ Context::Context(Vulkan& vk, } #endif // __ANDROID__ + +Context::Context(Vulkan& vk, + VkImage in0, VkImage in1, const std::vector& outN, + VkExtent2D extent, VkFormat format) { + this->inImg_0 = Core::Image(vk.device, in0, extent, format, VK_IMAGE_ASPECT_COLOR_BIT); + this->inImg_1 = Core::Image(vk.device, in1, extent, format, VK_IMAGE_ASPECT_COLOR_BIT); + + std::vector outImgs; + outImgs.reserve(outN.size()); + for (auto* img : outN) + outImgs.emplace_back(vk.device, img, extent, format, VK_IMAGE_ASPECT_COLOR_BIT); + + for (size_t i = 0; i < 8; i++) { + auto& data = this->data.at(i); + data.internalSemaphores.resize(vk.generationCount); + data.outSemaphores.resize(vk.generationCount); + data.completionFences.resize(vk.generationCount); + data.cmdBuffers2.resize(vk.generationCount); + } + + this->mipmaps = Shaders::Mipmaps(vk, this->inImg_0, this->inImg_1); + for (size_t i = 0; i < 7; i++) + this->alpha.at(i) = Shaders::Alpha(vk, this->mipmaps.getOutImages().at(i)); + this->beta = Shaders::Beta(vk, this->alpha.at(0).getOutImages()); + for (size_t i = 0; i < 7; i++) { + this->gamma.at(i) = Shaders::Gamma(vk, + this->alpha.at(6 - i).getOutImages(), + this->beta.getOutImages().at(std::min(6 - i, 5)), + (i == 0) ? std::nullopt : std::make_optional(this->gamma.at(i - 1).getOutImage())); + if (i < 4) continue; + + this->delta.at(i - 4) = Shaders::Delta(vk, + this->alpha.at(6 - i).getOutImages(), + this->beta.getOutImages().at(6 - i), + (i == 4) ? std::nullopt : std::make_optional(this->gamma.at(i - 1).getOutImage()), + (i == 4) ? std::nullopt : std::make_optional(this->delta.at(i - 5).getOutImage1()), + (i == 4) ? std::nullopt : std::make_optional(this->delta.at(i - 5).getOutImage2())); + } + this->generate = Shaders::Generate(vk, + this->inImg_0, this->inImg_1, + this->gamma.at(6).getOutImage(), + this->delta.at(2).getOutImage1(), + this->delta.at(2).getOutImage2(), + std::move(outImgs)); +} diff --git a/framegen/v3.1_src/lsfg.cpp b/framegen/v3.1_src/lsfg.cpp index 5021b79d..afad4550 100644 --- a/framegen/v3.1_src/lsfg.cpp +++ b/framegen/v3.1_src/lsfg.cpp @@ -26,6 +26,7 @@ namespace { std::optional instance; std::optional device; std::unordered_map contexts; + bool externalMode = false; } void LSFG_3_1::initialize(uint64_t deviceUUID, @@ -52,6 +53,33 @@ void LSFG_3_1::initialize(uint64_t deviceUUID, std::srand(static_cast(std::time(nullptr))); } +void LSFG_3_1::initializeExternal(PFN_vkGetInstanceProcAddr gipa, + VkInstance externalInstance, VkPhysicalDevice physicalDevice, + VkDevice externalDevice, uint32_t queueFamilyIdx, VkQueue queue, + bool isHdr, float flowScale, uint64_t generationCount, + const std::function(const std::string&)>& loader) { + if (instance.has_value() || device.has_value()) + return; + + externalMode = true; + instance.emplace(gipa, externalInstance); + device.emplace(Vulkan { + .device{*instance, physicalDevice, externalDevice, queueFamilyIdx, queue}, + .generationCount = generationCount, + .flowScale = flowScale, + .isHdr = isHdr + }); + contexts = std::unordered_map(); + + device->commandPool = Core::CommandPool(device->device); + device->descriptorPool = Core::DescriptorPool(device->device); + + device->resources = Pool::ResourcePool(device->isHdr, device->flowScale); + device->shaders = Pool::ShaderPool(loader); + + std::srand(static_cast(std::time(nullptr))); +} + int32_t LSFG_3_1::createContext( int in0, int in1, const std::vector& outN, VkExtent2D extent, VkFormat format) { @@ -114,9 +142,23 @@ int32_t LSFG_3_1::createContextFromAHB( #endif // __ANDROID__ +int32_t LSFG_3_1::createContextFromImages( + VkImage in0, VkImage in1, const std::vector& outN, + VkExtent2D extent, VkFormat format) { + if (!instance.has_value() || !device.has_value()) + throw LSFG::vulkan_error(VK_ERROR_INITIALIZATION_FAILED, "LSFG not initialized"); + + const int32_t id = std::rand(); + contexts.emplace(id, Context(*device, in0, in1, outN, extent, format)); + return id; +} + #ifdef __ANDROID__ void LSFG_3_1::waitIdle() { if (!device.has_value()) return; + // single-device mode: work is ordered on the caller's queue; idling the + // caller's whole device here would stall the game for no benefit + if (externalMode) return; vkDeviceWaitIdle(device->device.handle()); } #endif diff --git a/framegen/v3.1p_include/v3_1p/context.hpp b/framegen/v3.1p_include/v3_1p/context.hpp index 60248ccc..7ee9182c 100644 --- a/framegen/v3.1p_include/v3_1p/context.hpp +++ b/framegen/v3.1p_include/v3_1p/context.hpp @@ -51,6 +51,11 @@ namespace LSFG_3_1P { VkExtent2D extent, VkFormat format); #endif + /// Single-device variant wrapping caller-owned VkImages. + Context(Vulkan& vk, + VkImage in0, VkImage in1, const std::vector& outN, + VkExtent2D extent, VkFormat format); + /// /// Present on the context. /// @@ -62,6 +67,12 @@ namespace LSFG_3_1P { void present(Vulkan& vk, int inSem, const std::vector& outSem); +#ifdef __ANDROID__ + /// Key internal images of the flow chain, for pixel dumping. + [[nodiscard]] std::vector> + debugInternalImages() const; +#endif + // Trivially copyable, moveable and destructible Context(const Context&) = default; Context& operator=(const Context&) = default; diff --git a/framegen/v3.1p_src/context.cpp b/framegen/v3.1p_src/context.cpp index 48fc82f0..1419fb82 100644 --- a/framegen/v3.1p_src/context.cpp +++ b/framegen/v3.1p_src/context.cpp @@ -280,4 +280,59 @@ Context::Context(Vulkan& vk, std::move(outImgs)); } +std::vector> + Context::debugInternalImages() const { + return { + { "mip0", &this->mipmaps.getOutImages().at(0) }, + { "gamma6", &this->gamma.at(6).getOutImage() }, + { "delta2a", &this->delta.at(2).getOutImage1() }, + { "delta2b", &this->delta.at(2).getOutImage2() }, + }; +} + #endif // __ANDROID__ + +Context::Context(Vulkan& vk, + VkImage in0, VkImage in1, const std::vector& outN, + VkExtent2D extent, VkFormat format) { + this->inImg_0 = Core::Image(vk.device, in0, extent, format, VK_IMAGE_ASPECT_COLOR_BIT); + this->inImg_1 = Core::Image(vk.device, in1, extent, format, VK_IMAGE_ASPECT_COLOR_BIT); + + std::vector outImgs; + outImgs.reserve(outN.size()); + for (auto* img : outN) + outImgs.emplace_back(vk.device, img, extent, format, VK_IMAGE_ASPECT_COLOR_BIT); + + for (size_t i = 0; i < 8; i++) { + auto& data = this->data.at(i); + data.internalSemaphores.resize(vk.generationCount); + data.outSemaphores.resize(vk.generationCount); + data.completionFences.resize(vk.generationCount); + data.cmdBuffers2.resize(vk.generationCount); + } + + this->mipmaps = Shaders::Mipmaps(vk, this->inImg_0, this->inImg_1); + for (size_t i = 0; i < 7; i++) + this->alpha.at(i) = Shaders::Alpha(vk, this->mipmaps.getOutImages().at(i)); + this->beta = Shaders::Beta(vk, this->alpha.at(0).getOutImages()); + for (size_t i = 0; i < 7; i++) { + this->gamma.at(i) = Shaders::Gamma(vk, + this->alpha.at(6 - i).getOutImages(), + this->beta.getOutImages().at(std::min(6 - i, 5)), + (i == 0) ? std::nullopt : std::make_optional(this->gamma.at(i - 1).getOutImage())); + if (i < 4) continue; + + this->delta.at(i - 4) = Shaders::Delta(vk, + this->alpha.at(6 - i).getOutImages(), + this->beta.getOutImages().at(6 - i), + (i == 4) ? std::nullopt : std::make_optional(this->gamma.at(i - 1).getOutImage()), + (i == 4) ? std::nullopt : std::make_optional(this->delta.at(i - 5).getOutImage1()), + (i == 4) ? std::nullopt : std::make_optional(this->delta.at(i - 5).getOutImage2())); + } + this->generate = Shaders::Generate(vk, + this->inImg_0, this->inImg_1, + this->gamma.at(6).getOutImage(), + this->delta.at(2).getOutImage1(), + this->delta.at(2).getOutImage2(), + std::move(outImgs)); +} diff --git a/framegen/v3.1p_src/lsfg.cpp b/framegen/v3.1p_src/lsfg.cpp index 213ce6ce..88ef043b 100644 --- a/framegen/v3.1p_src/lsfg.cpp +++ b/framegen/v3.1p_src/lsfg.cpp @@ -26,6 +26,7 @@ namespace { std::optional instance; std::optional device; std::unordered_map contexts; + bool externalMode = false; } void LSFG_3_1P::initialize(uint64_t deviceUUID, @@ -52,6 +53,33 @@ void LSFG_3_1P::initialize(uint64_t deviceUUID, std::srand(static_cast(std::time(nullptr))); } +void LSFG_3_1P::initializeExternal(PFN_vkGetInstanceProcAddr gipa, + VkInstance externalInstance, VkPhysicalDevice physicalDevice, + VkDevice externalDevice, uint32_t queueFamilyIdx, VkQueue queue, + bool isHdr, float flowScale, uint64_t generationCount, + const std::function(const std::string&)>& loader) { + if (instance.has_value() || device.has_value()) + return; + + externalMode = true; + instance.emplace(gipa, externalInstance); + device.emplace(Vulkan { + .device{*instance, physicalDevice, externalDevice, queueFamilyIdx, queue}, + .generationCount = generationCount, + .flowScale = flowScale, + .isHdr = isHdr + }); + contexts = std::unordered_map(); + + device->commandPool = Core::CommandPool(device->device); + device->descriptorPool = Core::DescriptorPool(device->device); + + device->resources = Pool::ResourcePool(device->isHdr, device->flowScale); + device->shaders = Pool::ShaderPool(loader); + + std::srand(static_cast(std::time(nullptr))); +} + int32_t LSFG_3_1P::createContext( int in0, int in1, const std::vector& outN, VkExtent2D extent, VkFormat format) { @@ -114,9 +142,32 @@ int32_t LSFG_3_1P::createContextFromAHB( #endif // __ANDROID__ +int32_t LSFG_3_1P::createContextFromImages( + VkImage in0, VkImage in1, const std::vector& outN, + VkExtent2D extent, VkFormat format) { + if (!instance.has_value() || !device.has_value()) + throw LSFG::vulkan_error(VK_ERROR_INITIALIZATION_FAILED, "LSFG not initialized"); + + const int32_t id = std::rand(); + contexts.emplace(id, Context(*device, in0, in1, outN, extent, format)); + return id; +} + #ifdef __ANDROID__ void LSFG_3_1P::waitIdle() { if (!device.has_value()) return; + // single-device mode: work is ordered on the caller's queue; idling the + // caller's whole device here would stall the game for no benefit + if (externalMode) return; vkDeviceWaitIdle(device->device.handle()); } + +std::vector LSFG_3_1P::debugInternalImages(int32_t id) { + std::vector out; + auto it = contexts.find(id); + if (it == contexts.end()) return out; + for (const auto& [name, img] : it->second.debugInternalImages()) + out.push_back({ name, img->handle(), img->getExtent(), img->getFormat() }); + return out; +} #endif diff --git a/include/config/config.hpp b/include/config/config.hpp index eef37da2..c8122fde 100644 --- a/include/config/config.hpp +++ b/include/config/config.hpp @@ -24,6 +24,21 @@ namespace Config { bool performance{false}; /// Whether HDR is enabled bool hdr{false}; + /// Base frame-rate cap applied to real game frames (0 = uncapped). + /// The on-screen rate becomes fpsLimit * multiplier. + int fpsLimit{0}; + /// Debug: 1 = present the previous-frame input image in generated + /// slots instead of framegen output (isolates input vs graph + /// corruption); 2 = present the real swapchain image itself (isolates + /// the swapchain read from the format-converting copy). + int debugInputs{0}; + /// Debug: dump frame_0/frame_1/out_n pixels for this many consecutive + /// presents to /dump/ as PPM files, then stop (0 = off). + int debugDump{0}; + /// Use the DLL's translated mipmaps shader instead of the bundled + /// clean-room replacement (the translated one miscompiles on mesa's + /// Adreno gen8 backend). + bool origMipmaps{false}; /// Experimental flag for overriding the synchronization method. VkPresentModeKHR e_present; diff --git a/include/context.hpp b/include/context.hpp index 61b5c05f..4cedc19d 100644 --- a/include/context.hpp +++ b/include/context.hpp @@ -10,6 +10,7 @@ #include "hooks.hpp" #include "mini/commandbuffer.hpp" #include "mini/commandpool.hpp" +#include "mini/fence.hpp" #include "mini/image.hpp" #include "mini/semaphore.hpp" @@ -17,6 +18,7 @@ #include #include #include +#include /// /// This class is the frame generation context. There should be one instance per swapchain. @@ -51,6 +53,17 @@ class LsContext { VkResult present(const Hooks::DeviceInfo& info, const void* pNext, VkQueue queue, const std::vector& gameRenderSemaphores, uint32_t presentIdx); + /// Whether framegen was disabled for this swapchain after repeated sync + /// failures. The present hook passes frames through untouched when set. + [[nodiscard]] bool isDisabled() const { return this->forceDisabled; } + + /// + /// Pace and forward a present without frame generation (multiplier <= 1). + /// Applies the same vsync-locked fps limiter as the framegen path so the + /// cap also works as a plain frame limiter. + /// + VkResult presentPassthrough(VkQueue queue, const VkPresentInfoKHR* pPresentInfo); + // Non-copyable, trivially moveable and destructible LsContext(const LsContext&) = delete; LsContext& operator=(const LsContext&) = delete; @@ -69,6 +82,44 @@ class LsContext { Mini::CommandPool cmdPool; uint64_t frameIdx{0}; +#ifdef __ANDROID__ + void paceBaseFrame(int fpsLimit); + + Mini::Fence preCopyFence; // signaled when the swapchain -> frame_n copy completes + uint32_t copyFenceTimeouts{0}; + bool forceDisabled{false}; + + int64_t pacerAnchorNs{0}; // schedule anchor of the current real frame + int64_t pacerNextDueNs{0}; // next vsync-grid slot for the real frame + int64_t lastRealPresentNs{0}; // previous real-frame entry, for the EWMA + uint64_t baseIntervalEwmaNs{0}; // measured real-frame interval + + // display vsync grid, published by the app (vsync.txt next to conf.toml); + // Choreographer timestamps share CLOCK_MONOTONIC with the pacer + int64_t vsyncPhaseNs{0}; + int64_t vsyncPeriodNs{0}; + int64_t vsyncLastReadNs{0}; + + uint64_t statsPresents{0}; // presents (real + generated) in the window + uint64_t statsRealPresents{0}; // real presents in the window + int64_t statsWindowStartNs{0}; + + // per-stage timing accumulators for the stats window + uint64_t statsWorkNs{0}; // copy fence wait + framegen + waitIdle + uint64_t statsGenSleepNs{0}; // slept before generated presents + uint64_t statsGenLateNs{0}; // generated present lateness vs its slot + uint64_t statsGenSkips{0}; // generated frames dropped to hold cadence + uint64_t statsGenPresentNs{0}; // time blocked in generated queuePresent calls + uint64_t statsRealPresentNs{0}; // time blocked in the real queuePresent call + std::string statsSeq; // recent image-index sequence (R=real, g=gen) + + void dumpFrameImages(const Hooks::DeviceInfo& info, uint32_t presentIdx); + int dumpDoneFrames{0}; // presents already dumped (debug_dump) + bool dumpFailed{false}; +#else + static constexpr bool forceDisabled = false; +#endif + struct RenderPassInfo { Mini::CommandBuffer preCopyBuf; // copy from swapchain image to frame_0/frame_1 std::array preCopySemaphores; // signal when preCopyBuf is done diff --git a/include/layer.hpp b/include/layer.hpp index 461eea1f..9b6176b1 100644 --- a/include/layer.hpp +++ b/include/layer.hpp @@ -191,6 +191,17 @@ namespace Layer { uint32_t queueFamilyIndex, uint32_t queueIndex, VkQueue* pQueue); + /// Query physical device features (returns false when unavailable). + bool ovkGetPhysicalDeviceFeatures( + VkPhysicalDevice physicalDevice, + VkPhysicalDeviceFeatures* pFeatures); + /// Query physical device features2 (returns false when unavailable). + bool ovkGetPhysicalDeviceFeatures2( + VkPhysicalDevice physicalDevice, + VkPhysicalDeviceFeatures2* pFeatures); + /// The instance the layer initialized on. + VkInstance ovkInstance(); + /// Call to the original vkQueueSubmit function. VkResult ovkQueueSubmit( VkQueue queue, @@ -198,6 +209,30 @@ namespace Layer { const VkSubmitInfo* pSubmits, VkFence fence); + /// Call to the original vkCreateFence function. + VkResult ovkCreateFence( + VkDevice device, + const VkFenceCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkFence* pFence); + /// Call to the original vkDestroyFence function. + void ovkDestroyFence( + VkDevice device, + VkFence fence, + const VkAllocationCallbacks* pAllocator); + /// Call to the original vkWaitForFences function. + VkResult ovkWaitForFences( + VkDevice device, + uint32_t fenceCount, + const VkFence* pFences, + VkBool32 waitAll, + uint64_t timeout); + /// Call to the original vkResetFences function. + VkResult ovkResetFences( + VkDevice device, + uint32_t fenceCount, + const VkFence* pFences); + /// Call to the original vkCmdPipelineBarrier function. void ovkCmdPipelineBarrier( VkCommandBuffer commandBuffer, @@ -229,6 +264,54 @@ namespace Layer { VkSemaphore semaphore, VkFence fence, uint32_t* pImageIndex); + + /// Call to the original vkCreateBuffer function. + VkResult ovkCreateBuffer( + VkDevice device, + const VkBufferCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkBuffer* pBuffer); + /// Call to the original vkDestroyBuffer function. + void ovkDestroyBuffer( + VkDevice device, + VkBuffer buffer, + const VkAllocationCallbacks* pAllocator); + /// Call to the original vkGetBufferMemoryRequirements function. + void ovkGetBufferMemoryRequirements( + VkDevice device, + VkBuffer buffer, + VkMemoryRequirements* pMemoryRequirements); + /// Call to the original vkBindBufferMemory function. + VkResult ovkBindBufferMemory( + VkDevice device, + VkBuffer buffer, + VkDeviceMemory memory, + VkDeviceSize memoryOffset); + /// Call to the original vkMapMemory function. + VkResult ovkMapMemory( + VkDevice device, + VkDeviceMemory memory, + VkDeviceSize offset, + VkDeviceSize size, + VkMemoryMapFlags flags, + void** ppData); + /// Call to the original vkUnmapMemory function. + void ovkUnmapMemory( + VkDevice device, + VkDeviceMemory memory); + /// Call to the original vkInvalidateMappedMemoryRanges function. + VkResult ovkInvalidateMappedMemoryRanges( + VkDevice device, + uint32_t memoryRangeCount, + const VkMappedMemoryRange* pMemoryRanges); + /// Call to the original vkCmdCopyImageToBuffer function. + void ovkCmdCopyImageToBuffer( + VkCommandBuffer commandBuffer, + VkImage srcImage, + VkImageLayout srcImageLayout, + VkBuffer dstBuffer, + uint32_t regionCount, + const VkBufferImageCopy* pRegions); } /// Symbol definition for Vulkan instance layer. diff --git a/include/mini/commandbuffer.hpp b/include/mini/commandbuffer.hpp index 21895967..7a8a81e3 100644 --- a/include/mini/commandbuffer.hpp +++ b/include/mini/commandbuffer.hpp @@ -64,13 +64,15 @@ namespace Mini { /// @param queue Vulkan queue to submit to /// @param waitSemaphores Semaphores to wait on before executing the command buffer /// @param signalSemaphores Semaphores to signal after executing the command buffer + /// @param fence Optional fence to signal once execution completes /// /// @throws std::logic_error if the command buffer is not in Full state. /// @throws LSFG::vulkan_error if submission fails. /// void submit(VkQueue queue, const std::vector& waitSemaphores = {}, - const std::vector& signalSemaphores = {}); + const std::vector& signalSemaphores = {}, + VkFence fence = VK_NULL_HANDLE); /// Get the state of the command buffer. [[nodiscard]] CommandBufferState getState() const { return *this->state; } diff --git a/include/mini/fence.hpp b/include/mini/fence.hpp new file mode 100644 index 00000000..2084f98b --- /dev/null +++ b/include/mini/fence.hpp @@ -0,0 +1,58 @@ +#pragma once + +#include + +#include +#include + +namespace Mini { + + /// + /// C++ wrapper class for a Vulkan fence. + /// + /// This class manages the lifetime of a Vulkan fence. + /// + class Fence { + public: + Fence() noexcept = default; + + /// + /// Create the fence. + /// + /// @param device Vulkan device + /// @param signaled Whether the fence starts in the signaled state. + /// + /// @throws LSFG::vulkan_error if object creation fails. + /// + Fence(VkDevice device, bool signaled); + + /// + /// Wait for the fence to become signaled. + /// + /// @param timeoutNs Maximum time to wait in nanoseconds. + /// @return VK_SUCCESS, VK_TIMEOUT or an error code. + /// + [[nodiscard]] VkResult wait(uint64_t timeoutNs) const; + + /// + /// Reset the fence to the unsignaled state. + /// + /// @return VK_SUCCESS or an error code. + /// + VkResult reset() const; + + /// Get the Vulkan handle. + [[nodiscard]] auto handle() const { return *this->fence; } + + // Trivially copyable, moveable and destructible + Fence(const Fence&) noexcept = default; + Fence& operator=(const Fence&) noexcept = default; + Fence(Fence&&) noexcept = default; + Fence& operator=(Fence&&) noexcept = default; + ~Fence() = default; + private: + VkDevice device{}; + std::shared_ptr fence; + }; + +} diff --git a/include/mini/image.hpp b/include/mini/image.hpp index 593c136e..d944d47e 100644 --- a/include/mini/image.hpp +++ b/include/mini/image.hpp @@ -57,6 +57,17 @@ namespace Mini { [[nodiscard]] AHardwareBuffer* getAhb() const { return this->ahb; } #endif + /// + /// Create a plain device-local image with no external memory + /// (single-device framegen path: the image is shared with the framegen + /// chain by handle, on the same device). + /// + /// @throws LSFG::vulkan_error if object creation fails. + /// + static Image createDeviceLocal(VkDevice device, VkPhysicalDevice physicalDevice, + VkExtent2D extent, VkFormat format, + VkImageUsageFlags usage, VkImageAspectFlags aspectFlags); + /// Get the Vulkan handle. [[nodiscard]] auto handle() const { return *this->image; } /// Get the Vulkan device memory handle. diff --git a/shaders/mipmaps_replacement.comp b/shaders/mipmaps_replacement.comp new file mode 100644 index 00000000..d2fc75f5 --- /dev/null +++ b/shaders/mipmaps_replacement.comp @@ -0,0 +1,54 @@ +// Clean-room replacement for the LSFG mipmaps pass (written from the pass +// interface only: sample the input frame, write a 7-level R8 luma pyramid). +// Ships because mesa's Adreno gen8 backend miscompiles the DLL-translated +// original (writes stop landing after startup). Each level averages 4 +// bilinear taps spread over the destination texel's source footprint. +// Rebuild: glslangValidator -V --target-env vulkan1.3 -e main +// shaders/mipmaps_replacement.comp -o mipmaps_replacement.spv +#version 450 +layout(local_size_x = 32, local_size_y = 32) in; +layout(set = 0, binding = 0) uniform U { vec4 u; }; +layout(set = 0, binding = 1) uniform sampler s; +layout(set = 0, binding = 2) uniform texture2D tex; +layout(set = 0, binding = 3, r8) writeonly uniform image2D o0; +layout(set = 0, binding = 4, r8) writeonly uniform image2D o1; +layout(set = 0, binding = 5, r8) writeonly uniform image2D o2; +layout(set = 0, binding = 6, r8) writeonly uniform image2D o3; +layout(set = 0, binding = 7, r8) writeonly uniform image2D o4; +layout(set = 0, binding = 8, r8) writeonly uniform image2D o5; +layout(set = 0, binding = 9, r8) writeonly uniform image2D o6; + +float lumaBox(vec2 c, vec2 e) { + const vec3 W = vec3(0.299, 0.587, 0.114); + float l = 0.0; + l += dot(textureLod(sampler2D(tex, s), c + e * vec2(-0.25, -0.25), 0.0).rgb, W); + l += dot(textureLod(sampler2D(tex, s), c + e * vec2( 0.25, -0.25), 0.0).rgb, W); + l += dot(textureLod(sampler2D(tex, s), c + e * vec2(-0.25, 0.25), 0.0).rgb, W); + l += dot(textureLod(sampler2D(tex, s), c + e * vec2( 0.25, 0.25), 0.0).rgb, W); + return l * 0.25; +} + +#define WRITE_LEVEL(img, q) { \ + ivec2 sz = imageSize(img); \ + ivec2 pp = (q); \ + if (pp.x < sz.x && pp.y < sz.y) { \ + vec2 e = 1.0 / vec2(sz); \ + vec2 c = (vec2(pp) + 0.5) * e; \ + imageStore(img, pp, vec4(lumaBox(c, e))); \ + } \ +} + +void main() { + ivec2 gid = ivec2(gl_GlobalInvocationID.xy); + for (int dy = 0; dy < 2; dy++) + for (int dx = 0; dx < 2; dx++) { + ivec2 p = gid * 2 + ivec2(dx, dy); + WRITE_LEVEL(o0, p) + if ((p.x & 1) == 0 && (p.y & 1) == 0) WRITE_LEVEL(o1, p >> 1) + if ((p.x & 3) == 0 && (p.y & 3) == 0) WRITE_LEVEL(o2, p >> 2) + if ((p.x & 7) == 0 && (p.y & 7) == 0) WRITE_LEVEL(o3, p >> 3) + if ((p.x & 15) == 0 && (p.y & 15) == 0) WRITE_LEVEL(o4, p >> 4) + if ((p.x & 31) == 0 && (p.y & 31) == 0) WRITE_LEVEL(o5, p >> 5) + if ((p.x & 63) == 0 && (p.y & 63) == 0) WRITE_LEVEL(o6, p >> 6) + } +} diff --git a/src/config/config.cpp b/src/config/config.cpp index ea98a81c..8f68e8ad 100644 --- a/src/config/config.cpp +++ b/src/config/config.cpp @@ -74,6 +74,7 @@ void Config::updateConfig(const std::string& file) { const toml::value globalTable = toml::find_or_default(toml, "global"); const Configuration global{ .dll = toml::find_or(globalTable, "dll", std::string()), + .fpsLimit = toml::find_or(globalTable, "fps_limit", 0), .config_file = file, .timestamp = std::filesystem::last_write_time(file) }; @@ -101,6 +102,10 @@ void Config::updateConfig(const std::string& file) { .flowScale = toml::find_or(gameTable, "flow_scale", 1.0F), .performance = toml::find_or(gameTable, "performance_mode", false), .hdr = toml::find_or(gameTable, "hdr_mode", false), + .fpsLimit = toml::find_or(gameTable, "fps_limit", global.fpsLimit), + .debugInputs = toml::find_or(gameTable, "debug_inputs", 0), + .debugDump = toml::find_or(gameTable, "debug_dump", 0), + .origMipmaps = toml::find_or(gameTable, "orig_mipmaps", false), .e_present = into_present(toml::find_or(gameTable, "experimental_present_mode", "")), .config_file = file, .timestamp = global.timestamp @@ -111,6 +116,8 @@ void Config::updateConfig(const std::string& file) { throw std::runtime_error("Multiplier cannot be less than 1"); if (game.flowScale < 0.25F || game.flowScale > 1.0F) throw std::runtime_error("Flow scale must be between 0.25 and 1.0"); + if (game.fpsLimit < 0) + game.fpsLimit = 0; games[exe] = std::move(game); } diff --git a/src/context.cpp b/src/context.cpp index 8ed73ddc..c13b6c74 100644 --- a/src/context.cpp +++ b/src/context.cpp @@ -3,6 +3,7 @@ #include "common/exception.hpp" #include "extract/extract.hpp" #include "extract/trans.hpp" +#include "extract/mipmaps_replacement_spv.h" #include "utils/utils.hpp" #include "hooks.hpp" #include "layer.hpp" @@ -28,6 +29,191 @@ #include #include +#ifdef __ANDROID__ +#include +#include +#include +#include +#include + +namespace { + // Every GPU wait is bounded so an incompatible sync path degrades to + // passthrough presents instead of hanging the game's present thread. + constexpr uint64_t kSyncFenceTimeoutNs = 500'000'000ULL; + constexpr uint32_t kMaxFenceTimeouts = 6; + + int64_t nowNs() { + timespec ts{}; + clock_gettime(CLOCK_MONOTONIC, &ts); + return static_cast(ts.tv_sec) * 1'000'000'000LL + ts.tv_nsec; + } + + void sleepUntilNs(int64_t targetNs) { + const int64_t delta = targetNs - nowNs(); + if (delta <= 0) return; + timespec req{ + .tv_sec = static_cast(delta / 1'000'000'000LL), + .tv_nsec = static_cast(delta % 1'000'000'000LL) + }; + while (nanosleep(&req, &req) == -1 && errno == EINTR) {} + } + + void imageBarrier(VkCommandBuffer cmd, VkImage img, + VkPipelineStageFlags srcStage, VkAccessFlags srcAccess, + VkPipelineStageFlags dstStage, VkAccessFlags dstAccess, + VkImageLayout oldLayout, VkImageLayout newLayout, + uint32_t srcFamily = VK_QUEUE_FAMILY_IGNORED, + uint32_t dstFamily = VK_QUEUE_FAMILY_IGNORED) { + const VkImageMemoryBarrier barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .srcAccessMask = srcAccess, + .dstAccessMask = dstAccess, + .oldLayout = oldLayout, + .newLayout = newLayout, + .srcQueueFamilyIndex = srcFamily, + .dstQueueFamilyIndex = dstFamily, + .image = img, + .subresourceRange = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .levelCount = 1, + .layerCount = 1 + } + }; + Layer::ovkCmdPipelineBarrier(cmd, srcStage, dstStage, 0, + 0, nullptr, 0, nullptr, 1, &barrier); + } + + void blitFull(VkCommandBuffer cmd, VkImage src, VkImage dst, + uint32_t width, uint32_t height) { + const VkImageBlit blit{ + .srcSubresource = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .layerCount = 1 }, + .srcOffsets = { {0, 0, 0}, + { static_cast(width), static_cast(height), 1 } }, + .dstSubresource = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .layerCount = 1 }, + .dstOffsets = { {0, 0, 0}, + { static_cast(width), static_cast(height), 1 } }, + }; + Layer::ovkCmdBlitImage(cmd, + src, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + dst, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, &blit, VK_FILTER_NEAREST); + } + + // Parse the vsync grid the app publishes next to conf.toml. Returns false + // when the file is missing or malformed. + bool readVsyncFile(const std::filesystem::path& configFile, + int64_t* phaseNs, int64_t* periodNs) { + if (configFile.empty()) return false; + std::ifstream in(configFile.parent_path() / "vsync.txt"); + if (!in.is_open()) return false; + int64_t phase = 0, period = 0; + std::string line; + while (std::getline(in, line)) { + if (line.rfind("vsync_ns=", 0) == 0) + phase = std::strtoll(line.c_str() + 9, nullptr, 10); + else if (line.rfind("period_ns=", 0) == 0) + period = std::strtoll(line.c_str() + 10, nullptr, 10); + } + if (phase <= 0 || period < 1'000'000 || period > 100'000'000) return false; + *phaseNs = phase; + *periodNs = period; + return true; + } + + float halfToFloat(uint16_t h) { + const uint32_t sign = (h & 0x8000U) << 16; + uint32_t exp = (h >> 10) & 0x1FU; + uint32_t mant = h & 0x3FFU; + uint32_t bits; + if (exp == 0) { + if (mant == 0) { + bits = sign; + } else { + exp = 127 - 15 + 1; + while ((mant & 0x400U) == 0) { mant <<= 1; exp--; } + mant &= 0x3FFU; + bits = sign | (exp << 23) | (mant << 13); + } + } else if (exp == 31) { + bits = sign | 0x7F800000U | (mant << 13); + } else { + bits = sign | ((exp - 15 + 127) << 23) | (mant << 13); + } + float f; + std::memcpy(&f, &bits, sizeof(f)); + return f; + } + + size_t dumpBytesPerPixel(VkFormat format) { + switch (format) { + case VK_FORMAT_R8_UNORM: return 1; + case VK_FORMAT_R8G8B8A8_UNORM: return 4; + case VK_FORMAT_R16G16B16A16_SFLOAT: return 8; + default: return 0; + } + } + + // Write one image region of the mapped dump buffer as binary PPM. + // signedMap remaps [-1,1] to [0,1] so negative flow values stay visible. + bool writePpm(const std::filesystem::path& path, const uint8_t* pixels, + uint32_t width, uint32_t height, VkFormat format, bool signedMap) { + std::ofstream out(path, std::ios::binary | std::ios::trunc); + if (!out.is_open()) return false; + out << "P6\n" << width << ' ' << height << "\n255\n"; + std::vector row(static_cast(width) * 3); + for (uint32_t y = 0; y < height; y++) { + for (uint32_t x = 0; x < width; x++) { + if (format == VK_FORMAT_R8G8B8A8_UNORM) { + const uint8_t* px = pixels + + (static_cast(y) * width + x) * 4; + row[x * 3 + 0] = px[0]; + row[x * 3 + 1] = px[1]; + row[x * 3 + 2] = px[2]; + } else if (format == VK_FORMAT_R8_UNORM) { + const uint8_t v = pixels[static_cast(y) * width + x]; + row[x * 3 + 0] = v; + row[x * 3 + 1] = v; + row[x * 3 + 2] = v; + } else { + const uint16_t* px = reinterpret_cast(pixels) + + (static_cast(y) * width + x) * 4; + for (int c = 0; c < 3; c++) { + float v = halfToFloat(px[c]); + if (signedMap) v = v * 0.5F + 0.5F; + v = std::clamp(v, 0.0F, 1.0F); + row[x * 3 + c] = static_cast(v * 255.0F + 0.5F); + } + } + } + out.write(reinterpret_cast(row.data()), + static_cast(row.size())); + } + return out.good(); + } + + struct StatsDetail { + double workMs, genSleepMs, genLateMs, genPresentMs, realPresentMs; + uint64_t genSkips; + std::string seq; + }; + + void writeStats(const std::filesystem::path& configFile, double fps, double baseFps, + const StatsDetail& d) { + if (configFile.empty()) return; + std::ofstream out(configFile.parent_path() / "stats.txt", std::ios::trunc); + if (!out.is_open()) return; + out << "fps=" << fps << "\nbase=" << baseFps + << "\nwork_ms=" << d.workMs + << "\ngen_sleep_ms=" << d.genSleepMs + << "\ngen_late_ms=" << d.genLateMs + << "\ngen_present_ms=" << d.genPresentMs + << "\nreal_present_ms=" << d.realPresentMs + << "\ngen_skips=" << d.genSkips + << "\nseq=" << d.seq << '\n'; + } +} +#endif + LsContext::LsContext(const Hooks::DeviceInfo& info, VkSwapchainKHR swapchain, VkExtent2D extent, const std::vector& swapchainImages) : swapchain(swapchain), swapchainImages(swapchainImages), @@ -74,56 +260,86 @@ LsContext::LsContext(const Hooks::DeviceInfo& info, VkSwapchainKHR swapchain, : VK_FORMAT_R16G16B16A16_SFLOAT; #ifdef __ANDROID__ - // Android path: use AHardwareBuffer-backed images for sharing with framegen. - // Turnip/Mesa on Android doesn't support OPAQUE_FD export, so we use the - // AHB path (createContextFromAHB + presentContext with -1 + waitIdle). + // Android path: single-device framegen. All images live on the game's own + // device and every dispatch runs on the game's queue — two different ICDs + // (e.g. turnip for the game, the system driver for a private framegen + // device) interpret shared AHB memory differently and scramble it, so no + // cross-device sharing is used at all. - this->frame_0 = Mini::Image(info.device, info.physicalDevice, - extent, format, VK_IMAGE_USAGE_TRANSFER_DST_BIT, VK_IMAGE_ASPECT_COLOR_BIT); - this->frame_1 = Mini::Image(info.device, info.physicalDevice, - extent, format, VK_IMAGE_USAGE_TRANSFER_DST_BIT, VK_IMAGE_ASPECT_COLOR_BIT); + this->preCopyFence = Mini::Fence(info.device, true); - for (size_t i = 0; i < static_cast(conf.multiplier - 1); ++i) - this->out_n.emplace_back(info.device, info.physicalDevice, - extent, format, - VK_IMAGE_USAGE_TRANSFER_SRC_BIT, VK_IMAGE_ASPECT_COLOR_BIT); + const VkImageUsageFlags sharedUsage = VK_IMAGE_USAGE_STORAGE_BIT + | VK_IMAGE_USAGE_SAMPLED_BIT + | VK_IMAGE_USAGE_TRANSFER_SRC_BIT + | VK_IMAGE_USAGE_TRANSFER_DST_BIT; + this->frame_0 = Mini::Image::createDeviceLocal(info.device, info.physicalDevice, + extent, format, sharedUsage, VK_IMAGE_ASPECT_COLOR_BIT); + this->frame_1 = Mini::Image::createDeviceLocal(info.device, info.physicalDevice, + extent, format, sharedUsage, VK_IMAGE_ASPECT_COLOR_BIT); - // initialize lsfg - auto* lsfgInitialize = LSFG_3_1::initialize; - auto* lsfgDeleteContext = LSFG_3_1::deleteContext; - if (conf.performance) { - lsfgInitialize = LSFG_3_1P::initialize; - lsfgDeleteContext = LSFG_3_1P::deleteContext; - } + for (size_t i = 0; i < static_cast(conf.multiplier - 1); ++i) + this->out_n.emplace_back(Mini::Image::createDeviceLocal(info.device, info.physicalDevice, + extent, format, sharedUsage, VK_IMAGE_ASPECT_COLOR_BIT)); setenv("DISABLE_LSFG", "1", 1); // NOLINT - lsfgInitialize( - Utils::getDeviceUUID(info.physicalDevice), - conf.hdr, 1.0F / conf.flowScale, conf.multiplier - 1, - [](const std::string& name) { - auto dxbc = Extract::getShader(name); - auto spirv = Extract::translateShader(dxbc); - return spirv; + const auto shaderLoader = [](const std::string& name) { + // debug override: a raw SPIR-V file next to conf.toml replaces the + // translated DLL shader (override_.spv, brackets -> underscores) + const auto& confFile = Config::activeConf.config_file; + if (!confFile.empty()) { + std::string fname = "override_" + name + ".spv"; + for (auto& c : fname) if (c == '[' || c == ']') c = '_'; + const auto path = std::filesystem::path(confFile).parent_path() / fname; + std::ifstream in(path, std::ios::binary); + if (in.is_open()) { + std::vector spirv( + (std::istreambuf_iterator(in)), + std::istreambuf_iterator()); + if (spirv.size() >= 20) { + std::cerr << "lsfg-vk: using shader override " << path + << " (" << spirv.size() << " bytes)\n"; + return spirv; + } + } } - ); + if (!Config::activeConf.origMipmaps + && (name == "mipmaps" || name == "p_mipmaps")) { + return std::vector(mipmaps_replacement_spv, + mipmaps_replacement_spv + mipmaps_replacement_spv_len); + } + auto dxbc = Extract::getShader(name); + auto spirv = Extract::translateShader(dxbc); + return spirv; + }; + if (conf.performance) + LSFG_3_1P::initializeExternal(Layer::ovkGetInstanceProcAddr, Layer::ovkInstance(), + info.physicalDevice, info.device, + static_cast(info.queue.first), info.queue.second, + conf.hdr, 1.0F / conf.flowScale, conf.multiplier - 1, shaderLoader); + else + LSFG_3_1::initializeExternal(Layer::ovkGetInstanceProcAddr, Layer::ovkInstance(), + info.physicalDevice, info.device, + static_cast(info.queue.first), info.queue.second, + conf.hdr, 1.0F / conf.flowScale, conf.multiplier - 1, shaderLoader); - // Create framegen context using AHB sharing - std::vector outAhbs; - outAhbs.reserve(conf.multiplier - 1); + std::vector outImages; + outImages.reserve(conf.multiplier - 1); for (size_t i = 0; i < static_cast(conf.multiplier - 1); ++i) - outAhbs.push_back(this->out_n.at(i).getAhb()); + outImages.push_back(this->out_n.at(i).handle()); int32_t ctxId; if (conf.performance) - ctxId = LSFG_3_1P::createContextFromAHB( - this->frame_0.getAhb(), this->frame_1.getAhb(), - outAhbs, extent, format); + ctxId = LSFG_3_1P::createContextFromImages( + this->frame_0.handle(), this->frame_1.handle(), + outImages, extent, format); else - ctxId = LSFG_3_1::createContextFromAHB( - this->frame_0.getAhb(), this->frame_1.getAhb(), - outAhbs, extent, format); + ctxId = LSFG_3_1::createContextFromImages( + this->frame_0.handle(), this->frame_1.handle(), + outImages, extent, format); + auto* lsfgDeleteContext = conf.performance + ? LSFG_3_1P::deleteContext : LSFG_3_1::deleteContext; this->lsfgCtxId = std::shared_ptr( new int32_t(ctxId), [lsfgDeleteContext = lsfgDeleteContext](const int32_t* id) { @@ -133,7 +349,30 @@ LsContext::LsContext(const Hooks::DeviceInfo& info, VkSwapchainKHR swapchain, unsetenv("DISABLE_LSFG"); // NOLINT - std::cerr << "lsfg-vk: Android AHB context created (id=" << ctxId << ")\n"; + // one-time transition of the shared images to GENERAL, the layout they are + // handed back and forth in + { + this->cmdPool = Mini::CommandPool(info.device, info.queue.first); + Mini::CommandBuffer initCmd(info.device, this->cmdPool); + Mini::Fence initFence(info.device, false); + initCmd.begin(); + auto toGeneral = [&](VkImage img) { + imageBarrier(initCmd.handle(), img, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL); + }; + toGeneral(this->frame_0.handle()); + toGeneral(this->frame_1.handle()); + for (auto& out : this->out_n) toGeneral(out.handle()); + initCmd.end(); + initCmd.submit(info.queue.second, {}, {}, initFence.handle()); + if (initFence.wait(1'000'000'000ull) != VK_SUCCESS) + throw LSFG::vulkan_error(VK_TIMEOUT, "Timed out initializing shared image layouts"); + } + + std::cerr << "lsfg-vk: single-device framegen context created (id=" << ctxId << ")\n"; #else // Desktop Linux path: use OPAQUE_FD-based image sharing @@ -197,6 +436,228 @@ LsContext::LsContext(const Hooks::DeviceInfo& info, VkSwapchainKHR swapchain, } } +#ifdef __ANDROID__ +// Pace the REAL game frame. Phase-locked to the display's vsync grid when the +// app publishes it (vsync.txt) — a free-running sleep clock beats against the +// display and drops a vblank at every phase crossing. The cap quantizes to +// whole vsync counts (30 = every 2nd vsync at 60Hz, every 4th at 120Hz). +void LsContext::paceBaseFrame(int fpsLimit) { + const auto& conf = Config::activeConf; + const int64_t targetNs = fpsLimit > 0 + ? 1'000'000'000LL / static_cast(fpsLimit) : 0; + const int64_t entryNs = nowNs(); + if (targetNs > 0 && entryNs - this->vsyncLastReadNs > 1'000'000'000LL) { + this->vsyncLastReadNs = entryNs; + if (!readVsyncFile(conf.config_file, &this->vsyncPhaseNs, &this->vsyncPeriodNs)) { + this->vsyncPhaseNs = 0; + this->vsyncPeriodNs = 0; + } + } + if (targetNs > 0 && this->vsyncPeriodNs > 0) { + const int64_t stepNs = std::max(1, + (targetNs + this->vsyncPeriodNs / 2) / this->vsyncPeriodNs) * this->vsyncPeriodNs; + int64_t due = this->pacerNextDueNs; + if (due <= 0 || due < entryNs - stepNs || due > entryNs + 2 * stepNs) { + due = this->vsyncPhaseNs + + ((entryNs - this->vsyncPhaseNs) / stepNs + 1) * stepNs; + } + sleepUntilNs(due); + this->pacerAnchorNs = due; + this->pacerNextDueNs = due + stepNs; + } else if (targetNs > 0 && this->pacerAnchorNs != 0 + && entryNs < this->pacerAnchorNs + targetNs) { + sleepUntilNs(this->pacerAnchorNs + targetNs); + this->pacerAnchorNs += targetNs; + this->pacerNextDueNs = 0; + } else { + this->pacerAnchorNs = nowNs(); + this->pacerNextDueNs = 0; + } +} +// debug_dump: read frame_0, frame_1 and every out_n back to the CPU and write +// them as PPMs under /dump/. Runs synchronously (fence wait) — +// the frame stalls, which is fine for a pixel-inspection mode. +void LsContext::dumpFrameImages(const Hooks::DeviceInfo& info, uint32_t presentIdx) { + const auto& conf = Config::activeConf; + if (conf.config_file.empty()) { this->dumpFailed = true; return; } + + const VkFormat layerFormat = conf.hdr + ? VK_FORMAT_R8G8B8A8_UNORM : VK_FORMAT_R16G16B16A16_SFLOAT; + + struct DumpEntry { + VkImage image; + std::string name; + VkExtent2D extent; + VkFormat format; + bool signedMap; + size_t offset; + size_t bytes; + }; + std::vector entries; + entries.push_back({ this->frame_0.handle(), "frame0", + this->extent, layerFormat, false, 0, 0 }); + entries.push_back({ this->frame_1.handle(), "frame1", + this->extent, layerFormat, false, 0, 0 }); + for (size_t i = 0; i < this->out_n.size(); i++) + entries.push_back({ this->out_n.at(i).handle(), "out" + std::to_string(i), + this->extent, layerFormat, false, 0, 0 }); + if (conf.performance) + for (const auto& dbg : LSFG_3_1P::debugInternalImages(*this->lsfgCtxId)) + entries.push_back({ dbg.image, dbg.name, dbg.extent, dbg.format, + dbg.format == VK_FORMAT_R16G16B16A16_SFLOAT, 0, 0 }); + + size_t bufferBytes = 0; + for (auto& e : entries) { + const size_t bpp = dumpBytesPerPixel(e.format); + if (bpp == 0) { + std::cerr << "lsfg-vk: debug_dump skipping " << e.name + << " (unsupported format " << e.format << ")\n"; + e.image = VK_NULL_HANDLE; + continue; + } + e.bytes = static_cast(e.extent.width) * e.extent.height * bpp; + e.offset = (bufferBytes + 7) & ~size_t{7}; + bufferBytes = e.offset + e.bytes; + } + entries.erase(std::remove_if(entries.begin(), entries.end(), + [](const DumpEntry& e) { return e.image == VK_NULL_HANDLE; }), entries.end()); + if (entries.empty() || bufferBytes == 0) { + this->dumpFailed = true; + return; + } + + VkBuffer buffer = VK_NULL_HANDLE; + VkDeviceMemory memory = VK_NULL_HANDLE; + auto cleanup = [&]() { + if (buffer != VK_NULL_HANDLE) + Layer::ovkDestroyBuffer(info.device, buffer, nullptr); + if (memory != VK_NULL_HANDLE) + Layer::ovkFreeMemory(info.device, memory, nullptr); + }; + auto fail = [&](const char* what, VkResult res) { + std::cerr << "lsfg-vk: debug_dump disabled: " << what + << " (" << res << ")\n"; + this->dumpFailed = true; + cleanup(); + }; + + const VkBufferCreateInfo bufInfo{ + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .size = bufferBytes, + .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + }; + VkResult res = Layer::ovkCreateBuffer(info.device, &bufInfo, nullptr, &buffer); + if (res != VK_SUCCESS) return fail("vkCreateBuffer", res); + + VkMemoryRequirements reqs{}; + Layer::ovkGetBufferMemoryRequirements(info.device, buffer, &reqs); + VkPhysicalDeviceMemoryProperties memProps{}; + Layer::ovkGetPhysicalDeviceMemoryProperties(info.physicalDevice, &memProps); + uint32_t typeIdx = UINT32_MAX; + VkMemoryPropertyFlags typeFlags = 0; + const VkMemoryPropertyFlags wanted = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT + | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + for (uint32_t i = 0; i < memProps.memoryTypeCount; i++) { + if (!(reqs.memoryTypeBits & (1U << i))) continue; + const auto flags = memProps.memoryTypes[i].propertyFlags; + if (!(flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)) continue; + if (typeIdx == UINT32_MAX || (flags & wanted) == wanted) { + typeIdx = i; + typeFlags = flags; + if ((flags & wanted) == wanted) break; + } + } + if (typeIdx == UINT32_MAX) + return fail("no host-visible memory type", VK_ERROR_FEATURE_NOT_PRESENT); + + const VkMemoryAllocateInfo allocInfo{ + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .allocationSize = reqs.size, + .memoryTypeIndex = typeIdx, + }; + res = Layer::ovkAllocateMemory(info.device, &allocInfo, nullptr, &memory); + if (res != VK_SUCCESS) return fail("vkAllocateMemory", res); + res = Layer::ovkBindBufferMemory(info.device, buffer, memory, 0); + if (res != VK_SUCCESS) return fail("vkBindBufferMemory", res); + + Mini::CommandBuffer cmd(info.device, this->cmdPool); + Mini::Fence fence(info.device, false); + cmd.begin(); + for (const auto& e : entries) { + imageBarrier(cmd.handle(), e.image, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_READ_BIT, + VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL); + const VkBufferImageCopy region{ + .bufferOffset = e.offset, + .imageSubresource = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .layerCount = 1, + }, + .imageExtent = { e.extent.width, e.extent.height, 1 }, + }; + Layer::ovkCmdCopyImageToBuffer(cmd.handle(), e.image, + VK_IMAGE_LAYOUT_GENERAL, buffer, 1, ®ion); + imageBarrier(cmd.handle(), e.image, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_READ_BIT, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, + VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL); + } + cmd.end(); + cmd.submit(info.queue.second, {}, {}, fence.handle()); + res = fence.wait(2'000'000'000ull); + if (res != VK_SUCCESS) return fail("dump fence wait", res); + + void* mapped = nullptr; + res = Layer::ovkMapMemory(info.device, memory, 0, VK_WHOLE_SIZE, 0, &mapped); + if (res != VK_SUCCESS) return fail("vkMapMemory", res); + if (!(typeFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) { + const VkMappedMemoryRange range{ + .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, + .memory = memory, + .size = VK_WHOLE_SIZE, + }; + Layer::ovkInvalidateMappedMemoryRanges(info.device, 1, &range); + } + + const auto dumpDir = std::filesystem::path(conf.config_file).parent_path() / "dump"; + std::error_code ec; + std::filesystem::create_directories(dumpDir, ec); + bool ok = !ec; + for (const auto& e : entries) { + if (!ok) break; + const auto path = dumpDir / + ("f" + std::to_string(this->frameIdx) + "_" + e.name + ".ppm"); + ok = writePpm(path, static_cast(mapped) + e.offset, + e.extent.width, e.extent.height, e.format, e.signedMap); + } + Layer::ovkUnmapMemory(info.device, memory); + cleanup(); + buffer = VK_NULL_HANDLE; + memory = VK_NULL_HANDLE; + if (!ok) return fail("writing dump files", VK_ERROR_UNKNOWN); + + std::ofstream index(dumpDir / "index.txt", std::ios::app); + index << "frame=" << this->frameIdx + << " input=" << (this->frameIdx % 2 == 0 ? "frame0" : "frame1") + << " presentIdx=" << presentIdx + << " mult=" << conf.multiplier << '\n'; + this->dumpDoneFrames++; + std::cerr << "lsfg-vk: dumped frame " << this->frameIdx + << " (" << this->dumpDoneFrames << "/" << conf.debugDump << ")\n"; +} +#endif + +VkResult LsContext::presentPassthrough(VkQueue queue, const VkPresentInfoKHR* pPresentInfo) { +#ifdef __ANDROID__ + this->paceBaseFrame(Config::activeConf.fpsLimit); +#endif + return Layer::ovkQueuePresentKHR(queue, pPresentInfo); +} + VkResult LsContext::present(const Hooks::DeviceInfo& info, const void* pNext, VkQueue queue, const std::vector& gameRenderSemaphores, uint32_t presentIdx) { const auto& conf = Config::activeConf; @@ -206,19 +667,84 @@ VkResult LsContext::present(const Hooks::DeviceInfo& info, const void* pNext, Vk // Android path: synchronous frame generation using waitIdle() // instead of OPAQUE_FD semaphore export which Turnip doesn't support. + // Base frame-rate limiter (see paceBaseFrame): the on-screen rate becomes + // fps_limit * multiplier. + this->paceBaseFrame(conf.fpsLimit); + + // Track the delivered real-frame interval as an EWMA. A cap is a ceiling, + // not a promise on base performance, so generated-frame spacing follows + // the measured cadence. + if (this->lastRealPresentNs != 0 && this->pacerAnchorNs > this->lastRealPresentNs) { + const uint64_t delta = static_cast( + this->pacerAnchorNs - this->lastRealPresentNs); + // A delta this large is a pause/resume, not a slow frame; feeding it + // into the EWMA would mis-pace generated frames for seconds while it + // decays. Re-learn from scratch instead. + if (delta > 250'000'000ull) + this->baseIntervalEwmaNs = 0; + else + this->baseIntervalEwmaNs = this->baseIntervalEwmaNs == 0 + ? delta : (this->baseIntervalEwmaNs * 7 + delta) / 8; + } + this->lastRealPresentNs = this->pacerAnchorNs; + + // If the previous frame's copy still hasn't signaled, the sync path is + // stuck; pass the frame through untouched. Enough consecutive timeouts + // disable framegen for this swapchain entirely. + const auto fenceRes = this->preCopyFence.wait(kSyncFenceTimeoutNs); + if (fenceRes == VK_SUCCESS) + this->copyFenceTimeouts = 0; + if (fenceRes != VK_SUCCESS) { + if (++this->copyFenceTimeouts >= kMaxFenceTimeouts && !this->forceDisabled) { + this->forceDisabled = true; + std::cerr << "lsfg-vk: disabling frame generation for this swapchain after " + << this->copyFenceTimeouts << " consecutive copy fence timeouts\n"; + } + const VkPresentInfoKHR passInfo{ + .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, + .pNext = pNext, + .waitSemaphoreCount = static_cast(gameRenderSemaphores.size()), + .pWaitSemaphores = gameRenderSemaphores.data(), + .swapchainCount = 1, + .pSwapchains = &this->swapchain, + .pImageIndices = &presentIdx, + }; + return Layer::ovkQueuePresentKHR(queue, &passInfo); + } + this->preCopyFence.reset(); + // 1. copy swapchain image to frame_0/frame_1 - // Use a simple semaphore (no fd export) to synchronize the copy + // Use a simple semaphore (no fd export) to synchronize the copy. + // The input AHB is written by this device and read by framegen's + // separate device, so the write must be RELEASED to the external queue + // family — without the ownership transfer the other device may read + // stale data (Adreno caches are not coherent across devices). pass.preCopySemaphores.at(1) = Mini::Semaphore(info.device); pass.preCopyBuf = Mini::CommandBuffer(info.device, this->cmdPool); pass.preCopyBuf.begin(); - - Utils::copyImage(pass.preCopyBuf.handle(), - this->swapchainImages.at(presentIdx), - this->frameIdx % 2 == 0 ? this->frame_0.handle() : this->frame_1.handle(), - this->extent.width, this->extent.height, - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, - true, false); - + { + VkCommandBuffer cmd = pass.preCopyBuf.handle(); + VkImage inputImg = this->frameIdx % 2 == 0 + ? this->frame_0.handle() : this->frame_1.handle(); + imageBarrier(cmd, this->swapchainImages.at(presentIdx), + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_READ_BIT, + VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + imageBarrier(cmd, inputImg, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + blitFull(cmd, this->swapchainImages.at(presentIdx), inputImg, + this->extent.width, this->extent.height); + imageBarrier(cmd, this->swapchainImages.at(presentIdx), + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_READ_BIT, + VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); + imageBarrier(cmd, inputImg, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_SHADER_READ_BIT, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL); + } pass.preCopyBuf.end(); std::vector gameRenderSemaphores2 = gameRenderSemaphores; @@ -226,17 +752,17 @@ VkResult LsContext::present(const Hooks::DeviceInfo& info, const void* pNext, Vk gameRenderSemaphores2.emplace_back(this->passInfos.at((this->frameIdx - 1) % 8) .preCopySemaphores.at(1).handle()); - // Submit the copy and wait for it to complete synchronously. - // On Android we need the copy to finish before calling presentContext - // because there's no FD-based cross-device semaphore to chain them. + // Submit the copy. Same-queue submission order sequences framegen after + // it; the fence is only the next frame's watchdog. The second signal + // semaphore lets the real present wait for the copy (and thus the game's + // rendering) even when every generated frame is skipped. + pass.preCopySemaphores.at(0) = Mini::Semaphore(info.device); pass.preCopyBuf.submit(info.queue.second, gameRenderSemaphores2, - { pass.preCopySemaphores.at(1).handle() }); - - // Wait for the pre-copy to finish before telling framegen to start. - // This is a device-wide idle wait — heavier than semaphore-based sync - // but necessary because OPAQUE_FD is not available on Android. - Layer::ovkQueueSubmit(info.queue.second, 0, nullptr, VK_NULL_HANDLE); + { pass.preCopySemaphores.at(0).handle(), + pass.preCopySemaphores.at(1).handle() }, + this->preCopyFence.handle()); + const int64_t workStartNs = nowNs(); // 2. Tell framegen to generate intermediary frames // presentContext(id, -1, {}) — no semaphore FDs, synchronous @@ -246,16 +772,53 @@ VkResult LsContext::present(const Hooks::DeviceInfo& info, const void* pNext, Vk else LSFG_3_1::presentContext(*this->lsfgCtxId, -1, noOutSems); - // 3. Wait for framegen's GPU work to finish before reading output images. - // framegen uses its own VkDevice internally, so we need waitIdle() - // to ensure cross-device synchronization. - if (conf.performance) - LSFG_3_1P::waitIdle(); - else - LSFG_3_1::waitIdle(); + // 3. No CPU-side wait: framegen submitted on the same queue, so the + // generated-frame copies below are ordered after it by submission + // order plus the image barriers. + this->statsWorkNs += static_cast(nowNs() - workStartNs); + + if (conf.debugDump > 0 && this->dumpDoneFrames < conf.debugDump + && !this->dumpFailed) + this->dumpFrameImages(info, presentIdx); + + // Late-frame guard interval: 1/multiplier of the measured base interval, + // anchored on the paced real frame. Presents are not delayed to it — a + // generated frame far past its slot is dropped to hold cadence. + uint64_t baseIntervalNs = this->baseIntervalEwmaNs; + if (baseIntervalNs == 0 && conf.fpsLimit > 0) + baseIntervalNs = 1'000'000'000ull / static_cast(conf.fpsLimit); + const uint64_t outIntervalNs = baseIntervalNs / conf.multiplier; // 4. Copy generated frames to swapchain images and present them + int lastPresentedGen = -1; for (size_t i = 0; i < static_cast(conf.multiplier - 1); i++) { + // A generated frame far past its slot is dropped to hold cadence + // (presenting it late would push everything after it off-pace). + // Present as early as possible and let the FIFO queue space the frames + // across vblanks (desktop-path behavior): early enqueue maximizes the + // margin to each frame's vblank. Sleeping toward the nominal slot was + // measured to HALVE that margin and cause periodic drops. Only a frame + // already past its slot is dropped, to hold cadence. + // Space the presents across the base period: generated frame i at + // anchor + i*out, the real frame at anchor + (mult-1)*out below. When + // the total present rate saturates the display this sleep is a no-op + // (queue back-pressure spaces them); when it doesn't (e.g. cap 30 on a + // 120Hz panel) it prevents the pair landing on adjacent vblanks. + if (outIntervalNs > 0) { + const int64_t dueNs = this->pacerAnchorNs + + static_cast(i * outIntervalNs); + const int64_t nowGenNs = nowNs(); + if (nowGenNs > dueNs + static_cast(outIntervalNs) * 3 / 2) { + this->statsGenSkips++; + continue; + } + if (nowGenNs > dueNs) + this->statsGenLateNs += static_cast(nowGenNs - dueNs); + else + this->statsGenSleepNs += static_cast(dueNs - nowGenNs); + sleepUntilNs(dueNs); + } + // acquire next swapchain image pass.acquireSemaphores.at(i) = Mini::Semaphore(info.device); uint32_t imageIdx{}; @@ -263,23 +826,53 @@ VkResult LsContext::present(const Hooks::DeviceInfo& info, const void* pNext, Vk pass.acquireSemaphores.at(i).handle(), VK_NULL_HANDLE, &imageIdx); if (res != VK_SUCCESS && res != VK_SUBOPTIMAL_KHR) throw LSFG::vulkan_error(res, "Failed to acquire next swapchain image"); + if (this->statsSeq.size() < 200) + this->statsSeq += 'a' + static_cast(imageIdx % 26); - // copy output image to swapchain image + // copy output image to swapchain image (same device: plain + // compute->transfer barriers order the read after framegen's write) pass.postCopySemaphores.at(i) = Mini::Semaphore(info.device); + pass.prevPostCopySemaphores.at(i) = Mini::Semaphore(info.device); pass.postCopyBufs.at(i) = Mini::CommandBuffer(info.device, this->cmdPool); pass.postCopyBufs.at(i).begin(); - - Utils::copyImage(pass.postCopyBufs.at(i).handle(), - this->out_n.at(i).handle(), - this->swapchainImages.at(imageIdx), - this->extent.width, this->extent.height, - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, - false, true); - + { + VkCommandBuffer cmd = pass.postCopyBufs.at(i).handle(); + // debug_inputs: 1 = previous-frame input image, 2 = the real + // swapchain image itself (pure same-format round trip) + VkImage genSrc = this->out_n.at(i).handle(); + if (conf.debugInputs == 1) + genSrc = this->frameIdx % 2 == 0 + ? this->frame_1.handle() : this->frame_0.handle(); + else if (conf.debugInputs == 2) + genSrc = this->swapchainImages.at(presentIdx); + imageBarrier(cmd, genSrc, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_SHADER_WRITE_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_READ_BIT, + conf.debugInputs == 2 ? VK_IMAGE_LAYOUT_PRESENT_SRC_KHR : VK_IMAGE_LAYOUT_GENERAL, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + imageBarrier(cmd, this->swapchainImages.at(imageIdx), + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + blitFull(cmd, genSrc, + this->swapchainImages.at(imageIdx), + this->extent.width, this->extent.height); + imageBarrier(cmd, genSrc, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_READ_BIT, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + conf.debugInputs == 2 ? VK_IMAGE_LAYOUT_PRESENT_SRC_KHR : VK_IMAGE_LAYOUT_GENERAL); + imageBarrier(cmd, this->swapchainImages.at(imageIdx), + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, + VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); + } pass.postCopyBufs.at(i).end(); pass.postCopyBufs.at(i).submit(info.queue.second, { pass.acquireSemaphores.at(i).handle() }, - { pass.postCopySemaphores.at(i).handle() }); + { pass.postCopySemaphores.at(i).handle(), + pass.prevPostCopySemaphores.at(i).handle() }); // present swapchain image VkSemaphore postCopySem = pass.postCopySemaphores.at(i).handle(); @@ -292,26 +885,78 @@ VkResult LsContext::present(const Hooks::DeviceInfo& info, const void* pNext, Vk .pSwapchains = &this->swapchain, .pImageIndices = &imageIdx, }; + const int64_t genPresentStartNs = nowNs(); res = Layer::ovkQueuePresentKHR(queue, &presentInfo); + this->statsGenPresentNs += static_cast(nowNs() - genPresentStartNs); if (res != VK_SUCCESS && res != VK_SUBOPTIMAL_KHR) throw LSFG::vulkan_error(res, "Failed to present swapchain image"); + lastPresentedGen = static_cast(i); + this->statsPresents++; } - // 5. present actual next frame (the real capture, not a generated one) - // Wait for the last post-copy to finish - pass.prevPostCopySemaphores.at(conf.multiplier - 1 - 1) = Mini::Semaphore(info.device); - VkSemaphore lastPostCopySem = pass.postCopySemaphores.at(conf.multiplier - 1 - 1).handle(); + // 5. present actual next frame (the real capture, not a generated one). + // Waits on the last generated frame's second post-copy semaphore + // (the first one was consumed by that frame's own present). If every + // generated frame was skipped no semaphore is needed: the copy fence + // wait above already proved the game's rendering finished. + if (outIntervalNs > 0 && conf.fpsLimit > 0) + sleepUntilNs(this->pacerAnchorNs + + static_cast((conf.multiplier - 1) * outIntervalNs)); + if (this->statsSeq.size() < 200) { + this->statsSeq += 'A' + static_cast(presentIdx % 26); + this->statsSeq += '0' + static_cast(gameRenderSemaphores.size() % 10); + } + std::vector finalWaits = { pass.preCopySemaphores.at(0).handle() }; + if (lastPresentedGen >= 0) + finalWaits.push_back(pass.prevPostCopySemaphores + .at(static_cast(lastPresentedGen)).handle()); const VkPresentInfoKHR finalPresentInfo{ .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, - .waitSemaphoreCount = 1, - .pWaitSemaphores = &lastPostCopySem, + .waitSemaphoreCount = static_cast(finalWaits.size()), + .pWaitSemaphores = finalWaits.data(), .swapchainCount = 1, .pSwapchains = &this->swapchain, .pImageIndices = &presentIdx, }; + const int64_t realPresentStartNs = nowNs(); auto res = Layer::ovkQueuePresentKHR(queue, &finalPresentInfo); + this->statsRealPresentNs += static_cast(nowNs() - realPresentStartNs); if (res != VK_SUCCESS && res != VK_SUBOPTIMAL_KHR) throw LSFG::vulkan_error(res, "Failed to present swapchain image"); + this->statsPresents++; + this->statsRealPresents++; + + // Publish measured fps + per-stage timing for the HUD and diagnosis. + const int64_t statsNow = nowNs(); + if (this->statsWindowStartNs == 0) { + this->statsWindowStartNs = statsNow; + } else if (statsNow - this->statsWindowStartNs >= 500'000'000LL) { + const double windowSec = + static_cast(statsNow - this->statsWindowStartNs) / 1e9; + const double frames = static_cast(this->statsRealPresents); + writeStats(conf.config_file, + static_cast(this->statsPresents) / windowSec, + frames / windowSec, + StatsDetail{ + .workMs = static_cast(this->statsWorkNs) / 1e6 / frames, + .genSleepMs = static_cast(this->statsGenSleepNs) / 1e6 / frames, + .genLateMs = static_cast(this->statsGenLateNs) / 1e6 / frames, + .genPresentMs = static_cast(this->statsGenPresentNs) / 1e6 / frames, + .realPresentMs = static_cast(this->statsRealPresentNs) / 1e6 / frames, + .genSkips = this->statsGenSkips, + .seq = this->statsSeq, + }); + this->statsSeq.clear(); + this->statsPresents = 0; + this->statsRealPresents = 0; + this->statsWorkNs = 0; + this->statsGenSleepNs = 0; + this->statsGenLateNs = 0; + this->statsGenPresentNs = 0; + this->statsRealPresentNs = 0; + this->statsGenSkips = 0; + this->statsWindowStartNs = statsNow; + } this->frameIdx++; return res; diff --git a/src/extract/mipmaps_replacement_spv.h b/src/extract/mipmaps_replacement_spv.h new file mode 100644 index 00000000..8ca48750 --- /dev/null +++ b/src/extract/mipmaps_replacement_spv.h @@ -0,0 +1,1028 @@ +unsigned char mipmaps_replacement_spv[] = { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x06, 0x01, 0x00, 0x0b, 0x00, 0x08, 0x00, + 0x11, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x31, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x02, 0x00, 0x32, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, + 0x2e, 0x34, 0x35, 0x30, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x10, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x5f, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0xbe, 0x00, 0x00, 0x00, + 0xf7, 0x00, 0x00, 0x00, 0x30, 0x01, 0x00, 0x00, 0x69, 0x01, 0x00, 0x00, + 0xa3, 0x01, 0x00, 0x00, 0xdd, 0x01, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, + 0x10, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xc2, 0x01, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x07, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x6c, 0x75, 0x6d, 0x61, 0x42, 0x6f, 0x78, 0x28, 0x76, 0x66, 0x32, 0x3b, + 0x76, 0x66, 0x32, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x5b, 0x00, 0x00, 0x00, 0x67, 0x69, 0x64, 0x00, 0x05, 0x00, 0x08, 0x00, + 0x5f, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x47, 0x6c, 0x6f, 0x62, 0x61, + 0x6c, 0x49, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, + 0x44, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x65, 0x00, 0x00, 0x00, + 0x64, 0x79, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x70, 0x00, 0x00, 0x00, + 0x64, 0x78, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x78, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x73, 0x7a, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x83, 0x00, 0x00, 0x00, + 0x6f, 0x30, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x86, 0x00, 0x00, 0x00, + 0x70, 0x70, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x99, 0x00, 0x00, 0x00, + 0x65, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x9f, 0x00, 0x00, 0x00, + 0x63, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xa9, 0x00, 0x00, 0x00, + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0xab, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0xbd, 0x00, 0x00, 0x00, 0x73, 0x7a, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0xbe, 0x00, 0x00, 0x00, 0x6f, 0x31, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0xc1, 0x00, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0xd9, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0xe2, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, + 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xe4, 0x00, 0x00, 0x00, + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0xf6, 0x00, 0x00, 0x00, 0x73, 0x7a, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0xf7, 0x00, 0x00, 0x00, 0x6f, 0x32, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0xfa, 0x00, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x0d, 0x01, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x12, 0x01, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x1b, 0x01, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x1d, 0x01, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, + 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x2f, 0x01, 0x00, 0x00, + 0x73, 0x7a, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x30, 0x01, 0x00, 0x00, + 0x6f, 0x33, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x33, 0x01, 0x00, 0x00, + 0x70, 0x70, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x46, 0x01, 0x00, 0x00, + 0x65, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x4b, 0x01, 0x00, 0x00, + 0x63, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x54, 0x01, 0x00, 0x00, + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x56, 0x01, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x68, 0x01, 0x00, 0x00, 0x73, 0x7a, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x69, 0x01, 0x00, 0x00, 0x6f, 0x34, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x6c, 0x01, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x80, 0x01, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x85, 0x01, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x8e, 0x01, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, + 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x90, 0x01, 0x00, 0x00, + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0xa2, 0x01, 0x00, 0x00, 0x73, 0x7a, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0xa3, 0x01, 0x00, 0x00, 0x6f, 0x35, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0xa6, 0x01, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0xba, 0x01, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0xbf, 0x01, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0xc8, 0x01, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0xca, 0x01, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, + 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0xdc, 0x01, 0x00, 0x00, + 0x73, 0x7a, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0xdd, 0x01, 0x00, 0x00, + 0x6f, 0x36, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0xe0, 0x01, 0x00, 0x00, + 0x70, 0x70, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0xf4, 0x01, 0x00, 0x00, + 0x65, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0xf9, 0x01, 0x00, 0x00, + 0x63, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x02, 0x02, 0x00, 0x00, + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x04, 0x02, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x0e, 0x02, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x04, 0x00, 0x0e, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x75, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x10, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x5f, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0x83, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x83, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x83, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, + 0xbe, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0xbe, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0xbe, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0xf7, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xf7, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0xf7, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0x30, 0x01, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x30, 0x01, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x30, 0x01, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, + 0x69, 0x01, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x69, 0x01, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x69, 0x01, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0xa3, 0x01, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xa3, 0x01, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0xa3, 0x01, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0xdd, 0x01, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0xdd, 0x01, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xdd, 0x01, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, + 0x0e, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0x0e, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x10, 0x02, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x10, 0x02, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x21, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x02, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x03, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xbe, + 0x2c, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, + 0x1d, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x04, 0x00, 0x23, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0x87, 0x16, 0x99, 0x3e, 0x2b, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0xa2, 0x45, 0x16, 0x3f, + 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, + 0xd5, 0x78, 0xe9, 0x3d, 0x2c, 0x00, 0x06, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, + 0x27, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3e, 0x2c, 0x00, 0x05, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, + 0x1d, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x31, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x04, 0x00, 0x59, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x5a, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, + 0x5c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x04, 0x00, 0x5d, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x5e, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x5e, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x04, 0x00, 0x60, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2b, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, 0x6d, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x02, 0x00, 0x6e, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x09, 0x00, 0x81, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x81, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x82, 0x00, 0x00, 0x00, + 0x83, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, + 0x5c, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2b, 0x00, 0x04, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x9a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x2b, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xa2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, + 0x2b, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, 0xb1, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x82, 0x00, 0x00, 0x00, + 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, + 0x58, 0x00, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x82, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x23, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x82, 0x00, 0x00, 0x00, 0x30, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2b, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, 0x5c, 0x01, 0x00, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x82, 0x00, 0x00, 0x00, + 0x69, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x6e, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x2b, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, 0x96, 0x01, 0x00, 0x00, + 0x1f, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x82, 0x00, 0x00, 0x00, + 0xa3, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, + 0x58, 0x00, 0x00, 0x00, 0xa8, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x2b, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, 0xd0, 0x01, 0x00, 0x00, + 0x3f, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x82, 0x00, 0x00, 0x00, + 0xdd, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, + 0x58, 0x00, 0x00, 0x00, 0xe2, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x2b, 0x00, 0x04, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x0c, 0x02, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x06, 0x00, 0x5d, 0x00, 0x00, 0x00, + 0x0d, 0x02, 0x00, 0x00, 0x0c, 0x02, 0x00, 0x00, 0x0c, 0x02, 0x00, 0x00, + 0x90, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x0e, 0x02, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0f, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x0e, 0x02, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x0f, 0x02, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x5a, 0x00, 0x00, 0x00, + 0x5b, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x64, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x5a, 0x00, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x5a, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x5a, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x99, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x9f, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0xa9, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, + 0xab, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x5a, 0x00, 0x00, 0x00, 0xbd, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x5a, 0x00, 0x00, 0x00, 0xc1, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, + 0xd4, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x00, 0x00, 0xd9, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0xe2, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, + 0xe4, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x5a, 0x00, 0x00, 0x00, 0xf6, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x5a, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x0d, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x12, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x1b, 0x01, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x1d, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x5a, 0x00, 0x00, 0x00, 0x2f, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x5a, 0x00, 0x00, 0x00, 0x33, 0x01, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x46, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x4b, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x54, 0x01, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x56, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x5a, 0x00, 0x00, 0x00, 0x68, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x5a, 0x00, 0x00, 0x00, 0x6c, 0x01, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x80, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x85, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x8e, 0x01, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x90, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x5a, 0x00, 0x00, 0x00, 0xa2, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x5a, 0x00, 0x00, 0x00, 0xa6, 0x01, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, + 0xba, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x00, 0x00, 0xbf, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0xc8, 0x01, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, + 0xca, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x5a, 0x00, 0x00, 0x00, 0xdc, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x5a, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, + 0xf4, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x00, 0x00, 0xf9, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x04, 0x02, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x5d, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x00, + 0x4f, 0x00, 0x07, 0x00, 0x60, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, + 0x61, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x04, 0x00, 0x59, 0x00, 0x00, 0x00, + 0x63, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x5b, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x65, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x02, 0x00, + 0x67, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x67, 0x00, 0x00, 0x00, + 0xf6, 0x00, 0x04, 0x00, 0x69, 0x00, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x02, 0x00, 0x6b, 0x00, 0x00, 0x00, + 0xf8, 0x00, 0x02, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, + 0xb1, 0x00, 0x05, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x00, + 0x6c, 0x00, 0x00, 0x00, 0x6d, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x04, 0x00, + 0x6f, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, + 0xf8, 0x00, 0x02, 0x00, 0x68, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x02, 0x00, + 0x71, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x71, 0x00, 0x00, 0x00, + 0xf6, 0x00, 0x04, 0x00, 0x73, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x02, 0x00, 0x75, 0x00, 0x00, 0x00, + 0xf8, 0x00, 0x02, 0x00, 0x75, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, + 0xb1, 0x00, 0x05, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, + 0x76, 0x00, 0x00, 0x00, 0x6d, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x04, 0x00, + 0x77, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, + 0xf8, 0x00, 0x02, 0x00, 0x72, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x59, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x00, + 0x50, 0x00, 0x05, 0x00, 0x59, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, + 0x6d, 0x00, 0x00, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x84, 0x00, 0x05, 0x00, + 0x59, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, + 0x7a, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x7c, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, + 0x50, 0x00, 0x05, 0x00, 0x59, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, + 0x7c, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, + 0x59, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x78, 0x00, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x81, 0x00, 0x00, 0x00, + 0x84, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0x68, 0x00, 0x04, 0x00, + 0x59, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x80, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x59, 0x00, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x86, 0x00, 0x00, 0x00, + 0x87, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x89, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, 0x8a, 0x00, 0x00, 0x00, + 0x89, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x8b, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, + 0x8b, 0x00, 0x00, 0x00, 0xb1, 0x00, 0x05, 0x00, 0x6e, 0x00, 0x00, 0x00, + 0x8d, 0x00, 0x00, 0x00, 0x8a, 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, + 0xf7, 0x00, 0x03, 0x00, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfa, 0x00, 0x04, 0x00, 0x8d, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, + 0x8f, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x8e, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x64, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, + 0x86, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x64, 0x00, 0x00, 0x00, 0x93, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, 0x93, 0x00, 0x00, 0x00, + 0xb1, 0x00, 0x05, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, + 0x92, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x02, 0x00, + 0x8f, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x8f, 0x00, 0x00, 0x00, + 0xf5, 0x00, 0x07, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, + 0x8d, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, + 0x8e, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x03, 0x00, 0x98, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x04, 0x00, 0x96, 0x00, 0x00, 0x00, + 0x97, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, + 0x97, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x59, 0x00, 0x00, 0x00, + 0x9b, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x9b, 0x00, 0x00, 0x00, + 0x50, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x00, 0x00, + 0x9a, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x00, 0x00, + 0x9c, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x99, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x59, 0x00, 0x00, 0x00, + 0xa0, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0xa1, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, + 0x50, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0xa3, 0x00, 0x00, 0x00, + 0xa2, 0x00, 0x00, 0x00, 0xa2, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x07, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0xa1, 0x00, 0x00, 0x00, + 0xa3, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0xa5, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x07, 0x00, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, + 0xa5, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x9f, 0x00, 0x00, 0x00, + 0xa6, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x81, 0x00, 0x00, 0x00, + 0xa7, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x59, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, + 0x9f, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0xa9, 0x00, 0x00, 0x00, + 0xaa, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0xac, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0xab, 0x00, 0x00, 0x00, 0xac, 0x00, 0x00, 0x00, 0x39, 0x00, 0x06, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xad, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0xa9, 0x00, 0x00, 0x00, 0xab, 0x00, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, + 0x21, 0x00, 0x00, 0x00, 0xae, 0x00, 0x00, 0x00, 0xad, 0x00, 0x00, 0x00, + 0xad, 0x00, 0x00, 0x00, 0xad, 0x00, 0x00, 0x00, 0xad, 0x00, 0x00, 0x00, + 0x63, 0x00, 0x04, 0x00, 0xa7, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x00, 0x00, + 0xae, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x02, 0x00, 0x98, 0x00, 0x00, 0x00, + 0xf8, 0x00, 0x02, 0x00, 0x98, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, + 0x64, 0x00, 0x00, 0x00, 0xaf, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, + 0x88, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, + 0xb0, 0x00, 0x00, 0x00, 0xaf, 0x00, 0x00, 0x00, 0xc7, 0x00, 0x05, 0x00, + 0x58, 0x00, 0x00, 0x00, 0xb2, 0x00, 0x00, 0x00, 0xb0, 0x00, 0x00, 0x00, + 0xb1, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x05, 0x00, 0x6e, 0x00, 0x00, 0x00, + 0xb3, 0x00, 0x00, 0x00, 0xb2, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, + 0xf7, 0x00, 0x03, 0x00, 0xb5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfa, 0x00, 0x04, 0x00, 0xb3, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, + 0xb5, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0xb4, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x64, 0x00, 0x00, 0x00, 0xb6, 0x00, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x58, 0x00, 0x00, 0x00, 0xb7, 0x00, 0x00, 0x00, 0xb6, 0x00, 0x00, 0x00, + 0xc7, 0x00, 0x05, 0x00, 0x58, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00, + 0xb7, 0x00, 0x00, 0x00, 0xb1, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x05, 0x00, + 0x6e, 0x00, 0x00, 0x00, 0xb9, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00, + 0x66, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x02, 0x00, 0xb5, 0x00, 0x00, 0x00, + 0xf8, 0x00, 0x02, 0x00, 0xb5, 0x00, 0x00, 0x00, 0xf5, 0x00, 0x07, 0x00, + 0x6e, 0x00, 0x00, 0x00, 0xba, 0x00, 0x00, 0x00, 0xb3, 0x00, 0x00, 0x00, + 0x98, 0x00, 0x00, 0x00, 0xb9, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, + 0xf7, 0x00, 0x03, 0x00, 0xbc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfa, 0x00, 0x04, 0x00, 0xba, 0x00, 0x00, 0x00, 0xbb, 0x00, 0x00, 0x00, + 0xbc, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0xbb, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x81, 0x00, 0x00, 0x00, 0xbf, 0x00, 0x00, 0x00, + 0xbe, 0x00, 0x00, 0x00, 0x68, 0x00, 0x04, 0x00, 0x59, 0x00, 0x00, 0x00, + 0xc0, 0x00, 0x00, 0x00, 0xbf, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0xbd, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x59, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, + 0x50, 0x00, 0x05, 0x00, 0x59, 0x00, 0x00, 0x00, 0xc3, 0x00, 0x00, 0x00, + 0xb1, 0x00, 0x00, 0x00, 0xb1, 0x00, 0x00, 0x00, 0xc3, 0x00, 0x05, 0x00, + 0x59, 0x00, 0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, + 0xc3, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0xc1, 0x00, 0x00, 0x00, + 0xc4, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x64, 0x00, 0x00, 0x00, + 0xc5, 0x00, 0x00, 0x00, 0xc1, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, 0xc6, 0x00, 0x00, 0x00, + 0xc5, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x64, 0x00, 0x00, 0x00, + 0xc7, 0x00, 0x00, 0x00, 0xbd, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, 0xc8, 0x00, 0x00, 0x00, + 0xc7, 0x00, 0x00, 0x00, 0xb1, 0x00, 0x05, 0x00, 0x6e, 0x00, 0x00, 0x00, + 0xc9, 0x00, 0x00, 0x00, 0xc6, 0x00, 0x00, 0x00, 0xc8, 0x00, 0x00, 0x00, + 0xf7, 0x00, 0x03, 0x00, 0xcb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfa, 0x00, 0x04, 0x00, 0xc9, 0x00, 0x00, 0x00, 0xca, 0x00, 0x00, 0x00, + 0xcb, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0xca, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x64, 0x00, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, + 0xc1, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x58, 0x00, 0x00, 0x00, 0xcd, 0x00, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x64, 0x00, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, + 0xbd, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x58, 0x00, 0x00, 0x00, 0xcf, 0x00, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, + 0xb1, 0x00, 0x05, 0x00, 0x6e, 0x00, 0x00, 0x00, 0xd0, 0x00, 0x00, 0x00, + 0xcd, 0x00, 0x00, 0x00, 0xcf, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x02, 0x00, + 0xcb, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0xcb, 0x00, 0x00, 0x00, + 0xf5, 0x00, 0x07, 0x00, 0x6e, 0x00, 0x00, 0x00, 0xd1, 0x00, 0x00, 0x00, + 0xc9, 0x00, 0x00, 0x00, 0xbb, 0x00, 0x00, 0x00, 0xd0, 0x00, 0x00, 0x00, + 0xca, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x03, 0x00, 0xd3, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x04, 0x00, 0xd1, 0x00, 0x00, 0x00, + 0xd2, 0x00, 0x00, 0x00, 0xd3, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, + 0xd2, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x59, 0x00, 0x00, 0x00, + 0xd5, 0x00, 0x00, 0x00, 0xbd, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0xd6, 0x00, 0x00, 0x00, 0xd5, 0x00, 0x00, 0x00, + 0x50, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0xd7, 0x00, 0x00, 0x00, + 0x9a, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, + 0x07, 0x00, 0x00, 0x00, 0xd8, 0x00, 0x00, 0x00, 0xd7, 0x00, 0x00, 0x00, + 0xd6, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0xd4, 0x00, 0x00, 0x00, + 0xd8, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x59, 0x00, 0x00, 0x00, + 0xda, 0x00, 0x00, 0x00, 0xc1, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0xdb, 0x00, 0x00, 0x00, 0xda, 0x00, 0x00, 0x00, + 0x50, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0xdc, 0x00, 0x00, 0x00, + 0xa2, 0x00, 0x00, 0x00, 0xa2, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x07, 0x00, 0x00, 0x00, 0xdd, 0x00, 0x00, 0x00, 0xdb, 0x00, 0x00, 0x00, + 0xdc, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0xde, 0x00, 0x00, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x07, 0x00, 0x00, 0x00, 0xdf, 0x00, 0x00, 0x00, 0xdd, 0x00, 0x00, 0x00, + 0xde, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0xd9, 0x00, 0x00, 0x00, + 0xdf, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x81, 0x00, 0x00, 0x00, + 0xe0, 0x00, 0x00, 0x00, 0xbe, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x59, 0x00, 0x00, 0x00, 0xe1, 0x00, 0x00, 0x00, 0xc1, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0xe3, 0x00, 0x00, 0x00, + 0xd9, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0xe2, 0x00, 0x00, 0x00, + 0xe3, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0xe5, 0x00, 0x00, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0xe4, 0x00, 0x00, 0x00, 0xe5, 0x00, 0x00, 0x00, 0x39, 0x00, 0x06, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xe6, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0xe2, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, + 0x21, 0x00, 0x00, 0x00, 0xe7, 0x00, 0x00, 0x00, 0xe6, 0x00, 0x00, 0x00, + 0xe6, 0x00, 0x00, 0x00, 0xe6, 0x00, 0x00, 0x00, 0xe6, 0x00, 0x00, 0x00, + 0x63, 0x00, 0x04, 0x00, 0xe0, 0x00, 0x00, 0x00, 0xe1, 0x00, 0x00, 0x00, + 0xe7, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x02, 0x00, 0xd3, 0x00, 0x00, 0x00, + 0xf8, 0x00, 0x02, 0x00, 0xd3, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x02, 0x00, + 0xbc, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0xbc, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x64, 0x00, 0x00, 0x00, 0xe8, 0x00, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x58, 0x00, 0x00, 0x00, 0xe9, 0x00, 0x00, 0x00, 0xe8, 0x00, 0x00, 0x00, + 0xc7, 0x00, 0x05, 0x00, 0x58, 0x00, 0x00, 0x00, 0xeb, 0x00, 0x00, 0x00, + 0xe9, 0x00, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x05, 0x00, + 0x6e, 0x00, 0x00, 0x00, 0xec, 0x00, 0x00, 0x00, 0xeb, 0x00, 0x00, 0x00, + 0x66, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x03, 0x00, 0xee, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x04, 0x00, 0xec, 0x00, 0x00, 0x00, + 0xed, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, + 0xed, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x64, 0x00, 0x00, 0x00, + 0xef, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, + 0xef, 0x00, 0x00, 0x00, 0xc7, 0x00, 0x05, 0x00, 0x58, 0x00, 0x00, 0x00, + 0xf1, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00, + 0xaa, 0x00, 0x05, 0x00, 0x6e, 0x00, 0x00, 0x00, 0xf2, 0x00, 0x00, 0x00, + 0xf1, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x02, 0x00, + 0xee, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0xee, 0x00, 0x00, 0x00, + 0xf5, 0x00, 0x07, 0x00, 0x6e, 0x00, 0x00, 0x00, 0xf3, 0x00, 0x00, 0x00, + 0xec, 0x00, 0x00, 0x00, 0xbc, 0x00, 0x00, 0x00, 0xf2, 0x00, 0x00, 0x00, + 0xed, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x03, 0x00, 0xf5, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x04, 0x00, 0xf3, 0x00, 0x00, 0x00, + 0xf4, 0x00, 0x00, 0x00, 0xf5, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, + 0xf4, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x81, 0x00, 0x00, 0x00, + 0xf8, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x00, 0x00, 0x68, 0x00, 0x04, 0x00, + 0x59, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0xf6, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x59, 0x00, 0x00, 0x00, 0xfb, 0x00, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x59, 0x00, 0x00, 0x00, + 0xfc, 0x00, 0x00, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x6d, 0x00, 0x00, 0x00, + 0xc3, 0x00, 0x05, 0x00, 0x59, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x00, 0x00, + 0xfb, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0xfa, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, + 0x64, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x00, 0x00, + 0x88, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xf6, 0x00, 0x00, 0x00, + 0x88, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xb1, 0x00, 0x05, 0x00, + 0x6e, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0xf7, 0x00, 0x03, 0x00, 0x04, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x04, 0x00, 0x02, 0x01, 0x00, 0x00, + 0x03, 0x01, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, + 0x03, 0x01, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x05, 0x01, 0x00, 0x00, 0xfa, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, 0x06, 0x01, 0x00, 0x00, + 0x05, 0x01, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x07, 0x01, 0x00, 0x00, 0xf6, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, + 0x07, 0x01, 0x00, 0x00, 0xb1, 0x00, 0x05, 0x00, 0x6e, 0x00, 0x00, 0x00, + 0x09, 0x01, 0x00, 0x00, 0x06, 0x01, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, + 0xf9, 0x00, 0x02, 0x00, 0x04, 0x01, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, + 0x04, 0x01, 0x00, 0x00, 0xf5, 0x00, 0x07, 0x00, 0x6e, 0x00, 0x00, 0x00, + 0x0a, 0x01, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x00, + 0x09, 0x01, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0xf7, 0x00, 0x03, 0x00, + 0x0c, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x04, 0x00, + 0x0a, 0x01, 0x00, 0x00, 0x0b, 0x01, 0x00, 0x00, 0x0c, 0x01, 0x00, 0x00, + 0xf8, 0x00, 0x02, 0x00, 0x0b, 0x01, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x59, 0x00, 0x00, 0x00, 0x0e, 0x01, 0x00, 0x00, 0xf6, 0x00, 0x00, 0x00, + 0x6f, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0f, 0x01, 0x00, 0x00, + 0x0e, 0x01, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x10, 0x01, 0x00, 0x00, 0x9a, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0x00, + 0x88, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x11, 0x01, 0x00, 0x00, + 0x10, 0x01, 0x00, 0x00, 0x0f, 0x01, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x0d, 0x01, 0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x59, 0x00, 0x00, 0x00, 0x13, 0x01, 0x00, 0x00, 0xfa, 0x00, 0x00, 0x00, + 0x6f, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x14, 0x01, 0x00, 0x00, + 0x13, 0x01, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x15, 0x01, 0x00, 0x00, 0xa2, 0x00, 0x00, 0x00, 0xa2, 0x00, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x16, 0x01, 0x00, 0x00, + 0x14, 0x01, 0x00, 0x00, 0x15, 0x01, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x17, 0x01, 0x00, 0x00, 0x0d, 0x01, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x18, 0x01, 0x00, 0x00, + 0x16, 0x01, 0x00, 0x00, 0x17, 0x01, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x12, 0x01, 0x00, 0x00, 0x18, 0x01, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x81, 0x00, 0x00, 0x00, 0x19, 0x01, 0x00, 0x00, 0xf7, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x59, 0x00, 0x00, 0x00, 0x1a, 0x01, 0x00, 0x00, + 0xfa, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x1c, 0x01, 0x00, 0x00, 0x12, 0x01, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x1b, 0x01, 0x00, 0x00, 0x1c, 0x01, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x1e, 0x01, 0x00, 0x00, 0x0d, 0x01, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x1d, 0x01, 0x00, 0x00, 0x1e, 0x01, 0x00, 0x00, + 0x39, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1f, 0x01, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x1b, 0x01, 0x00, 0x00, 0x1d, 0x01, 0x00, 0x00, + 0x50, 0x00, 0x07, 0x00, 0x21, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, + 0x1f, 0x01, 0x00, 0x00, 0x1f, 0x01, 0x00, 0x00, 0x1f, 0x01, 0x00, 0x00, + 0x1f, 0x01, 0x00, 0x00, 0x63, 0x00, 0x04, 0x00, 0x19, 0x01, 0x00, 0x00, + 0x1a, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0xf9, 0x00, 0x02, 0x00, + 0x0c, 0x01, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x0c, 0x01, 0x00, 0x00, + 0xf9, 0x00, 0x02, 0x00, 0xf5, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, + 0xf5, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x21, 0x01, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, 0x22, 0x01, 0x00, 0x00, + 0x21, 0x01, 0x00, 0x00, 0xc7, 0x00, 0x05, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x24, 0x01, 0x00, 0x00, 0x22, 0x01, 0x00, 0x00, 0x23, 0x01, 0x00, 0x00, + 0xaa, 0x00, 0x05, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x25, 0x01, 0x00, 0x00, + 0x24, 0x01, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x03, 0x00, + 0x27, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x04, 0x00, + 0x25, 0x01, 0x00, 0x00, 0x26, 0x01, 0x00, 0x00, 0x27, 0x01, 0x00, 0x00, + 0xf8, 0x00, 0x02, 0x00, 0x26, 0x01, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x28, 0x01, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, + 0x90, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x29, 0x01, 0x00, 0x00, 0x28, 0x01, 0x00, 0x00, 0xc7, 0x00, 0x05, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x2a, 0x01, 0x00, 0x00, 0x29, 0x01, 0x00, 0x00, + 0x23, 0x01, 0x00, 0x00, 0xaa, 0x00, 0x05, 0x00, 0x6e, 0x00, 0x00, 0x00, + 0x2b, 0x01, 0x00, 0x00, 0x2a, 0x01, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, + 0xf9, 0x00, 0x02, 0x00, 0x27, 0x01, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, + 0x27, 0x01, 0x00, 0x00, 0xf5, 0x00, 0x07, 0x00, 0x6e, 0x00, 0x00, 0x00, + 0x2c, 0x01, 0x00, 0x00, 0x25, 0x01, 0x00, 0x00, 0xf5, 0x00, 0x00, 0x00, + 0x2b, 0x01, 0x00, 0x00, 0x26, 0x01, 0x00, 0x00, 0xf7, 0x00, 0x03, 0x00, + 0x2e, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x04, 0x00, + 0x2c, 0x01, 0x00, 0x00, 0x2d, 0x01, 0x00, 0x00, 0x2e, 0x01, 0x00, 0x00, + 0xf8, 0x00, 0x02, 0x00, 0x2d, 0x01, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x81, 0x00, 0x00, 0x00, 0x31, 0x01, 0x00, 0x00, 0x30, 0x01, 0x00, 0x00, + 0x68, 0x00, 0x04, 0x00, 0x59, 0x00, 0x00, 0x00, 0x32, 0x01, 0x00, 0x00, + 0x31, 0x01, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x2f, 0x01, 0x00, 0x00, + 0x32, 0x01, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x59, 0x00, 0x00, 0x00, + 0x34, 0x01, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, + 0x59, 0x00, 0x00, 0x00, 0x35, 0x01, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00, + 0xea, 0x00, 0x00, 0x00, 0xc3, 0x00, 0x05, 0x00, 0x59, 0x00, 0x00, 0x00, + 0x36, 0x01, 0x00, 0x00, 0x34, 0x01, 0x00, 0x00, 0x35, 0x01, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x33, 0x01, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x64, 0x00, 0x00, 0x00, 0x37, 0x01, 0x00, 0x00, + 0x33, 0x01, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, 0x37, 0x01, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x64, 0x00, 0x00, 0x00, 0x39, 0x01, 0x00, 0x00, + 0x2f, 0x01, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x3a, 0x01, 0x00, 0x00, 0x39, 0x01, 0x00, 0x00, + 0xb1, 0x00, 0x05, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x3b, 0x01, 0x00, 0x00, + 0x38, 0x01, 0x00, 0x00, 0x3a, 0x01, 0x00, 0x00, 0xf7, 0x00, 0x03, 0x00, + 0x3d, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x04, 0x00, + 0x3b, 0x01, 0x00, 0x00, 0x3c, 0x01, 0x00, 0x00, 0x3d, 0x01, 0x00, 0x00, + 0xf8, 0x00, 0x02, 0x00, 0x3c, 0x01, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x3e, 0x01, 0x00, 0x00, 0x33, 0x01, 0x00, 0x00, + 0x90, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x3f, 0x01, 0x00, 0x00, 0x3e, 0x01, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x2f, 0x01, 0x00, 0x00, + 0x90, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x41, 0x01, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0xb1, 0x00, 0x05, 0x00, + 0x6e, 0x00, 0x00, 0x00, 0x42, 0x01, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, + 0x41, 0x01, 0x00, 0x00, 0xf9, 0x00, 0x02, 0x00, 0x3d, 0x01, 0x00, 0x00, + 0xf8, 0x00, 0x02, 0x00, 0x3d, 0x01, 0x00, 0x00, 0xf5, 0x00, 0x07, 0x00, + 0x6e, 0x00, 0x00, 0x00, 0x43, 0x01, 0x00, 0x00, 0x3b, 0x01, 0x00, 0x00, + 0x2d, 0x01, 0x00, 0x00, 0x42, 0x01, 0x00, 0x00, 0x3c, 0x01, 0x00, 0x00, + 0xf7, 0x00, 0x03, 0x00, 0x45, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfa, 0x00, 0x04, 0x00, 0x43, 0x01, 0x00, 0x00, 0x44, 0x01, 0x00, 0x00, + 0x45, 0x01, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x44, 0x01, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x59, 0x00, 0x00, 0x00, 0x47, 0x01, 0x00, 0x00, + 0x2f, 0x01, 0x00, 0x00, 0x6f, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x48, 0x01, 0x00, 0x00, 0x47, 0x01, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x49, 0x01, 0x00, 0x00, 0x9a, 0x00, 0x00, 0x00, + 0x9a, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x4a, 0x01, 0x00, 0x00, 0x49, 0x01, 0x00, 0x00, 0x48, 0x01, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x46, 0x01, 0x00, 0x00, 0x4a, 0x01, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x59, 0x00, 0x00, 0x00, 0x4c, 0x01, 0x00, 0x00, + 0x33, 0x01, 0x00, 0x00, 0x6f, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x4d, 0x01, 0x00, 0x00, 0x4c, 0x01, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x4e, 0x01, 0x00, 0x00, 0xa2, 0x00, 0x00, 0x00, + 0xa2, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x4f, 0x01, 0x00, 0x00, 0x4d, 0x01, 0x00, 0x00, 0x4e, 0x01, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x50, 0x01, 0x00, 0x00, + 0x46, 0x01, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x51, 0x01, 0x00, 0x00, 0x4f, 0x01, 0x00, 0x00, 0x50, 0x01, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x4b, 0x01, 0x00, 0x00, 0x51, 0x01, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x81, 0x00, 0x00, 0x00, 0x52, 0x01, 0x00, 0x00, + 0x30, 0x01, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x59, 0x00, 0x00, 0x00, + 0x53, 0x01, 0x00, 0x00, 0x33, 0x01, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x55, 0x01, 0x00, 0x00, 0x4b, 0x01, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x54, 0x01, 0x00, 0x00, 0x55, 0x01, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x57, 0x01, 0x00, 0x00, + 0x46, 0x01, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x56, 0x01, 0x00, 0x00, + 0x57, 0x01, 0x00, 0x00, 0x39, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x58, 0x01, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x54, 0x01, 0x00, 0x00, + 0x56, 0x01, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x59, 0x01, 0x00, 0x00, 0x58, 0x01, 0x00, 0x00, 0x58, 0x01, 0x00, 0x00, + 0x58, 0x01, 0x00, 0x00, 0x58, 0x01, 0x00, 0x00, 0x63, 0x00, 0x04, 0x00, + 0x52, 0x01, 0x00, 0x00, 0x53, 0x01, 0x00, 0x00, 0x59, 0x01, 0x00, 0x00, + 0xf9, 0x00, 0x02, 0x00, 0x45, 0x01, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, + 0x45, 0x01, 0x00, 0x00, 0xf9, 0x00, 0x02, 0x00, 0x2e, 0x01, 0x00, 0x00, + 0xf8, 0x00, 0x02, 0x00, 0x2e, 0x01, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x5a, 0x01, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, + 0x88, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x5b, 0x01, 0x00, 0x00, 0x5a, 0x01, 0x00, 0x00, 0xc7, 0x00, 0x05, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x5d, 0x01, 0x00, 0x00, 0x5b, 0x01, 0x00, 0x00, + 0x5c, 0x01, 0x00, 0x00, 0xaa, 0x00, 0x05, 0x00, 0x6e, 0x00, 0x00, 0x00, + 0x5e, 0x01, 0x00, 0x00, 0x5d, 0x01, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, + 0xf7, 0x00, 0x03, 0x00, 0x60, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfa, 0x00, 0x04, 0x00, 0x5e, 0x01, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00, + 0x60, 0x01, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x5f, 0x01, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x64, 0x00, 0x00, 0x00, 0x61, 0x01, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x62, 0x01, 0x00, 0x00, 0x61, 0x01, 0x00, 0x00, + 0xc7, 0x00, 0x05, 0x00, 0x58, 0x00, 0x00, 0x00, 0x63, 0x01, 0x00, 0x00, + 0x62, 0x01, 0x00, 0x00, 0x5c, 0x01, 0x00, 0x00, 0xaa, 0x00, 0x05, 0x00, + 0x6e, 0x00, 0x00, 0x00, 0x64, 0x01, 0x00, 0x00, 0x63, 0x01, 0x00, 0x00, + 0x66, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x02, 0x00, 0x60, 0x01, 0x00, 0x00, + 0xf8, 0x00, 0x02, 0x00, 0x60, 0x01, 0x00, 0x00, 0xf5, 0x00, 0x07, 0x00, + 0x6e, 0x00, 0x00, 0x00, 0x65, 0x01, 0x00, 0x00, 0x5e, 0x01, 0x00, 0x00, + 0x2e, 0x01, 0x00, 0x00, 0x64, 0x01, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00, + 0xf7, 0x00, 0x03, 0x00, 0x67, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfa, 0x00, 0x04, 0x00, 0x65, 0x01, 0x00, 0x00, 0x66, 0x01, 0x00, 0x00, + 0x67, 0x01, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x66, 0x01, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x81, 0x00, 0x00, 0x00, 0x6a, 0x01, 0x00, 0x00, + 0x69, 0x01, 0x00, 0x00, 0x68, 0x00, 0x04, 0x00, 0x59, 0x00, 0x00, 0x00, + 0x6b, 0x01, 0x00, 0x00, 0x6a, 0x01, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x68, 0x01, 0x00, 0x00, 0x6b, 0x01, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x59, 0x00, 0x00, 0x00, 0x6d, 0x01, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, + 0x50, 0x00, 0x05, 0x00, 0x59, 0x00, 0x00, 0x00, 0x6f, 0x01, 0x00, 0x00, + 0x6e, 0x01, 0x00, 0x00, 0x6e, 0x01, 0x00, 0x00, 0xc3, 0x00, 0x05, 0x00, + 0x59, 0x00, 0x00, 0x00, 0x70, 0x01, 0x00, 0x00, 0x6d, 0x01, 0x00, 0x00, + 0x6f, 0x01, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x6c, 0x01, 0x00, 0x00, + 0x70, 0x01, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x71, 0x01, 0x00, 0x00, 0x6c, 0x01, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, 0x72, 0x01, 0x00, 0x00, + 0x71, 0x01, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x73, 0x01, 0x00, 0x00, 0x68, 0x01, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, 0x74, 0x01, 0x00, 0x00, + 0x73, 0x01, 0x00, 0x00, 0xb1, 0x00, 0x05, 0x00, 0x6e, 0x00, 0x00, 0x00, + 0x75, 0x01, 0x00, 0x00, 0x72, 0x01, 0x00, 0x00, 0x74, 0x01, 0x00, 0x00, + 0xf7, 0x00, 0x03, 0x00, 0x77, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfa, 0x00, 0x04, 0x00, 0x75, 0x01, 0x00, 0x00, 0x76, 0x01, 0x00, 0x00, + 0x77, 0x01, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x76, 0x01, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x64, 0x00, 0x00, 0x00, 0x78, 0x01, 0x00, 0x00, + 0x6c, 0x01, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x79, 0x01, 0x00, 0x00, 0x78, 0x01, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x64, 0x00, 0x00, 0x00, 0x7a, 0x01, 0x00, 0x00, + 0x68, 0x01, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x7b, 0x01, 0x00, 0x00, 0x7a, 0x01, 0x00, 0x00, + 0xb1, 0x00, 0x05, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x7c, 0x01, 0x00, 0x00, + 0x79, 0x01, 0x00, 0x00, 0x7b, 0x01, 0x00, 0x00, 0xf9, 0x00, 0x02, 0x00, + 0x77, 0x01, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x77, 0x01, 0x00, 0x00, + 0xf5, 0x00, 0x07, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x7d, 0x01, 0x00, 0x00, + 0x75, 0x01, 0x00, 0x00, 0x66, 0x01, 0x00, 0x00, 0x7c, 0x01, 0x00, 0x00, + 0x76, 0x01, 0x00, 0x00, 0xf7, 0x00, 0x03, 0x00, 0x7f, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x04, 0x00, 0x7d, 0x01, 0x00, 0x00, + 0x7e, 0x01, 0x00, 0x00, 0x7f, 0x01, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, + 0x7e, 0x01, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x59, 0x00, 0x00, 0x00, + 0x81, 0x01, 0x00, 0x00, 0x68, 0x01, 0x00, 0x00, 0x6f, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x82, 0x01, 0x00, 0x00, 0x81, 0x01, 0x00, 0x00, + 0x50, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x83, 0x01, 0x00, 0x00, + 0x9a, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x84, 0x01, 0x00, 0x00, 0x83, 0x01, 0x00, 0x00, + 0x82, 0x01, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x80, 0x01, 0x00, 0x00, + 0x84, 0x01, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x59, 0x00, 0x00, 0x00, + 0x86, 0x01, 0x00, 0x00, 0x6c, 0x01, 0x00, 0x00, 0x6f, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x87, 0x01, 0x00, 0x00, 0x86, 0x01, 0x00, 0x00, + 0x50, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x88, 0x01, 0x00, 0x00, + 0xa2, 0x00, 0x00, 0x00, 0xa2, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x89, 0x01, 0x00, 0x00, 0x87, 0x01, 0x00, 0x00, + 0x88, 0x01, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x8a, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x8b, 0x01, 0x00, 0x00, 0x89, 0x01, 0x00, 0x00, + 0x8a, 0x01, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x85, 0x01, 0x00, 0x00, + 0x8b, 0x01, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x81, 0x00, 0x00, 0x00, + 0x8c, 0x01, 0x00, 0x00, 0x69, 0x01, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x59, 0x00, 0x00, 0x00, 0x8d, 0x01, 0x00, 0x00, 0x6c, 0x01, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x8f, 0x01, 0x00, 0x00, + 0x85, 0x01, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x8e, 0x01, 0x00, 0x00, + 0x8f, 0x01, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x91, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x90, 0x01, 0x00, 0x00, 0x91, 0x01, 0x00, 0x00, 0x39, 0x00, 0x06, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x92, 0x01, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x8e, 0x01, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x93, 0x01, 0x00, 0x00, 0x92, 0x01, 0x00, 0x00, + 0x92, 0x01, 0x00, 0x00, 0x92, 0x01, 0x00, 0x00, 0x92, 0x01, 0x00, 0x00, + 0x63, 0x00, 0x04, 0x00, 0x8c, 0x01, 0x00, 0x00, 0x8d, 0x01, 0x00, 0x00, + 0x93, 0x01, 0x00, 0x00, 0xf9, 0x00, 0x02, 0x00, 0x7f, 0x01, 0x00, 0x00, + 0xf8, 0x00, 0x02, 0x00, 0x7f, 0x01, 0x00, 0x00, 0xf9, 0x00, 0x02, 0x00, + 0x67, 0x01, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x67, 0x01, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x64, 0x00, 0x00, 0x00, 0x94, 0x01, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x95, 0x01, 0x00, 0x00, 0x94, 0x01, 0x00, 0x00, + 0xc7, 0x00, 0x05, 0x00, 0x58, 0x00, 0x00, 0x00, 0x97, 0x01, 0x00, 0x00, + 0x95, 0x01, 0x00, 0x00, 0x96, 0x01, 0x00, 0x00, 0xaa, 0x00, 0x05, 0x00, + 0x6e, 0x00, 0x00, 0x00, 0x98, 0x01, 0x00, 0x00, 0x97, 0x01, 0x00, 0x00, + 0x66, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x03, 0x00, 0x9a, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x04, 0x00, 0x98, 0x01, 0x00, 0x00, + 0x99, 0x01, 0x00, 0x00, 0x9a, 0x01, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, + 0x99, 0x01, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x9b, 0x01, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, 0x9c, 0x01, 0x00, 0x00, + 0x9b, 0x01, 0x00, 0x00, 0xc7, 0x00, 0x05, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x9d, 0x01, 0x00, 0x00, 0x9c, 0x01, 0x00, 0x00, 0x96, 0x01, 0x00, 0x00, + 0xaa, 0x00, 0x05, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x9e, 0x01, 0x00, 0x00, + 0x9d, 0x01, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x02, 0x00, + 0x9a, 0x01, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x9a, 0x01, 0x00, 0x00, + 0xf5, 0x00, 0x07, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x9f, 0x01, 0x00, 0x00, + 0x98, 0x01, 0x00, 0x00, 0x67, 0x01, 0x00, 0x00, 0x9e, 0x01, 0x00, 0x00, + 0x99, 0x01, 0x00, 0x00, 0xf7, 0x00, 0x03, 0x00, 0xa1, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x04, 0x00, 0x9f, 0x01, 0x00, 0x00, + 0xa0, 0x01, 0x00, 0x00, 0xa1, 0x01, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, + 0xa0, 0x01, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x81, 0x00, 0x00, 0x00, + 0xa4, 0x01, 0x00, 0x00, 0xa3, 0x01, 0x00, 0x00, 0x68, 0x00, 0x04, 0x00, + 0x59, 0x00, 0x00, 0x00, 0xa5, 0x01, 0x00, 0x00, 0xa4, 0x01, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0xa2, 0x01, 0x00, 0x00, 0xa5, 0x01, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x59, 0x00, 0x00, 0x00, 0xa7, 0x01, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x59, 0x00, 0x00, 0x00, + 0xa9, 0x01, 0x00, 0x00, 0xa8, 0x01, 0x00, 0x00, 0xa8, 0x01, 0x00, 0x00, + 0xc3, 0x00, 0x05, 0x00, 0x59, 0x00, 0x00, 0x00, 0xaa, 0x01, 0x00, 0x00, + 0xa7, 0x01, 0x00, 0x00, 0xa9, 0x01, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0xa6, 0x01, 0x00, 0x00, 0xaa, 0x01, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, + 0x64, 0x00, 0x00, 0x00, 0xab, 0x01, 0x00, 0x00, 0xa6, 0x01, 0x00, 0x00, + 0x88, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, + 0xac, 0x01, 0x00, 0x00, 0xab, 0x01, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, + 0x64, 0x00, 0x00, 0x00, 0xad, 0x01, 0x00, 0x00, 0xa2, 0x01, 0x00, 0x00, + 0x88, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, + 0xae, 0x01, 0x00, 0x00, 0xad, 0x01, 0x00, 0x00, 0xb1, 0x00, 0x05, 0x00, + 0x6e, 0x00, 0x00, 0x00, 0xaf, 0x01, 0x00, 0x00, 0xac, 0x01, 0x00, 0x00, + 0xae, 0x01, 0x00, 0x00, 0xf7, 0x00, 0x03, 0x00, 0xb1, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x04, 0x00, 0xaf, 0x01, 0x00, 0x00, + 0xb0, 0x01, 0x00, 0x00, 0xb1, 0x01, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, + 0xb0, 0x01, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x64, 0x00, 0x00, 0x00, + 0xb2, 0x01, 0x00, 0x00, 0xa6, 0x01, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, 0xb3, 0x01, 0x00, 0x00, + 0xb2, 0x01, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x64, 0x00, 0x00, 0x00, + 0xb4, 0x01, 0x00, 0x00, 0xa2, 0x01, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, 0xb5, 0x01, 0x00, 0x00, + 0xb4, 0x01, 0x00, 0x00, 0xb1, 0x00, 0x05, 0x00, 0x6e, 0x00, 0x00, 0x00, + 0xb6, 0x01, 0x00, 0x00, 0xb3, 0x01, 0x00, 0x00, 0xb5, 0x01, 0x00, 0x00, + 0xf9, 0x00, 0x02, 0x00, 0xb1, 0x01, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, + 0xb1, 0x01, 0x00, 0x00, 0xf5, 0x00, 0x07, 0x00, 0x6e, 0x00, 0x00, 0x00, + 0xb7, 0x01, 0x00, 0x00, 0xaf, 0x01, 0x00, 0x00, 0xa0, 0x01, 0x00, 0x00, + 0xb6, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0xf7, 0x00, 0x03, 0x00, + 0xb9, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x04, 0x00, + 0xb7, 0x01, 0x00, 0x00, 0xb8, 0x01, 0x00, 0x00, 0xb9, 0x01, 0x00, 0x00, + 0xf8, 0x00, 0x02, 0x00, 0xb8, 0x01, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x59, 0x00, 0x00, 0x00, 0xbb, 0x01, 0x00, 0x00, 0xa2, 0x01, 0x00, 0x00, + 0x6f, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0xbc, 0x01, 0x00, 0x00, + 0xbb, 0x01, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, + 0xbd, 0x01, 0x00, 0x00, 0x9a, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0x00, + 0x88, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0xbe, 0x01, 0x00, 0x00, + 0xbd, 0x01, 0x00, 0x00, 0xbc, 0x01, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0xba, 0x01, 0x00, 0x00, 0xbe, 0x01, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x59, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xa6, 0x01, 0x00, 0x00, + 0x6f, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0xc1, 0x01, 0x00, 0x00, + 0xc0, 0x01, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, + 0xc2, 0x01, 0x00, 0x00, 0xa2, 0x00, 0x00, 0x00, 0xa2, 0x00, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0xc3, 0x01, 0x00, 0x00, + 0xc1, 0x01, 0x00, 0x00, 0xc2, 0x01, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0xc4, 0x01, 0x00, 0x00, 0xba, 0x01, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0xc5, 0x01, 0x00, 0x00, + 0xc3, 0x01, 0x00, 0x00, 0xc4, 0x01, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0xbf, 0x01, 0x00, 0x00, 0xc5, 0x01, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x81, 0x00, 0x00, 0x00, 0xc6, 0x01, 0x00, 0x00, 0xa3, 0x01, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x59, 0x00, 0x00, 0x00, 0xc7, 0x01, 0x00, 0x00, + 0xa6, 0x01, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0xc9, 0x01, 0x00, 0x00, 0xbf, 0x01, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0xc8, 0x01, 0x00, 0x00, 0xc9, 0x01, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0xcb, 0x01, 0x00, 0x00, 0xba, 0x01, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0xca, 0x01, 0x00, 0x00, 0xcb, 0x01, 0x00, 0x00, + 0x39, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0xcc, 0x01, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0xc8, 0x01, 0x00, 0x00, 0xca, 0x01, 0x00, 0x00, + 0x50, 0x00, 0x07, 0x00, 0x21, 0x00, 0x00, 0x00, 0xcd, 0x01, 0x00, 0x00, + 0xcc, 0x01, 0x00, 0x00, 0xcc, 0x01, 0x00, 0x00, 0xcc, 0x01, 0x00, 0x00, + 0xcc, 0x01, 0x00, 0x00, 0x63, 0x00, 0x04, 0x00, 0xc6, 0x01, 0x00, 0x00, + 0xc7, 0x01, 0x00, 0x00, 0xcd, 0x01, 0x00, 0x00, 0xf9, 0x00, 0x02, 0x00, + 0xb9, 0x01, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0xb9, 0x01, 0x00, 0x00, + 0xf9, 0x00, 0x02, 0x00, 0xa1, 0x01, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, + 0xa1, 0x01, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x64, 0x00, 0x00, 0x00, + 0xce, 0x01, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, 0xcf, 0x01, 0x00, 0x00, + 0xce, 0x01, 0x00, 0x00, 0xc7, 0x00, 0x05, 0x00, 0x58, 0x00, 0x00, 0x00, + 0xd1, 0x01, 0x00, 0x00, 0xcf, 0x01, 0x00, 0x00, 0xd0, 0x01, 0x00, 0x00, + 0xaa, 0x00, 0x05, 0x00, 0x6e, 0x00, 0x00, 0x00, 0xd2, 0x01, 0x00, 0x00, + 0xd1, 0x01, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x03, 0x00, + 0xd4, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x04, 0x00, + 0xd2, 0x01, 0x00, 0x00, 0xd3, 0x01, 0x00, 0x00, 0xd4, 0x01, 0x00, 0x00, + 0xf8, 0x00, 0x02, 0x00, 0xd3, 0x01, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, + 0x64, 0x00, 0x00, 0x00, 0xd5, 0x01, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, + 0x90, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, + 0xd6, 0x01, 0x00, 0x00, 0xd5, 0x01, 0x00, 0x00, 0xc7, 0x00, 0x05, 0x00, + 0x58, 0x00, 0x00, 0x00, 0xd7, 0x01, 0x00, 0x00, 0xd6, 0x01, 0x00, 0x00, + 0xd0, 0x01, 0x00, 0x00, 0xaa, 0x00, 0x05, 0x00, 0x6e, 0x00, 0x00, 0x00, + 0xd8, 0x01, 0x00, 0x00, 0xd7, 0x01, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, + 0xf9, 0x00, 0x02, 0x00, 0xd4, 0x01, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, + 0xd4, 0x01, 0x00, 0x00, 0xf5, 0x00, 0x07, 0x00, 0x6e, 0x00, 0x00, 0x00, + 0xd9, 0x01, 0x00, 0x00, 0xd2, 0x01, 0x00, 0x00, 0xa1, 0x01, 0x00, 0x00, + 0xd8, 0x01, 0x00, 0x00, 0xd3, 0x01, 0x00, 0x00, 0xf7, 0x00, 0x03, 0x00, + 0xdb, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x04, 0x00, + 0xd9, 0x01, 0x00, 0x00, 0xda, 0x01, 0x00, 0x00, 0xdb, 0x01, 0x00, 0x00, + 0xf8, 0x00, 0x02, 0x00, 0xda, 0x01, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x81, 0x00, 0x00, 0x00, 0xde, 0x01, 0x00, 0x00, 0xdd, 0x01, 0x00, 0x00, + 0x68, 0x00, 0x04, 0x00, 0x59, 0x00, 0x00, 0x00, 0xdf, 0x01, 0x00, 0x00, + 0xde, 0x01, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0xdc, 0x01, 0x00, 0x00, + 0xdf, 0x01, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x59, 0x00, 0x00, 0x00, + 0xe1, 0x01, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, + 0x59, 0x00, 0x00, 0x00, 0xe3, 0x01, 0x00, 0x00, 0xe2, 0x01, 0x00, 0x00, + 0xe2, 0x01, 0x00, 0x00, 0xc3, 0x00, 0x05, 0x00, 0x59, 0x00, 0x00, 0x00, + 0xe4, 0x01, 0x00, 0x00, 0xe1, 0x01, 0x00, 0x00, 0xe3, 0x01, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0xe0, 0x01, 0x00, 0x00, 0xe4, 0x01, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x64, 0x00, 0x00, 0x00, 0xe5, 0x01, 0x00, 0x00, + 0xe0, 0x01, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x58, 0x00, 0x00, 0x00, 0xe6, 0x01, 0x00, 0x00, 0xe5, 0x01, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x64, 0x00, 0x00, 0x00, 0xe7, 0x01, 0x00, 0x00, + 0xdc, 0x01, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x58, 0x00, 0x00, 0x00, 0xe8, 0x01, 0x00, 0x00, 0xe7, 0x01, 0x00, 0x00, + 0xb1, 0x00, 0x05, 0x00, 0x6e, 0x00, 0x00, 0x00, 0xe9, 0x01, 0x00, 0x00, + 0xe6, 0x01, 0x00, 0x00, 0xe8, 0x01, 0x00, 0x00, 0xf7, 0x00, 0x03, 0x00, + 0xeb, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x04, 0x00, + 0xe9, 0x01, 0x00, 0x00, 0xea, 0x01, 0x00, 0x00, 0xeb, 0x01, 0x00, 0x00, + 0xf8, 0x00, 0x02, 0x00, 0xea, 0x01, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, + 0x64, 0x00, 0x00, 0x00, 0xec, 0x01, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, + 0x90, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, + 0xed, 0x01, 0x00, 0x00, 0xec, 0x01, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, + 0x64, 0x00, 0x00, 0x00, 0xee, 0x01, 0x00, 0x00, 0xdc, 0x01, 0x00, 0x00, + 0x90, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, + 0xef, 0x01, 0x00, 0x00, 0xee, 0x01, 0x00, 0x00, 0xb1, 0x00, 0x05, 0x00, + 0x6e, 0x00, 0x00, 0x00, 0xf0, 0x01, 0x00, 0x00, 0xed, 0x01, 0x00, 0x00, + 0xef, 0x01, 0x00, 0x00, 0xf9, 0x00, 0x02, 0x00, 0xeb, 0x01, 0x00, 0x00, + 0xf8, 0x00, 0x02, 0x00, 0xeb, 0x01, 0x00, 0x00, 0xf5, 0x00, 0x07, 0x00, + 0x6e, 0x00, 0x00, 0x00, 0xf1, 0x01, 0x00, 0x00, 0xe9, 0x01, 0x00, 0x00, + 0xda, 0x01, 0x00, 0x00, 0xf0, 0x01, 0x00, 0x00, 0xea, 0x01, 0x00, 0x00, + 0xf7, 0x00, 0x03, 0x00, 0xf3, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfa, 0x00, 0x04, 0x00, 0xf1, 0x01, 0x00, 0x00, 0xf2, 0x01, 0x00, 0x00, + 0xf3, 0x01, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0xf2, 0x01, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x59, 0x00, 0x00, 0x00, 0xf5, 0x01, 0x00, 0x00, + 0xdc, 0x01, 0x00, 0x00, 0x6f, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0xf6, 0x01, 0x00, 0x00, 0xf5, 0x01, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, + 0x07, 0x00, 0x00, 0x00, 0xf7, 0x01, 0x00, 0x00, 0x9a, 0x00, 0x00, 0x00, + 0x9a, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, + 0xf8, 0x01, 0x00, 0x00, 0xf7, 0x01, 0x00, 0x00, 0xf6, 0x01, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0xf4, 0x01, 0x00, 0x00, 0xf8, 0x01, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x59, 0x00, 0x00, 0x00, 0xfa, 0x01, 0x00, 0x00, + 0xe0, 0x01, 0x00, 0x00, 0x6f, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0xfb, 0x01, 0x00, 0x00, 0xfa, 0x01, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, + 0x07, 0x00, 0x00, 0x00, 0xfc, 0x01, 0x00, 0x00, 0xa2, 0x00, 0x00, 0x00, + 0xa2, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, + 0xfd, 0x01, 0x00, 0x00, 0xfb, 0x01, 0x00, 0x00, 0xfc, 0x01, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0xfe, 0x01, 0x00, 0x00, + 0xf4, 0x01, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, + 0xff, 0x01, 0x00, 0x00, 0xfd, 0x01, 0x00, 0x00, 0xfe, 0x01, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0xf9, 0x01, 0x00, 0x00, 0xff, 0x01, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0xdd, 0x01, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x59, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0xf9, 0x01, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x02, 0x02, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x05, 0x02, 0x00, 0x00, + 0xf4, 0x01, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x04, 0x02, 0x00, 0x00, + 0x05, 0x02, 0x00, 0x00, 0x39, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x06, 0x02, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, + 0x04, 0x02, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x07, 0x02, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, + 0x06, 0x02, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x63, 0x00, 0x04, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x07, 0x02, 0x00, 0x00, + 0xf9, 0x00, 0x02, 0x00, 0xf3, 0x01, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, + 0xf3, 0x01, 0x00, 0x00, 0xf9, 0x00, 0x02, 0x00, 0xdb, 0x01, 0x00, 0x00, + 0xf8, 0x00, 0x02, 0x00, 0xdb, 0x01, 0x00, 0x00, 0xf9, 0x00, 0x02, 0x00, + 0x74, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x74, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x09, 0x02, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, 0xb1, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x70, 0x00, 0x00, 0x00, 0x09, 0x02, 0x00, 0x00, + 0xf9, 0x00, 0x02, 0x00, 0x71, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, + 0x73, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x02, 0x00, 0x6a, 0x00, 0x00, 0x00, + 0xf8, 0x00, 0x02, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x0a, 0x02, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x05, 0x00, 0x58, 0x00, 0x00, 0x00, 0x0b, 0x02, 0x00, 0x00, + 0x0a, 0x02, 0x00, 0x00, 0xb1, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x65, 0x00, 0x00, 0x00, 0x0b, 0x02, 0x00, 0x00, 0xf9, 0x00, 0x02, 0x00, + 0x67, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x69, 0x00, 0x00, 0x00, + 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, 0x36, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x56, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x1b, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x58, 0x00, 0x07, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x4f, 0x00, 0x08, 0x00, 0x23, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x94, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x2a, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, + 0x29, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x2b, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x56, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x33, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, + 0x2f, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x58, 0x00, 0x07, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x4f, 0x00, 0x08, 0x00, 0x23, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, + 0x35, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x94, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, + 0x37, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x39, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x3a, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x56, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, + 0x3a, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x58, 0x00, 0x07, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x4f, 0x00, 0x08, 0x00, 0x23, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, + 0x42, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x94, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x45, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x46, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x56, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x4d, 0x00, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, + 0x4a, 0x00, 0x00, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x58, 0x00, 0x07, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, + 0x4e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x4f, 0x00, 0x08, 0x00, 0x23, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x4f, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x94, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x52, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x53, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x54, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, + 0x31, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x02, 0x00, 0x55, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x01, 0x00 +}; +unsigned int mipmaps_replacement_spv_len = 12292; diff --git a/src/hooks.cpp b/src/hooks.cpp index bd1368eb..1d4910e8 100644 --- a/src/hooks.cpp +++ b/src/hooks.cpp @@ -75,6 +75,70 @@ namespace { VkDeviceCreateInfo createInfo = *pCreateInfo; createInfo.enabledExtensionCount = static_cast(extensions.size()); createInfo.ppEnabledExtensionNames = extensions.data(); + + // Single-device framegen runs the LSFG compute chain on THIS device, + // so its required features must be enabled here. Merge them into + // whatever feature structure the game supplied, gated on physical + // support. NOLINTBEGIN + VkPhysicalDeviceFeatures supported{}; + VkPhysicalDeviceFeatures mergedFeatures{}; + VkPhysicalDeviceVulkan12Features vk12Supported{ + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES }; + VkPhysicalDeviceFeatures2 supported2{ + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, + .pNext = &vk12Supported }; + VkPhysicalDeviceVulkan12Features extraVk12{ + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES }; + if (Layer::ovkGetPhysicalDeviceFeatures(physicalDevice, &supported)) { + Layer::ovkGetPhysicalDeviceFeatures2(physicalDevice, &supported2); + + auto mergeCore = [&](VkPhysicalDeviceFeatures* target) { + target->shaderStorageImageExtendedFormats |= supported.shaderStorageImageExtendedFormats; + target->shaderStorageImageReadWithoutFormat |= supported.shaderStorageImageReadWithoutFormat; + target->shaderStorageImageWriteWithoutFormat |= supported.shaderStorageImageWriteWithoutFormat; + target->shaderInt16 |= supported.shaderInt16; + }; + + VkPhysicalDeviceFeatures2* features2InChain = nullptr; + VkPhysicalDeviceVulkan12Features* vk12InChain = nullptr; + for (auto* node = const_cast( + reinterpret_cast(createInfo.pNext)); + node != nullptr; node = node->pNext) { + if (node->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2) + features2InChain = reinterpret_cast(node); + else if (node->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES) + vk12InChain = reinterpret_cast(node); + } + + if (features2InChain != nullptr) { + mergeCore(&features2InChain->features); + } else if (createInfo.pEnabledFeatures != nullptr) { + mergedFeatures = *createInfo.pEnabledFeatures; + mergeCore(&mergedFeatures); + createInfo.pEnabledFeatures = &mergedFeatures; + } else { + mergeCore(&mergedFeatures); + createInfo.pEnabledFeatures = &mergedFeatures; + } + + // fp16 shaders and the DXBC path's memory-model requirement live in + // the 1.2 feature struct. Merge into the game's struct when it has + // one; otherwise append ours (valid on 1.2+ devices, which every + // supported ICD here is). + if (vk12InChain != nullptr) { + vk12InChain->shaderFloat16 |= vk12Supported.shaderFloat16; + vk12InChain->vulkanMemoryModel |= vk12Supported.vulkanMemoryModel; + vk12InChain->vulkanMemoryModelDeviceScope |= vk12Supported.vulkanMemoryModelDeviceScope; + } else if (vk12Supported.shaderFloat16 || vk12Supported.vulkanMemoryModel) { + extraVk12.shaderFloat16 = vk12Supported.shaderFloat16; + extraVk12.vulkanMemoryModel = vk12Supported.vulkanMemoryModel; + extraVk12.vulkanMemoryModelDeviceScope = vk12Supported.vulkanMemoryModelDeviceScope; + extraVk12.pNext = const_cast(createInfo.pNext); + createInfo.pNext = &extraVk12; + } + } + // NOLINTEND + auto res = Layer::ovkCreateDevice(physicalDevice, &createInfo, pAllocator, pDevice); if (res == VK_ERROR_EXTENSION_NOT_PRESENT) throw std::runtime_error( @@ -266,8 +330,36 @@ namespace { !std::filesystem::exists(conf.config_file) || conf.timestamp != std::filesystem::last_write_time(conf.config_file) )) { - Layer::ovkQueuePresentKHR(queue, pPresentInfo); - return VK_ERROR_OUT_OF_DATE_KHR; + // A change to fps_limit or the debug keys is applied in place, + // without tearing down the swapchain. Anything else forces a + // recreation so LsContext picks it up in its constructor. + bool pacingOnly = false; + if (std::filesystem::exists(conf.config_file)) { + try { + Config::updateConfig(conf.config_file); + auto fresh = Config::getConfig(Utils::getProcessName()); + if (fresh.enable == conf.enable + && fresh.dll == conf.dll + && fresh.multiplier == conf.multiplier + && fresh.flowScale == conf.flowScale + && fresh.performance == conf.performance + && fresh.hdr == conf.hdr + && fresh.origMipmaps == conf.origMipmaps + && fresh.e_present == conf.e_present) { + conf.fpsLimit = fresh.fpsLimit; + conf.debugInputs = fresh.debugInputs; + conf.debugDump = fresh.debugDump; + conf.timestamp = fresh.timestamp; + pacingOnly = true; + } + } catch (const std::exception&) { + // fall through to the recreation path + } + } + if (!pacingOnly) { + Layer::ovkQueuePresentKHR(queue, pPresentInfo); + return VK_ERROR_OUT_OF_DATE_KHR; + } } // ensure present mode is still valid @@ -276,9 +368,13 @@ namespace { return VK_ERROR_OUT_OF_DATE_KHR; } - // skip if disabled + // no framegen at multiplier <= 1, but the fps cap still applies if (conf.multiplier <= 1) - return Layer::ovkQueuePresentKHR(queue, pPresentInfo); + return swapchain.presentPassthrough(queue, pPresentInfo); + + // skip if framegen force-disabled itself after repeated sync failures + if (swapchain.isDisabled()) + return swapchain.presentPassthrough(queue, pPresentInfo); // present the swapchain std::vector semaphores(pPresentInfo->waitSemaphoreCount); diff --git a/src/layer.cpp b/src/layer.cpp index 1f5b41d3..709feb6c 100644 --- a/src/layer.cpp +++ b/src/layer.cpp @@ -32,8 +32,12 @@ namespace { PFN_vkGetPhysicalDeviceQueueFamilyProperties next_vkGetPhysicalDeviceQueueFamilyProperties{}; PFN_vkGetPhysicalDeviceMemoryProperties next_vkGetPhysicalDeviceMemoryProperties{}; PFN_vkGetPhysicalDeviceProperties next_vkGetPhysicalDeviceProperties{}; + PFN_vkGetPhysicalDeviceFeatures next_vkGetPhysicalDeviceFeatures{}; + PFN_vkGetPhysicalDeviceFeatures2 next_vkGetPhysicalDeviceFeatures2{}; PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR next_vkGetPhysicalDeviceSurfaceCapabilitiesKHR{}; + VkInstance g_instanceHandle{}; + PFN_vkCreateSwapchainKHR next_vkCreateSwapchainKHR{}; PFN_vkQueuePresentKHR next_vkQueuePresentKHR{}; PFN_vkDestroySwapchainKHR next_vkDestroySwapchainKHR{}; @@ -59,9 +63,21 @@ namespace { #endif PFN_vkGetDeviceQueue next_vkGetDeviceQueue{}; PFN_vkQueueSubmit next_vkQueueSubmit{}; + PFN_vkCreateFence next_vkCreateFence{}; + PFN_vkDestroyFence next_vkDestroyFence{}; + PFN_vkWaitForFences next_vkWaitForFences{}; + PFN_vkResetFences next_vkResetFences{}; PFN_vkCmdPipelineBarrier next_vkCmdPipelineBarrier{}; PFN_vkCmdBlitImage next_vkCmdBlitImage{}; PFN_vkAcquireNextImageKHR next_vkAcquireNextImageKHR{}; + PFN_vkCreateBuffer next_vkCreateBuffer{}; + PFN_vkDestroyBuffer next_vkDestroyBuffer{}; + PFN_vkGetBufferMemoryRequirements next_vkGetBufferMemoryRequirements{}; + PFN_vkBindBufferMemory next_vkBindBufferMemory{}; + PFN_vkMapMemory next_vkMapMemory{}; + PFN_vkUnmapMemory next_vkUnmapMemory{}; + PFN_vkInvalidateMappedMemoryRanges next_vkInvalidateMappedMemoryRanges{}; + PFN_vkCmdCopyImageToBuffer next_vkCmdCopyImageToBuffer{}; template bool initInstanceFunc(VkInstance instance, const char* name, T* func) { @@ -145,6 +161,13 @@ namespace { if (!success) throw LSFG::vulkan_error(VK_ERROR_INITIALIZATION_FAILED, "Failed to get instance function pointers"); + // optional (core 1.0/1.1): used to merge framegen's required + // features into the game's device creation + initInstanceFunc(*pInstance, + "vkGetPhysicalDeviceFeatures", &next_vkGetPhysicalDeviceFeatures); + initInstanceFunc(*pInstance, + "vkGetPhysicalDeviceFeatures2", &next_vkGetPhysicalDeviceFeatures2); + g_instanceHandle = *pInstance; std::cerr << "lsfg-vk: Vulkan instance layer initialized successfully.\n"; } catch (const std::exception& e) { @@ -235,9 +258,21 @@ namespace { #endif success &= initDeviceFunc(*pDevice, "vkGetDeviceQueue", &next_vkGetDeviceQueue); success &= initDeviceFunc(*pDevice, "vkQueueSubmit", &next_vkQueueSubmit); + success &= initDeviceFunc(*pDevice, "vkCreateFence", &next_vkCreateFence); + success &= initDeviceFunc(*pDevice, "vkDestroyFence", &next_vkDestroyFence); + success &= initDeviceFunc(*pDevice, "vkWaitForFences", &next_vkWaitForFences); + success &= initDeviceFunc(*pDevice, "vkResetFences", &next_vkResetFences); success &= initDeviceFunc(*pDevice, "vkCmdPipelineBarrier", &next_vkCmdPipelineBarrier); success &= initDeviceFunc(*pDevice, "vkCmdBlitImage", &next_vkCmdBlitImage); success &= initDeviceFunc(*pDevice, "vkAcquireNextImageKHR", &next_vkAcquireNextImageKHR); + success &= initDeviceFunc(*pDevice, "vkCreateBuffer", &next_vkCreateBuffer); + success &= initDeviceFunc(*pDevice, "vkDestroyBuffer", &next_vkDestroyBuffer); + success &= initDeviceFunc(*pDevice, "vkGetBufferMemoryRequirements", &next_vkGetBufferMemoryRequirements); + success &= initDeviceFunc(*pDevice, "vkBindBufferMemory", &next_vkBindBufferMemory); + success &= initDeviceFunc(*pDevice, "vkMapMemory", &next_vkMapMemory); + success &= initDeviceFunc(*pDevice, "vkUnmapMemory", &next_vkUnmapMemory); + success &= initDeviceFunc(*pDevice, "vkInvalidateMappedMemoryRanges", &next_vkInvalidateMappedMemoryRanges); + success &= initDeviceFunc(*pDevice, "vkCmdCopyImageToBuffer", &next_vkCmdCopyImageToBuffer); if (!success) throw LSFG::vulkan_error(VK_ERROR_INITIALIZATION_FAILED, "Failed to get device function pointers"); @@ -353,6 +388,23 @@ namespace Layer { VkPhysicalDeviceProperties* pProperties) { next_vkGetPhysicalDeviceProperties(physicalDevice, pProperties); } + bool ovkGetPhysicalDeviceFeatures( + VkPhysicalDevice physicalDevice, + VkPhysicalDeviceFeatures* pFeatures) { + if (!next_vkGetPhysicalDeviceFeatures) return false; + next_vkGetPhysicalDeviceFeatures(physicalDevice, pFeatures); + return true; + } + bool ovkGetPhysicalDeviceFeatures2( + VkPhysicalDevice physicalDevice, + VkPhysicalDeviceFeatures2* pFeatures) { + if (!next_vkGetPhysicalDeviceFeatures2) return false; + next_vkGetPhysicalDeviceFeatures2(physicalDevice, pFeatures); + return true; + } + VkInstance ovkInstance() { + return g_instanceHandle; + } VkResult ovkGetPhysicalDeviceSurfaceCapabilitiesKHR( VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, @@ -518,6 +570,34 @@ namespace Layer { return next_vkQueueSubmit(queue, submitCount, pSubmits, fence); } + VkResult ovkCreateFence( + VkDevice device, + const VkFenceCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkFence* pFence) { + return next_vkCreateFence(device, pCreateInfo, pAllocator, pFence); + } + void ovkDestroyFence( + VkDevice device, + VkFence fence, + const VkAllocationCallbacks* pAllocator) { + next_vkDestroyFence(device, fence, pAllocator); + } + VkResult ovkWaitForFences( + VkDevice device, + uint32_t fenceCount, + const VkFence* pFences, + VkBool32 waitAll, + uint64_t timeout) { + return next_vkWaitForFences(device, fenceCount, pFences, waitAll, timeout); + } + VkResult ovkResetFences( + VkDevice device, + uint32_t fenceCount, + const VkFence* pFences) { + return next_vkResetFences(device, fenceCount, pFences); + } + void ovkCmdPipelineBarrier( VkCommandBuffer commandBuffer, VkPipelineStageFlags srcStageMask, @@ -555,4 +635,60 @@ namespace Layer { uint32_t* pImageIndex) { return next_vkAcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, pImageIndex); } + + VkResult ovkCreateBuffer( + VkDevice device, + const VkBufferCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkBuffer* pBuffer) { + return next_vkCreateBuffer(device, pCreateInfo, pAllocator, pBuffer); + } + void ovkDestroyBuffer( + VkDevice device, + VkBuffer buffer, + const VkAllocationCallbacks* pAllocator) { + next_vkDestroyBuffer(device, buffer, pAllocator); + } + void ovkGetBufferMemoryRequirements( + VkDevice device, + VkBuffer buffer, + VkMemoryRequirements* pMemoryRequirements) { + next_vkGetBufferMemoryRequirements(device, buffer, pMemoryRequirements); + } + VkResult ovkBindBufferMemory( + VkDevice device, + VkBuffer buffer, + VkDeviceMemory memory, + VkDeviceSize memoryOffset) { + return next_vkBindBufferMemory(device, buffer, memory, memoryOffset); + } + VkResult ovkMapMemory( + VkDevice device, + VkDeviceMemory memory, + VkDeviceSize offset, + VkDeviceSize size, + VkMemoryMapFlags flags, + void** ppData) { + return next_vkMapMemory(device, memory, offset, size, flags, ppData); + } + void ovkUnmapMemory( + VkDevice device, + VkDeviceMemory memory) { + next_vkUnmapMemory(device, memory); + } + VkResult ovkInvalidateMappedMemoryRanges( + VkDevice device, + uint32_t memoryRangeCount, + const VkMappedMemoryRange* pMemoryRanges) { + return next_vkInvalidateMappedMemoryRanges(device, memoryRangeCount, pMemoryRanges); + } + void ovkCmdCopyImageToBuffer( + VkCommandBuffer commandBuffer, + VkImage srcImage, + VkImageLayout srcImageLayout, + VkBuffer dstBuffer, + uint32_t regionCount, + const VkBufferImageCopy* pRegions) { + next_vkCmdCopyImageToBuffer(commandBuffer, srcImage, srcImageLayout, dstBuffer, regionCount, pRegions); + } } diff --git a/src/mini/commandbuffer.cpp b/src/mini/commandbuffer.cpp index 3b0e49af..0806cf23 100644 --- a/src/mini/commandbuffer.cpp +++ b/src/mini/commandbuffer.cpp @@ -66,7 +66,8 @@ void CommandBuffer::end() { void CommandBuffer::submit(VkQueue queue, const std::vector& waitSemaphores, - const std::vector& signalSemaphores) { + const std::vector& signalSemaphores, + VkFence fence) { if (*this->state != CommandBufferState::Full) throw std::logic_error("Command buffer is not in Full state"); @@ -83,7 +84,7 @@ void CommandBuffer::submit(VkQueue queue, .signalSemaphoreCount = static_cast(signalSemaphores.size()), .pSignalSemaphores = signalSemaphores.data() }; - auto res = Layer::ovkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE); + auto res = Layer::ovkQueueSubmit(queue, 1, &submitInfo, fence); if (res != VK_SUCCESS) throw LSFG::vulkan_error(res, "Unable to submit command buffer"); diff --git a/src/mini/fence.cpp b/src/mini/fence.cpp new file mode 100644 index 00000000..5bd6f0b5 --- /dev/null +++ b/src/mini/fence.cpp @@ -0,0 +1,38 @@ +#include "mini/fence.hpp" +#include "common/exception.hpp" +#include "layer.hpp" + +#include + +#include +#include + +using namespace Mini; + +Fence::Fence(VkDevice device, bool signaled) : device(device) { + // create fence + const VkFenceCreateInfo desc{ + .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, + .flags = signaled ? VK_FENCE_CREATE_SIGNALED_BIT : VkFenceCreateFlags{} + }; + VkFence fenceHandle{}; + auto res = Layer::ovkCreateFence(device, &desc, nullptr, &fenceHandle); + if (res != VK_SUCCESS || fenceHandle == VK_NULL_HANDLE) + throw LSFG::vulkan_error(res, "Unable to create fence"); + + // store fence in shared ptr + this->fence = std::shared_ptr( + new VkFence(fenceHandle), + [dev = device](VkFence* fenceHandle) { + Layer::ovkDestroyFence(dev, *fenceHandle, nullptr); + } + ); +} + +VkResult Fence::wait(uint64_t timeoutNs) const { + return Layer::ovkWaitForFences(this->device, 1, &(*this->fence), VK_TRUE, timeoutNs); +} + +VkResult Fence::reset() const { + return Layer::ovkResetFences(this->device, 1, &(*this->fence)); +} diff --git a/src/mini/image.cpp b/src/mini/image.cpp index 6d4b36cb..9e4aa406 100644 --- a/src/mini/image.cpp +++ b/src/mini/image.cpp @@ -116,6 +116,82 @@ Image::Image(VkDevice device, VkPhysicalDevice physicalDevice, ); } +Image Image::createDeviceLocal(VkDevice device, VkPhysicalDevice physicalDevice, + VkExtent2D extent, VkFormat format, + VkImageUsageFlags usage, VkImageAspectFlags aspectFlags) { + Image img; + img.extent = extent; + img.format = format; + img.aspectFlags = aspectFlags; + + const VkImageCreateInfo desc{ + .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .imageType = VK_IMAGE_TYPE_2D, + .format = format, + .extent = { extent.width, extent.height, 1 }, + .mipLevels = 1, + .arrayLayers = 1, + .samples = VK_SAMPLE_COUNT_1_BIT, + .tiling = VK_IMAGE_TILING_OPTIMAL, + .usage = usage, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, + }; + VkImage imageHandle{}; + auto res = Layer::ovkCreateImage(device, &desc, nullptr, &imageHandle); + if (res != VK_SUCCESS || imageHandle == VK_NULL_HANDLE) + throw LSFG::vulkan_error(res, "Failed to create device-local image"); + + VkMemoryRequirements memReqs; + Layer::ovkGetImageMemoryRequirements(device, imageHandle, &memReqs); + + VkPhysicalDeviceMemoryProperties memProps; + Layer::ovkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProps); + + uint32_t typeIndex = UINT32_MAX; + for (uint32_t i = 0; i < memProps.memoryTypeCount; ++i) { + if ((memReqs.memoryTypeBits & (1u << i)) && + (memProps.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) { + typeIndex = i; + break; + } + } + if (typeIndex == UINT32_MAX) { + for (uint32_t i = 0; i < memProps.memoryTypeCount; ++i) { + if (memReqs.memoryTypeBits & (1u << i)) { + typeIndex = i; + break; + } + } + } + if (typeIndex == UINT32_MAX) + throw LSFG::vulkan_error(VK_ERROR_UNKNOWN, "No memory type for device-local image"); + + const VkMemoryAllocateInfo allocInfo{ + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .allocationSize = memReqs.size, + .memoryTypeIndex = typeIndex, + }; + VkDeviceMemory memoryHandle{}; + res = Layer::ovkAllocateMemory(device, &allocInfo, nullptr, &memoryHandle); + if (res != VK_SUCCESS || memoryHandle == VK_NULL_HANDLE) + throw LSFG::vulkan_error(res, "Failed to allocate device-local image memory"); + + res = Layer::ovkBindImageMemory(device, imageHandle, memoryHandle, 0); + if (res != VK_SUCCESS) + throw LSFG::vulkan_error(res, "Failed to bind device-local image memory"); + + img.image = std::shared_ptr( + new VkImage(imageHandle), + [dev = device](VkImage* i) { Layer::ovkDestroyImage(dev, *i, nullptr); } + ); + img.memory = std::shared_ptr( + new VkDeviceMemory(memoryHandle), + [dev = device](VkDeviceMemory* mem) { Layer::ovkFreeMemory(dev, *mem, nullptr); } + ); + return img; +} + #ifdef __ANDROID__ Image::Image(VkDevice device, VkPhysicalDevice physicalDevice, VkExtent2D extent, VkFormat format, @@ -138,8 +214,7 @@ Image::Image(VkDevice device, VkPhysicalDevice physicalDevice, .layers = 1, .format = ahbFormat, .usage = AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE - | AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT - | AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, + | AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT, .stride = 0, .rfu0 = 0, .rfu1 = 0, From c682ad10dddfafdf2eeffe9cf9cf603635627895 Mon Sep 17 00:00:00 2001 From: Utkarsh Dalal Date: Sat, 15 Aug 2026 10:33:08 +0530 Subject: [PATCH 2/3] Bound every GPU wait in the present path On a saturated GPU (measured on a device running the game at 18 fps against a 30 cap) two unbounded waits could freeze the game's present thread permanently: the generated-frame vkAcquireNextImageKHR (infinite timeout) and the frame-slot completion fence wait (UINT64_MAX). - Acquire is bounded at 200 ms; a timed-out generated frame is dropped (counted as a skip). - The completion fence wait is bounded at 2 s and throws before any per-frame state changes, so the present falls through cleanly. - Graph-side fence timeouts feed the same watchdog as the copy fence: repeated timeouts disable framegen for the swapchain (full-speed passthrough) instead of stalling every frame. --- framegen/v3.1_src/context.cpp | 7 +++++-- framegen/v3.1p_src/context.cpp | 7 +++++-- src/context.cpp | 34 +++++++++++++++++++++++++++------- 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/framegen/v3.1_src/context.cpp b/framegen/v3.1_src/context.cpp index b9d3fa43..de148dc4 100644 --- a/framegen/v3.1_src/context.cpp +++ b/framegen/v3.1_src/context.cpp @@ -131,10 +131,13 @@ void Context::present(Vulkan& vk, int inSem, const std::vector& outSem) { auto& data = this->data.at(this->frameIdx % 8); - // 3. wait for completion of previous frame in this slot + // 3. wait for completion of previous frame in this slot. Bounded: on a + // saturated GPU an unbounded wait freezes the game's present thread. + // The throw happens before any per-frame state changes, so the caller + // can retry or fall back to passthrough cleanly. if (data.shouldWait) for (auto& fence : data.completionFences) - if (!fence.wait(vk.device, UINT64_MAX)) + if (!fence.wait(vk.device, 2'000'000'000ull)) throw LSFG::vulkan_error(VK_TIMEOUT, "Fence wait timed out"); data.shouldWait = true; diff --git a/framegen/v3.1p_src/context.cpp b/framegen/v3.1p_src/context.cpp index 1419fb82..f1b2aa72 100644 --- a/framegen/v3.1p_src/context.cpp +++ b/framegen/v3.1p_src/context.cpp @@ -132,10 +132,13 @@ void Context::present(Vulkan& vk, int inSem, const std::vector& outSem) { auto& data = this->data.at(this->frameIdx % 8); - // 3. wait for completion of previous frame in this slot + // 3. wait for completion of previous frame in this slot. Bounded: on a + // saturated GPU an unbounded wait freezes the game's present thread. + // The throw happens before any per-frame state changes, so the caller + // can retry or fall back to passthrough cleanly. if (data.shouldWait) for (auto& fence : data.completionFences) - if (!fence.wait(vk.device, UINT64_MAX)) + if (!fence.wait(vk.device, 2'000'000'000ull)) throw LSFG::vulkan_error(VK_TIMEOUT, "Fence wait timed out"); data.shouldWait = true; diff --git a/src/context.cpp b/src/context.cpp index c13b6c74..c70ce39f 100644 --- a/src/context.cpp +++ b/src/context.cpp @@ -765,12 +765,26 @@ VkResult LsContext::present(const Hooks::DeviceInfo& info, const void* pNext, Vk const int64_t workStartNs = nowNs(); // 2. Tell framegen to generate intermediary frames - // presentContext(id, -1, {}) — no semaphore FDs, synchronous + // presentContext(id, -1, {}) — no semaphore FDs, synchronous. + // A graph-side fence timeout counts toward the same watchdog as the + // copy fence: enough of them disables framegen for this swapchain + // (full-speed passthrough) instead of a 2s-stall-per-frame loop. std::vector noOutSems; // empty - if (conf.performance) - LSFG_3_1P::presentContext(*this->lsfgCtxId, -1, noOutSems); - else - LSFG_3_1::presentContext(*this->lsfgCtxId, -1, noOutSems); + try { + if (conf.performance) + LSFG_3_1P::presentContext(*this->lsfgCtxId, -1, noOutSems); + else + LSFG_3_1::presentContext(*this->lsfgCtxId, -1, noOutSems); + } catch (const LSFG::vulkan_error& e) { + if (e.error() == VK_TIMEOUT + && ++this->copyFenceTimeouts >= kMaxFenceTimeouts + && !this->forceDisabled) { + this->forceDisabled = true; + std::cerr << "lsfg-vk: disabling frame generation for this swapchain after " + << this->copyFenceTimeouts << " framegen fence timeouts\n"; + } + throw; + } // 3. No CPU-side wait: framegen submitted on the same queue, so the // generated-frame copies below are ordered after it by submission @@ -819,11 +833,17 @@ VkResult LsContext::present(const Hooks::DeviceInfo& info, const void* pNext, Vk sleepUntilNs(dueNs); } - // acquire next swapchain image + // acquire next swapchain image. Bounded: with the GPU saturated no + // image may free up, and an unbounded wait here freezes the game's + // present thread. A timed-out generated frame is just dropped. pass.acquireSemaphores.at(i) = Mini::Semaphore(info.device); uint32_t imageIdx{}; - auto res = Layer::ovkAcquireNextImageKHR(info.device, this->swapchain, UINT64_MAX, + auto res = Layer::ovkAcquireNextImageKHR(info.device, this->swapchain, 200'000'000ull, pass.acquireSemaphores.at(i).handle(), VK_NULL_HANDLE, &imageIdx); + if (res == VK_TIMEOUT || res == VK_NOT_READY) { + this->statsGenSkips++; + continue; + } if (res != VK_SUCCESS && res != VK_SUBOPTIMAL_KHR) throw LSFG::vulkan_error(res, "Failed to acquire next swapchain image"); if (this->statsSeq.size() < 200) From d744533f9d70595dea4a97ddba5efe28c11d0b71 Mon Sep 17 00:00:00 2001 From: Utkarsh Dalal Date: Sat, 15 Aug 2026 10:50:27 +0530 Subject: [PATCH 3/3] Trim comments --- framegen/src/core/device.cpp | 5 ++--- framegen/v3.1_src/context.cpp | 6 ++---- framegen/v3.1p_src/context.cpp | 6 ++---- include/config/config.hpp | 3 +-- src/context.cpp | 26 ++++++++------------------ 5 files changed, 15 insertions(+), 31 deletions(-) diff --git a/framegen/src/core/device.cpp b/framegen/src/core/device.cpp index 7e4ac319..6a178873 100644 --- a/framegen/src/core/device.cpp +++ b/framegen/src/core/device.cpp @@ -537,9 +537,8 @@ Device::Device(const Instance& instance, uint64_t deviceUUID) { VkExtent2D{1, 1}, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT); - // The fallback substitutes for nullDescriptor bindings, whose reads - // must return zeros. Its memory is undefined at creation — some - // drivers hand back zero pages, others garbage. + // nullDescriptor semantics require reads to return zeros; the memory + // is undefined at creation Utils::clearImage(*this, *this->fallbackDescriptorImage); } } diff --git a/framegen/v3.1_src/context.cpp b/framegen/v3.1_src/context.cpp index de148dc4..84cd2914 100644 --- a/framegen/v3.1_src/context.cpp +++ b/framegen/v3.1_src/context.cpp @@ -131,10 +131,8 @@ void Context::present(Vulkan& vk, int inSem, const std::vector& outSem) { auto& data = this->data.at(this->frameIdx % 8); - // 3. wait for completion of previous frame in this slot. Bounded: on a - // saturated GPU an unbounded wait freezes the game's present thread. - // The throw happens before any per-frame state changes, so the caller - // can retry or fall back to passthrough cleanly. + // 3. wait for completion of previous frame in this slot; bounded, and + // thrown before any per-frame state changes so the caller can retry if (data.shouldWait) for (auto& fence : data.completionFences) if (!fence.wait(vk.device, 2'000'000'000ull)) diff --git a/framegen/v3.1p_src/context.cpp b/framegen/v3.1p_src/context.cpp index f1b2aa72..a51464e9 100644 --- a/framegen/v3.1p_src/context.cpp +++ b/framegen/v3.1p_src/context.cpp @@ -132,10 +132,8 @@ void Context::present(Vulkan& vk, int inSem, const std::vector& outSem) { auto& data = this->data.at(this->frameIdx % 8); - // 3. wait for completion of previous frame in this slot. Bounded: on a - // saturated GPU an unbounded wait freezes the game's present thread. - // The throw happens before any per-frame state changes, so the caller - // can retry or fall back to passthrough cleanly. + // 3. wait for completion of previous frame in this slot; bounded, and + // thrown before any per-frame state changes so the caller can retry if (data.shouldWait) for (auto& fence : data.completionFences) if (!fence.wait(vk.device, 2'000'000'000ull)) diff --git a/include/config/config.hpp b/include/config/config.hpp index c8122fde..c2708450 100644 --- a/include/config/config.hpp +++ b/include/config/config.hpp @@ -36,8 +36,7 @@ namespace Config { /// presents to /dump/ as PPM files, then stop (0 = off). int debugDump{0}; /// Use the DLL's translated mipmaps shader instead of the bundled - /// clean-room replacement (the translated one miscompiles on mesa's - /// Adreno gen8 backend). + /// replacement (the translation miscompiles on mesa's Adreno gen8). bool origMipmaps{false}; /// Experimental flag for overriding the synchronization method. diff --git a/src/context.cpp b/src/context.cpp index c70ce39f..63613a43 100644 --- a/src/context.cpp +++ b/src/context.cpp @@ -260,11 +260,8 @@ LsContext::LsContext(const Hooks::DeviceInfo& info, VkSwapchainKHR swapchain, : VK_FORMAT_R16G16B16A16_SFLOAT; #ifdef __ANDROID__ - // Android path: single-device framegen. All images live on the game's own - // device and every dispatch runs on the game's queue — two different ICDs - // (e.g. turnip for the game, the system driver for a private framegen - // device) interpret shared AHB memory differently and scramble it, so no - // cross-device sharing is used at all. + // Android: single-device framegen on the game's own device and queue — + // two ICDs interpret shared AHB memory differently and scramble it. this->preCopyFence = Mini::Fence(info.device, true); @@ -284,8 +281,7 @@ LsContext::LsContext(const Hooks::DeviceInfo& info, VkSwapchainKHR swapchain, setenv("DISABLE_LSFG", "1", 1); // NOLINT const auto shaderLoader = [](const std::string& name) { - // debug override: a raw SPIR-V file next to conf.toml replaces the - // translated DLL shader (override_.spv, brackets -> underscores) + // debug override: override_.spv next to conf.toml (brackets -> underscores) const auto& confFile = Config::activeConf.config_file; if (!confFile.empty()) { std::string fname = "override_" + name + ".spv"; @@ -677,9 +673,7 @@ VkResult LsContext::present(const Hooks::DeviceInfo& info, const void* pNext, Vk if (this->lastRealPresentNs != 0 && this->pacerAnchorNs > this->lastRealPresentNs) { const uint64_t delta = static_cast( this->pacerAnchorNs - this->lastRealPresentNs); - // A delta this large is a pause/resume, not a slow frame; feeding it - // into the EWMA would mis-pace generated frames for seconds while it - // decays. Re-learn from scratch instead. + // a delta this large is a pause/resume, not a slow frame if (delta > 250'000'000ull) this->baseIntervalEwmaNs = 0; else @@ -764,11 +758,8 @@ VkResult LsContext::present(const Hooks::DeviceInfo& info, const void* pNext, Vk this->preCopyFence.handle()); const int64_t workStartNs = nowNs(); - // 2. Tell framegen to generate intermediary frames - // presentContext(id, -1, {}) — no semaphore FDs, synchronous. - // A graph-side fence timeout counts toward the same watchdog as the - // copy fence: enough of them disables framegen for this swapchain - // (full-speed passthrough) instead of a 2s-stall-per-frame loop. + // 2. Tell framegen to generate intermediary frames. Graph-side fence + // timeouts feed the same watchdog as the copy fence. std::vector noOutSems; // empty try { if (conf.performance) @@ -833,9 +824,8 @@ VkResult LsContext::present(const Hooks::DeviceInfo& info, const void* pNext, Vk sleepUntilNs(dueNs); } - // acquire next swapchain image. Bounded: with the GPU saturated no - // image may free up, and an unbounded wait here freezes the game's - // present thread. A timed-out generated frame is just dropped. + // bounded acquire: an unbounded wait freezes the present thread on a + // saturated GPU; a timed-out generated frame is dropped pass.acquireSemaphores.at(i) = Mini::Semaphore(info.device); uint32_t imageIdx{}; auto res = Layer::ovkAcquireNextImageKHR(info.device, this->swapchain, 200'000'000ull,