diff --git a/OptiScaler/Config.cpp b/OptiScaler/Config.cpp index 77f5bce5c..9cf6523ee 100644 --- a/OptiScaler/Config.cpp +++ b/OptiScaler/Config.cpp @@ -317,6 +317,8 @@ bool Config::Reload(std::filesystem::path iniPath) // --- DLSS 5 Neural Rendering (OptiScaler/dlssnr) --- DlssNrEnabled.set_from_config(readBool("DlssNr", "Enabled")); + DlssNrMode.set_from_config(readUInt("DlssNr", "Mode")); + DlssNrFeature1Pipeline.set_from_config(readUInt("DlssNr", "Feature1Pipeline")); DlssNrToggleKey.set_from_config(readInt("DlssNr", "ToggleKey")); DlssNrTransferStrength.set_from_config(readFloat("DlssNr", "TransferStrength")); DlssNrColourStrength.set_from_config(readFloat("DlssNr", "ColourStrength")); @@ -327,6 +329,14 @@ bool Config::Reload(std::filesystem::path iniPath) DlssNrCompareZoom.set_from_config(readFloat("DlssNr", "CompareZoom")); DlssNrCompareSwap.set_from_config(readBool("DlssNr", "CompareSwap")); DlssNrWorkingScale.set_from_config(readFloat("DlssNr", "WorkingScale")); + DlssNrWorkAtNative.set_from_config(readBool("DlssNr", "WorkAtNative")); + DlssNrTransfer.set_from_config(readUInt("DlssNr", "Transfer")); + DlssNrHdrLift.set_from_config(readUInt("DlssNr", "HdrLift")); + if (DlssNrWorkingScale.has_value() && DlssNrWorkingScale.value() <= 0.0f) + { + DlssNrWorkAtNative = true; + DlssNrWorkingScale = 1.0f; + } DlssNrProxyProbe.set_from_config(readBool("DlssNr", "ProxyProbe")); DlssNrUseProxy.set_from_config(readBool("DlssNr", "UseProxy")); DlssNrAutoCapture.set_from_config(readBool("DlssNr", "AutoCapture")); @@ -1166,38 +1176,43 @@ bool Config::SaveIni() { ini.SetValue("DLSS", "Enabled", GetBoolValue(Instance()->DLSSEnabled.value_for_config()).c_str()); - // --- DLSS 5 Neural Rendering (OptiScaler/dlssnr) --- - ini.SetValue("DlssNr", "Enabled", GetBoolValue(Instance()->DlssNrEnabled.value_for_config()).c_str()); - { - auto toggle = Instance()->DlssNrToggleKey.value_for_config(); - ini.SetValue("DlssNr", "ToggleKey", GetIntValue(toggle, toggle > 0).c_str()); - } - ini.SetValue("DlssNr", "TransferStrength", - GetFloatValue(Instance()->DlssNrTransferStrength.value_for_config()).c_str()); - ini.SetValue("DlssNr", "ColourStrength", - GetFloatValue(Instance()->DlssNrColourStrength.value_for_config()).c_str()); - ini.SetValue("DlssNr", "MaxRatio", GetFloatValue(Instance()->DlssNrMaxRatio.value_for_config()).c_str()); - ini.SetValue("DlssNr", "DebugView", GetIntValue(Instance()->DlssNrDebugView.value_for_config()).c_str()); - ini.SetValue("DlssNr", "Compare", GetIntValue(Instance()->DlssNrCompare.value_for_config()).c_str()); - ini.SetValue("DlssNr", "CompareSplit", - GetFloatValue(Instance()->DlssNrCompareSplit.value_for_config()).c_str()); - ini.SetValue("DlssNr", "CompareZoom", - GetFloatValue(Instance()->DlssNrCompareZoom.value_for_config()).c_str()); - ini.SetValue("DlssNr", "CompareSwap", - GetBoolValue(Instance()->DlssNrCompareSwap.value_for_config()).c_str()); - ini.SetValue("DlssNr", "WorkingScale", GetFloatValue(Instance()->DlssNrWorkingScale.value_for_config()).c_str()); - ini.SetValue("DlssNr", "AutoCapture", GetBoolValue(Instance()->DlssNrAutoCapture.value_for_config()).c_str()); - ini.SetValue("DlssNr", "WhitePointScale", - GetFloatValue(Instance()->DlssNrWhitePointScale.value_for_config()).c_str()); - ini.SetValue("DlssNr", "Preset", GetIntValue(Instance()->DlssNrPreset.value_for_config()).c_str()); - ini.SetValue("DlssNr", "Intensity", GetFloatValue(Instance()->DlssNrIntensity.value_for_config()).c_str()); - ini.SetValue("DlssNr", "Style", GetIntValue(Instance()->DlssNrStyle.value_for_config()).c_str()); - ini.SetValue("DlssNr", "LocalStructure", - GetFloatValue(Instance()->DlssNrLocalStructure.value_for_config()).c_str()); - ini.SetValue("DlssNr", "LocalTone", GetFloatValue(Instance()->DlssNrLocalTone.value_for_config()).c_str()); - ini.SetValue("DlssNr", "SkinStructure", - GetFloatValue(Instance()->DlssNrSkinStructure.value_for_config()).c_str()); - ini.SetValue("DlssNr", "AutoMask", GetBoolValue(Instance()->DlssNrAutoMask.value_for_config()).c_str()); + // --- DLSS 5 Neural Rendering (OptiScaler/dlssnr) --- + ini.SetValue("DlssNr", "Enabled", GetBoolValue(Instance()->DlssNrEnabled.value_for_config()).c_str()); + ini.SetValue("DlssNr", "Mode", GetIntValue(Instance()->DlssNrMode.value_for_config()).c_str()); + ini.SetValue("DlssNr", "Feature1Pipeline", + GetIntValue(Instance()->DlssNrFeature1Pipeline.value_for_config()).c_str()); + { + auto toggle = Instance()->DlssNrToggleKey.value_for_config(); + ini.SetValue("DlssNr", "ToggleKey", GetIntValue(toggle, toggle > 0).c_str()); + } + ini.SetValue("DlssNr", "TransferStrength", + GetFloatValue(Instance()->DlssNrTransferStrength.value_for_config()).c_str()); + ini.SetValue("DlssNr", "ColourStrength", + GetFloatValue(Instance()->DlssNrColourStrength.value_for_config()).c_str()); + ini.SetValue("DlssNr", "MaxRatio", GetFloatValue(Instance()->DlssNrMaxRatio.value_for_config()).c_str()); + ini.SetValue("DlssNr", "DebugView", GetIntValue(Instance()->DlssNrDebugView.value_for_config()).c_str()); + ini.SetValue("DlssNr", "Compare", GetIntValue(Instance()->DlssNrCompare.value_for_config()).c_str()); + ini.SetValue("DlssNr", "CompareSplit", + GetFloatValue(Instance()->DlssNrCompareSplit.value_for_config()).c_str()); + ini.SetValue("DlssNr", "CompareZoom", GetFloatValue(Instance()->DlssNrCompareZoom.value_for_config()).c_str()); + ini.SetValue("DlssNr", "CompareSwap", GetBoolValue(Instance()->DlssNrCompareSwap.value_for_config()).c_str()); + ini.SetValue("DlssNr", "WorkingScale", + GetFloatValue(Instance()->DlssNrWorkingScale.value_for_config()).c_str()); + ini.SetValue("DlssNr", "WorkAtNative", GetBoolValue(Instance()->DlssNrWorkAtNative.value_for_config()).c_str()); + ini.SetValue("DlssNr", "Transfer", GetIntValue(Instance()->DlssNrTransfer.value_for_config()).c_str()); + ini.SetValue("DlssNr", "HdrLift", GetIntValue(Instance()->DlssNrHdrLift.value_for_config()).c_str()); + ini.SetValue("DlssNr", "AutoCapture", GetBoolValue(Instance()->DlssNrAutoCapture.value_for_config()).c_str()); + ini.SetValue("DlssNr", "WhitePointScale", + GetFloatValue(Instance()->DlssNrWhitePointScale.value_for_config()).c_str()); + ini.SetValue("DlssNr", "Preset", GetIntValue(Instance()->DlssNrPreset.value_for_config()).c_str()); + ini.SetValue("DlssNr", "Intensity", GetFloatValue(Instance()->DlssNrIntensity.value_for_config()).c_str()); + ini.SetValue("DlssNr", "Style", GetIntValue(Instance()->DlssNrStyle.value_for_config()).c_str()); + ini.SetValue("DlssNr", "LocalStructure", + GetFloatValue(Instance()->DlssNrLocalStructure.value_for_config()).c_str()); + ini.SetValue("DlssNr", "LocalTone", GetFloatValue(Instance()->DlssNrLocalTone.value_for_config()).c_str()); + ini.SetValue("DlssNr", "SkinStructure", + GetFloatValue(Instance()->DlssNrSkinStructure.value_for_config()).c_str()); + ini.SetValue("DlssNr", "AutoMask", GetBoolValue(Instance()->DlssNrAutoMask.value_for_config()).c_str()); ini.SetValue("DLSS", "RenderPresetOverride", GetBoolValue(Instance()->RenderPresetOverride.value_for_config()).c_str()); ini.SetValue("DLSS", "RenderPresetForAll", diff --git a/OptiScaler/Config.h b/OptiScaler/Config.h index 0006faf0b..6e60cf66c 100644 --- a/OptiScaler/Config.h +++ b/OptiScaler/Config.h @@ -257,6 +257,12 @@ class Config // DLSS Neural Rendering: a detail-synthesis pass over the upscaler's output. Off by default -- it is // an undocumented feature driven directly through its snippet, not something NVIDIA exposes. CustomOptional DlssNrEnabled { false }; + // 0 post-process (after the upscaler), 1 multi-pass (1:1 first pass, then the model, then a + // spatial enlarge). OptiScaler's own Dx12 DLSS / Ray Reconstruction only; anything else falls + // back to post-process. + CustomOptional DlssNrMode { 0 }; + // Which upscaler the multi-pass first pass is: 0 Ray Reconstruction, 1 Super Resolution. + CustomOptional DlssNrFeature1Pipeline { 0 }; // Toggles the pass in game. Unbound by default -- a key that does something unexpected is worse // than one that does nothing. CustomOptional DlssNrToggleKey { UnboundKey }; @@ -276,12 +282,11 @@ class Config CustomOptional DlssNrTransferStrength { 1.0f }; CustomOptional DlssNrColourStrength { 1.0f }; - // The most the pass may multiply or divide a pixel by. A detail pass has no business restyling a // light source, whatever the model returns. CustomOptional DlssNrMaxRatio { 2.0f }; - // 0 off, 1 the picture the model was shown, 2 its raw answer, 3 what it changed, amplified. + // 0–5: off, proxy, model, amplified difference, full-res proxy, matched T. CustomOptional DlssNrDebugView { 0 }; // Showing the pass against itself, without having to toggle it and remember what the last frame @@ -300,10 +305,18 @@ class Config // Which side the edited frame sits on, in both comparison modes. CustomOptional DlssNrCompareSwap { false }; - // The fraction of the frame's resolution the model works at. The frame itself is never reduced -- - // only the model's contribution is computed small and enlarged, so the picture underneath is - // untouched whatever this is set to. 1.0 is full resolution and behaves exactly as before. + // The model's working raster as a fraction of display resolution, not of the colour buffer. + // The frame itself is never reduced -- only the model's contribution is computed small and + // enlarged. 1.0 is display resolution, then clamped to the colour buffer so Multi-pass cannot + // invent pixels past Feature 1. CustomOptional DlssNrWorkingScale { 1.0f }; + // Lock the model to the game's render (native) raster. The slider then shows the live R/D + // ratio and is not adjustable. WorkingScale is left alone so unchecking restores it. + CustomOptional DlssNrWorkAtNative { false }; + // 0 Classic, 1 Matched residual (default). How a below-frame model is brought back. + CustomOptional DlssNrTransfer { 1 }; + // 0 UpgradeToneMap (H0), 1 additive headroom (H1). Used only if Transfer == 1. + CustomOptional DlssNrHdrLift { 0 }; // Ask the driver's own nvngx.dll whether it will dispatch Neural Rendering, once per session. // @@ -337,18 +350,10 @@ class Config // folder is cleared at the start of each run, so it holds one session's worth and never grows. CustomOptional DlssNrAutoCapture { true }; - - - - // Multiplies the (auto or manual) white point before the encode: what the model considers "white". // Higher means highlights sit lower on the curve and the model treats them as less extreme. CustomOptional DlssNrWhitePointScale { 1.0f }; - - - - // --- end DLSS 5 Neural Rendering ------------------------------------------------------------- // DLSS diff --git a/OptiScaler/OptiScaler.vcxproj b/OptiScaler/OptiScaler.vcxproj index f830b679c..a7a2d9c67 100644 --- a/OptiScaler/OptiScaler.vcxproj +++ b/OptiScaler/OptiScaler.vcxproj @@ -824,6 +824,7 @@ copy NUL "$(SolutionDir)x64\Release\a\!! EXTRACT ALL FILES TO GAME FOLDER !!" /Y + diff --git a/OptiScaler/dlssnr/DlssNr.h b/OptiScaler/dlssnr/DlssNr.h index 67edf5709..8d450ff7a 100644 --- a/OptiScaler/dlssnr/DlssNr.h +++ b/OptiScaler/dlssnr/DlssNr.h @@ -14,7 +14,8 @@ // shaders/dlssnr/DlssNr_Dx12 the composition pass, as a Shader_Dx12 // // Call sites, for the record: -// inputs/NVNGX_DLSS_Dx12.cpp the pass after an upscale +// inputs/NVNGX_DLSS_Dx12.cpp the pass after an upscale (PostProcess) +// upscalers/IFeature_Dx12.cpp multi-pass stage (resources in, at R) // upscalers/IFeature_Dx11wDx12.cpp the pass inside the D3D11-on-D3D12 bridge // menu/menu_common.cpp the settings panel diff --git a/OptiScaler/dlssnr/DlssNrFeature_Dx12.h b/OptiScaler/dlssnr/DlssNrFeature_Dx12.h index 4443a446e..88b62f065 100644 --- a/OptiScaler/dlssnr/DlssNrFeature_Dx12.h +++ b/OptiScaler/dlssnr/DlssNrFeature_Dx12.h @@ -33,21 +33,22 @@ namespace DlssNr void EvaluateAfterUpscale(ID3D12GraphicsCommandList* cmdList, NVSDK_NGX_Parameter* params, ID3D12CommandQueue* timingQueue = nullptr); - +// Resources in. Builds the composition pass on first use if it is not already +// on this device, then Dispatches. Call sites that already hold the textures +// -- the multi-pass pipeline stage -- use this rather than reading an NGX +// parameter block. +void Run(ID3D12GraphicsCommandList* cmdList, ID3D12Resource* colour, ID3D12Resource* depth, ID3D12Resource* motion, + ID3D12Resource* output, const DlssNrFrameInfo& frame, ID3D12CommandQueue* timingQueue = nullptr); // Frame generation titles tag their UI layer through Streamline; a copy of it makes the HUD mask // exact at the finished frame. Called at tag time. - - - // The settings panel, drawn inside OptiScaler's menu. void RenderMenu(::Config* config, float menuResScale); // Clears the session failure latch, so a failure caused by transient thrash does not cost a restart. void RetryAfterFailure(); - // Whether the model is loaded and running, for the overlay. bool IsRunning(); diff --git a/OptiScaler/dlssnr/DlssNr_Area.h b/OptiScaler/dlssnr/DlssNr_Area.h new file mode 100644 index 000000000..efd02dedb --- /dev/null +++ b/OptiScaler/dlssnr/DlssNr_Area.h @@ -0,0 +1,64 @@ +#pragma once + +#include +#include + +namespace DlssNr +{ + +inline float Overlap1D(float box0, float box1, int i) +{ + const float t0 = (float) i; + const float t1 = (float) (i + 1); + const float a = box0 > t0 ? box0 : t0; + const float b = box1 < t1 ? box1 : t1; + return b > a ? (b - a) : 0.0f; +} + +struct AreaSample +{ + float r, g, b, a; +}; + +// src is row-major srcW*srcH. Matches spec §7.1 / HLSL Mode 2 Matched. +inline AreaSample AreaDownsamplePixel(const AreaSample* src, int srcW, int srcH, int dstW, int dstH, int x, int y) +{ + if (dstW == srcW && dstH == srcH) + return src[y * srcW + x]; + + const float x0 = ((float) x * (float) srcW) / (float) dstW; + const float x1 = ((float) (x + 1) * (float) srcW) / (float) dstW; + const float y0 = ((float) y * (float) srcH) / (float) dstH; + const float y1 = ((float) (y + 1) * (float) srcH) / (float) dstH; + const float area = (x1 - x0) * (y1 - y0); + + const int i0 = (int) floorf(x0); + const int i1 = (int) ceilf(x1) - 1; + const int j0 = (int) floorf(y0); + const int j1 = (int) ceilf(y1) - 1; + + float ar = 0.f, ag = 0.f, ab = 0.f; + for (int j = j0; j <= j1; ++j) + { + const int jj = std::clamp(j, 0, srcH - 1); + const float wy = Overlap1D(y0, y1, j); + for (int i = i0; i <= i1; ++i) + { + const int ii = std::clamp(i, 0, srcW - 1); + const float w = Overlap1D(x0, x1, i) * wy; + const AreaSample& s = src[jj * srcW + ii]; + ar += s.r * w; + ag += s.g * w; + ab += s.b * w; + } + } + + const float cx = ((float) x + 0.5f) * (float) srcW / (float) dstW; + const float cy = ((float) y + 0.5f) * (float) srcH / (float) dstH; + const int acx = std::clamp((int) floorf(cx), 0, srcW - 1); + const int acy = std::clamp((int) floorf(cy), 0, srcH - 1); + + return { ar / area, ag / area, ab / area, src[acy * srcW + acx].a }; +} + +} // namespace DlssNr diff --git a/OptiScaler/dlssnr/DlssNr_Capture.h b/OptiScaler/dlssnr/DlssNr_Capture.h index 196a72466..392c5518c 100644 --- a/OptiScaler/dlssnr/DlssNr_Capture.h +++ b/OptiScaler/dlssnr/DlssNr_Capture.h @@ -216,13 +216,11 @@ class FrameCapture buf.SampleDesc.Count = 1; buf.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; - return SUCCEEDED(device->CreateCommittedResource(&heap, D3D12_HEAP_FLAG_NONE, &buf, - D3D12_RESOURCE_STATE_COPY_DEST, nullptr, - IID_PPV_ARGS(&shot.readback))); + return SUCCEEDED(device->CreateCommittedResource( + &heap, D3D12_HEAP_FLAG_NONE, &buf, D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&shot.readback))); } - static void copy(ID3D12GraphicsCommandList* cmd, ID3D12Resource* src, D3D12_RESOURCE_STATES state, - Shot& shot) + static void copy(ID3D12GraphicsCommandList* cmd, ID3D12Resource* src, D3D12_RESOURCE_STATES state, Shot& shot) { if (shot.readback == nullptr) return; @@ -320,11 +318,10 @@ class FrameCapture { std::fprintf(f, "frames %u\n", captured_); std::fprintf(f, "before width %llu height %u format %d rowPitch %u\n", - (unsigned long long) beforeDesc_.Width, beforeDesc_.Height, - (int) beforeDesc_.Format, + (unsigned long long) beforeDesc_.Width, beforeDesc_.Height, (int) beforeDesc_.Format, beforeShots_.empty() ? 0 : beforeShots_[0].layout.Footprint.RowPitch); - std::fprintf(f, "after width %llu height %u format %d rowPitch %u\n", - (unsigned long long) afterDesc_.Width, afterDesc_.Height, (int) afterDesc_.Format, + std::fprintf(f, "after width %llu height %u format %d rowPitch %u\n", (unsigned long long) afterDesc_.Width, + afterDesc_.Height, (int) afterDesc_.Format, afterShots_.empty() ? 0 : afterShots_[0].layout.Footprint.RowPitch); std::fprintf(f, "\nbefore_NN.raw is the frame as the upscaler produced it.\n"); std::fprintf(f, "after_NN.raw is the same frame once the model's edit was applied.\n"); diff --git a/OptiScaler/dlssnr/DlssNr_Codec.h b/OptiScaler/dlssnr/DlssNr_Codec.h index e5e9d242b..353045e64 100644 --- a/OptiScaler/dlssnr/DlssNr_Codec.h +++ b/OptiScaler/dlssnr/DlssNr_Codec.h @@ -164,7 +164,8 @@ float3 EditAt(float2 uvq) return m - p; } -)" R"( +)" + R"( [numthreads(8, 8, 1)] void main(uint3 id : SV_DispatchThreadID) { @@ -275,7 +276,8 @@ void main(uint3 id : SV_DispatchThreadID) // frame even on a static scene; blending each frame's edit with its own reprojected history keeps // the consistent part -- the detail -- and cancels the part that re-randomises. NVIDIA's own // motion vectors carry the history to where the surface is now. -)" R"( +)" + R"( // The composition. The model's answer is not treated as a difference to add onto the frame -- it // is a complete picture in its own right, and it is brought back by rescaling it to sit where the // original's luminance says it should. Adding a difference is what let colour run away: nothing @@ -341,7 +343,6 @@ struct Params float mvScaleY; unsigned int guideWidth; unsigned int guideHeight; - }; static_assert(sizeof(Params) % 4 == 0, "root constants are dwords"); @@ -384,8 +385,8 @@ class Codec ID3DBlob* code = nullptr; ID3DBlob* errors = nullptr; - if (FAILED(D3DCompile(kShaderSource, strlen(kShaderSource), nullptr, nullptr, nullptr, "main", - "cs_5_1", 0, 0, &code, &errors))) + if (FAILED(D3DCompile(kShaderSource, strlen(kShaderSource), nullptr, nullptr, nullptr, "main", "cs_5_1", 0, 0, + &code, &errors))) { if (errors != nullptr) errors->Release(); @@ -470,9 +471,8 @@ class Codec // Every texture must already be in the state its slot needs: sources shader-readable, targets // writable. Slots a pass does not read still have to be populated, or the descriptor is undefined. void dispatch(ID3D12GraphicsCommandList* cmd, const Params& constants, ID3D12Resource* source, - ID3D12Resource* model, ID3D12Resource* original, ID3D12Resource* target, - ID3D12Resource* keep, ID3D12Resource* motion = nullptr, - ID3D12Resource* prevEdit = nullptr) + ID3D12Resource* model, ID3D12Resource* original, ID3D12Resource* target, ID3D12Resource* keep, + ID3D12Resource* motion = nullptr, ID3D12Resource* prevEdit = nullptr) { if (pipeline_ == nullptr) return; @@ -485,10 +485,8 @@ class Codec D3D12_GPU_DESCRIPTOR_HANDLE gpu = heap_->GetGPUDescriptorHandleForHeapStart(); gpu.ptr += (UINT64) slot * kPerDispatch * stride_; - ID3D12Resource* srvs[5] = { source, model != nullptr ? model : source, - original != nullptr ? original : source, - motion != nullptr ? motion : source, - prevEdit != nullptr ? prevEdit : source }; + ID3D12Resource* srvs[5] = { source, model != nullptr ? model : source, original != nullptr ? original : source, + motion != nullptr ? motion : source, prevEdit != nullptr ? prevEdit : source }; for (int i = 0; i < 5; ++i) { diff --git a/OptiScaler/dlssnr/DlssNr_Menu.cpp b/OptiScaler/dlssnr/DlssNr_Menu.cpp index e73113dcc..c715d7681 100644 --- a/OptiScaler/dlssnr/DlssNr_Menu.cpp +++ b/OptiScaler/dlssnr/DlssNr_Menu.cpp @@ -1,9 +1,10 @@ #include "pch.h" #include "DlssNr.h" - +#include "DlssNr_Modes.h" #include +#include #include #include @@ -17,7 +18,7 @@ static void HelpMarker(const char* tip) ImGui::SameLine(); ImGui::TextDisabled("(?)"); - if (ImGui::IsItemHovered()) + if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) { ImGui::BeginTooltip(); ImGui::PushTextWrapPos(ImGui::GetFontSize() * 40.0f); @@ -27,6 +28,50 @@ static void HelpMarker(const char* tip) } } +struct ComboItem +{ + const char* label; + bool disabled = false; + const char* itemTip = nullptr; +}; + +static bool ComboWithDisabledItems(const char* label, int* current, const ComboItem* items, int count) +{ + const char* preview = ""; + if (*current >= 0 && *current < count) + preview = items[*current].label; + + bool changed = false; + if (ImGui::BeginCombo(label, preview)) + { + for (int i = 0; i < count; ++i) + { + if (items[i].disabled) + ImGui::BeginDisabled(); + + const bool selected = (*current == i); + if (ImGui::Selectable(items[i].label, selected) && !items[i].disabled) + { + *current = i; + changed = true; + } + + if (selected) + ImGui::SetItemDefaultFocus(); + + if (items[i].itemTip != nullptr && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) + ImGui::SetTooltip("%s", items[i].itemTip); + + if (items[i].disabled) + ImGui::EndDisabled(); + } + + ImGui::EndCombo(); + } + + return changed; +} + void RenderMenu(Config* config, float menuResScale) { @@ -42,10 +87,10 @@ void RenderMenu(Config* config, float menuResScale) config->DlssNrEnabled = enabled; HelpMarker("Synthesises detail in the upscaler's output, before frame generation sees it." - "\n\nNeeds two similarly named files beside OptiScaler, one character apart:" - "\n nvngx_dlssnr.dll NVIDIA's model (~165 MB) -- you supply it" - "\n nvngx.dll_dlssnr.dll the forwarder (~13 KB) -- ships in this package" - "\nUndocumented and driven directly, so none of this is officially supported."); + "\n\nNeeds two similarly named files beside OptiScaler, one character apart:" + "\n nvngx_dlssnr.dll NVIDIA's model (~165 MB) -- you supply it" + "\n nvngx.dll_dlssnr.dll the forwarder (~13 KB) -- ships in this package" + "\nUndocumented and driven directly, so none of this is officially supported."); // The toggle can be bound to a key, and nobody would think to look for it under Keybinds // unless told. Dimmed, because it is a note rather than a setting. @@ -74,8 +119,7 @@ void RenderMenu(Config* config, float menuResScale) const auto ms = DlssNr::LastGpuTime(); if (ms.has_value()) - ImGui::TextColored(ImVec4(0.4f, 0.9f, 0.5f, 1.0f), "Running - %.2f ms per frame", - ms.value()); + ImGui::TextColored(ImVec4(0.4f, 0.9f, 0.5f, 1.0f), "Running - %.2f ms per frame", ms.value()); else ImGui::TextColored(ImVec4(0.4f, 0.9f, 0.5f, 1.0f), "Running."); @@ -91,71 +135,226 @@ void RenderMenu(Config* config, float menuResScale) ImGui::Spacing(); ImGui::PushItemWidth(220.0f * menuResScale); + ImGui::SeparatorText("Placement"); + + { + static const char* modeNames[] = { "Post-process", "Multi-pass" }; + int mode = (int) config->DlssNrMode.value_or_default(); + if (mode < 0 || mode > (int) DlssNr::Mode::MultiPass) + mode = (int) DlssNr::Mode::PostProcess; + + if (ImGui::Combo("Placement", &mode, modeNames, IM_ARRAYSIZE(modeNames))) + config->DlssNrMode = (uint32_t) mode; + + HelpMarker("Where the model sits relative to the upscaler." + "\n\nPost-process runs it on the finished frame. It is the only one that needs" + "\nnothing from the upscaler, so it is also the only one that works when a game" + "\nis using its own DLSS and OptiScaler is passing it straight through." + "\n\nMulti-pass runs a first pass 1:1 -- denoising if that is Ray Reconstruction," + "\nantialiasing as DLAA if it is Super Resolution -- then the model at render" + "\nresolution, then a spatial FSR1 enlarge to display. Dx12 DLSS / Ray" + "\nReconstruction only; anything else, or a first-pass pipeline that does not" + "\nmatch the live upscaler, falls back to post-process."); + + const auto selected = (DlssNr::Mode) config->DlssNrMode.value_or_default(); + + if (DlssNr::UsesTwoFeatures(selected)) + { + static const char* pipelineNames[] = { "Ray Reconstruction", "Super Resolution" }; + int pipeline = (int) config->DlssNrFeature1Pipeline.value_or_default(); + + if (ImGui::Combo("First pass", &pipeline, pipelineNames, IM_ARRAYSIZE(pipelineNames))) + config->DlssNrFeature1Pipeline = (uint32_t) pipeline; + + HelpMarker("Which upscaler the first pass is." + "\n\nThis states what the game is set up for; it does not switch anything." + "\nOptiScaler cannot substitute one for the other -- Ray Reconstruction needs" + "\nG-buffer inputs a Super Resolution integration never supplies -- so a" + "\nmismatch falls back to post-process rather than half-applying the" + "\narrangement, and says so in the log."); + } + } + ImGui::SeparatorText("Cost"); - // Any percentage, rather than a handful of steps somebody chose in advance. The lower bound - // is 25%: below that the model is working on so little of the picture that its answer no - // longer survives being enlarged onto it. - // Applied when the handle is let go, not while it is moving. - // - // Every distinct value here is a different working size, and a different working size tears - // down the scratch textures and rebuilds the model. Writing it on each pixel of a drag meant - // dozens of rebuilds in a second, which is felt as the whole frame hitching. The slider still - // reads live; only the commit waits. + // The slider is always work / display, so the same number costs the same + // in both placements. Match-render locks work to the game's native raster + // and does not overwrite WorkingScale, so unchecking restores it. + bool atNative = config->DlssNrWorkAtNative.value_or_default(); + if (ImGui::Checkbox("Match render resolution", &atNative)) + config->DlssNrWorkAtNative = atNative; + + HelpMarker("Lock the model to the game's render resolution." + "\n\nThe slider then shows that size as a percentage of display and cannot be" + "\nmoved. Unchecking puts the last display-relative value back, clamped to" + "\nwhatever the current placement allows."); + + unsigned int displayH = 0; + unsigned int nativeH = 0; + + if (auto* feature = State::Instance().currentFeature; feature != nullptr && !feature->IsFrozen()) + { + displayH = feature->DisplayHeight(); + nativeH = feature->NRSourceHeight(); + + if (nativeH == 0) + nativeH = feature->RenderHeight(); + } + + const bool haveRatio = displayH != 0 && nativeH != 0; + int nativePercent = 100; + + if (haveRatio) + nativePercent = (int) lroundf((float) nativeH / (float) displayH * 100.0f); + + if (nativePercent < 1) + nativePercent = 1; + + const bool multiPass = DlssNr::ConfiguredMode() == DlssNr::Mode::MultiPass; + int maxPercent = 100; + + if (multiPass && haveRatio && nativePercent < maxPercent) + maxPercent = nativePercent; + + int minPercent = 25; + + if (maxPercent < minPercent) + minPercent = maxPercent; + + // Applied when the handle is let go, not while it is moving. Every distinct + // value here is a different working size, and a different working size tears + // down the scratch textures and rebuilds the model. static int pendingScale = -1; - int scalePercent = pendingScale >= 0 - ? pendingScale - : (int) lroundf(config->DlssNrWorkingScale.value_or_default() * 100.0f); + if (atNative) + pendingScale = -1; + + int scalePercent = 100; + + if (atNative) + scalePercent = haveRatio ? nativePercent : 100; + else if (pendingScale >= 0) + scalePercent = pendingScale; + else + scalePercent = (int) lroundf(config->DlssNrWorkingScale.value_or_default() * 100.0f); + + if (scalePercent < minPercent) + scalePercent = minPercent; + + if (scalePercent > maxPercent) + scalePercent = maxPercent; + + ImGui::BeginDisabled(atNative); - if (ImGui::SliderInt("Model resolution", &scalePercent, 25, 100, "%d%%")) + if (ImGui::SliderInt("Model resolution", &scalePercent, minPercent, maxPercent, "%d%%")) pendingScale = scalePercent; if (ImGui::IsItemDeactivatedAfterEdit() && pendingScale >= 0) { - config->DlssNrWorkingScale = std::clamp(pendingScale, 25, 100) / 100.0f; + config->DlssNrWorkingScale = std::clamp(pendingScale, minPercent, maxPercent) / 100.0f; pendingScale = -1; } - HelpMarker("What fraction of the frame the model works at. Cost falls with the square of" - "\nthis, so half resolution is roughly a quarter of the time." - "\n\nThe frame is never reduced. Only the model's contribution is computed small" - "\nand enlarged, so the picture underneath is untouched whatever this says." - "\n\nWhat it trades: the shading the model adds is broad and survives enlargement;" - "\nthe fine structure it synthesises does not, and softens. Worth having when the" - "\npass costs more than you want to pay for the detail it returns." - "\n\nThe frame itself stays at full detail whatever this says -- only the" - "\nmodel's own work is done small."); + ImGui::EndDisabled(); + + HelpMarker("What fraction of display resolution the model works at. The same percentage" + "\ncosts the same in post-process and multi-pass. Cost falls with the square of" + "\nthis, so half resolution is roughly a quarter of the time." + "\n\nThe frame is never reduced. Only the model's contribution is computed small" + "\nand enlarged, so the picture underneath is untouched whatever this says." + "\n\nMulti-pass cannot go above the game's render resolution as a fraction of" + "\ndisplay -- there is nothing larger for the model to read." + "\n\nWhat it trades: the shading the model adds is broad and survives enlargement;" + "\nthe fine structure it synthesises does not, and softens."); + + { + static const char* transferNames[] = { "Classic", "Matched residual" }; + int transfer = (int) config->DlssNrTransfer.value_or_default(); + if (transfer < 0 || transfer > (int) DlssNr::Transfer::MatchedResidual) + transfer = (int) DlssNr::Transfer::Classic; + if (ImGui::Combo("Transfer", &transfer, transferNames, IM_ARRAYSIZE(transferNames))) + config->DlssNrTransfer = (uint32_t) transfer; + + HelpMarker("How a below-frame model is brought back onto the picture this pass writes." + "\n\nMatched residual is the default: keep a sharp copy of the picture the model was" + "\nshown, add only what the model changed, then run the highlight-aware compose. The" + "\nshrink is an area filter so the model is not fed an aliased thumbnail." + "\n\nClassic shrinks with bilinear and folds the small answer directly onto the" + "\nfull-resolution picture." + "\n\nWhen the model is the same size as this frame the two match. Changing this does" + "\nnot rebuild the model; the next frame uses the new shrink and the new compose."); + } + + const bool matched = DlssNr::ConfiguredTransfer() == DlssNr::Transfer::MatchedResidual; + const bool debugOn = config->DlssNrDebugView.value_or_default() != 0; + int lift = (int) config->DlssNrHdrLift.value_or_default(); + if (lift < 0 || lift > 1) + lift = 0; + + ImGui::BeginDisabled(!matched || debugOn); + { + static const char* liftNames[] = { "Highlight-aware", "Add the change" }; + if (ImGui::Combo("HDR lift", &lift, liftNames, IM_ARRAYSIZE(liftNames))) + config->DlssNrHdrLift = (uint32_t) lift; + HelpMarker("Matched residual only. Highlight-aware is the default compose: the model's" + "\nchange is folded back with the same highlight-aware path as Classic." + "\n\nAdd the change adds the model's difference onto the original. Detail" + "\nstrength still moves toward that sum; Colour strength does not apply." + "\n\nA debug view replaces the composed picture, so this does not change what" + "\nyou see while one is on."); + } + ImGui::EndDisabled(); + + const bool additive = matched && lift == 1; ImGui::SeparatorText("How much of it lands"); + ImGui::BeginDisabled(debugOn); float transfer = config->DlssNrTransferStrength.value_or_default(); if (ImGui::SliderFloat("Detail strength", &transfer, 0.0f, 2.0f, "%.2f")) config->DlssNrTransferStrength = transfer; + ImGui::EndDisabled(); HelpMarker("How far the frame moves toward the model's picture." - "\n\nThe model's answer is not added to the frame -- it is a complete picture of its" - "\nown, rescaled so its luminance sits where the original says it should. This" - "\nblends between the two, so both ends are real pictures and everything between" - "\nthem is one too." - "\n\n0 gives back exactly what the upscaler produced. 1 is the model's picture." - "\n\nAbove 1 carries on past it in the same direction, which is not something the" - "\nmodel asked for -- use it to see what it is doing, then come back down. This" - "\nis the control to push if you want more effect: Intensity belongs to the model" - "\nand it decides what to do with it."); - + "\n\nThe model's answer is not added to the frame -- it is a complete picture of its" + "\nown, rescaled so its luminance sits where the original says it should. This" + "\nblends between the two, so both ends are real pictures and everything between" + "\nthem is one too." + "\n\n0 writes back the frame as this pass first saw it. That is not the Capture" + "\nbefore image, which is what the model was shown." + "\n1 is the model's picture." + "\n\nAbove 1 carries on past it in the same direction, which is not something the" + "\nmodel asked for -- use it to see what it is doing, then come back down. This" + "\nis the control to push if you want more effect: Intensity belongs to the model" + "\nand it decides what to do with it."); + + ImGui::BeginDisabled(debugOn || additive); float colour = config->DlssNrColourStrength.value_or_default(); if (ImGui::SliderFloat("Colour strength", &colour, 0.0f, 1.0f, "%.2f")) config->DlssNrColourStrength = colour; + ImGui::EndDisabled(); HelpMarker("Whether the model's colour arrives with its light." - "\n\n0 keeps the game's own hue exactly -- every pixel is the original colour with" - "\nonly its brightness carrying the model's verdict. Game-accurate colour, with" - "\nthe detail. 1 brings the model's colour as well, in its own hue, clamped into" - "\nAP1 so nothing unreachable is asked for." - "\n\nThis cannot shift hue on its own: it interpolates between two finished" - "\npictures rather than adding a colour difference to one, which is what used to" - "\nlet a warm subject come back green."); + "\n\n0 keeps the game's own hue exactly -- every pixel is the original colour with" + "\nonly its brightness carrying the model's verdict. Game-accurate colour, with" + "\nthe detail. 1 brings the model's colour as well, in its own hue, clamped into" + "\nAP1 so nothing unreachable is asked for." + "\n\nThis cannot shift hue on its own: it interpolates between two finished" + "\npictures rather than adding a colour difference to one, which is what used to" + "\nlet a warm subject come back green." + "\n\nAdd the change (under HDR lift) has no colour mix; this slider is ignored" + "\nthen."); + + float maxRatio = config->DlssNrMaxRatio.value_or_default(); + if (ImGui::SliderFloat("Highlight guard", &maxRatio, 1.0f, 8.0f, "%.1fx")) + config->DlssNrMaxRatio = maxRatio; + + HelpMarker("The most the pass may brighten any pixel, as a multiple of what it already" + "\nwas. Darkening is not capped by this -- only growth is." + "\n\nLights are where the model has least to say and where rescaling its answer" + "\ninto the frame does the most damage: an early version turned every strip light" + "\nin the scene into a string of coloured cells. 2x leaves detail intact while" + "\nmaking that failure impossible. Raise it only if bright areas look clipped."); ImGui::SeparatorText("Model"); @@ -167,8 +366,8 @@ void RenderMenu(Config* config, float menuResScale) config->DlssNrPreset = (uint32_t) preset; HelpMarker("Default leaves the choice to the model." - "\n\nNot the same scale as the super resolution or ray reconstruction presets --" - "\nthe same number means something different here."); + "\n\nNot the same scale as the super resolution or ray reconstruction presets --" + "\nthe same number means something different here."); static const char* nrStyleNames[] = { "Default (standard)", "Natural", "Cinematic" }; int style = (int) config->DlssNrStyle.value_or_default(); @@ -194,23 +393,28 @@ void RenderMenu(Config* config, float menuResScale) config->DlssNrIntensity = intensity; HelpMarker("The model's own strength control, applied inside it. Distinct from detail" - "\nstrength above, which scales the result afterwards."); + "\nstrength above, which scales the result afterwards."); float localStructure = config->DlssNrLocalStructure.value_or_default(); if (ImGui::SliderFloat("Local structure", &localStructure, 0.0f, 2.0f, "%.2f")) config->DlssNrLocalStructure = localStructure; + HelpMarker("How strongly the model rebuilds local detail." + "\n\nRead when the model is built, so a change rebuilds it after a moment."); + float localTone = config->DlssNrLocalTone.value_or_default(); if (ImGui::SliderFloat("Local tone", &localTone, 0.0f, 2.0f, "%.2f")) config->DlssNrLocalTone = localTone; + HelpMarker("How strongly the model remaps local brightness." + "\n\nRead when the model is built, so a change rebuilds it after a moment."); float skin = config->DlssNrSkinStructure.value_or_default(); if (ImGui::SliderFloat("Skin structure", &skin, -1.0f, 2.0f, "%.2f")) config->DlssNrSkinStructure = skin; HelpMarker("-1 means follow local structure, and is the model's own default -- it is not a" - "\nstrength of zero. 0 and above set skin independently of the rest of the frame."); + "\nstrength of zero. 0 and above set skin independently of the rest of the frame."); bool autoMask = config->DlssNrAutoMask.value_or_default(); if (ImGui::Checkbox("Auto skin mask", &autoMask)) @@ -221,41 +425,23 @@ void RenderMenu(Config* config, float menuResScale) ImGui::SeparatorText("Colour"); ImGui::TextDisabled("The model was trained on finished, sRGB-encoded frames. The upscaler's\n" - "output is not one: it is linear and open-ended. These decide how it is\n" - "mapped into something the model recognises. A frame the game reports as\n" + "output is not one: it is linear and open-ended. Paper white decides how it\n" + "is mapped into something the model recognises. A frame the game reports as\n" "already tone-mapped is passed over untouched and none of this applies."); - { float wpScale = config->DlssNrWhitePointScale.value_or_default(); if (ImGui::SliderFloat("Paper white", &wpScale, 0.25f, 4.0f, "%.2fx")) config->DlssNrWhitePointScale = wpScale; HelpMarker("What the frame is divided by before the model sees it. There is no other white" - "\npoint; this is the whole of it." - "\n\nThe model was trained on finished frames where white sits at 1. The" - "\nupscaler's output is linear and open-ended, so something has to say where" - "\nwhite is, and 1.0 is right for most games." - "\n\nAbove 1 the picture handed over is darker, so highlights sit lower on the" - "\ncurve and the model treats them as less extreme; below 1, the opposite. If a" - "\ngame looks washed out or flat, this is the first thing to move." - "\n\nThis was once a multiplier on a measured white point. The measurement is" - "\ngone: it read scene brightness rather than where white belongs, handed the" - "\nmodel a picture three times too dark, and left the highlight path nothing to" - "\ngive back." - "\n\nAt strength zero the frame is still bit-identical whatever this says."); - - float maxRatio = config->DlssNrMaxRatio.value_or_default(); - if (ImGui::SliderFloat("Highlight guard", &maxRatio, 1.0f, 8.0f, "%.1fx")) - config->DlssNrMaxRatio = maxRatio; - - HelpMarker("The most the pass may brighten any pixel, as a multiple of what it already" - "\nwas. Darkening is not capped by this -- only growth is." - "\n\nLights are where the model has least to say and where rescaling its answer" - "\ninto the frame does the most damage: an early version turned every strip light" - "\nin the scene into a string of coloured cells. 2x leaves detail intact while" - "\nmaking that failure impossible. Raise it only if bright areas look clipped."); - - } + "\npoint; this is the whole of it." + "\n\nThe model was trained on finished frames where white sits at 1. The" + "\nupscaler's output is linear and open-ended, so something has to say where" + "\nwhite is, and 1.0 is right for most games." + "\n\nAbove 1 the picture handed over is darker, so highlights sit lower on the" + "\ncurve and the model treats them as less extreme; below 1, the opposite. If a" + "\ngame looks washed out or flat, this is the first thing to move." + "\n\nAt detail strength 0 this does not move the edited picture."); ImGui::SeparatorText("Inspect"); @@ -263,85 +449,105 @@ void RenderMenu(Config* config, float menuResScale) { ImGui::TextDisabled("Capturing..."); } - else if (ImGui::Button("Capture 8 frames")) + else { - DlssNr::RequestCapture(8); + ImGui::BeginDisabled(debugOn); + if (ImGui::Button("Capture 8 frames")) + DlssNr::RequestCapture(8); + ImGui::EndDisabled(); } HelpMarker("Writes eight consecutive frames twice: as the upscaler produced them, and again" - "\nonce the model's edit was applied." - "\n\nSame frames, same run, one variable -- which is what comparing two video" - "\ncaptures can never be, since they have different camera paths and a codec in" - "\nbetween that discards exactly the fine temporal detail in question." - "\n\nRaw, into a dlssnr-capture folder beside OptiScaler. Bounded to eight frames," - "\nand each run overwrites the last."); - + "\nonce the pass has run." + "\n\nIt writes whatever the pass is currently showing. Turn Debug view off if you" + "\nwant the composed after image." + "\n\nSame frames, same run, one variable -- which is what comparing two video" + "\ncaptures can never be, since they have different camera paths and a codec in" + "\nbetween that discards exactly the fine temporal detail in question." + "\n\nRaw, into a dlssnr-capture folder beside OptiScaler. Bounded to eight frames," + "\nand each run overwrites the last."); + + ImGui::BeginDisabled(debugOn); static const char* compareNames[] = { "Off", "Side by side", "Wipe" }; int compare = (int) config->DlssNrCompare.value_or_default(); if (ImGui::Combo("Compare", &compare, compareNames, IM_ARRAYSIZE(compareNames))) config->DlssNrCompare = (uint32_t) compare; + ImGui::EndDisabled(); HelpMarker("Shows the pass against itself, so the two can be seen at once rather than" - "\ntoggled and remembered." - "\n\nSide by side puts the whole frame in each half, untouched on the left and" - "\nedited on the right. Both halves are squeezed horizontally to fit, so it is" - "\nfor looking at rather than playing in." - "\n\nWipe cuts a single frame at the split and resamples nothing, so the picture" - "\nis the right shape and can be played normally. Drag the split below; it is a" - "\nstored setting and stays put once the menu is closed." - "\n\nNeither needs the menu open to keep working. A hairline marks the join."); - - if (compare != 0) + "\ntoggled and remembered." + "\n\nSide by side puts the whole frame in each half. A half is half as wide as" + "\nthe frame, so Zoom decides whether you see letterbox bars or cropped sides." + "\nIt is for looking at rather than playing in." + "\n\nWipe cuts a single frame at the split and resamples nothing, so the picture" + "\nis the right shape and can be played normally. Drag the split below; it is a" + "\nstored setting and stays put once the menu is closed." + "\n\nSwap sides decides which half is the untouched frame. Neither needs the" + "\nmenu open to keep working. A hairline marks the join." + "\n\nA debug view replaces the composed picture, so comparison is off while one" + "\nis on."); + + if (compare != 0 && !debugOn) { bool swap = config->DlssNrCompareSwap.value_or_default(); if (ImGui::Checkbox("Swap sides", &swap)) config->DlssNrCompareSwap = swap; HelpMarker("Puts the edited frame on the other side." - "\n\nWorth doing once you have decided which you prefer: the eye is not" - "\neven-handed about left and right, and a difference can read as an" - "\nimprovement purely from where it sits. If the same side still wins after" - "\nswapping, it is the pass you are seeing and not the placement."); + "\n\nWorth doing once you have decided which you prefer: the eye is not" + "\neven-handed about left and right, and a difference can read as an" + "\nimprovement purely from where it sits. If the same side still wins after" + "\nswapping, it is the pass you are seeing and not the placement."); } - if (compare == 1) + if (compare == 1 && !debugOn) { float zoom = config->DlssNrCompareZoom.value_or_default(); if (ImGui::SliderFloat("Zoom", &zoom, 1.0f, 2.0f, "%.2f")) config->DlssNrCompareZoom = std::clamp(zoom, 1.0f, 2.0f); HelpMarker("How much of the frame each half shows." - "\n\nA half is half as wide as the frame and just as tall, so the frame" - "\ncannot fill it and keep its shape." - "\n\nAt 1 the whole frame is there at its right proportions, with bars above" - "\nand below. At 2 the half is filled and the sides are cropped away" - "\ninstead. Anything between trades one for the other."); + "\n\nA half is half as wide as the frame and just as tall, so the frame" + "\ncannot fill it and keep its shape." + "\n\nAt 1 the whole frame is there at its right proportions, with bars above" + "\nand below. At 2 the half is filled and the sides are cropped away" + "\ninstead. Anything between trades one for the other."); } - if (compare == 2) + if (compare == 2 && !debugOn) { float split = config->DlssNrCompareSplit.value_or_default(); if (ImGui::SliderFloat("Split", &split, 0.0f, 1.0f, "%.2f")) config->DlssNrCompareSplit = std::clamp(split, 0.0f, 1.0f); - HelpMarker("Where the wipe cuts. Left of it is the frame as the upscaler produced it," - "\nright of it is the frame the model edited."); + HelpMarker("Where the wipe cuts. Swap sides decides which side is the untouched frame."); } - static const char* debugNames[] = { "Off", "Proxy (what the model sees)", "Model output (raw)", - "Difference (amplified)" }; + const ComboItem debugItems[] = { + { "Off" }, + { "Proxy (what the model sees)" }, + { "Model output (raw)" }, + { "Difference (amplified)" }, + { "Full-res proxy", !matched, "Matched residual only." }, + { "Matched picture", !matched, "Matched residual only." }, + }; int debugView = (int) config->DlssNrDebugView.value_or_default(); - if (ImGui::Combo("Debug view", &debugView, debugNames, IM_ARRAYSIZE(debugNames))) + if (ComboWithDisabledItems("Debug view", &debugView, debugItems, IM_ARRAYSIZE(debugItems))) config->DlssNrDebugView = (uint32_t) debugView; - HelpMarker("Proxy is the picture handed to the model -- if that looks wrong, the white point" - "\nis wrong and nothing downstream can be judged." - "\n\nDifference shows what the model actually changed, amplified twenty times and" - "\ncentred on grey. A flat grey frame there means it is doing nothing."); + HelpMarker("Off is the composed picture." + "\n\nProxy is the small picture handed to the model -- if that looks wrong, the" + "\nwhite point is wrong and nothing downstream can be judged." + "\n\nModel output is the model's raw answer, the same size as the proxy." + "\n\nDifference shows what the model actually changed, amplified twenty times and" + "\ncentred on grey. A flat grey frame there means it is doing nothing." + "\n\nFull-res proxy is the sharp picture the model was shown, before it was" + "\nshrunk. Matched residual only; Classic writes black." + "\n\nMatched picture is that sharp proxy plus only what the model changed, before" + "\nHDR lift. Matched residual only; Classic writes black."); ImGui::PopItemWidth(); } } } // namespace DlssNr - diff --git a/OptiScaler/dlssnr/DlssNr_Modes.cpp b/OptiScaler/dlssnr/DlssNr_Modes.cpp new file mode 100644 index 000000000..31c39e01c --- /dev/null +++ b/OptiScaler/dlssnr/DlssNr_Modes.cpp @@ -0,0 +1,26 @@ +#include + +#include "DlssNr_Modes.h" + +#include + +namespace DlssNr +{ + +Mode ConfiguredMode() +{ + const auto raw = Config::Instance()->DlssNrMode.value_or_default(); + + // A value from a hand-edited ini that names no mode is post-process, not + // undefined behaviour. + if (raw > (uint32_t) Mode::MultiPass) + return Mode::PostProcess; + + return (Mode) raw; +} + +Transfer ConfiguredTransfer() { return ClampTransfer(Config::Instance()->DlssNrTransfer.value_or_default()); } + +uint32_t ConfiguredHdrLift() { return ClampHdrLift(Config::Instance()->DlssNrHdrLift.value_or_default()); } + +} // namespace DlssNr diff --git a/OptiScaler/dlssnr/DlssNr_Modes.h b/OptiScaler/dlssnr/DlssNr_Modes.h new file mode 100644 index 000000000..4659b1124 --- /dev/null +++ b/OptiScaler/dlssnr/DlssNr_Modes.h @@ -0,0 +1,163 @@ +#pragma once + +#include + +// Where the Neural Rendering pass sits relative to the upscaler. +// +// Kept in its own small header so Config and IFeature can name these without +// pulling in D3D12. + +namespace DlssNr +{ + +enum class Mode : uint32_t +{ + // The model runs on the finished frame, at display resolution. The only + // placement that needs nothing from the upscaler, so it is also the only + // one that works when a game's own DLSS is passing straight through. + PostProcess = 0, + + // Two stages on OptiScaler's own Dx12 DLSS / Ray Reconstruction. The first + // feature runs 1:1 at render resolution, the model runs on that, and + // OS_Dx12 FSR1 enlarges to display. Changing to or from this rebuilds the + // first feature: its target size is latched at creation. + MultiPass = 1, +}; + +enum class Transfer : uint32_t +{ + Classic = 0, + MatchedResidual = 1, +}; + +inline Transfer ClampTransfer(uint32_t raw) +{ + return raw > (uint32_t) Transfer::MatchedResidual ? Transfer::Classic : (Transfer) raw; +} + +inline uint32_t ClampHdrLift(uint32_t raw) { return raw > 1u ? 0u : raw; } + +Transfer ConfiguredTransfer(); +uint32_t ConfiguredHdrLift(); + +// Which upscaler the first pass is. OptiScaler does not substitute one for the +// other -- Ray Reconstruction needs G-buffer inputs a Super Resolution +// integration never supplies -- so this states which the game is set up for, +// and a mismatch falls back rather than half-applying. +enum class Feature1Pipeline : uint32_t +{ + RayReconstruction = 0, + SuperResolution = 1, +}; + +// The mode as configured, before any fallback. Reads config; safe from any thread. +Mode ConfiguredMode(); + +inline bool UsesTwoFeatures(Mode mode) { return mode == Mode::MultiPass; } + +// The model's working raster. +// +// WorkingScale is a fraction of display resolution, clamped to [0.25, 1], then +// clamped to the colour buffer so work never invents pixels. WorkAtNative uses +// the guide size -- the same integer raster as depth and motion vectors -- when +// both axes fit inside colour; otherwise colour. Both guide axes have to fit or +// neither is used: taking one axis from the guide and the other from the colour +// frame invents a third aspect ratio that matches neither. +struct WorkingExtent +{ + unsigned int width; + unsigned int height; +}; + +inline WorkingExtent ResolveWorkingSize(float workScale, bool atNative, unsigned int colorWidth, + unsigned int colorHeight, unsigned int displayWidth, unsigned int displayHeight, + unsigned int guideWidth, unsigned int guideHeight) +{ + if (atNative) + { + const bool guideFits = + guideWidth != 0 && guideHeight != 0 && guideWidth < colorWidth && guideHeight < colorHeight; + return guideFits ? WorkingExtent { guideWidth, guideHeight } : WorkingExtent { colorWidth, colorHeight }; + } + + if (displayWidth == 0 || displayHeight == 0) + { + displayWidth = colorWidth; + displayHeight = colorHeight; + } + + workScale = workScale < 0.25f ? 0.25f : (workScale > 1.0f ? 1.0f : workScale); + + unsigned int workWidth = (unsigned int) (displayWidth * workScale + 0.5f); + unsigned int workHeight = (unsigned int) (displayHeight * workScale + 0.5f); + + if (workWidth > colorWidth) + workWidth = colorWidth; + + if (workHeight > colorHeight) + workHeight = colorHeight; + + if (workWidth == 0) + workWidth = 1; + + if (workHeight == 0) + workHeight = 1; + + return { workWidth, workHeight }; +} + +// Holds the model's working raster while the requested size is still moving -- +// DRS under WorkAtNative, or a quality-preset change -- so the feature is not +// parked and recreated every frame. Recreating skips that evaluate, and doing +// it on every guide-size tick leaves NR off for the whole DRS window. +struct WorkingSizeHold +{ + unsigned int pendingWidth = 0; + unsigned int pendingHeight = 0; + unsigned long long pendingSince = 0; +}; + +inline WorkingExtent TickWorkingSizeHold(WorkingSizeHold& hold, WorkingExtent requested, WorkingExtent latched, + bool haveFeature, bool colorChanged, unsigned long long frame, + unsigned long long settleFrames, bool& commit) +{ + commit = false; + + if (!haveFeature) + { + hold = {}; + return requested; + } + + if (colorChanged) + { + hold = {}; + commit = true; + return requested; + } + + if (requested.width == latched.width && requested.height == latched.height) + { + hold = {}; + return latched; + } + + if (hold.pendingWidth != requested.width || hold.pendingHeight != requested.height) + { + hold.pendingWidth = requested.width; + hold.pendingHeight = requested.height; + hold.pendingSince = frame; + return latched; + } + + if (frame - hold.pendingSince >= settleFrames) + { + hold = {}; + commit = true; + return requested; + } + + return latched; +} + +} // namespace DlssNr diff --git a/OptiScaler/dlssnr/DlssNr_Proxy.cpp b/OptiScaler/dlssnr/DlssNr_Proxy.cpp index e59eebf7c..93b3e4c8f 100644 --- a/OptiScaler/dlssnr/DlssNr_Proxy.cpp +++ b/OptiScaler/dlssnr/DlssNr_Proxy.cpp @@ -1,7 +1,6 @@ #include "pch.h" #include "DlssNr_Proxy.h" - #include #include #include @@ -94,8 +93,7 @@ ProxyState g_proxy; // These have to be set before create, not at evaluate. The model reads its tuning once, while // building the feature; values written only at evaluate are ignored, which is why several of these // controls appeared to do nothing for a long time. -void SetCreationParameters(NVSDK_NGX_Parameter* params, const Config& cfg, unsigned int width, - unsigned int height) +void SetCreationParameters(NVSDK_NGX_Parameter* params, const Config& cfg, unsigned int width, unsigned int height) { SetUInt(params, "DLSSNR.Enabled", 1u); SetUInt(params, "DLSSNR.Width", width); @@ -144,10 +142,9 @@ void Release() g_proxy.height = 0; } -unsigned int Run(ID3D12GraphicsCommandList* cmdList, ID3D12Device* device, ID3D12Resource* color, - ID3D12Resource* depth, ID3D12Resource* motion, ID3D12Resource* output, - unsigned int width, unsigned int height, unsigned int guideWidth, - unsigned int guideHeight, bool depthInverted, bool reset, float mvScaleX, +unsigned int Run(ID3D12GraphicsCommandList* cmdList, ID3D12Device* device, ID3D12Resource* color, ID3D12Resource* depth, + ID3D12Resource* motion, ID3D12Resource* output, unsigned int width, unsigned int height, + unsigned int guideWidth, unsigned int guideHeight, bool depthInverted, bool reset, float mvScaleX, float mvScaleY) { if (g_proxy.failed || !Available()) @@ -206,9 +203,9 @@ unsigned int Run(ID3D12GraphicsCommandList* cmdList, ID3D12Device* device, ID3D1 NVSDK_NGX_FeatureCommonInfo fcInfo {}; NVNGXProxy::GetFeatureCommonInfo(&fcInfo); - const auto initResult = NVNGXProxy::D3D12_Init_Ext()( - app_id_override, State::Instance().NVNGX_ApplicationDataPath.c_str(), device, - (NVSDK_NGX_Version) 0x0000015, &fcInfo); + const auto initResult = + NVNGXProxy::D3D12_Init_Ext()(app_id_override, State::Instance().NVNGX_ApplicationDataPath.c_str(), + device, (NVSDK_NGX_Version) 0x0000015, &fcInfo); // Success here means very little. NGX init is idempotent: a second call on an already // initialised core returns success and changes nothing, which the driver's own log @@ -288,11 +285,9 @@ unsigned int Run(ID3D12GraphicsCommandList* cmdList, ID3D12Device* device, ID3D1 SetFloat(params, "DLSSNR.SkinStructureStrength", cfg.DlssNrSkinStructure.value_or_default()); SetUInt(params, "DLSSNR.UseAutoMask", cfg.DlssNrAutoMask.value_or_default() ? 1u : 0u); - const auto result = - NVNGXProxy::D3D12_EvaluateFeature()(cmdList, g_proxy.feature, params, nullptr); + const auto result = NVNGXProxy::D3D12_EvaluateFeature()(cmdList, g_proxy.feature, params, nullptr); return (unsigned int) result; } } // namespace Proxy } // namespace DlssNr - diff --git a/OptiScaler/dlssnr/DlssNr_Proxy.h b/OptiScaler/dlssnr/DlssNr_Proxy.h index 6e990a68c..a4efb55f8 100644 --- a/OptiScaler/dlssnr/DlssNr_Proxy.h +++ b/OptiScaler/dlssnr/DlssNr_Proxy.h @@ -27,8 +27,6 @@ // // Off by default until it has been shown to produce the same picture as the forwarder path. - - #include namespace DlssNr @@ -40,14 +38,12 @@ bool Available(); // Creates the feature if it does not exist, or if the resolution changed, and evaluates it. // Returns the NGX result of the evaluate, or 0 when nothing could be attempted. -unsigned int Run(ID3D12GraphicsCommandList* cmdList, ID3D12Device* device, ID3D12Resource* color, - ID3D12Resource* depth, ID3D12Resource* motion, ID3D12Resource* output, - unsigned int width, unsigned int height, unsigned int guideWidth, - unsigned int guideHeight, bool depthInverted, bool reset, float mvScaleX, +unsigned int Run(ID3D12GraphicsCommandList* cmdList, ID3D12Device* device, ID3D12Resource* color, ID3D12Resource* depth, + ID3D12Resource* motion, ID3D12Resource* output, unsigned int width, unsigned int height, + unsigned int guideWidth, unsigned int guideHeight, bool depthInverted, bool reset, float mvScaleX, float mvScaleY); // Drops the feature and its parameter block, for a resolution change or shutdown. void Release(); } // namespace Proxy } // namespace DlssNr - diff --git a/OptiScaler/dlssnr/README.md b/OptiScaler/dlssnr/README.md index 10aa9ceaa..f9eaa6732 100644 --- a/OptiScaler/dlssnr/README.md +++ b/OptiScaler/dlssnr/README.md @@ -62,11 +62,16 @@ part of the solution, and builds with everything else. ## Design notes worth knowing before changing anything -- **Ratio composition, not a delta.** The model is shown an encoded proxy; what it returns is - composed back as a ratio against the original's luminance, scaled by a measured slope, with the - chroma added. Composing it additively — which earlier revisions did — discards the model's - behaviour in highlights and makes every arrangement look alike. At strength zero the frame is - bit-identical, always. +- **Two transfers, Matched residual default.** Matched residual is an area shrink, then the + model's *change* added onto the sharp full-res proxy (`colorCopy`) before the same + UpgradeToneMap (or additive H1, under Transfer). Classic is the older path: bilinear shrink, + then ratio compose of the small answer onto the full-resolution frame. Switching does not + rebuild the model. +- **Ratio composition, after an optional residual.** Classic still composes by ratio against the + original's luminance, not by adding a raw delta to the frame. Matched residual builds `T` first, + then runs that same ratio compose with the sharp proxy as the reference. At strength zero both + options write `hdrCopy` (the encode keep). That is not bit-identity with the upscaler UAV, and + it is not the Capture button's “before” image (`colorCopy`). - **Create-time parameters.** The model's tuning (preset, style, intensity, local *) is latched at feature creation; changes rebuild the feature after a settle. The driver's parameter block is not the SDK header's vtable (floats sit at slot 6); the forwarder probes it. Rebuilding every frame diff --git a/OptiScaler/dlssnr/design/2026-09-02-matched-residual-plan.md b/OptiScaler/dlssnr/design/2026-09-02-matched-residual-plan.md new file mode 100644 index 000000000..880d85559 --- /dev/null +++ b/OptiScaler/dlssnr/design/2026-09-02-matched-residual-plan.md @@ -0,0 +1,840 @@ +--- +name: Matched residual transfer +overview: "Add the Classic | Matched residual Transfer option from the 2026-09-02 spec: area downsample + residual compose onto colorCopy, Classic remaining the live default. No new textures, no feature rebuild, no second network." +todos: + - id: ta-area + content: Host-side area downsample fixture (TA) + DlssNr_Area.h + status: pending + - id: config-enums + content: Transfer/HdrLift enums, Config keys, cbuffer fields, offset asserts + status: pending + - id: numeric-views + content: CreateScratch via TypedFormat; remapped format-changed compare; numeric SRVs + status: pending + - id: dispatch-bind + content: InProxy bind colorCopy; set Transfer on downsample+resolve; HdrLift on resolve + status: pending + - id: shader-cso + content: Mode 2 area + Mode 1 resolve/H1/debug/strength-0; rebuild DlssNr_cso + status: pending + - id: menu-readme + content: Cost/Inspect menu + README; run T0–T7 / P / M / G + status: pending +isProject: false +--- + +# Matched Residual Transfer Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Ship `DlssNrTransfer` as a user-facing Classic | Matched residual option that changes shrink + compose on the next `Dispatch`, leaving the live Classic picture as the default. + +**Architecture:** One existing compute shader (`precompile/dlssnr.hlsl`), three modes. Encode is unchanged. Mode 2 branches on `gTransfer` (bilinear vs encoded area). Mode 1 Classic keeps today’s UpgradeToneMap(`H`, `U(p)`, `U(m)`); Matched builds `T = P + (U(m)-U(p))` (or `T = m` at same-rate), cube-scales `T` in HDR, then H0 or Inspect-only H1. `t3` is always `colorCopy` (`gProxy`). No new scratch. + +**Tech Stack:** D3D12 compute (`cs_6_0`), OptiScaler Config/ImGui, NGX NR feature (untouched). Host fixture is a standalone `cl.exe` program — this repo has no gtest. + +**Source of truth:** [OptiScaler/dlssnr/design/2026-09-02-matched-residual.md](OptiScaler/dlssnr/design/2026-09-02-matched-residual.md). Algorithm text there wins if a snippet here drifts. + +**Save the working copy of this plan next to the spec** as [OptiScaler/dlssnr/design/2026-09-02-matched-residual-plan.md](OptiScaler/dlssnr/design/2026-09-02-matched-residual-plan.md) when implementation starts (Plan mode does not write it yet). + +## Global Constraints + +- Default `Transfer = Classic` (0). Fresh INI / missing key must match the live build at nonzero strength. +- `gTransfer` (this combo) and `gTransferStrength` (detail slider) are different fields. Do not overload one for the other. +- Do not rebuild the NR feature, swap the PSO, or allocate new scratch when Transfer changes. +- Set `gTransfer` on **downsample and resolve**. Zero-init downsample is a Classic bilinear bug. +- Do not change `DlssNrUseProxy` early return, NGX evaluate, guides, WorkingScale hold, or feature-create hold. +- Do not rewrite [OptiScaler/dlssnr/DlssNr_Codec.h](OptiScaler/dlssnr/DlssNr_Codec.h). Live shader is `precompile/dlssnr.hlsl`. Call `codec::TypedFormat` only. +- Do not invert K/SRTM, temporally accumulate model RGB, EASU/Lanczos model RGB, or downsample open linear then re-encode. +- Do not implement C1 affine / JBU / LUT / GLU. Do not add a work-res residual UAV. +- Strength 0 + `DebugView == 0` + `Compare == 0`: edited-side bytes equal `hdrCopy`. No `/W`, no lerp, no extra `max`. Not a claim on the upscaler UAV. +- Empty-model gate on `dot(m, kLuma) <= 1e-5`, never on `T`. +- `sameRate` from `gSource.GetDimensions()`, not `gGuideWidth` (that field is zero today). +- Area: float box, loop the overlapping range (no 5-tap cap), clamp-to-edge, divide by geometric `area`, centre-texel alpha, `Load` stored RGB (no decode). +- Numeric views in this slice. `CreateScratch` via `TypedFormat`. Compare rebuilds against the **remapped** format or `_SRGB` games park/recreate every frame. +- Commit only updated `dlssnr.hlsl` + `DlssNr_Shader.h` (`DlssNr_cso`). Do not add the bat’s Dx11 outputs or a new `.cso` (the tree only embeds the header). +- Do not mention “legacy”, “A/B only”, or “temporary” in the UI. +- T0–T7 / G default matrix: Placement = Post-process, `WorkAtNative` off, Output Scaling off, `DlssNrUseProxy` off, Compare off unless a row says otherwise. Multi-pass is **M only**. + +```mermaid +flowchart TD + colourUAV[colourSizedUAV] --> encode[Encode_unchanged] + encode --> colorCopy[colorCopy_E] + encode --> hdrCopy[hdrCopy_H] + colorCopy --> downCheck{work_ne_colour} + downCheck -->|Classic| bilinear[Mode2_bilinear] + downCheck -->|Matched| area[Mode2_area] + downCheck -->|sameRate| ngxDirect[NGX_NR] + bilinear --> ngx[NGX_NR] + area --> ngx + ngx --> resolve[Resolve] + ngxDirect --> resolve + colorCopy --> resolve + hdrCopy --> resolve + resolve --> out[gameOutput] +``` + +--- + +### Task 1: Host-side area downsample (TA) + +**Files:** +- Create: [OptiScaler/dlssnr/DlssNr_Area.h](OptiScaler/dlssnr/DlssNr_Area.h) +- Create: [OptiScaler/dlssnr/tests/area_fixture.cpp](OptiScaler/dlssnr/tests/area_fixture.cpp) +- Do **not** add either file to `OptiScaler.vcxproj` (standalone host program). + +**Interfaces:** +- Consumes: spec §7.1 only +- Produces: `DlssNr::Overlap1D`, `DlssNr::AreaSample`, `DlssNr::AreaDownsamplePixel` — HLSL Mode 2 Matched must match these formulas + +- [ ] **Step 1: Write the failing fixture** (header not present yet) + +```cpp +// OptiScaler/dlssnr/tests/area_fixture.cpp +#include "../DlssNr_Area.h" + +#include +#include +#include +#include + +using DlssNr::AreaDownsamplePixel; +using DlssNr::AreaSample; + +static int gFails = 0; + +static void ExpectNear(const char* name, float got, float want, float eps = 1e-5f) +{ + if (std::fabs(got - want) > eps) + { + std::printf("FAIL %s: got %g want %g\n", name, got, want); + ++gFails; + } +} + +static void TestOverlap1D() +{ + // dest pixel 0 of 3->2: box [0, 1.5). Texel 0 weight 1, texel 1 weight 0.5 + ExpectNear("ovl0", DlssNr::Overlap1D(0.f, 1.5f, 0), 1.f); + ExpectNear("ovl1", DlssNr::Overlap1D(0.f, 1.5f, 1), 0.5f); + ExpectNear("ovl2", DlssNr::Overlap1D(0.f, 1.5f, 2), 0.f); +} + +static void Test2x2Mean() +{ + AreaSample src[4] = { + { 1, 0, 0, 1 }, { 0, 1, 0, 1 }, + { 0, 0, 1, 1 }, { 1, 1, 1, 1 }, + }; + const auto p = AreaDownsamplePixel(src, 2, 2, 1, 1, 0, 0); + ExpectNear("mean.r", p.r, 0.5f); + ExpectNear("mean.g", p.g, 0.5f); + ExpectNear("mean.b", p.b, 0.5f); +} + +static void TestCopy() +{ + AreaSample src[1] = { { 0.25f, 0.5f, 0.75f, 0.125f } }; + const auto p = AreaDownsamplePixel(src, 1, 1, 1, 1, 0, 0); + ExpectNear("copy.r", p.r, 0.25f); + ExpectNear("copy.a", p.a, 0.125f); +} + +static void Test3to2NotUintTruncated() +{ + // src 3, dst 2, x=1: float box [1.5, 3). uint box would be [1, 3) and overweight texel 1. + AreaSample src[3] = { { 1, 0, 0, 1 }, { 0, 1, 0, 1 }, { 0, 0, 1, 1 } }; + const auto p = AreaDownsamplePixel(src, 3, 1, 2, 1, 1, 0); + const float area = 1.5f; + ExpectNear("3to2.r", p.r, 0.f); + ExpectNear("3to2.g", p.g, 0.5f / area); + ExpectNear("3to2.b", p.b, 1.f / area); +} + +int main() +{ + TestOverlap1D(); + Test2x2Mean(); + TestCopy(); + Test3to2NotUintTruncated(); + if (gFails) + { + std::printf("%d FAIL\n", gFails); + return 1; + } + std::printf("TA PASS\n"); + return 0; +} +``` + +- [ ] **Step 2: Compile; expect FAIL** (missing header) + +```bat +cd /d C:\Workspace\OptiScaler_DLSSNR-pr-multipass +cl /nologo /EHsc /std:c++17 /W4 OptiScaler\dlssnr\tests\area_fixture.cpp /Fe:OptiScaler\dlssnr\tests\area_fixture.exe +``` + +Expected: `fatal error C1083: Cannot open include file: '../DlssNr_Area.h'` + +- [ ] **Step 3: Write the header** + +```cpp +// OptiScaler/dlssnr/DlssNr_Area.h +#pragma once + +#include +#include + +namespace DlssNr +{ + +inline float Overlap1D(float box0, float box1, int i) +{ + const float t0 = (float) i; + const float t1 = (float) (i + 1); + const float a = box0 > t0 ? box0 : t0; + const float b = box1 < t1 ? box1 : t1; + return b > a ? (b - a) : 0.0f; +} + +struct AreaSample +{ + float r, g, b, a; +}; + +// src is row-major srcW*srcH. Matches spec §7.1 / HLSL Mode 2 Matched. +inline AreaSample AreaDownsamplePixel(const AreaSample* src, int srcW, int srcH, int dstW, int dstH, + int x, int y) +{ + if (dstW == srcW && dstH == srcH) + return src[y * srcW + x]; + + const float x0 = ((float) x * (float) srcW) / (float) dstW; + const float x1 = ((float) (x + 1) * (float) srcW) / (float) dstW; + const float y0 = ((float) y * (float) srcH) / (float) dstH; + const float y1 = ((float) (y + 1) * (float) srcH) / (float) dstH; + const float area = (x1 - x0) * (y1 - y0); + + const int i0 = (int) floorf(x0); + const int i1 = (int) ceilf(x1) - 1; + const int j0 = (int) floorf(y0); + const int j1 = (int) ceilf(y1) - 1; + + float ar = 0.f, ag = 0.f, ab = 0.f; + for (int j = j0; j <= j1; ++j) + { + const int jj = std::clamp(j, 0, srcH - 1); + const float wy = Overlap1D(y0, y1, j); + for (int i = i0; i <= i1; ++i) + { + const int ii = std::clamp(i, 0, srcW - 1); + const float w = Overlap1D(x0, x1, i) * wy; + const AreaSample& s = src[jj * srcW + ii]; + ar += s.r * w; + ag += s.g * w; + ab += s.b * w; + } + } + + const float cx = ((float) x + 0.5f) * (float) srcW / (float) dstW; + const float cy = ((float) y + 0.5f) * (float) srcH / (float) dstH; + const int acx = std::clamp((int) floorf(cx), 0, srcW - 1); + const int acy = std::clamp((int) floorf(cy), 0, srcH - 1); + + return { ar / area, ag / area, ab / area, src[acy * srcW + acx].a }; +} + +} // namespace DlssNr +``` + +- [ ] **Step 4: Recompile and run** + +```bat +cl /nologo /EHsc /std:c++17 /W4 OptiScaler\dlssnr\tests\area_fixture.cpp /Fe:OptiScaler\dlssnr\tests\area_fixture.exe +OptiScaler\dlssnr\tests\area_fixture.exe +``` + +Expected: `TA PASS` + +- [ ] **Step 5: Commit** `feat(dlssnr): host fixture for matched-residual area downsample` + +--- + +### Task 2: Enums, Config, constant buffer + +**Files:** +- Modify: [OptiScaler/dlssnr/DlssNr_Modes.h](OptiScaler/dlssnr/DlssNr_Modes.h) +- Modify: [OptiScaler/dlssnr/DlssNr_Modes.cpp](OptiScaler/dlssnr/DlssNr_Modes.cpp) +- Modify: [OptiScaler/shaders/dlssnr/DlssNr_Common.h](OptiScaler/shaders/dlssnr/DlssNr_Common.h) +- Modify: [OptiScaler/Config.h](OptiScaler/Config.h) (DlssNr block ~259–316) +- Modify: [OptiScaler/Config.cpp](OptiScaler/Config.cpp) (read ~318–348, write ~1177–1212) +- Modify: [OptiScaler/dlssnr/tests/area_fixture.cpp](OptiScaler/dlssnr/tests/area_fixture.cpp) — add clamp tests + +**Interfaces:** +- Consumes: `ConfiguredMode()` clamp pattern in `DlssNr_Modes.cpp` +- Produces: + - `enum class Transfer : uint32_t { Classic = 0, MatchedResidual = 1 };` + - `inline Transfer ClampTransfer(uint32_t raw);` + - `inline uint32_t ClampHdrLift(uint32_t raw);` — unknown → `0` + - `Transfer ConfiguredTransfer();` + - `uint32_t ConfiguredHdrLift();` + - `DlssNrConstants::Transfer`, `::HdrLift` at offsets 68 and 72 + - `Config::DlssNrTransfer { 0 }`, `Config::DlssNrHdrLift { 0 }` + - INI `[DlssNr] Transfer=0|1`, `HdrLift=0|1` + +- [ ] **Step 1: Add clamp tests to the fixture** (will fail until Modes.h grows) + +```cpp +#include "../DlssNr_Modes.h" + +static void TestClamps() +{ + if (DlssNr::ClampTransfer(0) != DlssNr::Transfer::Classic) { std::printf("FAIL t0\n"); ++gFails; } + if (DlssNr::ClampTransfer(1) != DlssNr::Transfer::MatchedResidual) { std::printf("FAIL t1\n"); ++gFails; } + if (DlssNr::ClampTransfer(2) != DlssNr::Transfer::Classic) { std::printf("FAIL t2\n"); ++gFails; } + if (DlssNr::ClampHdrLift(0) != 0) { std::printf("FAIL h0\n"); ++gFails; } + if (DlssNr::ClampHdrLift(1) != 1) { std::printf("FAIL h1\n"); ++gFails; } + if (DlssNr::ClampHdrLift(9) != 0) { std::printf("FAIL h9\n"); ++gFails; } +} +``` + +Call `TestClamps()` from `main`. Rebuild: expect compile FAIL (`ClampTransfer` not found). + +- [ ] **Step 2: Add enum + inlines to `DlssNr_Modes.h`** immediately after `enum class Mode` + +```cpp +enum class Transfer : uint32_t +{ + Classic = 0, + MatchedResidual = 1, +}; + +inline Transfer ClampTransfer(uint32_t raw) +{ + return raw > (uint32_t) Transfer::MatchedResidual ? Transfer::Classic : (Transfer) raw; +} + +inline uint32_t ClampHdrLift(uint32_t raw) { return raw > 1u ? 0u : raw; } + +Transfer ConfiguredTransfer(); +uint32_t ConfiguredHdrLift(); +``` + +- [ ] **Step 3: Implement readers in `DlssNr_Modes.cpp`** next to `ConfiguredMode()` + +```cpp +Transfer ConfiguredTransfer() +{ + return ClampTransfer(Config::Instance()->DlssNrTransfer.value_or_default()); +} + +uint32_t ConfiguredHdrLift() +{ + return ClampHdrLift(Config::Instance()->DlssNrHdrLift.value_or_default()); +} +``` + +- [ ] **Step 4: Config keys** + +In `Config.h`, after `DlssNrWorkAtNative`: + +```cpp +// 0 Classic (live path), 1 Matched residual. How a below-frame model is brought back. +CustomOptional DlssNrTransfer { 0 }; +// 0 UpgradeToneMap (H0), 1 additive headroom (H1). Used only if Transfer == 1. +CustomOptional DlssNrHdrLift { 0 }; +``` + +Change the `DlssNrDebugView` comment from `0 off, 1 … 3 …` to `0–5` (views 4–5 are full-res proxy / T). + +In `Config.cpp` read (with the other `DlssNr*` keys): + +```cpp +DlssNrTransfer.set_from_config(readUInt("DlssNr", "Transfer")); +DlssNrHdrLift.set_from_config(readUInt("DlssNr", "HdrLift")); +``` + +In `Config.cpp` write: + +```cpp +ini.SetValue("DlssNr", "Transfer", GetIntValue(Instance()->DlssNrTransfer.value_for_config()).c_str()); +ini.SetValue("DlssNr", "HdrLift", GetIntValue(Instance()->DlssNrHdrLift.value_for_config()).c_str()); +``` + +- [ ] **Step 5: Constant buffer** — append after `CompareSwap` in `DlssNrConstants` ([DlssNr_Common.h](OptiScaler/shaders/dlssnr/DlssNr_Common.h) ~106). Do not touch `MvScale*` or `GuideWidth`/`GuideHeight`. + +```cpp + uint32_t Transfer; // 0 Classic, 1 Matched residual + uint32_t HdrLift; // 0 H0, 1 H1; ignored unless Transfer == 1 +}; + +#include + +static_assert(offsetof(DlssNrConstants, Transfer) == 68, "HLSL gTransfer must sit at 68"); +static_assert(offsetof(DlssNrConstants, HdrLift) == 72, "HLSL gHdrLift must sit at 72"); +static_assert(sizeof(DlssNrConstants) == 256, "CBV size is 256-aligned"); +``` + +If the compiler inserts padding, add explicit `uint32_t _pad0` **before** `Transfer` so those offsets hold, then re-check. HLSL field order in Task 5 must match. + +- [ ] **Step 6: Rebuild fixture + OptiScaler** + +```bat +cl /nologo /EHsc /std:c++17 /W4 OptiScaler\dlssnr\tests\area_fixture.cpp /Fe:OptiScaler\dlssnr\tests\area_fixture.exe +OptiScaler\dlssnr\tests\area_fixture.exe +``` + +Expected: `TA PASS`. Then build OptiScaler (Release or current config). Expected: compile OK. Behavior still Classic — new uints are zero-init until Task 4 writes them. + +- [ ] **Step 7: Commit** `feat(dlssnr): add Transfer and HdrLift config plus cbuffer fields` + +--- + +### Task 3: Numeric scratch views + +**Files:** +- Modify: [OptiScaler/shaders/dlssnr/DlssNr_Dx12.cpp](OptiScaler/shaders/dlssnr/DlssNr_Dx12.cpp) `CreateScratch` (~519), `ReleaseSurfacesIfFormatChanged` (~502), `DispatchPass` SRV loop (~845) + +**Interfaces:** +- Consumes: `codec::TypedFormat` in [DlssNr_Codec.h](OptiScaler/dlssnr/DlssNr_Codec.h) (already included; maps `*_UNORM_SRGB` → `*_UNORM`) +- Produces: `colorCopy` / `colorSmall` / `g_nr.output` / `hdrCopy` created numeric; SRVs in this pass never stay `_SRGB` + +**Landmine:** `ReleaseSurfacesIfFormatChanged(desc.Format)` compares the game colour format to `g_nr.output->GetDesc().Format`. After remapping, those differ on every passthrough SDR frame and the feature is parked forever. Compare the remapped format. + +- [ ] **Step 1: Remap in `CreateScratch`** + +```cpp +ID3D12Resource* CreateScratch(ID3D12Device* device, DXGI_FORMAT format, unsigned int width, + unsigned int height) +{ + format = codec::TypedFormat(format); + // ... existing desc / CreateCommittedResource, using `format` +} +``` + +- [ ] **Step 2: Remap the rebuild compare** + +```cpp +void ReleaseSurfacesIfFormatChanged(DXGI_FORMAT needed) +{ + needed = codec::TypedFormat(needed); + if (g_nr.output == nullptr || g_nr.output->GetDesc().Format == needed) + return; + // ... existing park +} +``` + +- [ ] **Step 3: Numeric SRV override** — `CreateShaderResourceView` already takes `DXGI_FORMAT format = DXGI_FORMAT_UNKNOWN` ([Shader_Dx12.h](OptiScaler/shaders/Shader_Dx12.h)). `TranslateTypelessFormats` does **not** strip `_SRGB`. + +In `DispatchPass`: + +```cpp +for (uint32_t i = 0; i < kSrvCount; ++i) +{ + CreateShaderResourceView(_device, srvs[i], currentHeap.GetSrvCPU(i), + codec::TypedFormat(srvs[i]->GetDesc().Format)); +} +``` + +Do not change `CreateUnorderedAccessView` in the base class. Scratch UAVs inherit the remapped resource format. The game output UAV stays the game’s format. + +- [ ] **Step 4: Build OptiScaler.** HDR (`R16G16B16A16_FLOAT`) is a no-op remap. No log spam of `rebuilding surfaces: format`. + +- [ ] **Step 5: Commit** `fix(dlssnr): create scratch and SRVs as numeric formats` + +--- + +### Task 4: Bind `gProxy` and upload Transfer + +**Files:** +- Modify: [OptiScaler/shaders/dlssnr/DlssNr_Dx12.h](OptiScaler/shaders/dlssnr/DlssNr_Dx12.h) `DispatchPass` (~76–79) +- Modify: [OptiScaler/shaders/dlssnr/DlssNr_Dx12.cpp](OptiScaler/shaders/dlssnr/DlssNr_Dx12.cpp) `DispatchPass` + downsample constants (~1202) + resolve constants/call (~1313) + +**Interfaces:** +- Consumes: `ConfiguredTransfer()`, `ConfiguredHdrLift()`, `g_nr.colorCopy` +- Produces: slot 3 is `colorCopy` on resolve for **both** Transfer values; downsample constants carry `Transfer` + +- [ ] **Step 1: Rename the 4th input** `InMotion` → `InProxy` in the header and the `.cpp` definition. Stand-in stays `InSource` when null. `kSrvCount` stays 5. + +- [ ] **Step 2: Downsample constants** (required) + +```cpp +DlssNrConstants down {}; +down.Mode = DlssNrMode_Downsample; +down.Width = workWidth; +down.Height = workHeight; +down.Transfer = (uint32_t) DlssNr::ConfiguredTransfer(); +DispatchPass(cmdList, down, modelInput, nullptr, nullptr, nullptr, nullptr, + g_nr.colorSmall, nullptr); +``` + +- [ ] **Step 3: Resolve constants + bind** + +```cpp +resolveParams.Transfer = (uint32_t) DlssNr::ConfiguredTransfer(); +resolveParams.HdrLift = DlssNr::ConfiguredHdrLift(); +// existing fields unchanged +DispatchPass(cmdList, resolveParams, modelInput, g_nr.output, g_nr.hdrCopy, g_nr.colorCopy, + nullptr, target, nullptr); +``` + +Encode still passes `nullptr` for `InProxy`. `motionIn` remains NGX-only (`ReadableGuide` / `evaluate`). Do not bind it to the compose shader. + +Do **not** change the `DlssNrUseProxy` block that returns before resolve. + +- [ ] **Step 4: Build.** With the current CSO, extra constants are ignored and slot 3 is unread. Picture must match today’s Classic. + +- [ ] **Step 5: Commit** `feat(dlssnr): bind colorCopy as gProxy and upload Transfer` + +--- + +### Task 5: Shader (Mode 2 + Mode 1) and CSO + +**Files:** +- Modify: [OptiScaler/shaders/dlssnr/precompile/dlssnr.hlsl](OptiScaler/shaders/dlssnr/precompile/dlssnr.hlsl) +- Modify: [OptiScaler/shaders/dlssnr/precompile/DlssNr_Shader.h](OptiScaler/shaders/dlssnr/precompile/DlssNr_Shader.h) via rebuild +- Do not commit `dlssnr_Shader_Dx11.cso` / `*_Dx11.h` / a new `.cso` if the bat writes them + +**Interfaces:** +- Consumes: cbuffer layout from Task 2; `DlssNr_Area.h` formulas; spec §8 and §11 +- Produces: live CSO matching the new HLSL (`DlssNr_cso`) + +- [ ] **Step 1: Extend the cbuffer** after `gCompareSwap` — order must match C++ offsets 68/72 + +```hlsl + uint gCompareSwap; + uint gTransfer; // 0 Classic, 1 Matched residual + uint gHdrLift; // 0 H0, 1 H1; ignored unless gTransfer == 1 +}; +``` + +- [ ] **Step 2: Rename the slot** + +```hlsl +Texture2D gProxy : register(t3); // resolve: colorCopy (full-res encoded proxy) +``` + +Leave unused `EditAt` as-is (it only reads `gSource`/`gModel`). + +- [ ] **Step 3: Replace Mode 2** (today: one `SampleLevel`). Classic line is bit-for-bit the live one. + +```hlsl + if (gMode == 2) + { + if (gTransfer == 0) + { + gTarget[id.xy] = gSource.SampleLevel(gLinear, uv, 0); + return; + } + + uint srcW, srcH; + gSource.GetDimensions(srcW, srcH); + if (srcW == gWidth && srcH == gHeight) + { + gTarget[id.xy] = gSource.Load(int3(id.xy, 0)); + return; + } + + const float x0 = ((float) id.x * (float) srcW) / (float) gWidth; + const float x1 = ((float) (id.x + 1) * (float) srcW) / (float) gWidth; + const float y0 = ((float) id.y * (float) srcH) / (float) gHeight; + const float y1 = ((float) (id.y + 1) * (float) srcH) / (float) gHeight; + const float area = (x1 - x0) * (y1 - y0); + + const int i0 = (int) floor(x0); + const int i1 = (int) ceil(x1) - 1; + const int j0 = (int) floor(y0); + const int j1 = (int) ceil(y1) - 1; + + float3 acc = 0.0; + for (int j = j0; j <= j1; ++j) + { + const int jj = clamp(j, 0, (int) srcH - 1); + const float t0y = (float) j; + const float aY = max(y0, t0y); + const float bY = min(y1, t0y + 1.0); + const float wy = max(bY - aY, 0.0); + for (int i = i0; i <= i1; ++i) + { + const int ii = clamp(i, 0, (int) srcW - 1); + const float t0x = (float) i; + const float aX = max(x0, t0x); + const float bX = min(x1, t0x + 1.0); + const float w = max(bX - aX, 0.0) * wy; + acc += gSource.Load(int3(ii, jj, 0)).rgb * w; + } + } + + const int acx = clamp((int) floor(((float) id.x + 0.5) * (float) srcW / (float) gWidth), 0, (int) srcW - 1); + const int acy = clamp((int) floor(((float) id.y + 0.5) * (float) srcH / (float) gHeight), 0, (int) srcH - 1); + const float a = gSource.Load(int3(acx, acy, 0)).a; + gTarget[id.xy] = float4(acc / area, a); + return; + } +``` + +No `[unroll]`, no 5-tap cap, no weight renormalise. + +- [ ] **Step 4: Mode 1 helpers** (after `SrgbToLinear`) + +```hlsl +float3 DecodeRgb(float3 rgb) +{ + return gPassthrough != 0 ? rgb : SrgbToLinear(rgb); +} + +float3 CubeScaleResidual(float3 P, float3 T) +{ + if (gPassthrough != 0) + return T; + float3 d = T - P; + float alpha = 1.0; + [unroll] for (int c = 0; c < 3; ++c) + { + if (d[c] > 1e-6) + alpha = min(alpha, (1.0 - P[c]) / d[c]); + else if (d[c] < -1e-6) + alpha = min(alpha, (0.0 - P[c]) / d[c]); + } + return P + saturate(alpha) * d; +} + +// Live UpgradeToneMap body, parameterized. Empty-model gate is the caller's job. +float3 ComposeUpgrade(float3 H, float3 proxyRgb, float3 modelRgb) +{ + float originalLuma = dot(H, kLuma); + float proxyLuma = dot(proxyRgb, kLuma); + float modelLuma = dot(modelRgb, kLuma); + float ratio; + if (originalLuma < proxyLuma) + ratio = originalLuma / max(proxyLuma, 1e-6); + else + ratio = (modelLuma + max(0.0, originalLuma - proxyLuma)) / modelLuma; + float3 upgraded = lerp(H, HueOkLab(modelRgb * ratio, modelRgb), gTransferStrength); + float upgradedLuma = dot(upgraded, kLuma); + const float kRatioFloor = 1.0 / 512.0; + float lumaRatio = clamp((upgradedLuma + kRatioFloor) / (originalLuma + kRatioFloor), 0.0, gMaxRatio); + return lerp(H * lumaRatio, upgraded, gColourStrength); +} +``` + +- [ ] **Step 5: Replace resolve control flow** after `cmpUv` / `showOriginal` / `onDivider` / `outsideFrame` are known. Follow spec §11 exactly. + +```hlsl + float3 p = DecodeRgb(gSource.SampleLevel(gLinear, cmpUv, 0).rgb); + float3 m = DecodeRgb(gModel.SampleLevel(gLinear, cmpUv, 0).rgb); + float4 originalSample = gCompareMode == 1 ? gOriginal.SampleLevel(gLinear, cmpUv, 0) + : gOriginal.Load(int3(id.xy, 0)); + const float normScale = gPassthrough != 0 ? 1.0 : max(gWhitePoint, 1e-4); + float3 H = originalSample.rgb / normScale; + + if (gDebugView == 1) + { + gTarget[id.xy] = float4(p * gWhitePoint, originalSample.a); + return; + } + if (gDebugView == 2) + { + gTarget[id.xy] = float4(m * gWhitePoint, originalSample.a); + return; + } + if (gDebugView == 3) + { + float3 shown = saturate(0.5 + (m - p) * 20.0); + gTarget[id.xy] = float4(SrgbToLinear(shown) * gWhitePoint, originalSample.a); + return; + } + + if (gDebugView == 4 || gDebugView == 5) + { + float3 shown = 0.0; + if (gTransfer == 1) + { + float4 eSample = gCompareMode == 1 ? gProxy.SampleLevel(gLinear, cmpUv, 0) + : gProxy.Load(int3(id.xy, 0)); + float3 P = DecodeRgb(eSample.rgb); + uint srcW, srcH; + gSource.GetDimensions(srcW, srcH); + bool sameRate = (srcW == gWidth && srcH == gHeight); + float3 T = sameRate ? m : CubeScaleResidual(P, P + (m - p)); + shown = (gDebugView == 4 ? P : T) * gWhitePoint; + } + gTarget[id.xy] = float4(shown, originalSample.a); + return; + } + + if (gTransferStrength == 0) + { + float4 o = gCompareMode == 1 ? gOriginal.SampleLevel(gLinear, cmpUv, 0) + : gOriginal.Load(int3(id.xy, 0)); + float3 rgb = o.rgb; + if (outsideFrame) + rgb = 0.0; + if (onDivider) + rgb = gWhitePoint; + gTarget[id.xy] = float4(rgb, o.a); + return; + } + + float3 result; + if (gTransfer == 0) + { + if (dot(m, kLuma) <= 1e-5) + result = H * normScale; + else + result = ComposeUpgrade(H, p, m) * normScale; + } + else + { + float4 eSample = gCompareMode == 1 ? gProxy.SampleLevel(gLinear, cmpUv, 0) + : gProxy.Load(int3(id.xy, 0)); + float3 P = DecodeRgb(eSample.rgb); + uint srcW, srcH; + gSource.GetDimensions(srcW, srcH); + bool sameRate = (srcW == gWidth && srcH == gHeight); + float3 T = sameRate ? m : CubeScaleResidual(P, P + (m - p)); + + if (dot(m, kLuma) <= 1e-5) + result = H * normScale; + else if (gHdrLift == 1) + result = lerp(H, H + (T - P), gTransferStrength) * normScale; + else + result = ComposeUpgrade(H, P, T) * normScale; + } + + if (showOriginal) + result = originalSample.rgb; + if (outsideFrame) + result = 0.0; + if (onDivider) + result = gWhitePoint; + gTarget[id.xy] = float4(max(result, 0.0), originalSample.a); +``` + +Encode (`gMode == 0`) stays the current function body. + +Wipe (`CompareMode == 2`) Loads `P` via the `gCompareMode == 1 ? Sample : Load` already in the snippet. Side-by-side samples `P` at `cmpUv`. + +- [ ] **Step 6: Rebuild CSO** from `OptiScaler/shaders/dlssnr/precompile`. The stock bat writes `dlssnr_Shader.h` / `dlssnr_cso` and Dx11 artifacts; the live include is `DlssNr_Shader.h` / `DlssNr_cso`. Drive the header directly: + +```bat +cd /d C:\Workspace\OptiScaler_DLSSNR-pr-multipass\OptiScaler\shaders\dlssnr\precompile +..\shader_tools\dxc.exe -T cs_6_0 -E CSMain -O3 -Qstrip_debug -Qstrip_reflect dlssnr.hlsl -Fo dlssnr_Shader.cso +python ..\shader_tools\create_header.py dlssnr_Shader.cso DlssNr_Shader.h DlssNr_cso +del /q dlssnr_Shader.cso 2>nul +del /q dlssnr_Shader_Dx11.cso DlssNr_Shader_Dx11.h dlssnr_Shader_Dx11.h 2>nul +``` + +Constructor uses `source = nullptr`; a stale header is a silent old shader. + +- [ ] **Step 7: Build OptiScaler and smoke Classic** (T4 / T0): Transfer unset or 0, WorkingScale 0.67, nonzero strength — picture matches the live build. Strength 0 + Compare off + Debug off — output matches `hdrCopy` (T1; do not use Capture “before”, that is `colorCopy`). + +- [ ] **Step 8: T7 (implementer-only, revert)** — temporarily bind `gSource = gModel` on resolve (`DispatchPass(..., g_nr.output, g_nr.output, ...)`). Debug 5 must equal debug 4 (`m=p ⇒ T=P`). Revert the bind before commit. + +- [ ] **Step 9: Commit** `feat(dlssnr): matched-residual downsample and resolve` (hlsl + `DlssNr_Shader.h` only) + +--- + +### Task 6: Menu, README, in-game matrix + +**Files:** +- Modify: [OptiScaler/dlssnr/DlssNr_Menu.cpp](OptiScaler/dlssnr/DlssNr_Menu.cpp) +- Modify: [OptiScaler/dlssnr/README.md](OptiScaler/dlssnr/README.md) + +**Interfaces:** +- Consumes: `DlssNrTransfer`, `DlssNrHdrLift`, `ConfiguredTransfer()` +- Produces: Cost combo under the model-resolution slider; Inspect HDR lift; six debug names always + +- [ ] **Step 1: Cost combo** — after the Model resolution `HelpMarker` (~226), still under `SeparatorText("Cost")`, before `"How much of it lands"`: + +```cpp +{ + static const char* transferNames[] = { "Classic", "Matched residual" }; + int transfer = (int) config->DlssNrTransfer.value_or_default(); + if (transfer < 0 || transfer > (int) DlssNr::Transfer::MatchedResidual) + transfer = (int) DlssNr::Transfer::Classic; + if (ImGui::Combo("Transfer", &transfer, transferNames, IM_ARRAYSIZE(transferNames))) + config->DlssNrTransfer = (uint32_t) transfer; + + HelpMarker("How a below-frame model is brought back onto the picture this pass writes." + "\n\nClassic is the current path: shrink with bilinear, then fold the small answer" + "\ndirectly onto the full-resolution picture. It is the default." + "\n\nMatched residual keeps a sharp copy of the picture the model was shown, adds only" + "\nwhat the model changed, and then runs the same highlight-aware compose. The shrink" + "\nis an area filter so the model is not fed an aliased thumbnail." + "\n\nWhen the model is the same size as this frame the two match. Changing this does" + "\nnot rebuild the model; the next frame uses the new shrink and the new compose."); +} +``` + +Keep the combo **enabled** at 100% Cost. + +- [ ] **Step 2: Detail-strength help** — replace “0 gives back exactly what the upscaler produced” with identity = `hdrCopy` (the encode keep), not the upscaler UAV. + +- [ ] **Step 3: Inspect** — HDR lift enabled only when Transfer is Matched; debug combo always six names: + +```cpp +const bool matched = DlssNr::ConfiguredTransfer() == DlssNr::Transfer::MatchedResidual; +ImGui::BeginDisabled(!matched); +{ + static const char* liftNames[] = { "UpgradeToneMap", "Additive headroom" }; + int lift = (int) config->DlssNrHdrLift.value_or_default(); + if (lift < 0 || lift > 1) + lift = 0; + if (ImGui::Combo("HDR lift", &lift, liftNames, IM_ARRAYSIZE(liftNames))) + config->DlssNrHdrLift = (uint32_t) lift; + HelpMarker("Matched residual only. UpgradeToneMap is the default highlight-aware compose." + "\n\nAdditive headroom adds the model's change onto the original. Detail strength" + "\nstill lerps toward that sum; Colour strength does not apply."); +} +ImGui::EndDisabled(); + +static const char* debugNames[] = { + "Off", + "Proxy (what the model sees)", + "Model output (raw)", + "Difference (amplified)", + "Full-res proxy", + "Matched T", +}; +``` + +Do not shrink the debug combo on Classic (a stored `DebugView == 4` would then sit past the last item). Views 4–5 write black on Classic (shader). No “legacy” / “A/B” / “temporary” copy. + +- [ ] **Step 4: README** — in [OptiScaler/dlssnr/README.md](OptiScaler/dlssnr/README.md) “Design notes”: + - Two transfers; Classic is the default live path; Matched residual is area shrink + residual onto `colorCopy` then the same UpgradeToneMap (or Inspect H1). + - Replace “composition is not a delta” as the only story: Classic is still ratio compose; Matched transfers a residual onto `P` first. + - Replace “At strength zero the frame is bit-identical, always” (upscaler UAV) with: strength 0 writes `hdrCopy`. Capture “before” is `colorCopy` and is not that identity. + +- [ ] **Step 5: In-game matrix** (freeze model version and scene; do not move Transfer / Colour / WhitePoint in the same take as T2) + + - **TA:** `area_fixture.exe` still prints `TA PASS` + - **T0:** fresh config, Transfer unset — default strengths match the live build + - **T1:** `TransferStrength = 0`, either option, Compare 0, Debug 0 — bytes match `hdrCopy` + - **T2:** toggle Classic ↔ Matched at WorkingScale 0.67 — picture changes, no hitch; log must **not** show `ParkNrFeature` / `resolutionChanged` / a feature rebuild + - **T3:** toggle at WorkingScale 1.0 — no visible difference; INI still records the choice; combo stays enabled + - **T4:** Classic @ 0.67 — matches live bilinear + cross-rate compose (nonzero strength) + - **T5:** Matched vs Classic @ 0.67 — less edge halo, original detail kept; Debug-1 may differ (area vs bilinear) + - **T6:** Matched H0 vs H1 — H1 does not amplify highlight boil; Detail strength moves H1; Colour strength does not; default H0 + - **P:** 8-bit / non-float game — no double encode; no per-frame scratch rebuild + - **M:** Multi-pass + either Transfer — Feature 1 1:1; FSR1 after `Run`; no double NR (not the T2/T5 scale) + - **G:** timestamps — Matched extra vs Classic ≪ one DLSS SR + +- [ ] **Step 6: Commit** `feat(dlssnr): expose Transfer and HDR lift in the NR menu` + +--- + +## Self-review (spec coverage) + +- §2 product / INI / enum / same-rate / runtime / H1 → Tasks 2, 4, 5, 6 +- §3 non-goals → Global Constraints +- §4 contracts (strength 0, alpha, passthrough, empty model, identities) → Tasks 5, 6 +- §6 bindings + numeric views + downsample `Transfer` → Tasks 3, 4 +- §7 area algorithm → Tasks 1, 5 +- §8 resolve / cube-scale / H0 / H1 / debug → Task 5 +- §9 menu copy → Task 6 +- §10 / §13 file list + CSO names → Tasks 2–6 (`DlssNr_cso`, no Dx11) +- §14 / §15 tests + acceptance → Tasks 1, 5, 6 +- Open items (§17): offset asserts in Task 2; debug 4/5 SBS samples `P` at `cmpUv` in Task 5 + +No C1 affine, no residual UAV, no `GuideWidth` overload, no `LegacyResolve`. diff --git a/OptiScaler/dlssnr/design/2026-09-02-matched-residual.md b/OptiScaler/dlssnr/design/2026-09-02-matched-residual.md new file mode 100644 index 000000000..229c82d8d --- /dev/null +++ b/OptiScaler/dlssnr/design/2026-09-02-matched-residual.md @@ -0,0 +1,742 @@ +# Working-scale transfer (Classic | Matched residual) + +**Status:** design for implementation +**Date:** 2026-09-02 (revised: first-class UI option, Classic default; plan-input locks) +**Scope:** `DlssNr_Dx12::Dispatch`, `precompile/dlssnr.hlsl`, Config, NR menu +**Placement:** Post-process and Multi-pass (both already call the same `Dispatch`) + +The new compose is a **user-facing option**, not a silent replace. Default stays the +live path so existing INIs and eyes do not move. Switching does not rebuild the NR +feature and does not add a second network, invert the encode knee, or change Feature 1 / +FSR1 Multi-pass enlarge. + +Cost when the new option is on: well under one DLSS Super Resolution pass. Classic +stays at today’s cost. + +`gTransfer` (this option) and `gTransferStrength` (the detail slider) are different +fields. Do not overload one for the other. + +--- + +## 1. Why a second transfer exists + +The model already has a full-resolution encoded proxy (`g_nr.colorCopy`) and a +full-resolution untouched frame (`g_nr.hdrCopy`). Classic resolve ignores `colorCopy` +and compares the linear original \(H\) to a bilinear upsample of the small proxy +\(U(p)\). + +That difference is two things glued together: + +\[ +H - U(p) = \underbrace{(H - P)}_{\text{encode knee / headroom}} + \underbrace{(P - U(p))}_{\text{spatial loss}} +\] + +Classic UpgradeToneMap treats both as HDR headroom. Halo at high-contrast edges is +mostly the second term. + +**Matched residual** splits them. It transfers the model’s *change* onto the sharp +proxy \(P\), then runs the same UpgradeToneMap at one resolution: + +\[ +T = P + \big(U(m) - U(p)\big), \qquad F = \mathcal{H}(H, P, T) +\] + +\(U(\cdot)\) is hardware-bilinear `SampleLevel` of the **stored** small texture, +then `Decode`. That is \(P + (S^{-1}(U(m_e)) - S^{-1}(U(p_e)))\), **not** +\(U(m-p)\) and not a work-res residual UAV. + +\(P\) is `colorCopy` decoded (or copied, in passthrough). \(H\) is `hdrCopy`. \(p,m\) +stay at working resolution. + +Affine / BGU / JBU / GLU are out of this document. They are a later upgrade if Matched +residual still cannot restyle hue across edges. + +--- + +## 2. Product option + +One combo, one INI key, two packed behaviours. Do not split “downsample kernel” and +“compose” into two user controls in this slice — switching compose while the model +still sees a different shrink makes A/B meaningless. + +| `DlssNrTransfer` | Menu label | Downsample (work ≠ colour) | Resolve | +|---|---|---|---| +| `0` (default) | Classic | today’s `SampleLevel` bilinear | today’s UpgradeToneMap on \(H\), \(U(p)\), \(U(m)\) | +| `1` | Matched residual | encoded exact area (§7.1) | \(T = P+(U(m)-U(p))\) (or \(T=m\) if same-rate), then H0 or H1 | + +`width` / `height` in this document are the Dispatch colour size (`target` desc): +post-process is usually display; Multi-pass is Feature 1. “Same-rate” means +`workW == width && workH == height`, not “Cost slider reads 100%”. + +### 2.1 Where it lives + +Menu: **Cost** section, immediately under the model-resolution slider (same group as +WorkingScale). It is about how a *small* model is brought back, not about Placement. + +INI: `[DlssNr] Transfer=0|1`. Read/write like the other `DlssNr*` keys. + +C++ enum (add next to `DlssNr::Mode` in `DlssNr_Modes.h`): + +```cpp +enum class Transfer : uint32_t +{ + Classic = 0, + MatchedResidual = 1, +}; +``` + +Unknown / hand-edited values clamp to Classic, same pattern as `ConfiguredMode()`. +Add `ConfiguredTransfer()` next to it. `ConfiguredHdrLift()` clamps unknown values +to `0` (H0). + +### 2.2 When the two options differ + +They differ only when `workW != width || workH != height`. At same-rate (WorkingScale +`== 1`, WorkAtNative off, and the colour buffer is that size — not a smaller native +raster, not an Output-Scaling buffer larger than display) both options must produce +the same compose: \(T = m\), UpgradeToneMap\((H, P, m)\) with \(P\) the full-res +proxy, which matches Classic’s 1:1 sample of `colorCopy`. + +Keep the combo **enabled** at full resolution so the INI value is not lost when the +user drags Cost to 100% and back. Help text states that the control only changes the +picture when the model is smaller than the frame this pass composes onto. + +### 2.3 Runtime change + +Changing Transfer takes effect on the **next** `Dispatch`. No NR feature rebuild, no +PSO swap, no new scratch (both kernels already share `colorSmall`). The model will +see a different \(p_e\) if work ≠ colour, because the shrink is part of the pack — +that is intended. Work size unchanged ⇒ `TickWorkingSizeHold` does not rebuild. + +`gTransfer` is read in **Mode 2 and Mode 1**. The downsample `DlssNrConstants` must +set it (clamped). Today that struct only fills `Mode` / `Width` / `Height`; leaving +`gTransfer` at zero-init keeps Classic bilinear even when the menu says Matched. + +NGX’s own history may take a few frames to settle after the shrink changes. That is +not a feature rebuild. Do not wait for the 30-frame work-size hold — Transfer does +not change work size. + +`DlssNrUseProxy` still returns before resolve (pre-existing). Transfer is a no-op +on that path. Do not change the early return. + +### 2.4 H1 + +`HdrLift` (H0 / H1) is an Inspect control and applies **only** when Transfer is +Matched residual. Classic ignores it. Do not expose four combinations. + +H1 still uses `TransferStrength` as a lerp toward the additive result. It ignores +`ColourStrength` (no OkLab mix). See §8.8. + +### 2.5 What this is not + +- Not a third Placement. +- Not `LegacyResolve` as a hidden checkbox to delete later. +- Not two independent sliders for “filter” and “mix”. + +--- + +## 3. Non-goals + +- Do not train or run a second neural net. +- Do not invert \(K\) or SRTM after the model. +- Do not temporally accumulate model RGB (already measured dead). +- Do not EASU / Lanczos the model RGB. Multi-pass FSR1 after `Run` is a different + upsample (R→D of the finished frame) and stays as it is. +- Do not downsample open linear HDR and re-encode. Encode stays first; shrink stays + in the encoded domain for **both** options. +- Do not implement C1 affine, C3 JBU, C4 LUT, or C5 GLU in this slice. +- Do not rewrite `OptiScaler/dlssnr/DlssNr_Codec.h`. That string is not the live CSO. + Leave a one-line note at the top if you touch the file at all: live shader is + `precompile/dlssnr.hlsl`. +- Do not change NGX evaluate, guides, WorkingScale hold, or feature-create hold. +- Do not change the `DlssNrUseProxy` early return. +- Do not change Classic’s nonzero-strength math relative to the live shader, except + the shared Strength-0 bypass (§4). + +--- + +## 4. Contracts (must hold) + +Shared by both Transfer values: + +| Name | Rule | How | +|---|---|---| +| Strength 0 | Edited-side bytes equal `hdrCopy` | If `TransferStrength == 0` **and** `DebugView == 0`, write `gOriginal` (`hdrCopy`) with no `/W`, no lerp, no extra `max`. Encode already stored `max(source, 0)` into `hdrCopy`. This is **not** a claim on the upscaler UAV. Applies to Classic too — today’s `/W` lerp path is not bit-identical to `hdrCopy`. | +| Strength 0 test | T1 is measurable | `Compare == 0` (no letterbox / divider / SBS resample). The Capture button’s “before” is `colorCopy` (encoded) — do not use it for T1. | +| Alpha | Unchanged | Copy `hdrCopy.a` (or the Load of `gOriginal`). | +| Guides | Untouched | Depth / motion stay render-resolution NGX inputs. | +| Passthrough | No sRGB, no headroom module | `ColourIsLinearHdr && FormatCanHoldLinearHdr` is false → `gPassthrough = 1`, \(E = P = O_{rgb}\). | +| Default look | Fresh install / missing INI | Transfer = Classic. Picture matches the live build at nonzero strength. | +| Empty model | \(H\) untouched | Gate on \(\mathrm{dot}(m, kLuma) \le 10^{-5}\), **not** \(\mathrm{dot}(T, kLuma)\). If \(m \approx 0\) then \(T = P - p\) is a high-pass and must not enter UpgradeToneMap or H1. Both options, H0 and H1. Live compatibility; do not “fix” the threshold. | + +Matched residual only: + +| Name | Rule | How | +|---|---|---| +| Algorithm identity | \(m = p \Rightarrow T = P\) | Residual path. Not bit-identity of the final resource unless strength is 0. | +| Scale-one identity | `workW == width && workH == height` | Skip residual upsample. \(T = m\). Then \(\mathcal{H}(H,P,T)\). | + +--- + +## 5. Data flow + +``` +colour-sized UAV (after upscaler or Feature 1) + │ + ▼ +Encode → colorCopy = E (encoded proxy, colour size) UNCHANGED + hdrCopy = H (untouched linear or native; already max(rgb,0)) + │ + ▼ +[if work != colour] + Mode 2: Classic → bilinear SampleLevel + Matched → encoded area (§7.1) + gTransfer must be set on this dispatch + modelInput = colorSmall +[else] + modelInput = colorCopy + │ + ▼ +NGX NR(modelInput) → output = m_e UNCHANGED + │ + ▼ +Resolve (colour-sized dispatch) + Classic: UpgradeToneMap(H, U(p), U(m)) live shader + Matched: T = P + (U(m) − U(p)) or T = m if same-rate + F = H0(H, P, T) or H1 if Inspect says so +``` + +Multi-pass: `IFeature_Dx12::Evaluate` already calls `DlssNr::Run` then FSR1. This spec +only changes `Run` / `Dispatch`. Feature 1 1:1 and the enlarger are unchanged. Transfer +is available in both Placements. + +--- + +## 6. Resources and bindings + +No new scratch textures. `colorCopy` already lives for the whole evaluate. + +Always bind `colorCopy` as `t3` on resolve, including Classic (unread there). One +call site, no branch in C++ around descriptors. + +### 6.1 Shader registers (unchanged count) + +`kSrvCount` stays 5. Slot 3 (`t3`) is currently bound to motion and unread in every +mode. This slice **repurposes `t3` as the full-resolution proxy**. + +| Register | Name | Encode | Downsample | Resolve | +|---|---|---|---|---| +| t0 | `gSource` | display colour | `colorCopy` | work proxy \(p_e\) (`colorSmall` or `colorCopy`) | +| t1 | `gModel` | stand-in | stand-in | `g_nr.output` \(m_e\) | +| t2 | `gOriginal` | stand-in | stand-in | `hdrCopy` \(H\) | +| t3 | `gProxy` | stand-in | stand-in | `colorCopy` \(E\), always colour-sized | +| t4 | unused | stand-in | stand-in | stand-in | +| u0 | `gTarget` | `colorCopy` | `colorSmall` | game output | +| u1 | `gKeep` | `hdrCopy` | stand-in | stand-in | +| s0 | `gLinear` | unused | Classic downsample | Classic and Matched residual | + +Rename in HLSL: `gMotion` → `gProxy`. Do not keep a motion semantic on that slot. + +### 6.2 `DispatchPass` C++ + +Rename the 4th input from `InMotion` to `InProxy` (same pointer argument, new name). + +Resolve call site today: + +```cpp +DispatchPass(cmdList, resolveParams, modelInput, g_nr.output, g_nr.hdrCopy, motionIn, + nullptr, target, nullptr); +``` + +becomes (both Transfer values): + +```cpp +DispatchPass(cmdList, resolveParams, modelInput, g_nr.output, g_nr.hdrCopy, g_nr.colorCopy, + nullptr, target, nullptr); +``` + +Encode and downsample still pass `nullptr` for `InProxy` (stand-in = `InSource`). + +`motionIn` remains an NGX evaluate input only. Do not bind it to the compose shader. + +Downsample constants (required, both Transfer values): + +```cpp +DlssNrConstants down {}; +down.Mode = DlssNrMode_Downsample; +down.Width = workWidth; +down.Height = workHeight; +down.Transfer = (uint32_t) DlssNr::ConfiguredTransfer(); +``` + +Zero-init `Transfer` is Classic bilinear. That is a bug if the menu is Matched. + +### 6.3 Views must be numeric, not `_SRGB` (required) + +Area downsample and residual both need the **stored** encode numbers. Hardware sRGB +views filter in linear (`D3D11.3` functional spec). Today `CreateScratch` copies the +output format, and `Shader_Dx12::CreateShaderResourceView` / +`CreateUnorderedAccessView` only remap TYPELESS — `_UNORM_SRGB` stays `_SRGB`. +`CreateUnorderedAccessView` has no format override. Passthrough SDR (the P test) +is this path, not a rare HDR edge. + +**Must, in this slice:** + +1. `CreateScratch` stores the typed non-sRGB mapping already described in + `DlssNr_Codec.h::TypedFormat` (`R8G8B8A8_UNORM_SRGB` → `R8G8B8A8_UNORM`, same + for BGRA). Apply to `colorCopy`, `colorSmall`, `g_nr.output`, and `hdrCopy` + (float HDR is unchanged). UAV create then inherits a numeric format. +2. If any SRV in this pass would still bind an `_SRGB` format, pass the UNORM + override into `CreateShaderResourceView`. + +Typical HDR path (`R16G16B16A16_FLOAT`) is already numeric. Do not leave this to +PR discovery. + +--- + +## 7. Downsample (Mode 2) + +Runs only if `workWidth != width || workHeight != height`. Writes `colorSmall`. + +Branch on `gTransfer` (same clamped uint uploaded on this pass — §6.2): + +**Classic** — keep the live line, bit for bit: + +``` +gTarget[id.xy] = gSource.SampleLevel(gLinear, uv, 0); +``` + +**Matched residual** — encoded exact area (§7.1). Kaiser / Lanczos are a later A/B, +not this slice, and would be a third Transfer value if they ever ship. + +### 7.1 Area algorithm (Matched only) + +Destination dispatch is `workW × workH` (`gWidth`, `gHeight`). Source size from +`gSource.GetDimensions(srcW, srcH)` — do not pack sizes into unused constants. +Box bounds are **float** (`(x * srcW) / dstW` in float, not uint). Integer +truncation of the box is a bug. + +Source texel \(i\) occupies \([i, i+1)\). Destination pixel \((x,y)\) covers + +\[ +\left[\frac{x \cdot srcW}{dstW},\; \frac{(x+1)\cdot srcW}{dstW}\right) +\times +\left[\frac{y \cdot srcH}{dstH},\; \frac{(y+1)\cdot srcH}{dstH}\right). +\] + +Let \(x_0,x_1\) be those float edges. Overlapping integer texels are +\(i = \lfloor x_0 \rfloor \ldots \lceil x_1 \rceil - 1\) (empty if \(x_1 \le x_0\)). +For each overlapping source texel, weight = product of 1D overlap lengths. +`Load` `clamp(i, 0, srcW-1)` (clamp-to-edge: out-of-range indices still contribute +the edge texel). Output is `acc / area` where `area = (x1-x0)*(y1-y0)`. +Do **not** renormalise by the summed weights — that would change edge behaviour. +Weights are non-negative. No negative lobes. + +**Do not hard-cap at 5 taps.** `WorkingScale ∈ [0.25, 1]` is typically ≤5 per +axis; `WorkAtNative` uses the guide raster and can be 4K/720p (≈5.3) or +4K/540p (≈7). A hardcoded `i0 .. i0+4` that still divides by the full dest box +darkens the model input. Loop the overlapping range. Do not `[unroll]` a +fixed 5. A dest pixel’s overlap count is at most \(\lceil src/dst \rceil + 1\) +per axis. + +If `dst == src`, `Load` and copy (C++ `reduced` should not reach this). + +Use `Load`, not `SampleLevel`. Average stored RGB. **Centre-texel alpha:** the +source texel that contains the dest pixel centre +\(((x+0.5)\cdot srcW/dstW,\; (y+0.5)\cdot srcH/dstH)\), clamped. NR does not use +alpha. Do not decode: this is \(D_e(E)\), not \(S(D(P))\). + +--- + +## 8. Resolve (Mode 1) + +### 8.1 Decode helper + +``` +Decode(rgb) = passthrough ? rgb : SrgbToLinear(rgb) +``` + +`SrgbToLinear` already saturates. That is correct for the encoded cube. + +### 8.2 Strength 0 (both options) + +Runs only after debug views (§11). After compare UV is known: + +``` +if (gTransferStrength == 0) +{ + float4 o = (gCompareMode == 1) + ? gOriginal.SampleLevel(gLinear, cmpUv, 0) + : gOriginal.Load(int3(id.xy, 0)); + // letterbox / divider still apply + gTarget[id.xy] = float4(o.rgb, o.a); // no extra max, no /W + return; +} +``` + +`o` is `hdrCopy`. Encode already clamped negatives. T1 requires Compare off so +this is a Load of that keep. + +### 8.3 Samples used by both options + +``` +float3 p = Decode(gSource.SampleLevel(gLinear, cmpUv, 0).rgb); +float3 m = Decode(gModel.SampleLevel(gLinear, cmpUv, 0).rgb); + +float4 originalSample = (gCompareMode == 1) + ? gOriginal.SampleLevel(gLinear, cmpUv, 0) + : gOriginal.Load(int3(id.xy, 0)); + +float normScale = passthrough ? 1 : max(gWhitePoint, 1e-4); +float3 H = originalSample.rgb / normScale; +``` + +Classic does not read `gProxy`. Matched does: + +``` +float4 eSample = (gCompareMode == 1) + ? gProxy.SampleLevel(gLinear, cmpUv, 0) + : gProxy.Load(int3(id.xy, 0)); +float3 P = Decode(eSample.rgb); +``` + +Wipe (`CompareMode == 2`) Loads \(P\) and \(H\). Side-by-side samples at `cmpUv` +(including debug 4/5). + +`sameRate` comes from the small source, **not** from `gGuideWidth` (that field is +zero today and is not overloaded in this slice): + +``` +uint srcW, srcH; +gSource.GetDimensions(srcW, srcH); +bool sameRate = (srcW == gWidth && srcH == gHeight); +``` + +Do not set `GuideWidth` / `GuideHeight` to work size for this. Do not overload +`MvScale*`. + +### 8.4 Classic compose (Transfer == 0) + +Live shader, with `proxy = p`, `model = m`, `original = H`. Do not substitute \(P\). +Do not build \(T\). Do not run cube-scale or H1. + +This is the default. A user who never opens the combo must see today’s picture at +nonzero strength. + +### 8.5 Matched residual — \(T\) + +``` +float3 T = sameRate ? m : (P + (m - p)); +``` + +**Residual domain (locked).** One resolve pass, no extra UAV: + +1. Hardware-bilinear `SampleLevel` on the **encoded** (or native) small textures. +2. `Decode` each sample. +3. \(d = m - p\) in that decoded space, \(T = P + d\). + +That is \(S^{-1}(U(m_e)) - S^{-1}(U(p_e))\), not \(U(S^{-1}(m_e) - S^{-1}(p_e))\). A +work-res `RGBA16F` residual is a later A/B if colour still smears. Do not add that +pass now. + +Both small textures are sampled at the same `cmpUv`. Do not upsample \(m\) as a +finished picture and call that \(T\). + +### 8.6 Keep \(T\) inside the proxy cube (Matched + HDR only) + +``` +if (!passthrough) +{ + float3 d = T - P; + float alpha = 1.0; + [unroll] for (int c = 0; c < 3; ++c) + { + if (d[c] > 1e-6) alpha = min(alpha, (1.0 - P[c]) / d[c]); + else if (d[c] < -1e-6) alpha = min(alpha, (0.0 - P[c]) / d[c]); + } + alpha = saturate(alpha); + T = P + alpha * d; +} +``` + +Passthrough: leave \(T\) as computed. + +### 8.7 H0 — Matched default lift + +Same UpgradeToneMap as Classic, but `proxy = P`, `model = T`, `original = H`. +Empty-model gate is on \(m\), not \(T\): + +``` +if dot(m, kLuma) <= 1e-5: + upgraded = H +else + proxyLuma = dot(P, kLuma) + modelLuma = dot(T, kLuma) + originalLuma = dot(H, kLuma) + if originalLuma < proxyLuma: ratio = originalLuma / max(proxyLuma, 1e-6) + else: ratio = (modelLuma + max(0, originalLuma - proxyLuma)) / modelLuma + upgraded = lerp(H, HueOkLab(T * ratio, T), TransferStrength) + +lumaRatio = clamp((upgradedLuma + 1/512) / (originalLuma + 1/512), 0, MaxRatio) +result = lerp(H * lumaRatio, upgraded, ColourStrength) * normScale +``` + +OkLab’s hue source is \(T\), not \(H\). `MaxRatio` still only clamps the luma-only mix, +as today. + +### 8.8 H1 — Inspect only, Matched only + +Empty-model gate first (same \(m\) test as H0): result is \(H \cdot normScale\). + +Otherwise: + +``` +float3 add = H + (T - P); +float3 F = lerp(H, add, gTransferStrength); +result = F * normScale; +``` + +No UpgradeToneMap, no OkLab, no MaxRatio, no `ColourStrength`. Detail strength still +moves the picture; Colour strength is ignored (do not hide the slider — it is a +shared Inspect control). Store `max(result, 0)` with the common compose store. + +Allowed to look worse on highlight hue. Default `HdrLift = 0` (H0). Classic ignores +this field. + +### 8.9 Debug views + +Always listed in the menu (six entries). Do not shrink the combo when Transfer is +Classic — a stored `DebugView == 4` would then sit past the last item. + +| `DlssNrDebugView` | Picture | Notes | +|---|---|---| +| 0 | composed \(F\) | Current Transfer + lift | +| 1 | \(U(p) \cdot W\) | Live view 1. What the model was shown | +| 2 | \(U(m) \cdot W\) | Live view 2 | +| 3 | live wrap | `SrgbToLinear(saturate(0.5 + (m-p)*20)) * W` — keep the wrap; do not “simplify” to `0.5+(m-p)*20` | +| 4 | \(P \cdot W\) | Full-res proxy. Classic: write 0 | +| 5 | \(T \cdot W\) | After cube-scale (§8.6), before lift. Classic: write 0 | + +Views 1–5 run **before** the strength-0 return, so they work at strength 0 (live +behaviour for 1–3). Views 1–3 always use \(p,m\). Compare wipe / side-by-side +unchanged (original side is `originalSample.rgb`). + +--- + +## 9. Config and menu + +| Key | Type | Default | Meaning | +|---|---|---|---| +| `Transfer` | uint | 0 | 0 Classic, 1 Matched residual | +| `HdrLift` | uint | 0 | 0 H0, 1 H1. Used only if Transfer == 1 | + +No `LegacyResolve`. + +`TransferStrength == 0` remains the identity control (`hdrCopy`). No extra bypass +checkbox. Update the Detail-strength help if it still claims bit-identity with the +upscaler UAV — identity is `hdrCopy`. + +**Cost** section (after the resolution slider): + +``` +Transfer [ Classic v ] + [ Matched residual ] +``` + +Help for Transfer (same tone as Placement / Cost): + +> How a below-frame model is brought back onto the picture this pass writes. +> +> Classic is the current path: shrink with bilinear, then fold the small answer +> directly onto the full-resolution picture. It is the default. +> +> Matched residual keeps a sharp copy of the picture the model was shown, adds only +> what the model changed, and then runs the same highlight-aware compose. The shrink +> is an area filter so the model is not fed an aliased thumbnail. +> +> When the model is the same size as this frame the two match. Changing this does +> not rebuild the model; the next frame uses the new shrink and the new compose. + +**Inspect:** + +- Combo **HDR lift** (enabled only if Transfer == Matched residual): + `UpgradeToneMap` / `Additive headroom`. Classic ignores the stored value. +- Debug view combo always has six names. Views 4–5 are black on Classic. +- HDR lift help: additive path uses Detail strength; Colour strength does not apply. + +Do not mention “legacy”, “A/B only”, or “temporary” in the UI. + +--- + +## 10. File-level work + +| File | Change | +|---|---| +| `dlssnr/DlssNr_Modes.h` | `enum class Transfer`; `ConfiguredTransfer()`; `ConfiguredHdrLift()` | +| `shaders/dlssnr/precompile/dlssnr.hlsl` | Mode 2 branch; `gProxy`; Classic vs Matched resolve; H1; strength-0; debug 4/5; empty-model gate on \(m\) | +| `shaders/dlssnr/precompile/DlssNr_Shader.h` | Rebuild CSO (§13) | +| `shaders/dlssnr/DlssNr_Dx12.cpp` | Bind `colorCopy` as slot 3; `CreateScratch` via `TypedFormat`; set `Transfer` on **downsample and resolve**; set `HdrLift` on resolve | +| `shaders/dlssnr/DlssNr_Dx12.h` | Rename `InMotion` → `InProxy` | +| `shaders/dlssnr/DlssNr_Common.h` | Add `Transfer`, `HdrLift` to constants. Do not document `GuideWidth` as work size | +| `Config.h` / `Config.cpp` | `DlssNrTransfer { 0 }`, `DlssNrHdrLift { 0 }`; DebugView comment becomes 0–5 | +| `dlssnr/DlssNr_Menu.cpp` | §9 | +| `dlssnr/README.md` | Two transfers, Classic default. Replace the lines that say composition is “not a delta” and that strength zero is bit-identical to the upscaler UAV | + +### 10.1 Constant buffer + +Add two uints at the end of `DlssNrConstants` (256-aligned; there is room). Do not +overload `MvScale*` or `GuideWidth` / `GuideHeight`. + +``` +uint gTransfer; // 0 Classic, 1 Matched residual +uint gHdrLift; // 0 H0, 1 H1; ignored unless gTransfer == 1 +``` + +after `gCompareSwap`. HLSL cbuffer order must match C++. Pad explicitly if the +compiler inserts padding; `static_assert` offsets in the PR if needed. + +`CreateConstantsBuffer` uploads `sizeof(DlssNrConstants)`. + +--- + +## 11. Shader structure (Mode 1) + +``` +if id out of range: return +compute uv, cmpUv, showOriginal, onDivider, outsideFrame + +sample p, m, H as §8.3 + +if DebugView == 1,2,3: existing live wrap, using p/m; return +if DebugView == 4 or 5: + if Transfer != Matched: write 0; return + build P (and T if 5, after §8.6); write P*W or T*W; return + +if TransferStrength == 0: write hdrCopy rgb/a (plus letterbox/divider); return + +if Transfer == Classic: + result = live UpgradeToneMap(H, p, m) // empty-model gate on m, as today +else: + build P, T as §8.5–8.6 + if dot(m, kLuma) <= 1e-5: result = H * normScale + else if HdrLift == 1: result = lerp(H, H + (T - P), TransferStrength) * normScale + else: result = H0(H, P, T) // gate already applied + +apply showOriginal / letterbox / divider +store max(result,0), original alpha // compose path only; strength 0 already returned +``` + +Encode (Mode 0) is unchanged. + +Mode 2: `if (gTransfer == 0) bilinear; else area;` — `gTransfer` from **this** +dispatch’s constants. + +--- + +## 12. Performance + +| Pass | Classic | Matched residual | +|---|---|---| +| Encode | unchanged | unchanged | +| Downsample | 1 bilinear (today) | area `Load`s over the dest box, work pixels only. Typically ≤5×5 at WorkingScale ≥ 0.25; more under WorkAtNative | +| NR | unchanged | unchanged (same size; different pixels) | +| Resolve | today | +1 full-res `Load` of `colorCopy` | + +No extra resource, no extra barrier. Matched add: a fraction of a millisecond at 4K. +If it approaches a Super Resolution pass, the bug is a mistaken extra full-res pass. + +\(T\) is in-register. Classic must not pay the area kernel. + +--- + +## 13. Rebuild the CSO + +From `OptiScaler/shaders/dlssnr/precompile`: + +```bat +..\shader_tools\build_precompiled_shader.bat dlssnr +``` + +The bat also writes `dlssnr_Shader_Dx11.cso` and `DlssNr_Shader_Dx11.h`. NR has no +Dx11 consume path and those files are not in the tree. **Do not add them.** Commit +only `dlssnr_Shader.cso` and `DlssNr_Shader.h` (`DlssNr_cso`) with the HLSL. + +The Dx12 constructor passes `source = nullptr`; a stale header is a silent old +shader. + +--- + +## 14. Test plan + +Freeze model version and scene. Use the wipe and debug views. Do not move +Transfer / Colour / WhitePoint sliders in the same take as T2. + +**Default matrix for T0–T7 and G:** Placement = Post-process, `WorkAtNative` off, +Output Scaling off, `DlssNrUseProxy` off, Compare off unless a row says otherwise. +“Cost 67% / 100%” means `WorkingScale` 0.67 / 1.0 of **display**, which is also +same-rate vs colour in this matrix. Multi-pass Quality caps the slider at +native/display (often ~67%) — that would make T2/T5 same-rate. Multi-pass is **M +only**. + +| ID | Change | Pass if | +|---|---|---| +| TA | Host-side area fixture (float box, no GPU) | 1D overlap weights match the dest interval; 2×2→1 is the mean of four stored RGB; `dst==src` is a copy; a 3→2 box is not uint-truncated | +| T0 | Fresh config, Transfer unset | Looks like today’s live build at default strengths. | +| T1 | `TransferStrength = 0`, either option; Compare 0, Debug 0 | Output bytes match `hdrCopy` (encode keep), not the Capture “before” (`colorCopy`). | +| T2 | Toggle Classic ↔ Matched at WorkingScale 0.67 | Picture changes; no hitch. Log must **not** show `ParkNrFeature` / resolutionChanged / a feature rebuild. A few frames of NGX history drift is allowed; that is not the 30-frame work-size hold. | +| T3 | Toggle at WorkingScale 1.0 | No visible difference (scale-one). INI still records the choice. Combo stays enabled. | +| T4 | Classic, WorkingScale 0.67 | Matches live bilinear + cross-rate compose (nonzero strength). | +| T5 | Matched vs Classic, WorkingScale 0.67 | Matched: less edge halo, original detail kept. Debug-1 may differ (area vs bilinear). | +| T6 | Matched H0 vs H1 | H1 does not amplify highlight boil; hue follow may weaken. Detail strength still moves H1; Colour strength does not. Default H0. | +| T7 | Implementer-only: resolve bind `gSource` = `gModel` for one take (no menu control) | Debug 5 equals debug 4 (\(m=p \Rightarrow T=P\)). Revert the bind. | +| P | Passthrough game (8-bit or non-float) | No double encode on either option. Scratch / views are numeric (§6.3). | +| M | Multi-pass + either Transfer | Feature 1 1:1; FSR1 after `Run`; no double NR. Do not use this row as the T2/T5 scale. | +| G | Timestamps, Matched | Extra vs Classic ≪ one DLSS SR. | + +--- + +## 15. Acceptance + +Ship when: + +1. TA, T0, and T1 hold. +2. T2 / T3: combo is sticky, instant, and does not rebuild NR. +3. T4: Classic is the live path at nonzero strength. +4. T5: Matched is a real option (visible difference, not a rename). +5. Multi-pass and passthrough run on both values. +6. Matched extra GPU time is under one Super Resolution pass. + +Do not start C1 affine until someone is actually using Matched and still loses +hue-across-stripes. Next design would still apply to \(P\) then H0 — not a new +downsample, and not EASU on \(m\). + +--- + +## 16. Decisions locked + +| Topic | Choice | Rejected | +|---|---|---| +| Product | One Transfer combo; Classic default | Silent replace; hidden Legacy checkbox; split kernel vs compose | +| Classic | Live bilinear + live UpgradeToneMap | Area shrink while Classic is selected | +| Matched pack | Area + residual onto \(P\) + H0 | Upsample \(m\) as a picture; RGB ratio; \(U(m-p)\) residual UAV | +| Residual domain | Bilinear stored texels, then decode | Work-res decoded residual UAV (later) | +| H1 | Inspect, Matched only; lerp with Detail strength; ignore Colour strength | Fourth user combo; dead sliders; full additive with no lerp | +| Empty model | Gate on \(\mathrm{luma}(m)\) | Gate on \(\mathrm{luma}(T)\) | +| \(s=1\) | Both options: \(T = m\); `sameRate` from `gSource.GetDimensions` | Fit at full res; `GuideWidth` as work size | +| Strength 0 | Copy `hdrCopy` on both options when Debug is off | Algebraic lerp through `/W`; claim on the upscaler UAV; extra `max` | +| Debug | Views 1–5 before strength 0; six menu entries always | Strength 0 first; hide 4/5 on Classic | +| Area taps | Loop the overlapping box; float bounds; clamp-to-edge; geometric `area` | Hard 5-tap cap; uint box; renormalise after dropping taps | +| Numeric views | `CreateScratch` + TypedFormat in this slice | Leave `_SRGB` to PR discovery | +| Extra textures | None | Full-res \(T\) UAV | +| Slot 3 | Always `gProxy` = `colorCopy` | 6th SRV; bind motion; bind only if Matched | +| Runtime | Constants only, next frame; downsample **and** resolve carry `gTransfer` | Feature rebuild on toggle; Transfer only on resolve | +| CSO | Commit Dx12 header + hlsl | Add the bat’s Dx11 outputs | + +--- + +## 17. Open only if implementation hits them + +Not algorithm TBDs. Resolve in the PR if they appear: + +- `DlssNrConstants` vs HLSL padding after the two new uints. +- Debug 4/5 in side-by-side: sample \(P\) at `cmpUv` (§8.3). diff --git a/OptiScaler/dlssnr/forwarder/dlssnr_forwarder.cpp b/OptiScaler/dlssnr/forwarder/dlssnr_forwarder.cpp index d9e61286d..e54190896 100644 --- a/OptiScaler/dlssnr/forwarder/dlssnr_forwarder.cpp +++ b/OptiScaler/dlssnr/forwarder/dlssnr_forwarder.cpp @@ -15,7 +15,8 @@ #include #include -namespace { +namespace +{ // Slot indices confirmed by round-tripping values through the live block: a setter at N is read back by // the getter at N+8. The unsigned setter is slot 3 (a feature create driven through it succeeds) and the @@ -29,54 +30,60 @@ constexpr int VT_SET_ULL = 0; int g_floatSlot = 1; constexpr int VT_SET_UINT = 3; -using PFN_SetULL = void(__thiscall *)(void *, const char *, unsigned long long); -using PFN_SetFloat = void(__thiscall *)(void *, const char *, float); -using PFN_SetUInt = void(__thiscall *)(void *, const char *, unsigned int); +using PFN_SetULL = void(__thiscall*)(void*, const char*, unsigned long long); +using PFN_SetFloat = void(__thiscall*)(void*, const char*, float); +using PFN_SetUInt = void(__thiscall*)(void*, const char*, unsigned int); -void setUInt(void *params, const char *name, unsigned int v) { - void **vt = *reinterpret_cast(params); +void setUInt(void* params, const char* name, unsigned int v) +{ + void** vt = *reinterpret_cast(params); reinterpret_cast(vt[VT_SET_UINT])(params, name, v); } -void setFloat(void *params, const char *name, float v) { - void **vt = *reinterpret_cast(params); +void setFloat(void* params, const char* name, float v) +{ + void** vt = *reinterpret_cast(params); reinterpret_cast(vt[g_floatSlot])(params, name, v); } -void setResourcePtr(void *params, const char *name, void *v) { - void **vt = *reinterpret_cast(params); +void setResourcePtr(void* params, const char* name, void* v) +{ + void** vt = *reinterpret_cast(params); reinterpret_cast(vt[VT_SET_ULL])(params, name, (unsigned long long) v); } -void setResource(void *params, const char *name, ID3D12Resource *v) { - void **vt = *reinterpret_cast(params); +void setResource(void* params, const char* name, ID3D12Resource* v) +{ + void** vt = *reinterpret_cast(params); reinterpret_cast(vt[VT_SET_ULL])(params, name, (unsigned long long) v); } -using PFN_NrInitExt = int(__cdecl *)(unsigned long long, const wchar_t *, ID3D12Device *, int, - const void *); -using PFN_NrCreate = int(__cdecl *)(ID3D12GraphicsCommandList *, int, const void *, void **); -using PFN_NrEvaluate = int(__cdecl *)(ID3D12GraphicsCommandList *, const void *, const void *, void *); -using PFN_NrRelease = int(__cdecl *)(void *); +using PFN_NrInitExt = int(__cdecl*)(unsigned long long, const wchar_t*, ID3D12Device*, int, const void*); +using PFN_NrCreate = int(__cdecl*)(ID3D12GraphicsCommandList*, int, const void*, void**); +using PFN_NrEvaluate = int(__cdecl*)(ID3D12GraphicsCommandList*, const void*, const void*, void*); +using PFN_NrRelease = int(__cdecl*)(void*); -struct Snippet { +struct Snippet +{ HMODULE module = nullptr; PFN_NrInitExt init = nullptr; PFN_NrCreate create = nullptr; PFN_NrEvaluate evaluate = nullptr; PFN_NrRelease release = nullptr; bool initialised = false; - }; Snippet g_snip; -bool loadSnippet(const wchar_t *path) { - if (g_snip.module) { +bool loadSnippet(const wchar_t* path) +{ + if (g_snip.module) + { return g_snip.create != nullptr; } g_snip.module = LoadLibraryExW(path, nullptr, LOAD_WITH_ALTERED_SEARCH_PATH); - if (!g_snip.module) { + if (!g_snip.module) + { return false; } g_snip.init = (PFN_NrInitExt) GetProcAddress(g_snip.module, "NVSDK_NGX_D3D12_Init_Ext"); @@ -84,184 +91,192 @@ bool loadSnippet(const wchar_t *path) { g_snip.evaluate = (PFN_NrEvaluate) GetProcAddress(g_snip.module, "NVSDK_NGX_D3D12_EvaluateFeature"); g_snip.release = (PFN_NrRelease) GetProcAddress(g_snip.module, "NVSDK_NGX_D3D12_ReleaseFeature"); - return g_snip.create != nullptr && g_snip.evaluate != nullptr; } - } // namespace -extern "C" { +extern "C" +{ -// Called once, after the host has worked out which slot this block keeps floats in. -__declspec(dllexport) void dlssnr_call_set_float_slot(int slot) { - if (slot >= 0 && slot < 8) { - g_floatSlot = slot; + // Called once, after the host has worked out which slot this block keeps floats in. + __declspec(dllexport) void dlssnr_call_set_float_slot(int slot) + { + if (slot >= 0 && slot < 8) + { + g_floatSlot = slot; + } } -} -// Writes a float through an arbitrary slot, so the host can find the right one by testing. -__declspec(dllexport) void dlssnr_call_probe_float(void *params, const char *name, float value, - int slot) { - if (!params || slot < 0 || slot >= 8) { - return; + // Writes a float through an arbitrary slot, so the host can find the right one by testing. + __declspec(dllexport) void dlssnr_call_probe_float(void* params, const char* name, float value, int slot) + { + if (!params || slot < 0 || slot >= 8) + { + return; + } + void** vt = *reinterpret_cast(params); + reinterpret_cast(vt[slot])(params, name, value); } - void **vt = *reinterpret_cast(params); - reinterpret_cast(vt[slot])(params, name, value); -} - -// Last init and create results, so the add-on can log why a feature never appeared. -__declspec(dllexport) int dlssnr_call_last_init = 0; -__declspec(dllexport) int dlssnr_call_last_create = 0; -// Creates a persistent Neural Rendering feature. The handle records initialisation work into cmd, so it -// must outlive that command list's execution; releasing it early loses the device. -__declspec(dllexport) void *dlssnr_call_create(const wchar_t *snippetPath, const wchar_t *dataPath, - ID3D12Device *device, ID3D12GraphicsCommandList *cmd, - void *capabilityParams, unsigned int width, - unsigned int height, int preset, float intensity, - int style, float localStructure, float localTone, - float skinStructure, int useAutoMask, - int uiCorrection) { - if (!loadSnippet(snippetPath) || !capabilityParams) { - return nullptr; - } - if (!g_snip.initialised && g_snip.init) { - // OptiScaler's own generic application id, the one it already hands DLSS when a game's id is - // not wanted. What was here before was 0x4350324B -- "CP2K" -- so every game that ever loaded - // this announced itself to the driver as Cyberpunk 2077. - dlssnr_call_last_init = g_snip.init(0x24480451ull, dataPath, device, 0x0000015, capabilityParams); - g_snip.initialised = (dlssnr_call_last_init == 1); - if (!g_snip.initialised) { + // Last init and create results, so the add-on can log why a feature never appeared. + __declspec(dllexport) int dlssnr_call_last_init = 0; + __declspec(dllexport) int dlssnr_call_last_create = 0; + + // Creates a persistent Neural Rendering feature. The handle records initialisation work into cmd, so it + // must outlive that command list's execution; releasing it early loses the device. + __declspec(dllexport) void* dlssnr_call_create(const wchar_t* snippetPath, const wchar_t* dataPath, + ID3D12Device* device, ID3D12GraphicsCommandList* cmd, + void* capabilityParams, unsigned int width, unsigned int height, + int preset, float intensity, int style, float localStructure, + float localTone, float skinStructure, int useAutoMask, + int uiCorrection) + { + if (!loadSnippet(snippetPath) || !capabilityParams) + { return nullptr; } + if (!g_snip.initialised && g_snip.init) + { + // OptiScaler's own generic application id, the one it already hands DLSS when a game's id is + // not wanted. What was here before was 0x4350324B -- "CP2K" -- so every game that ever loaded + // this announced itself to the driver as Cyberpunk 2077. + dlssnr_call_last_init = g_snip.init(0x24480451ull, dataPath, device, 0x0000015, capabilityParams); + g_snip.initialised = (dlssnr_call_last_init == 1); + if (!g_snip.initialised) + { + return nullptr; + } + } + setUInt(capabilityParams, "DLSSNR.Enabled", 1); + setUInt(capabilityParams, "DLSSNR.Width", width); + setUInt(capabilityParams, "DLSSNR.Height", height); + setUInt(capabilityParams, "CreationNodeMask", 1); + setUInt(capabilityParams, "VisibilityNodeMask", 1); + // Written unconditionally, including zero. This block belongs to the driver and outlives the + // feature, so skipping the write for "default" left whichever preset was chosen last still sitting + // in it -- and going back to default did nothing at all. + setUInt(capabilityParams, "DLSSNR.Hint.Render.Preset", (unsigned int) preset); + + // The tuning has to be here rather than at evaluate. Everything this sets before create takes + // effect; everything set only at evaluate is ignored, which is why none of these controls did + // anything for a long time. The model reads them once, when it builds the feature. + setFloat(capabilityParams, "DLSSNR.Intensity", intensity); + setUInt(capabilityParams, "DLSSNR.Style", (unsigned int) style); + setFloat(capabilityParams, "DLSSNR.LocalStructureStrength", localStructure); + setFloat(capabilityParams, "DLSSNR.LocalToneStrength", localTone); + setFloat(capabilityParams, "DLSSNR.SkinStructureStrength", skinStructure); + setUInt(capabilityParams, "DLSSNR.UseAutoMask", (unsigned int) useAutoMask); + setUInt(capabilityParams, "DLSSNR.UICorrection", (unsigned int) uiCorrection); + void* handle = nullptr; + dlssnr_call_last_create = g_snip.create(cmd, 18, capabilityParams, &handle); + return handle; } - setUInt(capabilityParams, "DLSSNR.Enabled", 1); - setUInt(capabilityParams, "DLSSNR.Width", width); - setUInt(capabilityParams, "DLSSNR.Height", height); - setUInt(capabilityParams, "CreationNodeMask", 1); - setUInt(capabilityParams, "VisibilityNodeMask", 1); - // Written unconditionally, including zero. This block belongs to the driver and outlives the - // feature, so skipping the write for "default" left whichever preset was chosen last still sitting - // in it -- and going back to default did nothing at all. - setUInt(capabilityParams, "DLSSNR.Hint.Render.Preset", (unsigned int) preset); - - // The tuning has to be here rather than at evaluate. Everything this sets before create takes - // effect; everything set only at evaluate is ignored, which is why none of these controls did - // anything for a long time. The model reads them once, when it builds the feature. - setFloat(capabilityParams, "DLSSNR.Intensity", intensity); - setUInt(capabilityParams, "DLSSNR.Style", (unsigned int) style); - setFloat(capabilityParams, "DLSSNR.LocalStructureStrength", localStructure); - setFloat(capabilityParams, "DLSSNR.LocalToneStrength", localTone); - setFloat(capabilityParams, "DLSSNR.SkinStructureStrength", skinStructure); - setUInt(capabilityParams, "DLSSNR.UseAutoMask", (unsigned int) useAutoMask); - setUInt(capabilityParams, "DLSSNR.UICorrection", (unsigned int) uiCorrection); - void *handle = nullptr; - dlssnr_call_last_create = g_snip.create(cmd, 18, capabilityParams, &handle); - return handle; -} -// Colour and output are display resolution; depth and motion come from the game's own DLSS evaluation and -// may be render resolution, so each resource carries its own subrect and motion scales by the ratio. -__declspec(dllexport) int dlssnr_call_evaluate(ID3D12GraphicsCommandList *cmd, void *feature, - void *capabilityParams, ID3D12Resource *color, - ID3D12Resource *depth, ID3D12Resource *motion, - ID3D12Resource *output, unsigned int width, - unsigned int height, unsigned int guideWidth, - unsigned int guideHeight, int depthInverted, int reset, - float intensity, int style, float localStructure, - float localTone, float skinStructure, int useAutoMask, - float mvScaleX, float mvScaleY) { - if (!feature || !capabilityParams || !g_snip.evaluate) { - return 0; + // Colour and output are display resolution; depth and motion come from the game's own DLSS evaluation and + // may be render resolution, so each resource carries its own subrect and motion scales by the ratio. + __declspec(dllexport) int dlssnr_call_evaluate(ID3D12GraphicsCommandList* cmd, void* feature, + void* capabilityParams, ID3D12Resource* color, ID3D12Resource* depth, + ID3D12Resource* motion, ID3D12Resource* output, unsigned int width, + unsigned int height, unsigned int guideWidth, + unsigned int guideHeight, int depthInverted, int reset, + float intensity, int style, float localStructure, float localTone, + float skinStructure, int useAutoMask, float mvScaleX, float mvScaleY) + { + if (!feature || !capabilityParams || !g_snip.evaluate) + { + return 0; + } + setResource(capabilityParams, "DLSSNR.Color", color); + setResource(capabilityParams, "DLSSNR.Depth", depth); + setResource(capabilityParams, "DLSSNR.MVec", motion); + setResource(capabilityParams, "DLSSNR.Output", output); + + // The block is shared with the game's own DLSS, which overwrites these between frames, so every + // value the feature reads is set again here rather than relying on what create left behind. + setUInt(capabilityParams, "DLSSNR.Enabled", 1); + setUInt(capabilityParams, "DLSSNR.Width", width); + setUInt(capabilityParams, "DLSSNR.Height", height); + setUInt(capabilityParams, "DLSSNR.DepthInverted", (unsigned int) depthInverted); + setUInt(capabilityParams, "DLSSNR.Reset", (unsigned int) reset); + + setUInt(capabilityParams, "DLSSNR.ColorSubrectBaseX", 0); + setUInt(capabilityParams, "DLSSNR.ColorSubrectBaseY", 0); + setUInt(capabilityParams, "DLSSNR.ColorSubrectWidth", width); + setUInt(capabilityParams, "DLSSNR.ColorSubrectHeight", height); + setUInt(capabilityParams, "DLSSNR.OutputSubrectBaseX", 0); + setUInt(capabilityParams, "DLSSNR.OutputSubrectBaseY", 0); + setUInt(capabilityParams, "DLSSNR.OutputSubrectWidth", width); + setUInt(capabilityParams, "DLSSNR.OutputSubrectHeight", height); + setUInt(capabilityParams, "DLSSNR.DepthSubrectBaseX", 0); + setUInt(capabilityParams, "DLSSNR.DepthSubrectBaseY", 0); + setUInt(capabilityParams, "DLSSNR.DepthSubrectWidth", guideWidth); + setUInt(capabilityParams, "DLSSNR.DepthSubrectHeight", guideHeight); + setUInt(capabilityParams, "DLSSNR.MVecSubrectBaseX", 0); + setUInt(capabilityParams, "DLSSNR.MVecSubrectBaseY", 0); + setUInt(capabilityParams, "DLSSNR.MVecSubrectWidth", guideWidth); + setUInt(capabilityParams, "DLSSNR.MVecSubrectHeight", guideHeight); + + // The game's own encoding, passed through. Deriving this from the resolutions was a guess, and at + // native resolution it came out as exactly 1.0 -- so a game using normalised vectors was telling + // the model almost nothing had moved. + setFloat(capabilityParams, "DLSSNR.MVecScaleX", mvScaleX); + setFloat(capabilityParams, "DLSSNR.MVecScaleY", mvScaleY); + + setFloat(capabilityParams, "DLSSNR.Intensity", intensity); + setUInt(capabilityParams, "DLSSNR.Style", (unsigned int) style); + setFloat(capabilityParams, "DLSSNR.LocalStructureStrength", localStructure); + setFloat(capabilityParams, "DLSSNR.LocalToneStrength", localTone); + setFloat(capabilityParams, "DLSSNR.SkinStructureStrength", skinStructure); + setUInt(capabilityParams, "DLSSNR.UseAutoMask", (unsigned int) useAutoMask); + + // The result must not be returned directly. `return f(...)` is a tail call, and the compiler emits a + // jmp rather than a call, which leaves this module's frame behind: the snippet then resolves its + // caller to whoever called us and rejects it. Keeping the value in a volatile forces a real call and + // a return through this module, which is the whole reason this file exists. + volatile int result = g_snip.evaluate(cmd, feature, capabilityParams, nullptr); + return result; } - setResource(capabilityParams, "DLSSNR.Color", color); - setResource(capabilityParams, "DLSSNR.Depth", depth); - setResource(capabilityParams, "DLSSNR.MVec", motion); - setResource(capabilityParams, "DLSSNR.Output", output); - - // The block is shared with the game's own DLSS, which overwrites these between frames, so every - // value the feature reads is set again here rather than relying on what create left behind. - setUInt(capabilityParams, "DLSSNR.Enabled", 1); - setUInt(capabilityParams, "DLSSNR.Width", width); - setUInt(capabilityParams, "DLSSNR.Height", height); - setUInt(capabilityParams, "DLSSNR.DepthInverted", (unsigned int) depthInverted); - setUInt(capabilityParams, "DLSSNR.Reset", (unsigned int) reset); - - setUInt(capabilityParams, "DLSSNR.ColorSubrectBaseX", 0); - setUInt(capabilityParams, "DLSSNR.ColorSubrectBaseY", 0); - setUInt(capabilityParams, "DLSSNR.ColorSubrectWidth", width); - setUInt(capabilityParams, "DLSSNR.ColorSubrectHeight", height); - setUInt(capabilityParams, "DLSSNR.OutputSubrectBaseX", 0); - setUInt(capabilityParams, "DLSSNR.OutputSubrectBaseY", 0); - setUInt(capabilityParams, "DLSSNR.OutputSubrectWidth", width); - setUInt(capabilityParams, "DLSSNR.OutputSubrectHeight", height); - setUInt(capabilityParams, "DLSSNR.DepthSubrectBaseX", 0); - setUInt(capabilityParams, "DLSSNR.DepthSubrectBaseY", 0); - setUInt(capabilityParams, "DLSSNR.DepthSubrectWidth", guideWidth); - setUInt(capabilityParams, "DLSSNR.DepthSubrectHeight", guideHeight); - setUInt(capabilityParams, "DLSSNR.MVecSubrectBaseX", 0); - setUInt(capabilityParams, "DLSSNR.MVecSubrectBaseY", 0); - setUInt(capabilityParams, "DLSSNR.MVecSubrectWidth", guideWidth); - setUInt(capabilityParams, "DLSSNR.MVecSubrectHeight", guideHeight); - // The game's own encoding, passed through. Deriving this from the resolutions was a guess, and at - // native resolution it came out as exactly 1.0 -- so a game using normalised vectors was telling - // the model almost nothing had moved. - setFloat(capabilityParams, "DLSSNR.MVecScaleX", mvScaleX); - setFloat(capabilityParams, "DLSSNR.MVecScaleY", mvScaleY); - - setFloat(capabilityParams, "DLSSNR.Intensity", intensity); - setUInt(capabilityParams, "DLSSNR.Style", (unsigned int) style); - setFloat(capabilityParams, "DLSSNR.LocalStructureStrength", localStructure); - setFloat(capabilityParams, "DLSSNR.LocalToneStrength", localTone); - setFloat(capabilityParams, "DLSSNR.SkinStructureStrength", skinStructure); - setUInt(capabilityParams, "DLSSNR.UseAutoMask", (unsigned int) useAutoMask); - - // The result must not be returned directly. `return f(...)` is a tail call, and the compiler emits a - // jmp rather than a call, which leaves this module's frame behind: the snippet then resolves its - // caller to whoever called us and rejects it. Keeping the value in a volatile forces a real call and - // a return through this module, which is the whole reason this file exists. - volatile int result = g_snip.evaluate(cmd, feature, capabilityParams, nullptr); - return result; -} - -// Inputs NVIDIA's own Streamline plugin sets that the positional exports predate: the model's global -// tone strength (read at create), and the interface as the game draws it -- its layer, its alpha, and -// the composited back buffer -- which is what the model's UI correction was designed around. Called -// before create and before every evaluate; absent resources are written as null, because the block -// outlives everything and a stale pointer is a freed resource. -__declspec(dllexport) void dlssnr_call_set_extras(void *capabilityParams, float globalTone, - ID3D12Resource *ui, ID3D12Resource *uiAlpha, - ID3D12Resource *backbuffer, unsigned int uiWidth, - unsigned int uiHeight, unsigned int bbWidth, - unsigned int bbHeight) { - if (!capabilityParams) { - return; + // Inputs NVIDIA's own Streamline plugin sets that the positional exports predate: the model's global + // tone strength (read at create), and the interface as the game draws it -- its layer, its alpha, and + // the composited back buffer -- which is what the model's UI correction was designed around. Called + // before create and before every evaluate; absent resources are written as null, because the block + // outlives everything and a stale pointer is a freed resource. + __declspec(dllexport) void dlssnr_call_set_extras(void* capabilityParams, float globalTone, ID3D12Resource* ui, + ID3D12Resource* uiAlpha, ID3D12Resource* backbuffer, + unsigned int uiWidth, unsigned int uiHeight, unsigned int bbWidth, + unsigned int bbHeight) + { + if (!capabilityParams) + { + return; + } + setFloat(capabilityParams, "DLSSNR.GlobalToneStrength", globalTone); + setResource(capabilityParams, "DLSSNR.UI", ui); + setResource(capabilityParams, "DLSSNR.UIAlpha", uiAlpha); + setResource(capabilityParams, "DLSSNR.Backbuffer", backbuffer); + setUInt(capabilityParams, "DLSSNR.UISubrectBaseX", 0); + setUInt(capabilityParams, "DLSSNR.UISubrectBaseY", 0); + setUInt(capabilityParams, "DLSSNR.UISubrectWidth", uiWidth); + setUInt(capabilityParams, "DLSSNR.UISubrectHeight", uiHeight); + setUInt(capabilityParams, "DLSSNR.UIAlphaSubrectBaseX", 0); + setUInt(capabilityParams, "DLSSNR.UIAlphaSubrectBaseY", 0); + setUInt(capabilityParams, "DLSSNR.UIAlphaSubrectWidth", uiWidth); + setUInt(capabilityParams, "DLSSNR.UIAlphaSubrectHeight", uiHeight); + setUInt(capabilityParams, "DLSSNR.BackbufferSubrectBaseX", 0); + setUInt(capabilityParams, "DLSSNR.BackbufferSubrectBaseY", 0); + setUInt(capabilityParams, "DLSSNR.BackbufferSubrectWidth", bbWidth); + setUInt(capabilityParams, "DLSSNR.BackbufferSubrectHeight", bbHeight); } - setFloat(capabilityParams, "DLSSNR.GlobalToneStrength", globalTone); - setResource(capabilityParams, "DLSSNR.UI", ui); - setResource(capabilityParams, "DLSSNR.UIAlpha", uiAlpha); - setResource(capabilityParams, "DLSSNR.Backbuffer", backbuffer); - setUInt(capabilityParams, "DLSSNR.UISubrectBaseX", 0); - setUInt(capabilityParams, "DLSSNR.UISubrectBaseY", 0); - setUInt(capabilityParams, "DLSSNR.UISubrectWidth", uiWidth); - setUInt(capabilityParams, "DLSSNR.UISubrectHeight", uiHeight); - setUInt(capabilityParams, "DLSSNR.UIAlphaSubrectBaseX", 0); - setUInt(capabilityParams, "DLSSNR.UIAlphaSubrectBaseY", 0); - setUInt(capabilityParams, "DLSSNR.UIAlphaSubrectWidth", uiWidth); - setUInt(capabilityParams, "DLSSNR.UIAlphaSubrectHeight", uiHeight); - setUInt(capabilityParams, "DLSSNR.BackbufferSubrectBaseX", 0); - setUInt(capabilityParams, "DLSSNR.BackbufferSubrectBaseY", 0); - setUInt(capabilityParams, "DLSSNR.BackbufferSubrectWidth", bbWidth); - setUInt(capabilityParams, "DLSSNR.BackbufferSubrectHeight", bbHeight); -} -__declspec(dllexport) void dlssnr_call_release(void *feature) { - if (feature && g_snip.release) { - volatile int result = g_snip.release(feature); // not a tail call, for the reason above - (void) result; + __declspec(dllexport) void dlssnr_call_release(void* feature) + { + if (feature && g_snip.release) + { + volatile int result = g_snip.release(feature); // not a tail call, for the reason above + (void) result; + } } -} } // extern "C" diff --git a/OptiScaler/dlssnr/tests/area_fixture.cpp b/OptiScaler/dlssnr/tests/area_fixture.cpp new file mode 100644 index 000000000..7c0387a2b --- /dev/null +++ b/OptiScaler/dlssnr/tests/area_fixture.cpp @@ -0,0 +1,112 @@ +#include "../DlssNr_Area.h" +#include "../DlssNr_Modes.h" + +#include +#include +#include +#include + +using DlssNr::AreaDownsamplePixel; +using DlssNr::AreaSample; + +static int gFails = 0; + +static void ExpectNear(const char* name, float got, float want, float eps = 1e-5f) +{ + if (std::fabs(got - want) > eps) + { + std::printf("FAIL %s: got %g want %g\n", name, got, want); + ++gFails; + } +} + +static void TestOverlap1D() +{ + // dest pixel 0 of 3->2: box [0, 1.5). Texel 0 weight 1, texel 1 weight 0.5 + ExpectNear("ovl0", DlssNr::Overlap1D(0.f, 1.5f, 0), 1.f); + ExpectNear("ovl1", DlssNr::Overlap1D(0.f, 1.5f, 1), 0.5f); + ExpectNear("ovl2", DlssNr::Overlap1D(0.f, 1.5f, 2), 0.f); +} + +static void Test2x2Mean() +{ + AreaSample src[4] = { + { 1, 0, 0, 1 }, + { 0, 1, 0, 1 }, + { 0, 0, 1, 1 }, + { 1, 1, 1, 1 }, + }; + const auto p = AreaDownsamplePixel(src, 2, 2, 1, 1, 0, 0); + ExpectNear("mean.r", p.r, 0.5f); + ExpectNear("mean.g", p.g, 0.5f); + ExpectNear("mean.b", p.b, 0.5f); +} + +static void TestCopy() +{ + AreaSample src[1] = { { 0.25f, 0.5f, 0.75f, 0.125f } }; + const auto p = AreaDownsamplePixel(src, 1, 1, 1, 1, 0, 0); + ExpectNear("copy.r", p.r, 0.25f); + ExpectNear("copy.a", p.a, 0.125f); +} + +static void Test3to2NotUintTruncated() +{ + // src 3, dst 2, x=1: float box [1.5, 3). uint box would be [1, 3) and overweight texel 1. + AreaSample src[3] = { { 1, 0, 0, 1 }, { 0, 1, 0, 1 }, { 0, 0, 1, 1 } }; + const auto p = AreaDownsamplePixel(src, 3, 1, 2, 1, 1, 0); + const float area = 1.5f; + ExpectNear("3to2.r", p.r, 0.f); + ExpectNear("3to2.g", p.g, 0.5f / area); + ExpectNear("3to2.b", p.b, 1.f / area); +} + +static void TestClamps() +{ + if (DlssNr::ClampTransfer(0) != DlssNr::Transfer::Classic) + { + std::printf("FAIL t0\n"); + ++gFails; + } + if (DlssNr::ClampTransfer(1) != DlssNr::Transfer::MatchedResidual) + { + std::printf("FAIL t1\n"); + ++gFails; + } + if (DlssNr::ClampTransfer(2) != DlssNr::Transfer::Classic) + { + std::printf("FAIL t2\n"); + ++gFails; + } + if (DlssNr::ClampHdrLift(0) != 0) + { + std::printf("FAIL h0\n"); + ++gFails; + } + if (DlssNr::ClampHdrLift(1) != 1) + { + std::printf("FAIL h1\n"); + ++gFails; + } + if (DlssNr::ClampHdrLift(9) != 0) + { + std::printf("FAIL h9\n"); + ++gFails; + } +} + +int main() +{ + TestOverlap1D(); + Test2x2Mean(); + TestCopy(); + Test3to2NotUintTruncated(); + TestClamps(); + if (gFails) + { + std::printf("%d FAIL\n", gFails); + return 1; + } + std::printf("TA PASS\n"); + return 0; +} diff --git a/OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp b/OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp index a0c4ed2b3..951f4e782 100644 --- a/OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp +++ b/OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp @@ -1159,7 +1159,15 @@ NVSDK_NGX_API NVSDK_NGX_Result NVSDK_NGX_D3D12_EvaluateFeature(ID3D12GraphicsCom // motion vectors too, and its handle can reach here because the branch above does not // return, so filtering on the parameter block alone would run the model twice a frame. if (result == NVSDK_NGX_Result_Success && feature != NVSDK_NGX_Feature_FrameGeneration) - DlssNr::EvaluateAfterUpscale(InCmdList, InParameters); + { + // Multi-pass already ran the model at render resolution inside + // IFeature_Dx12::Evaluate. Ask the built mode, not the configured + // one, so the rebuild frame stays consistent. Native passthrough + // has no currentFeature and is PostProcess only. + auto* current = State::Instance().currentFeature; + if (current == nullptr || current->NRBuiltMode() == DlssNr::Mode::PostProcess) + DlssNr::EvaluateAfterUpscale(InCmdList, InParameters); + } return result; } @@ -1186,7 +1194,11 @@ NVSDK_NGX_API NVSDK_NGX_Result NVSDK_NGX_D3D12_EvaluateFeature(ID3D12GraphicsCom // Same pass, for OptiScaler's own upscalers rather than native DLSS. if (optiResult == NVSDK_NGX_Result_Success && feature != NVSDK_NGX_Feature_FrameGeneration) - DlssNr::EvaluateAfterUpscale(InCmdList, InParameters); + { + auto* current = State::Instance().currentFeature; + if (current == nullptr || current->NRBuiltMode() == DlssNr::Mode::PostProcess) + DlssNr::EvaluateAfterUpscale(InCmdList, InParameters); + } return optiResult; } diff --git a/OptiScaler/shaders/dlssnr/DlssNr_Common.h b/OptiScaler/shaders/dlssnr/DlssNr_Common.h index 21a35be08..8ef5759fe 100644 --- a/OptiScaler/shaders/dlssnr/DlssNr_Common.h +++ b/OptiScaler/shaders/dlssnr/DlssNr_Common.h @@ -104,8 +104,17 @@ struct alignas(256) DlssNrConstants // Which side the edited frame is on. Swapping matters because the eye is not even-handed about // left and right, so a difference can look like an improvement purely from where it sits. uint32_t CompareSwap; + + uint32_t Transfer; // 0 Classic, 1 Matched residual + uint32_t HdrLift; // 0 H0, 1 H1; ignored unless Transfer == 1 }; +#include + +static_assert(offsetof(DlssNrConstants, Transfer) == 68, "HLSL gTransfer must sit at 68"); +static_assert(offsetof(DlssNrConstants, HdrLift) == 72, "HLSL gHdrLift must sit at 72"); +static_assert(sizeof(DlssNrConstants) == 256, "CBV size is 256-aligned"); + class DlssNr_Common { protected: diff --git a/OptiScaler/shaders/dlssnr/DlssNr_Dx12.cpp b/OptiScaler/shaders/dlssnr/DlssNr_Dx12.cpp index f3920b71b..28d3dae22 100644 --- a/OptiScaler/shaders/dlssnr/DlssNr_Dx12.cpp +++ b/OptiScaler/shaders/dlssnr/DlssNr_Dx12.cpp @@ -3,7 +3,7 @@ #include #include - +#include #include #include @@ -34,26 +34,46 @@ const char* NgxResultName(unsigned int r) { switch (r) { - case 0x1: return "Success"; - case 0xBAD00001: return "FAIL_FeatureNotSupported"; - case 0xBAD00002: return "FAIL_PlatformError"; - case 0xBAD00003: return "FAIL_FeatureAlreadyExists"; - case 0xBAD00004: return "FAIL_FeatureNotFound"; - case 0xBAD00005: return "FAIL_InvalidParameter"; - case 0xBAD00006: return "FAIL_ScratchBufferTooSmall"; - case 0xBAD00007: return "FAIL_NotInitialized"; - case 0xBAD00008: return "FAIL_UnsupportedInputFormat"; - case 0xBAD00009: return "FAIL_RWFlagMissing"; - case 0xBAD0000A: return "FAIL_MissingInput"; - case 0xBAD0000B: return "FAIL_UnableToInitializeFeature"; - case 0xBAD0000C: return "FAIL_OutOfDate"; - case 0xBAD0000D: return "FAIL_OutOfGPUMemory"; - case 0xBAD0000E: return "FAIL_UnsupportedFormat"; - case 0xBAD0000F: return "FAIL_UnableToWriteToAppDataPath"; - case 0xBAD00010: return "FAIL_UnsupportedParameter"; - case 0xBAD00011: return "FAIL_Denied"; - case 0xBAD00012: return "FAIL_NotImplemented"; - default: return "unknown"; + case 0x1: + return "Success"; + case 0xBAD00001: + return "FAIL_FeatureNotSupported"; + case 0xBAD00002: + return "FAIL_PlatformError"; + case 0xBAD00003: + return "FAIL_FeatureAlreadyExists"; + case 0xBAD00004: + return "FAIL_FeatureNotFound"; + case 0xBAD00005: + return "FAIL_InvalidParameter"; + case 0xBAD00006: + return "FAIL_ScratchBufferTooSmall"; + case 0xBAD00007: + return "FAIL_NotInitialized"; + case 0xBAD00008: + return "FAIL_UnsupportedInputFormat"; + case 0xBAD00009: + return "FAIL_RWFlagMissing"; + case 0xBAD0000A: + return "FAIL_MissingInput"; + case 0xBAD0000B: + return "FAIL_UnableToInitializeFeature"; + case 0xBAD0000C: + return "FAIL_OutOfDate"; + case 0xBAD0000D: + return "FAIL_OutOfGPUMemory"; + case 0xBAD0000E: + return "FAIL_UnsupportedFormat"; + case 0xBAD0000F: + return "FAIL_UnableToWriteToAppDataPath"; + case 0xBAD00010: + return "FAIL_UnsupportedParameter"; + case 0xBAD00011: + return "FAIL_Denied"; + case 0xBAD00012: + return "FAIL_NotImplemented"; + default: + return "unknown"; } } @@ -118,17 +138,15 @@ void ProbeProxyDispatch(ID3D12GraphicsCommandList* cmdList) release(handle); NVSDK_NGX_Handle* controlHandle = nullptr; - const auto control = - (unsigned int) create(cmdList, (NVSDK_NGX_Feature) 200, params, &controlHandle); + const auto control = (unsigned int) create(cmdList, (NVSDK_NGX_Feature) 200, params, &controlHandle); if (controlHandle != nullptr && release != nullptr) release(controlHandle); - LOG_INFO("DLSS-NR proxy probe: feature 18 -> 0x{:X} ({}), control feature 200 -> 0x{:X} ({})", - result, NgxResultName(result), control, NgxResultName(control)); + LOG_INFO("DLSS-NR proxy probe: feature 18 -> 0x{:X} ({}), control feature 200 -> 0x{:X} ({})", result, + NgxResultName(result), control, NgxResultName(control)); - const bool rejectedOutright = - result == 0xBAD00004 || result == 0xBAD00001 || result == 0xBAD00012; + const bool rejectedOutright = result == 0xBAD00004 || result == 0xBAD00001 || result == 0xBAD00012; if (result == control) LOG_INFO("DLSS-NR proxy probe: both answers identical, so this says nothing about feature 18 " @@ -147,18 +165,16 @@ void ProbeProxyDispatch(ID3D12GraphicsCommandList* cmdList) // Everything the model is reached through. The snippet refuses callers whose module path does not // contain "nvngx.dll", so the calls are made from a small library named for exactly that reason and // shipped beside OptiScaler; see nvngx.dll_dlssnr.dll. -using PFN_NrCreate = void*(__cdecl*) (const wchar_t*, const wchar_t*, ID3D12Device*, - ID3D12GraphicsCommandList*, void*, unsigned int, unsigned int, int, - float, int, float, float, float, int, int); -using PFN_NrEvaluate = int(__cdecl*) (ID3D12GraphicsCommandList*, void*, void*, ID3D12Resource*, - ID3D12Resource*, ID3D12Resource*, ID3D12Resource*, unsigned int, - unsigned int, unsigned int, unsigned int, int, int, float, int, - float, float, float, int, float, float); -using PFN_NrRelease = void(__cdecl*) (void*); -using PFN_NrSetExtras = void(__cdecl*) (void*, float, ID3D12Resource*, ID3D12Resource*, ID3D12Resource*, - unsigned int, unsigned int, unsigned int, unsigned int); -using PFN_NrSetFloatSlot = void(__cdecl*) (int); -using PFN_NrProbeFloat = void(__cdecl*) (void*, const char*, float, int); +using PFN_NrCreate = void*(__cdecl*) (const wchar_t*, const wchar_t*, ID3D12Device*, ID3D12GraphicsCommandList*, void*, + unsigned int, unsigned int, int, float, int, float, float, float, int, int); +using PFN_NrEvaluate = int(__cdecl*)(ID3D12GraphicsCommandList*, void*, void*, ID3D12Resource*, ID3D12Resource*, + ID3D12Resource*, ID3D12Resource*, unsigned int, unsigned int, unsigned int, + unsigned int, int, int, float, int, float, float, float, int, float, float); +using PFN_NrRelease = void(__cdecl*)(void*); +using PFN_NrSetExtras = void(__cdecl*)(void*, float, ID3D12Resource*, ID3D12Resource*, ID3D12Resource*, unsigned int, + unsigned int, unsigned int, unsigned int); +using PFN_NrSetFloatSlot = void(__cdecl*)(int); +using PFN_NrProbeFloat = void(__cdecl*)(void*, const char*, float, int); // One per back buffer, so an allocator is never reset while its frame is still in flight. @@ -192,6 +208,7 @@ struct NrState unsigned int workWidth = 0; unsigned int workHeight = 0; + DlssNr::WorkingSizeHold workHold; // Cloned unconditionally when running at present, and only for typeless formats otherwise. ID3D12Resource* depthClone = nullptr; @@ -329,8 +346,7 @@ bool EnsureForwarder() if (!found.has_value()) { - LOG_ERROR("nvngx.dll_dlssnr.dll not found beside OptiScaler ({}) or the game executable", - g_dllDir.string()); + LOG_ERROR("nvngx.dll_dlssnr.dll not found beside OptiScaler ({}) or the game executable", g_dllDir.string()); g_nr.reason = "nvngx.dll_dlssnr.dll is missing"; return false; } @@ -341,8 +357,7 @@ bool EnsureForwarder() if (g_nr.forwarder == nullptr) { - LOG_ERROR("nvngx.dll_dlssnr.dll found at {} but would not load, error {}", path.string(), - GetLastError()); + LOG_ERROR("nvngx.dll_dlssnr.dll found at {} but would not load, error {}", path.string(), GetLastError()); g_nr.reason = "nvngx.dll_dlssnr.dll would not load"; return false; } @@ -411,8 +426,7 @@ bool EnsureCapabilityParams(ID3D12Device* device) // local tone and skin structure never did anything. void DiscoverFloatSlot(NVSDK_NGX_Parameter* params) { - if (g_nr.floatSlotKnown || params == nullptr || g_nr.probeFloat == nullptr || - g_nr.setFloatSlot == nullptr) + if (g_nr.floatSlotKnown || params == nullptr || g_nr.probeFloat == nullptr || g_nr.setFloatSlot == nullptr) return; g_nr.floatSlotKnown = true; @@ -500,24 +514,25 @@ void TickNrRetired() void ReleaseSurfacesIfFormatChanged(DXGI_FORMAT needed) { + needed = codec::TypedFormat(needed); if (g_nr.output == nullptr || g_nr.output->GetDesc().Format == needed) return; - LOG_INFO("DLSS-NR rebuilding surfaces: format {} -> {} (inject point changed)", - (int) g_nr.output->GetDesc().Format, (int) needed); + LOG_INFO("DLSS-NR rebuilding surfaces: format {} -> {} (inject point changed)", (int) g_nr.output->GetDesc().Format, + (int) needed); ParkNrFeature(g_nr.feature); - for (ID3D12Resource** r : - { &g_nr.output, &g_nr.colorCopy, &g_nr.hdrCopy, &g_nr.colorSmall }) + for (ID3D12Resource** r : { &g_nr.output, &g_nr.colorCopy, &g_nr.hdrCopy, &g_nr.colorSmall }) ParkNrResource(*r); g_nr.reset = true; } -ID3D12Resource* CreateScratch(ID3D12Device* device, DXGI_FORMAT format, unsigned int width, - unsigned int height) +ID3D12Resource* CreateScratch(ID3D12Device* device, DXGI_FORMAT format, unsigned int width, unsigned int height) { + format = codec::TypedFormat(format); + D3D12_HEAP_PROPERTIES heap {}; heap.Type = D3D12_HEAP_TYPE_DEFAULT; @@ -534,8 +549,8 @@ ID3D12Resource* CreateScratch(ID3D12Device* device, DXGI_FORMAT format, unsigned desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; ID3D12Resource* res = nullptr; - device->CreateCommittedResource(&heap, D3D12_HEAP_FLAG_NONE, &desc, - D3D12_RESOURCE_STATE_UNORDERED_ACCESS, nullptr, IID_PPV_ARGS(&res)); + device->CreateCommittedResource(&heap, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, nullptr, + IID_PPV_ARGS(&res)); return res; } @@ -595,8 +610,8 @@ ID3D12Resource* CreateGuideClone(ID3D12Device* device, ID3D12Resource* source) heap.Type = D3D12_HEAP_TYPE_DEFAULT; ID3D12Resource* res = nullptr; - device->CreateCommittedResource(&heap, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_COPY_DEST, - nullptr, IID_PPV_ARGS(&res)); + device->CreateCommittedResource(&heap, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr, + IID_PPV_ARGS(&res)); return res; } @@ -604,8 +619,8 @@ ID3D12Resource* CreateGuideClone(ID3D12Device* device, ID3D12Resource* source) // of it when it is not. NGX requires its inputs in NON_PIXEL_SHADER_RESOURCE at evaluate time, which is // a documented contract rather than a guess about any one game's frame graph, so that is the state // transitioned away from and back to here. -ID3D12Resource* ReadableGuide(ID3D12Device* device, ID3D12GraphicsCommandList* cmdList, - ID3D12Resource* source, ID3D12Resource** clone) +ID3D12Resource* ReadableGuide(ID3D12Device* device, ID3D12GraphicsCommandList* cmdList, ID3D12Resource* source, + ID3D12Resource** clone) { if (source == nullptr || !IsTypeless(source->GetDesc().Format)) return source; @@ -617,17 +632,13 @@ ID3D12Resource* ReadableGuide(ID3D12Device* device, ID3D12GraphicsCommandList* c if (*clone == nullptr) return nullptr; - LOG_DEBUG("DLSS-NR cloned a typeless guide as format {}", - (int) TypedGuideFormat(source->GetDesc().Format)); + LOG_DEBUG("DLSS-NR cloned a typeless guide as format {}", (int) TypedGuideFormat(source->GetDesc().Format)); } - Barrier(cmdList, source, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, - D3D12_RESOURCE_STATE_COPY_SOURCE); + Barrier(cmdList, source, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_COPY_SOURCE); cmdList->CopyResource(*clone, source); - Barrier(cmdList, source, D3D12_RESOURCE_STATE_COPY_SOURCE, - D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); - Barrier(cmdList, *clone, D3D12_RESOURCE_STATE_COPY_DEST, - D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); + Barrier(cmdList, source, D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); + Barrier(cmdList, *clone, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); return *clone; } @@ -708,8 +719,7 @@ void SetExtras(const Config& cfg, ID3D12Resource* ui, ID3D12Resource* backbuffer // Global tone is written at the model's own default: the control that exposed it changed nothing // that could be seen, and the block persists, so a value still has to be put there. - g_nr.setExtras(g_nr.capabilityParams, 1.0f, ui, ui, backbuffer, - uiWidth, uiHeight, bbWidth, bbHeight); + g_nr.setExtras(g_nr.capabilityParams, 1.0f, ui, ui, backbuffer, uiWidth, uiHeight, bbWidth, bbHeight); } bool TuningMatchesFeature(const Config& cfg) @@ -759,8 +769,7 @@ void ReportSkipOnce(const char* reason) // of OptiScaler sees. // --------------------------------------------------------------------------------------------- -DlssNr_Dx12::DlssNr_Dx12(std::string InName, ID3D12Device* InDevice) - : Shader_Dx12(InName, InDevice) +DlssNr_Dx12::DlssNr_Dx12(std::string InName, ID3D12Device* InDevice) : Shader_Dx12(InName, InDevice) { if (InDevice == nullptr) { @@ -817,10 +826,9 @@ DlssNr_Dx12::DlssNr_Dx12(std::string InName, ID3D12Device* InDevice) } bool DlssNr_Dx12::DispatchPass(ID3D12GraphicsCommandList* InCmdList, const DlssNrConstants& InConstants, - ID3D12Resource* InSource, ID3D12Resource* InModel, - ID3D12Resource* InOriginal, ID3D12Resource* InMotion, - ID3D12Resource* InPrevEdit, ID3D12Resource* OutTarget, - ID3D12Resource* OutKeep) + ID3D12Resource* InSource, ID3D12Resource* InModel, ID3D12Resource* InOriginal, + ID3D12Resource* InProxy, ID3D12Resource* InPrevEdit, ID3D12Resource* OutTarget, + ID3D12Resource* OutKeep) { if (!_init || InCmdList == nullptr || _device == nullptr || InSource == nullptr || OutTarget == nullptr) return false; @@ -837,12 +845,15 @@ bool DlssNr_Dx12::DispatchPass(ID3D12GraphicsCommandList* InCmdList, const DlssN InSource, InModel != nullptr ? InModel : InSource, InOriginal != nullptr ? InOriginal : InSource, - InMotion != nullptr ? InMotion : InSource, + InProxy != nullptr ? InProxy : InSource, InPrevEdit != nullptr ? InPrevEdit : InSource, }; for (uint32_t i = 0; i < kSrvCount; ++i) - CreateShaderResourceView(_device, srvs[i], currentHeap.GetSrvCPU(i)); + { + CreateShaderResourceView(_device, srvs[i], currentHeap.GetSrvCPU(i), + codec::TypedFormat(srvs[i]->GetDesc().Format)); + } ID3D12Resource* const uavs[kUavCount] = { OutTarget, @@ -885,15 +896,15 @@ DlssNr_Dx12::~DlssNr_Dx12() } } -void DlssNr_Dx12::Dispatch(ID3D12GraphicsCommandList* cmdList, ID3D12Resource* colour, - ID3D12Resource* depth, ID3D12Resource* motion, ID3D12Resource* output, - const DlssNrFrameInfo& frame, ID3D12CommandQueue* timingQueue) +void DlssNr_Dx12::Dispatch(ID3D12GraphicsCommandList* cmdList, ID3D12Resource* colour, ID3D12Resource* depth, + ID3D12Resource* motion, ID3D12Resource* output, const DlssNrFrameInfo& frame, + ID3D12CommandQueue* timingQueue) { std::lock_guard nrLock(g_nrMutex); const Config& cfg = *Config::Instance(); - if (g_nr.failed || cmdList == nullptr || colour == nullptr || depth == nullptr || - motion == nullptr || output == nullptr) + if (g_nr.failed || cmdList == nullptr || colour == nullptr || depth == nullptr || motion == nullptr || + output == nullptr) { ReportSkipOnce(g_nr.failed ? "it already failed this session" : "a resource was missing"); return; @@ -949,8 +960,8 @@ void DlssNr_Dx12::Dispatch(ID3D12GraphicsCommandList* cmdList, ID3D12Resource* c { reportedGuides = true; LOG_INFO("DLSS-NR guides: depth {}, motion vector scale {} x {}, guides {}x{} for a {}x{} frame", - g_nr.guideDepthInverted ? "inverted" : "not inverted", g_nr.guideMvScaleX, - g_nr.guideMvScaleY, guideWidth, guideHeight, width, height); + g_nr.guideDepthInverted ? "inverted" : "not inverted", g_nr.guideMvScaleX, g_nr.guideMvScaleY, + guideWidth, guideHeight, width, height); } if (cfg.DlssNrProxyProbe.value_or_default()) @@ -965,17 +976,40 @@ void DlssNr_Dx12::Dispatch(ID3D12GraphicsCommandList* cmdList, ID3D12Resource* c } // What the model works at. The frame and its edit stay full resolution; only the model's input and - // answer shrink, and the resolve enlarges the answer while compositing. - float workScale = cfg.DlssNrWorkingScale.value_or_default(); - workScale = workScale < 0.25f ? 0.25f : (workScale > 1.0f ? 1.0f : workScale); - const auto workWidth = (unsigned int) (width * workScale + 0.5f); - const auto workHeight = (unsigned int) (height * workScale + 0.5f); + // answer shrink, and the resolve enlarges the answer while compositing. WorkingScale is a fraction + // of display; WorkAtNative matches the guide raster. A moving guide (DRS) is held at the last + // latched size until it settles, so the feature is not rebuilt every frame. + ++g_frames; + constexpr unsigned long long kSettleFrames = 30; + unsigned int displayWidth = width; + unsigned int displayHeight = height; + + if (auto* feature = State::Instance().currentFeature) + { + if (feature->DisplayWidth() != 0 && feature->DisplayHeight() != 0) + { + displayWidth = feature->DisplayWidth(); + displayHeight = feature->DisplayHeight(); + } + } + + const auto requested = + DlssNr::ResolveWorkingSize(cfg.DlssNrWorkingScale.value_or_default(), cfg.DlssNrWorkAtNative.value_or_default(), + width, height, displayWidth, displayHeight, guideWidth, guideHeight); + const bool colorChanged = g_nr.feature != nullptr && (g_nr.width != width || g_nr.height != height); + bool commitWork = false; + (void) commitWork; + const auto work = + DlssNr::TickWorkingSizeHold(g_nr.workHold, requested, { g_nr.workWidth, g_nr.workHeight }, + g_nr.feature != nullptr, colorChanged, g_frames, kSettleFrames, commitWork); + const auto workWidth = work.width; + const auto workHeight = work.height; const bool reduced = workWidth != width || workHeight != height; ReleaseSurfacesIfFormatChanged(desc.Format); - const bool resolutionChanged = g_nr.width != width || g_nr.height != height || - g_nr.workWidth != workWidth || g_nr.workHeight != workHeight; + const bool resolutionChanged = + g_nr.width != width || g_nr.height != height || g_nr.workWidth != workWidth || g_nr.workHeight != workHeight; // The model reads its tuning once, while the feature is built, so a changed setting only takes // effect when the feature is rebuilt. TuningMatchesFeature was written to notice that and then @@ -1012,8 +1046,7 @@ void DlssNr_Dx12::Dispatch(ID3D12GraphicsCommandList* cmdList, ID3D12Resource* c if (reduced && g_nr.colorSmall == nullptr) g_nr.colorSmall = CreateScratch(device, desc.Format, workWidth, workHeight); - if (g_nr.feature == nullptr && g_nr.output != nullptr && g_nr.colorCopy != nullptr && - g_nr.hdrCopy != nullptr) + if (g_nr.feature == nullptr && g_nr.output != nullptr && g_nr.colorCopy != nullptr && g_nr.hdrCopy != nullptr) { auto snippet = Util::FindFilePath(g_dllDir, "nvngx_dlssnr.dll"); @@ -1030,17 +1063,15 @@ void DlssNr_Dx12::Dispatch(ID3D12GraphicsCommandList* cmdList, ID3D12Resource* c } SetExtras(cfg, nullptr, nullptr, 0, 0, 0, 0); - g_nr.feature = - g_nr.create(snippet->wstring().c_str(), State::Instance().NVNGX_ApplicationDataPath.c_str(), - device, cmdList, g_nr.capabilityParams, workWidth, workHeight, - (int) cfg.DlssNrPreset.value_or_default(), - cfg.DlssNrIntensity.value_or_default(), (int) cfg.DlssNrStyle.value_or_default(), - cfg.DlssNrLocalStructure.value_or_default(), cfg.DlssNrLocalTone.value_or_default(), - cfg.DlssNrSkinStructure.value_or_default(), - cfg.DlssNrAutoMask.value_or_default() ? 1 : 0, - // UI correction at the model's own default: with no UI layer fed to it there - // is nothing for it to correct. - 1); + g_nr.feature = g_nr.create( + snippet->wstring().c_str(), State::Instance().NVNGX_ApplicationDataPath.c_str(), device, cmdList, + g_nr.capabilityParams, workWidth, workHeight, (int) cfg.DlssNrPreset.value_or_default(), + cfg.DlssNrIntensity.value_or_default(), (int) cfg.DlssNrStyle.value_or_default(), + cfg.DlssNrLocalStructure.value_or_default(), cfg.DlssNrLocalTone.value_or_default(), + cfg.DlssNrSkinStructure.value_or_default(), cfg.DlssNrAutoMask.value_or_default() ? 1 : 0, + // UI correction at the model's own default: with no UI layer fed to it there + // is nothing for it to correct. + 1); if (g_nr.feature == nullptr) { @@ -1061,8 +1092,8 @@ void DlssNr_Dx12::Dispatch(ID3D12GraphicsCommandList* cmdList, ID3D12Resource* c g_nr.height = height; g_nr.reset = true; RecordBuiltTuning(cfg); - LOG_INFO("DLSS-NR running at {}x{}, guides {}x{} (preset {}, intensity {}, style {})", width, - height, guideWidth, guideHeight, g_nr.builtPreset, g_nr.builtIntensity, g_nr.builtStyle); + LOG_INFO("DLSS-NR running at {}x{}, guides {}x{} (preset {}, intensity {}, style {})", width, height, + guideWidth, guideHeight, g_nr.builtPreset, g_nr.builtIntensity, g_nr.builtStyle); // Creating and evaluating a feature in the same command list is the dice-roll that hung the // GPU (every crash died on a creation frame). The creation goes through the game's own submit @@ -1094,8 +1125,7 @@ void DlssNr_Dx12::Dispatch(ID3D12GraphicsCommandList* cmdList, ID3D12Resource* c { reportedHdr = true; LOG_INFO("DLSS-NR: the game's DLSS buffer is {} so the colour transform is {}", - isHdrBuffer ? "linear HDR" : "already tone-mapped", - isHdrBuffer ? "on" : "off"); + isHdrBuffer ? "linear HDR" : "already tone-mapped", isHdrBuffer ? "on" : "off"); } const bool haveCodec = IsInit(); @@ -1113,7 +1143,6 @@ void DlssNr_Dx12::Dispatch(ID3D12GraphicsCommandList* cmdList, ID3D12Resource* c // finished, sRGB-encoded frames. The white point is what maps one to the other, and it is a property // of the game's exposure rather than a number worth asking anyone to guess: measured means of 0.065, // 1.8 and 185 have all been seen in this one game. - ++g_frames; TickNrRetired(); CheckCaptureTrigger(); @@ -1156,13 +1185,10 @@ void DlssNr_Dx12::Dispatch(ID3D12GraphicsCommandList* cmdList, ID3D12Resource* c encodeParams.Width = width; encodeParams.Height = height; - Barrier(cmdList, target, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, - D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); - DispatchPass(cmdList, encodeParams, target, nullptr, nullptr, nullptr, nullptr, - g_nr.colorCopy, g_nr.hdrCopy); + Barrier(cmdList, target, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); + DispatchPass(cmdList, encodeParams, target, nullptr, nullptr, nullptr, nullptr, g_nr.colorCopy, g_nr.hdrCopy); - Barrier(cmdList, target, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, - D3D12_RESOURCE_STATE_UNORDERED_ACCESS); + Barrier(cmdList, target, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS); // The transitions double as the wait for the encode's writes. Barrier(cmdList, g_nr.colorCopy, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); @@ -1179,8 +1205,8 @@ void DlssNr_Dx12::Dispatch(ID3D12GraphicsCommandList* cmdList, ID3D12Resource* c down.Mode = DlssNrMode_Downsample; down.Width = workWidth; down.Height = workHeight; - DispatchPass(cmdList, down, modelInput, nullptr, nullptr, nullptr, nullptr, - g_nr.colorSmall, nullptr); + down.Transfer = (uint32_t) DlssNr::ConfiguredTransfer(); + DispatchPass(cmdList, down, modelInput, nullptr, nullptr, nullptr, nullptr, g_nr.colorSmall, nullptr); Barrier(cmdList, g_nr.colorSmall, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); modelInput = g_nr.colorSmall; @@ -1211,9 +1237,8 @@ void DlssNr_Dx12::Dispatch(ID3D12GraphicsCommandList* cmdList, ID3D12Resource* c if (cfg.DlssNrUseProxy.value_or_default()) { const unsigned int proxyResult = DlssNr::Proxy::Run( - cmdList, device, modelInput, depthIn, motionIn, g_nr.output, workWidth, workHeight, - guideWidth, guideHeight, g_nr.guideDepthInverted, g_nr.reset, - g_nr.guideMvScaleX * mvToWork, g_nr.guideMvScaleY * mvToWork); + cmdList, device, modelInput, depthIn, motionIn, g_nr.output, workWidth, workHeight, guideWidth, guideHeight, + g_nr.guideDepthInverted, g_nr.reset, g_nr.guideMvScaleX * mvToWork, g_nr.guideMvScaleY * mvToWork); g_nr.reset = false; @@ -1221,8 +1246,8 @@ void DlssNr_Dx12::Dispatch(ID3D12GraphicsCommandList* cmdList, ID3D12Resource* c { g_nr.failed = true; g_nr.reason = "the proxy path could not run the model"; - LOG_ERROR("DLSS-NR (proxy): evaluate returned 0x{:X} ({}), disabling for this session", - proxyResult, NgxResultName(proxyResult)); + LOG_ERROR("DLSS-NR (proxy): evaluate returned 0x{:X} ({}), disabling for this session", proxyResult, + NgxResultName(proxyResult)); } device->Release(); @@ -1230,13 +1255,12 @@ void DlssNr_Dx12::Dispatch(ID3D12GraphicsCommandList* cmdList, ID3D12Resource* c } const int result = g_nr.evaluate( - cmdList, g_nr.feature, g_nr.capabilityParams, modelInput, depthIn, motionIn, g_nr.output, - workWidth, workHeight, guideWidth, guideHeight, g_nr.guideDepthInverted ? 1 : 0, - g_nr.reset ? 1 : 0, cfg.DlssNrIntensity.value_or_default(), - (int) cfg.DlssNrStyle.value_or_default(), cfg.DlssNrLocalStructure.value_or_default(), - cfg.DlssNrLocalTone.value_or_default(), cfg.DlssNrSkinStructure.value_or_default(), - cfg.DlssNrAutoMask.value_or_default() ? 1 : 0, g_nr.guideMvScaleX * mvToWork, - g_nr.guideMvScaleY * mvToWork); + cmdList, g_nr.feature, g_nr.capabilityParams, modelInput, depthIn, motionIn, g_nr.output, workWidth, workHeight, + guideWidth, guideHeight, g_nr.guideDepthInverted ? 1 : 0, g_nr.reset ? 1 : 0, + cfg.DlssNrIntensity.value_or_default(), (int) cfg.DlssNrStyle.value_or_default(), + cfg.DlssNrLocalStructure.value_or_default(), cfg.DlssNrLocalTone.value_or_default(), + cfg.DlssNrSkinStructure.value_or_default(), cfg.DlssNrAutoMask.value_or_default() ? 1 : 0, + g_nr.guideMvScaleX * mvToWork, g_nr.guideMvScaleY * mvToWork); g_nr.reset = false; @@ -1253,8 +1277,7 @@ void DlssNr_Dx12::Dispatch(ID3D12GraphicsCommandList* cmdList, ID3D12Resource* c { float value = 0.0f; const NVSDK_NGX_Result r = g_nr.capabilityParams->Get(name, &value); - LOG_INFO("DLSS-NR readback {} -> {} (we wrote {}, result 0x{:X})", name, value, wrote, - (uint32_t) r); + LOG_INFO("DLSS-NR readback {} -> {} (we wrote {}, result 0x{:X})", name, value, wrote, (uint32_t) r); }; const Config& rcfg = *Config::Instance(); @@ -1270,15 +1293,14 @@ void DlssNr_Dx12::Dispatch(ID3D12GraphicsCommandList* cmdList, ID3D12Resource* c // The preset is the last control whose arrival has never been checked, and three of them look // identical in play. Either it is not landing or the presets really are alike. unsigned int preset = 0; - const NVSDK_NGX_Result presetResult = - g_nr.capabilityParams->Get("DLSSNR.Hint.Render.Preset", &preset); + const NVSDK_NGX_Result presetResult = g_nr.capabilityParams->Get("DLSSNR.Hint.Render.Preset", &preset); LOG_DEBUG("DLSS-NR readback DLSSNR.Hint.Render.Preset -> {} (result 0x{:X}, we wrote {})", preset, - (uint32_t) presetResult, cfg.DlssNrPreset.value_or_default()); + (uint32_t) presetResult, cfg.DlssNrPreset.value_or_default()); LOG_DEBUG("DLSS-NR wrote intensity {}, local structure {}, local tone {}, skin {}, style {}", - cfg.DlssNrIntensity.value_or_default(), cfg.DlssNrLocalStructure.value_or_default(), - cfg.DlssNrLocalTone.value_or_default(), cfg.DlssNrSkinStructure.value_or_default(), - cfg.DlssNrStyle.value_or_default()); + cfg.DlssNrIntensity.value_or_default(), cfg.DlssNrLocalStructure.value_or_default(), + cfg.DlssNrLocalTone.value_or_default(), cfg.DlssNrSkinStructure.value_or_default(), + cfg.DlssNrStyle.value_or_default()); } if (result == NVSDK_NGX_Result_Success) @@ -1300,11 +1322,13 @@ void DlssNr_Dx12::Dispatch(ID3D12GraphicsCommandList* cmdList, ID3D12Resource* c resolveParams.CompareSplit = cfg.DlssNrCompareSplit.value_or_default(); resolveParams.CompareZoom = std::max(1.0f, cfg.DlssNrCompareZoom.value_or_default()); resolveParams.CompareSwap = cfg.DlssNrCompareSwap.value_or_default() ? 1u : 0u; + resolveParams.Transfer = (uint32_t) DlssNr::ConfiguredTransfer(); + resolveParams.HdrLift = DlssNr::ConfiguredHdrLift(); Barrier(cmdList, g_nr.output, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); - DispatchPass(cmdList, resolveParams, modelInput, g_nr.output, g_nr.hdrCopy, motionIn, - nullptr, target, nullptr); + DispatchPass(cmdList, resolveParams, modelInput, g_nr.output, g_nr.hdrCopy, g_nr.colorCopy, nullptr, target, + nullptr); Barrier(cmdList, g_nr.output, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS); @@ -1314,8 +1338,7 @@ void DlssNr_Dx12::Dispatch(ID3D12GraphicsCommandList* cmdList, ID3D12Resource* c // own. if (g_capture.isActive()) { - g_capture.record(cmdList, device, g_nr.colorCopy, - D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, target, + g_capture.record(cmdList, device, g_nr.colorCopy, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, target, D3D12_RESOURCE_STATE_UNORDERED_ACCESS); if (g_capture.readyToWrite() && g_captureWriteAtFrame == 0) @@ -1342,8 +1365,8 @@ void DlssNr_Dx12::Dispatch(ID3D12GraphicsCommandList* cmdList, ID3D12Resource* c // invoked on serves. The bridges have to say, because they run on a queue of their own that // State never learns about -- a Vulkan game creates no D3D12 swapchain, so nothing ever sets // currentCommandQueue and the cost went unreported. - auto* queue = timingQueue != nullptr ? timingQueue - : (ID3D12CommandQueue*) State::Instance().currentCommandQueue; + auto* queue = + timingQueue != nullptr ? timingQueue : (ID3D12CommandQueue*) State::Instance().currentCommandQueue; if (queue != nullptr) { @@ -1379,14 +1402,52 @@ void RetryAfterFailure() g_nr.failed = false; g_nr.reason = ""; g_nr.reset = true; +} + +// Resources in. The pass is the object, so the caller holds it. Built once, on +// the device the frame is on. Reused by the seam and by the multi-pass stage. +void Run(ID3D12GraphicsCommandList* cmdList, ID3D12Resource* colour, ID3D12Resource* depth, ID3D12Resource* motion, + ID3D12Resource* output, const DlssNrFrameInfo& frame, ID3D12CommandQueue* timingQueue) +{ + if (!Config::Instance()->DlssNrEnabled.value_or_default()) + { + ReportSkipOnce("it is switched off"); + return; + } + + if (cmdList == nullptr || colour == nullptr || depth == nullptr || motion == nullptr || output == nullptr) + { + ReportSkipOnce("a resource was missing"); + return; + } + + ID3D12Device* device = nullptr; + + if (FAILED(output->GetDevice(IID_PPV_ARGS(&device))) || device == nullptr) + { + ReportSkipOnce("the output texture belongs to no D3D12 device"); + return; + } + if (g_compose == nullptr) + g_compose = std::make_unique("Neural Rendering", device); + + device->Release(); + + if (g_compose == nullptr) + { + ReportSkipOnce("the pass could not be created"); + return; + } + + g_compose->Dispatch(cmdList, colour, depth, motion, output, frame, timingQueue); } // Reads the game's parameter block and runs the pass on what it finds. // // This is the call site's job, not the pass's. A caller that has the resources in hand -- a // reprojection stage, a frame generation path, anything that is not the upscaler seam -- calls -// RunPass directly and never touches an NGX parameter block. +// Run directly and never touches an NGX parameter block. void EvaluateAfterUpscale(ID3D12GraphicsCommandList* cmdList, NVSDK_NGX_Parameter* params, ID3D12CommandQueue* timingQueue) { @@ -1410,9 +1471,9 @@ void EvaluateAfterUpscale(ID3D12GraphicsCommandList* cmdList, NVSDK_NGX_Paramete // carry none of it -- so it stays quiet and tries again next frame. if (target == nullptr || depth == nullptr || motion == nullptr) { - ReportSkipOnce(target == nullptr ? "the parameters carried no output texture" - : depth == nullptr ? "the parameters carried no depth" - : "the parameters carried no motion vectors"); + ReportSkipOnce(target == nullptr ? "the parameters carried no output texture" + : depth == nullptr ? "the parameters carried no depth" + : "the parameters carried no motion vectors"); return; } @@ -1429,30 +1490,7 @@ void EvaluateAfterUpscale(ID3D12GraphicsCommandList* cmdList, NVSDK_NGX_Paramete if (params->Get(NVSDK_NGX_Parameter_MV_Scale_Y, &frame.MvScaleY) != NVSDK_NGX_Result_Success) frame.MvScaleY = 1.0f; - // The upscaler's inputs are at render resolution while colour and output are at display - // resolution; the model takes that as a subrect per resource, which the pass reads from the - // resources themselves. - ID3D12Device* device = nullptr; - - if (FAILED(target->GetDevice(IID_PPV_ARGS(&device))) || device == nullptr) - { - ReportSkipOnce("the output texture belongs to no D3D12 device"); - return; - } - - // The pass is the object, so the caller holds it. Built once, on the device the frame is on. - if (g_compose == nullptr) - g_compose = std::make_unique("Neural Rendering", device); - - device->Release(); - - if (g_compose == nullptr) - { - ReportSkipOnce("the pass could not be created"); - return; - } - - g_compose->Dispatch(cmdList, target, depth, motion, target, frame, timingQueue); + Run(cmdList, target, depth, motion, target, frame, timingQueue); } // The pass. Resources in, nothing read from anywhere the caller cannot see. diff --git a/OptiScaler/shaders/dlssnr/DlssNr_Dx12.h b/OptiScaler/shaders/dlssnr/DlssNr_Dx12.h index e972e3cd3..dfdef8bd1 100644 --- a/OptiScaler/shaders/dlssnr/DlssNr_Dx12.h +++ b/OptiScaler/shaders/dlssnr/DlssNr_Dx12.h @@ -74,7 +74,7 @@ class DlssNr_Dx12 : public Shader_Dx12, public DlssNr_Common // their place so every descriptor in the table is valid. // One compute pass. The public entry below drives three of these plus the model. bool DispatchPass(ID3D12GraphicsCommandList* InCmdList, const DlssNrConstants& InConstants, - ID3D12Resource* InSource, ID3D12Resource* InModel, ID3D12Resource* InOriginal, - ID3D12Resource* InMotion, ID3D12Resource* InPrevEdit, ID3D12Resource* OutTarget, - ID3D12Resource* OutKeep); + ID3D12Resource* InSource, ID3D12Resource* InModel, ID3D12Resource* InOriginal, + ID3D12Resource* InProxy, ID3D12Resource* InPrevEdit, ID3D12Resource* OutTarget, + ID3D12Resource* OutKeep); }; diff --git a/OptiScaler/shaders/dlssnr/precompile/DlssNr_Shader.h b/OptiScaler/shaders/dlssnr/precompile/DlssNr_Shader.h index c10c88057..01e47bf71 100644 --- a/OptiScaler/shaders/dlssnr/precompile/DlssNr_Shader.h +++ b/OptiScaler/shaders/dlssnr/precompile/DlssNr_Shader.h @@ -1,748 +1,620 @@ #pragma once inline static const unsigned char DlssNr_cso[] = { - 0x44, 0x58, 0x42, 0x43, 0x3e, 0xea, 0x32, 0x22, 0x4e, 0xe9, 0x18, 0xc6, - 0x09, 0xa3, 0x7e, 0xd9, 0x52, 0x8b, 0x5d, 0xfb, 0x01, 0x00, 0x00, 0x00, - 0xd4, 0x22, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, - 0x98, 0x05, 0x00, 0x00, 0xa8, 0x05, 0x00, 0x00, 0xb8, 0x05, 0x00, 0x00, - 0x38, 0x22, 0x00, 0x00, 0x52, 0x44, 0x45, 0x46, 0x5c, 0x05, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x54, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3c, 0x00, 0x00, 0x00, 0x00, 0x05, 0x53, 0x43, 0x00, 0x81, 0x00, 0x00, - 0x31, 0x05, 0x00, 0x00, 0x52, 0x44, 0x31, 0x31, 0x3c, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, - 0x24, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1c, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x24, 0x01, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x2c, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, - 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, - 0x33, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x3d, 0x01, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x45, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, - 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, - 0x4b, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x67, 0x4c, 0x69, 0x6e, - 0x65, 0x61, 0x72, 0x00, 0x67, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x00, - 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x00, 0x67, 0x4f, 0x72, 0x69, 0x67, - 0x69, 0x6e, 0x61, 0x6c, 0x00, 0x67, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, - 0x00, 0x67, 0x4b, 0x65, 0x65, 0x70, 0x00, 0x50, 0x61, 0x72, 0x61, 0x6d, - 0x73, 0x00, 0xab, 0xab, 0x4b, 0x01, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x6c, 0x01, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x14, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x04, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x44, 0x04, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x58, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, - 0x7c, 0x04, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x20, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x00, 0x83, 0x04, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x04, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x04, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x58, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, - 0x9d, 0x04, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x58, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x00, 0xad, 0x04, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x04, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x04, 0x00, 0x00, - 0x1c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x58, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, - 0xc2, 0x04, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x20, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x00, 0xcf, 0x04, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x04, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xd9, 0x04, 0x00, 0x00, - 0x28, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x58, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, - 0xe3, 0x04, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x20, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x00, 0xef, 0x04, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x04, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x04, 0x00, 0x00, - 0x34, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x20, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, - 0x09, 0x05, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x58, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x00, 0x17, 0x05, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x58, 0x04, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x24, 0x05, 0x00, 0x00, - 0x40, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x20, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, - 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x00, 0x64, 0x77, 0x6f, 0x72, 0x64, 0x00, - 0x00, 0x00, 0x13, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x04, 0x00, 0x00, - 0x67, 0x57, 0x68, 0x69, 0x74, 0x65, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x00, - 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x00, 0xab, 0xab, 0x00, 0x00, 0x03, 0x00, - 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x50, 0x04, 0x00, 0x00, 0x67, 0x57, 0x69, 0x64, - 0x74, 0x68, 0x00, 0x67, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x00, 0x67, - 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, - 0x6e, 0x67, 0x74, 0x68, 0x00, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x75, 0x72, - 0x53, 0x74, 0x72, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x00, 0x67, 0x44, 0x65, - 0x62, 0x75, 0x67, 0x56, 0x69, 0x65, 0x77, 0x00, 0x67, 0x4d, 0x61, 0x78, - 0x52, 0x61, 0x74, 0x69, 0x6f, 0x00, 0x67, 0x50, 0x61, 0x73, 0x73, 0x74, - 0x68, 0x72, 0x6f, 0x75, 0x67, 0x68, 0x00, 0x67, 0x4d, 0x76, 0x53, 0x63, - 0x61, 0x6c, 0x65, 0x58, 0x00, 0x67, 0x4d, 0x76, 0x53, 0x63, 0x61, 0x6c, - 0x65, 0x59, 0x00, 0x67, 0x47, 0x75, 0x69, 0x64, 0x65, 0x57, 0x69, 0x64, - 0x74, 0x68, 0x00, 0x67, 0x47, 0x75, 0x69, 0x64, 0x65, 0x48, 0x65, 0x69, - 0x67, 0x68, 0x74, 0x00, 0x67, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, - 0x4d, 0x6f, 0x64, 0x65, 0x00, 0x67, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, - 0x65, 0x53, 0x70, 0x6c, 0x69, 0x74, 0x00, 0x67, 0x43, 0x6f, 0x6d, 0x70, - 0x61, 0x72, 0x65, 0x5a, 0x6f, 0x6f, 0x6d, 0x00, 0x67, 0x43, 0x6f, 0x6d, - 0x70, 0x61, 0x72, 0x65, 0x53, 0x77, 0x61, 0x70, 0x00, 0x4d, 0x69, 0x63, - 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, - 0x4c, 0x53, 0x4c, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, - 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2e, 0x31, - 0x00, 0xab, 0xab, 0xab, 0x49, 0x53, 0x47, 0x4e, 0x08, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x4f, 0x53, 0x47, 0x4e, - 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x53, 0x48, 0x45, 0x58, 0x78, 0x1c, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, - 0x1e, 0x07, 0x00, 0x00, 0x6a, 0x08, 0x00, 0x01, 0x59, 0x00, 0x00, 0x04, - 0x46, 0x8e, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x5a, 0x00, 0x00, 0x03, 0x00, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x58, 0x18, 0x00, 0x04, 0x00, 0x70, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x55, 0x55, 0x00, 0x00, 0x58, 0x18, 0x00, 0x04, 0x00, 0x70, 0x10, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x55, 0x55, 0x00, 0x00, 0x58, 0x18, 0x00, 0x04, - 0x00, 0x70, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x55, 0x55, 0x00, 0x00, - 0x9c, 0x18, 0x00, 0x04, 0x00, 0xe0, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x55, 0x55, 0x00, 0x00, 0x9c, 0x18, 0x00, 0x04, 0x00, 0xe0, 0x11, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x55, 0x55, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x02, - 0x32, 0x00, 0x02, 0x00, 0x68, 0x00, 0x00, 0x02, 0x0a, 0x00, 0x00, 0x00, - 0x9b, 0x00, 0x00, 0x04, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x07, 0x32, 0x00, 0x10, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, 0x02, 0x00, 0xe6, 0x8a, 0x20, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x07, - 0x12, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x10, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1f, 0x00, 0x04, 0x03, 0x0a, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x00, 0x01, 0x15, 0x00, 0x00, 0x01, 0x56, 0x00, 0x00, 0x04, - 0x32, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, 0x02, 0x00, - 0x00, 0x00, 0x00, 0x0a, 0x32, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x46, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x06, 0xc2, 0x00, 0x10, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xa6, 0x8e, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x07, 0x62, 0x00, 0x10, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xa6, 0x0b, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x08, - 0x82, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x80, 0x20, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x04, 0x03, 0x3a, 0x00, 0x10, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x8d, 0xc2, 0x00, 0x00, 0x80, - 0x43, 0x55, 0x15, 0x00, 0xf2, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x96, 0x05, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x7e, 0x10, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x06, - 0xf2, 0xe0, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x05, 0x02, 0x00, - 0x46, 0x0e, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x01, - 0x15, 0x00, 0x00, 0x01, 0x1f, 0x00, 0x00, 0x04, 0x0a, 0x80, 0x20, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x04, - 0x32, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x46, 0x00, 0x02, 0x00, - 0x36, 0x00, 0x00, 0x08, 0xc2, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x89, - 0xc2, 0x00, 0x00, 0x80, 0x43, 0x55, 0x15, 0x00, 0xf2, 0x00, 0x10, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x46, 0x0e, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x46, 0x7e, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x0a, - 0x72, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xa4, 0x00, 0x00, 0x06, 0xf2, 0xe0, 0x11, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x46, 0x05, 0x02, 0x00, 0x46, 0x0e, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x1f, 0x00, 0x04, 0x04, 0x0a, 0x80, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x06, 0xf2, 0xe0, 0x11, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x46, 0x05, 0x02, 0x00, 0x46, 0x0e, 0x10, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x01, 0x15, 0x00, 0x00, 0x01, - 0x34, 0x00, 0x00, 0x08, 0x82, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1a, 0x80, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x40, 0x00, 0x00, 0x17, 0xb7, 0xd1, 0x38, 0x0e, 0x00, 0x00, 0x07, - 0x72, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, - 0x01, 0x00, 0x00, 0x00, 0xf6, 0x0f, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x0a, 0x82, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, - 0xd0, 0xb3, 0x59, 0x3e, 0x59, 0x17, 0x37, 0x3f, 0x98, 0xdd, 0x93, 0x3d, - 0x00, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x07, 0x82, 0x00, 0x10, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x40, 0x3f, - 0x3a, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, - 0x12, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x10, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x40, 0xbf, - 0x38, 0x00, 0x00, 0x07, 0x12, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x0a, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, - 0x3b, 0xaa, 0xb8, 0xc0, 0x19, 0x00, 0x00, 0x05, 0x12, 0x00, 0x10, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x08, 0x12, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x0a, 0x00, 0x10, 0x80, 0x41, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x32, 0x00, 0x00, 0x09, - 0x12, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3e, - 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x40, 0x3f, 0x0e, 0x00, 0x00, 0x07, - 0x82, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x00, 0x07, 0x72, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, - 0xf6, 0x0f, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x37, 0x20, 0x00, 0x09, 0x72, 0x00, 0x10, 0x00, - 0x02, 0x00, 0x00, 0x00, 0xf6, 0x0f, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x0a, 0x72, 0x00, 0x10, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x02, 0x40, 0x00, 0x00, 0x52, 0xb8, 0x4e, 0x41, 0x52, 0xb8, 0x4e, 0x41, - 0x52, 0xb8, 0x4e, 0x41, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x0a, - 0x72, 0x00, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x77, 0xcc, 0x2b, 0x32, - 0x77, 0xcc, 0x2b, 0x32, 0x77, 0xcc, 0x2b, 0x32, 0x00, 0x00, 0x00, 0x00, - 0x2f, 0x00, 0x00, 0x05, 0x72, 0x00, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x0a, - 0x72, 0x00, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x55, 0x55, 0xd5, 0x3e, - 0x55, 0x55, 0xd5, 0x3e, 0x55, 0x55, 0xd5, 0x3e, 0x00, 0x00, 0x00, 0x00, - 0x19, 0x00, 0x00, 0x05, 0x72, 0x00, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x0f, - 0x72, 0x00, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x3d, 0x0a, 0x87, 0x3f, - 0x3d, 0x0a, 0x87, 0x3f, 0x3d, 0x0a, 0x87, 0x3f, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x40, 0x00, 0x00, 0xae, 0x47, 0x61, 0xbd, 0xae, 0x47, 0x61, 0xbd, - 0xae, 0x47, 0x61, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x0a, - 0x72, 0x00, 0x10, 0x00, 0x05, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x1c, 0x2e, 0x4d, 0x3b, - 0x1c, 0x2e, 0x4d, 0x3b, 0x1c, 0x2e, 0x4d, 0x3b, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x0a, 0x72, 0x00, 0x10, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, - 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f, - 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x0d, 0x72, 0x00, 0x10, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x80, 0x41, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x52, 0xb8, 0x4e, 0x41, - 0x52, 0xb8, 0x4e, 0x41, 0x52, 0xb8, 0x4e, 0x41, 0x00, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x09, - 0x72, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x06, - 0xf2, 0xe0, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x05, 0x02, 0x00, - 0x46, 0x0e, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x01, - 0x15, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x08, 0x82, 0x00, 0x10, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1a, 0x80, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x31, 0x00, 0x00, 0x07, 0x12, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x1a, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x07, 0x22, 0x00, 0x10, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1a, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, - 0x42, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x10, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbf, - 0x00, 0x00, 0x00, 0x07, 0x82, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x2a, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x10, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x09, 0x12, 0x00, 0x10, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x1a, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x10, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xa2, 0x00, 0x10, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x06, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbf, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbf, 0x0e, 0x00, 0x00, 0x08, - 0x12, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x10, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x3a, 0x80, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x12, 0x00, 0x10, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x07, - 0x12, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x10, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x0e, 0x00, 0x00, 0x08, 0x12, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0a, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x80, 0x20, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, - 0x22, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, - 0x37, 0x00, 0x00, 0x09, 0x52, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xf6, 0x0f, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x10, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x56, 0x06, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x00, 0x8d, 0xc2, 0x00, 0x00, 0x80, 0x43, 0x55, 0x15, 0x00, - 0x72, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x86, 0x00, 0x10, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x46, 0x7e, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x36, 0x20, 0x00, 0x05, 0x72, 0x00, 0x10, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x00, 0x0a, 0x72, 0x00, 0x10, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, - 0x91, 0x83, 0x9e, 0x3d, 0x91, 0x83, 0x9e, 0x3d, 0x91, 0x83, 0x9e, 0x3d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x72, 0x00, 0x10, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x40, 0x00, 0x00, 0xae, 0x47, 0x61, 0x3d, 0xae, 0x47, 0x61, 0x3d, - 0xae, 0x47, 0x61, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x0a, - 0x72, 0x00, 0x10, 0x00, 0x06, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x6f, 0xa7, 0x72, 0x3f, - 0x6f, 0xa7, 0x72, 0x3f, 0x6f, 0xa7, 0x72, 0x3f, 0x00, 0x00, 0x00, 0x00, - 0x2f, 0x00, 0x00, 0x05, 0x72, 0x00, 0x10, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x00, 0x06, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x0a, - 0x72, 0x00, 0x10, 0x00, 0x06, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x9a, 0x99, 0x19, 0x40, - 0x9a, 0x99, 0x19, 0x40, 0x9a, 0x99, 0x19, 0x40, 0x00, 0x00, 0x00, 0x00, - 0x19, 0x00, 0x00, 0x05, 0x72, 0x00, 0x10, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x0a, - 0x72, 0x00, 0x10, 0x00, 0x07, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0xe6, 0xae, 0x25, 0x3d, - 0xe6, 0xae, 0x25, 0x3d, 0xe6, 0xae, 0x25, 0x3d, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x0a, 0x72, 0x00, 0x10, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x00, 0x07, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, - 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f, - 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x0d, 0x72, 0x00, 0x10, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x80, 0x41, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x91, 0x83, 0x9e, 0x3d, - 0x91, 0x83, 0x9e, 0x3d, 0x91, 0x83, 0x9e, 0x3d, 0x00, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x00, 0x06, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x09, - 0x72, 0x00, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x00, 0x05, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x0a, - 0x72, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x80, 0x20, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x00, 0x8d, 0xc2, 0x00, 0x00, 0x80, 0x43, 0x55, 0x15, 0x00, - 0xf2, 0x00, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, 0x86, 0x00, 0x10, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x46, 0x7e, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x00, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x04, 0x32, 0x00, 0x10, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x46, 0x00, 0x02, 0x00, 0x36, 0x00, 0x00, 0x08, - 0xc2, 0x00, 0x10, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x89, 0xc2, 0x00, 0x00, 0x80, - 0x43, 0x55, 0x15, 0x00, 0xf2, 0x00, 0x10, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x46, 0x0e, 0x10, 0x00, 0x05, 0x00, 0x00, 0x00, 0x46, 0x7e, 0x10, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x09, 0xf2, 0x00, 0x10, 0x00, - 0x04, 0x00, 0x00, 0x00, 0xf6, 0x0f, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x46, 0x0e, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, 0x46, 0x0e, 0x10, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x08, 0x22, 0x00, 0x10, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x2a, 0x80, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x1f, 0x00, 0x04, 0x03, 0x1a, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x00, 0x08, 0x72, 0x00, 0x10, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x56, 0x85, 0x20, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x05, - 0x82, 0x00, 0x10, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x10, 0x00, - 0x04, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x06, 0xf2, 0xe0, 0x11, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x46, 0x05, 0x02, 0x00, 0x46, 0x0e, 0x10, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x01, 0x15, 0x00, 0x00, 0x01, - 0x48, 0x00, 0x00, 0x8d, 0xc2, 0x00, 0x00, 0x80, 0x43, 0x55, 0x15, 0x00, - 0x72, 0x00, 0x10, 0x00, 0x05, 0x00, 0x00, 0x00, 0x86, 0x00, 0x10, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x46, 0x7e, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x36, 0x20, 0x00, 0x05, 0x72, 0x00, 0x10, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x00, 0x0a, 0x72, 0x00, 0x10, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, - 0x91, 0x83, 0x9e, 0x3d, 0x91, 0x83, 0x9e, 0x3d, 0x91, 0x83, 0x9e, 0x3d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x72, 0x00, 0x10, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x02, 0x40, 0x00, 0x00, 0xae, 0x47, 0x61, 0x3d, 0xae, 0x47, 0x61, 0x3d, - 0xae, 0x47, 0x61, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x0a, - 0x72, 0x00, 0x10, 0x00, 0x08, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x6f, 0xa7, 0x72, 0x3f, - 0x6f, 0xa7, 0x72, 0x3f, 0x6f, 0xa7, 0x72, 0x3f, 0x00, 0x00, 0x00, 0x00, - 0x2f, 0x00, 0x00, 0x05, 0x72, 0x00, 0x10, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x00, 0x08, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x0a, - 0x72, 0x00, 0x10, 0x00, 0x08, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x9a, 0x99, 0x19, 0x40, - 0x9a, 0x99, 0x19, 0x40, 0x9a, 0x99, 0x19, 0x40, 0x00, 0x00, 0x00, 0x00, - 0x19, 0x00, 0x00, 0x05, 0x72, 0x00, 0x10, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x00, 0x08, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x0a, - 0x72, 0x00, 0x10, 0x00, 0x09, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0xe6, 0xae, 0x25, 0x3d, - 0xe6, 0xae, 0x25, 0x3d, 0xe6, 0xae, 0x25, 0x3d, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x0a, 0x72, 0x00, 0x10, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x00, 0x09, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, - 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f, - 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x0d, 0x72, 0x00, 0x10, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x80, 0x41, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x91, 0x83, 0x9e, 0x3d, - 0x91, 0x83, 0x9e, 0x3d, 0x91, 0x83, 0x9e, 0x3d, 0x00, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x00, 0x08, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x09, - 0x72, 0x00, 0x10, 0x00, 0x06, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, - 0x09, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x00, 0x07, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x0a, - 0x72, 0x00, 0x10, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x80, 0x20, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x00, 0x08, 0x12, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x2a, 0x80, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x01, 0x40, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x04, 0x03, - 0x0a, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x08, - 0x72, 0x00, 0x10, 0x00, 0x06, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x56, 0x85, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x05, 0x82, 0x00, 0x10, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, - 0xa4, 0x00, 0x00, 0x06, 0xf2, 0xe0, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x46, 0x05, 0x02, 0x00, 0x46, 0x0e, 0x10, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x00, 0x01, 0x15, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x08, - 0x12, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x80, 0x20, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x04, 0x03, 0x0a, 0x00, 0x10, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x72, 0x00, 0x10, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x80, 0x41, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x32, 0x20, 0x00, 0x0f, 0x72, 0x00, 0x10, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, - 0x00, 0x00, 0xa0, 0x41, 0x00, 0x00, 0xa0, 0x41, 0x00, 0x00, 0xa0, 0x41, - 0x00, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, - 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x00, 0x0a, 0x72, 0x00, 0x10, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, - 0x91, 0x83, 0x9e, 0x3d, 0x91, 0x83, 0x9e, 0x3d, 0x91, 0x83, 0x9e, 0x3d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x72, 0x00, 0x10, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x02, 0x40, 0x00, 0x00, 0xae, 0x47, 0x61, 0x3d, 0xae, 0x47, 0x61, 0x3d, - 0xae, 0x47, 0x61, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x0a, - 0x72, 0x00, 0x10, 0x00, 0x08, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x6f, 0xa7, 0x72, 0x3f, - 0x6f, 0xa7, 0x72, 0x3f, 0x6f, 0xa7, 0x72, 0x3f, 0x00, 0x00, 0x00, 0x00, - 0x2f, 0x00, 0x00, 0x05, 0x72, 0x00, 0x10, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x00, 0x08, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x0a, - 0x72, 0x00, 0x10, 0x00, 0x08, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x9a, 0x99, 0x19, 0x40, - 0x9a, 0x99, 0x19, 0x40, 0x9a, 0x99, 0x19, 0x40, 0x00, 0x00, 0x00, 0x00, - 0x19, 0x00, 0x00, 0x05, 0x72, 0x00, 0x10, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x00, 0x08, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x0a, - 0x72, 0x00, 0x10, 0x00, 0x09, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0xe6, 0xae, 0x25, 0x3d, - 0xe6, 0xae, 0x25, 0x3d, 0xe6, 0xae, 0x25, 0x3d, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x0a, 0x72, 0x00, 0x10, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x00, 0x09, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, - 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f, - 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x0d, 0x72, 0x00, 0x10, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x80, 0x41, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x91, 0x83, 0x9e, 0x3d, - 0x91, 0x83, 0x9e, 0x3d, 0x91, 0x83, 0x9e, 0x3d, 0x00, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x00, 0x08, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x09, - 0x72, 0x00, 0x10, 0x00, 0x06, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, - 0x09, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x00, 0x07, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x08, - 0x72, 0x00, 0x10, 0x00, 0x06, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x56, 0x85, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x05, 0x82, 0x00, 0x10, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, - 0xa4, 0x00, 0x00, 0x06, 0xf2, 0xe0, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x46, 0x05, 0x02, 0x00, 0x46, 0x0e, 0x10, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x00, 0x01, 0x15, 0x00, 0x00, 0x01, 0x27, 0x00, 0x00, 0x08, - 0x12, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x80, 0x20, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x07, 0x12, 0x00, 0x10, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0a, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x0a, - 0x32, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x46, 0x00, 0x10, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x31, 0x00, 0x00, 0x0a, 0x32, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, 0x10, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x07, 0x42, 0x00, 0x10, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x0a, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x07, - 0x42, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x10, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3c, 0x00, 0x00, 0x07, 0x42, 0x00, 0x10, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1a, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x10, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x08, 0x42, 0x00, 0x10, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x2a, 0x80, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x56, 0x00, 0x00, 0x05, 0x42, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x2a, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0a, - 0x42, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, - 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f, - 0x00, 0x00, 0x80, 0x3f, 0x2a, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x31, 0x00, 0x00, 0x08, 0x22, 0x00, 0x10, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x2a, 0x00, 0x10, 0x80, 0x81, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x2a, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x08, - 0x12, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x80, 0x20, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x08, 0x22, 0x00, 0x10, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x2a, 0x80, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x27, 0x00, 0x00, 0x07, 0x12, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x0a, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x10, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x12, 0x00, 0x10, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x2a, 0x80, 0x20, 0x80, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x08, 0x22, 0x00, 0x10, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x80, 0x81, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x07, 0x32, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x46, 0x00, 0x10, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x05, 0x42, 0x00, 0x10, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x37, 0x00, 0x00, 0x09, 0x72, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xf6, 0x0f, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x34, 0x00, 0x00, 0x08, 0x82, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1a, 0x80, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x40, 0x00, 0x00, 0x17, 0xb7, 0xd1, 0x38, 0x37, 0x00, 0x00, 0x0a, - 0x82, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x80, 0x20, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, - 0x00, 0x00, 0x80, 0x3f, 0x3a, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0e, 0x00, 0x00, 0x07, 0x72, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, 0xf6, 0x0f, 0x10, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x0a, 0x82, 0x00, 0x10, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x02, 0x40, 0x00, 0x00, 0xd0, 0xb3, 0x59, 0x3e, 0x59, 0x17, 0x37, 0x3f, - 0x98, 0xdd, 0x93, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x0a, - 0x12, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0xd0, 0xb3, 0x59, 0x3e, - 0x59, 0x17, 0x37, 0x3f, 0x98, 0xdd, 0x93, 0x3d, 0x00, 0x00, 0x00, 0x00, - 0x1d, 0x00, 0x00, 0x07, 0x22, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x01, 0x40, 0x00, 0x00, 0xac, 0xc5, 0x27, 0x37, 0x0a, 0x00, 0x10, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x04, 0x03, 0x1a, 0x00, 0x10, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x05, 0xe2, 0x00, 0x10, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x06, 0x09, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x12, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x0a, 0x12, 0x00, 0x10, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x02, 0x40, 0x00, 0x00, 0xd0, 0xb3, 0x59, 0x3e, 0x59, 0x17, 0x37, 0x3f, - 0x98, 0xdd, 0x93, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x07, - 0x22, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x10, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x34, 0x00, 0x00, 0x07, 0x42, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x0a, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, - 0xbd, 0x37, 0x86, 0x35, 0x0e, 0x00, 0x00, 0x07, 0x42, 0x00, 0x10, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x2a, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, - 0x12, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x10, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x80, 0x41, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x07, 0x12, 0x00, 0x10, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, - 0x12, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x0e, 0x00, 0x00, 0x07, 0x12, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x0a, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x09, 0x12, 0x00, 0x10, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x2a, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x07, 0x72, 0x00, 0x10, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x00, 0x05, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x0a, - 0x12, 0x00, 0x10, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, - 0xb1, 0x0e, 0xd3, 0x3e, 0x17, 0x4d, 0x09, 0x3f, 0x09, 0xb9, 0x52, 0x3d, - 0x00, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x0a, 0x22, 0x00, 0x10, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x02, 0x40, 0x00, 0x00, 0x3b, 0xfd, 0x58, 0x3e, 0x53, 0x42, 0x2e, 0x3f, - 0xf0, 0xf2, 0xdb, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x0a, 0x42, 0x00, 0x10, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0xec, 0xd7, 0xb4, 0x3d, - 0x74, 0x3d, 0x90, 0x3e, 0x49, 0x46, 0x21, 0x3f, 0x00, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x0a, - 0x72, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x31, 0x00, 0x00, 0x0a, 0x72, 0x00, 0x10, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x08, 0x72, 0x00, 0x10, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x80, 0x41, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x2b, 0x00, 0x00, 0x05, 0x72, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x06, - 0x72, 0x00, 0x10, 0x00, 0x06, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x80, - 0x81, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x0a, - 0x72, 0x00, 0x10, 0x00, 0x06, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0xab, 0xaa, 0xaa, 0x3e, - 0xab, 0xaa, 0xaa, 0x3e, 0xab, 0xaa, 0xaa, 0x3e, 0x00, 0x00, 0x00, 0x00, - 0x19, 0x00, 0x00, 0x05, 0x72, 0x00, 0x10, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x00, 0x06, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x07, - 0x72, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x0a, 0x12, 0x00, 0x10, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x02, 0x40, 0x00, 0x00, 0x52, 0x81, 0x57, 0x3e, 0x89, 0x2a, 0x4b, 0x3f, - 0xce, 0x6e, 0x85, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x0a, 0x12, 0x00, 0x10, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x0e, 0x2f, 0xfd, 0x3f, - 0x0e, 0x6e, 0x1b, 0xc0, 0x38, 0xb4, 0xe6, 0x3e, 0x00, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x0a, - 0x22, 0x00, 0x10, 0x00, 0x07, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, - 0xb4, 0x34, 0xd4, 0x3c, 0xbb, 0x63, 0x48, 0x3f, 0x60, 0x05, 0x4f, 0xbf, - 0x00, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x0a, 0x12, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x02, 0x40, 0x00, 0x00, 0xb1, 0x0e, 0xd3, 0x3e, 0x17, 0x4d, 0x09, 0x3f, - 0x09, 0xb9, 0x52, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x0a, 0x22, 0x00, 0x10, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x3b, 0xfd, 0x58, 0x3e, - 0x53, 0x42, 0x2e, 0x3f, 0xf0, 0xf2, 0xdb, 0x3d, 0x00, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x00, 0x05, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x0a, - 0x42, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, - 0xec, 0xd7, 0xb4, 0x3d, 0x74, 0x3d, 0x90, 0x3e, 0x49, 0x46, 0x21, 0x3f, - 0x00, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x31, 0x00, 0x00, 0x0a, 0x72, 0x00, 0x10, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x0a, 0x72, 0x00, 0x10, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x08, - 0x72, 0x00, 0x10, 0x00, 0x05, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x80, - 0x41, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x05, 0x72, 0x00, 0x10, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x2f, 0x00, 0x00, 0x06, 0x72, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x80, 0x81, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x00, 0x0a, 0x72, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, - 0xab, 0xaa, 0xaa, 0x3e, 0xab, 0xaa, 0xaa, 0x3e, 0xab, 0xaa, 0xaa, 0x3e, - 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x05, 0x72, 0x00, 0x10, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x00, 0x07, 0x72, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x0a, 0x22, 0x00, 0x10, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x0e, 0x2f, 0xfd, 0x3f, - 0x0e, 0x6e, 0x1b, 0xc0, 0x38, 0xb4, 0xe6, 0x3e, 0x00, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x0a, - 0x42, 0x00, 0x10, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, - 0xb4, 0x34, 0xd4, 0x3c, 0xbb, 0x63, 0x48, 0x3f, 0x60, 0x05, 0x4f, 0xbf, - 0x00, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x0f, 0x00, 0x00, 0x07, 0x12, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x46, 0x00, 0x10, 0x00, 0x07, 0x00, 0x00, 0x00, 0x46, 0x00, 0x10, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x05, 0x12, 0x00, 0x10, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x0f, 0x00, 0x00, 0x07, 0x12, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x96, 0x05, 0x10, 0x00, 0x05, 0x00, 0x00, 0x00, 0x96, 0x05, 0x10, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x05, 0x12, 0x00, 0x10, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x07, 0x22, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x0a, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x07, 0x12, 0x00, 0x10, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x0a, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x09, - 0x12, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x10, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, - 0x0a, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x07, - 0x62, 0x00, 0x10, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x10, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x56, 0x06, 0x10, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x0a, 0x12, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0xc9, 0xec, 0xca, 0x3e, - 0xa9, 0xfb, 0x5c, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x0a, 0x22, 0x00, 0x10, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, - 0x8c, 0x30, 0xd8, 0xbd, 0xfa, 0xc5, 0x82, 0xbd, 0x00, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x00, 0x06, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x0a, - 0x42, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, - 0x00, 0x00, 0x80, 0x3f, 0x7b, 0x43, 0xb7, 0xbd, 0x66, 0x4f, 0xa5, 0xbf, - 0x00, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x00, 0x07, 0x72, 0x00, 0x10, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x07, 0x72, 0x00, 0x10, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x00, 0x05, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x0a, - 0x12, 0x00, 0x10, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, - 0xab, 0x74, 0x82, 0x40, 0x8c, 0xb1, 0x53, 0xc0, 0x62, 0x83, 0x6c, 0x3e, - 0x00, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x0a, 0x22, 0x00, 0x10, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x02, 0x40, 0x00, 0x00, 0x2d, 0x5c, 0xa2, 0xbf, 0x44, 0x06, 0x27, 0x40, - 0x6a, 0xc1, 0xae, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x0a, 0x42, 0x00, 0x10, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x53, 0x7f, 0x89, 0xbb, - 0x3e, 0x13, 0x34, 0xbf, 0x1e, 0x93, 0xda, 0x3f, 0x00, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x0a, - 0x12, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, - 0xed, 0xf3, 0x1c, 0x3f, 0xf5, 0xd5, 0xad, 0x3e, 0x7b, 0x10, 0x42, 0x3d, - 0x00, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x0a, 0x22, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x02, 0x40, 0x00, 0x00, 0xdf, 0xc1, 0x8f, 0x3d, 0x2d, 0x96, 0x6a, 0x3f, - 0xc7, 0x65, 0x5c, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x0a, 0x42, 0x00, 0x10, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0xe3, 0xe2, 0xa8, 0x3c, - 0x3c, 0x66, 0xe0, 0x3d, 0x32, 0xac, 0x5e, 0x3f, 0x00, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x00, 0x05, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x0a, - 0x72, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x0a, 0x12, 0x00, 0x10, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x02, 0x40, 0x00, 0x00, 0x1c, 0x3f, 0xda, 0x3f, 0xc3, 0x2d, 0x1f, 0xbf, - 0xb2, 0x83, 0xaa, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x0a, 0x22, 0x00, 0x10, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0xd4, 0x61, 0x05, 0xbe, - 0xe6, 0x05, 0x92, 0x3f, 0x85, 0xd1, 0x2c, 0xbc, 0x00, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x0a, - 0x42, 0x00, 0x10, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, - 0xf1, 0xa1, 0xc4, 0xbc, 0x73, 0x10, 0x04, 0xbe, 0x96, 0x94, 0x93, 0x3f, - 0x00, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x08, 0x72, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x80, 0x41, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x00, 0x05, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x0a, - 0xe2, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x80, 0x20, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x09, 0x10, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x06, 0x09, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x15, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x0a, 0x12, 0x00, 0x10, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x96, 0x07, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x02, 0x40, 0x00, 0x00, 0xd0, 0xb3, 0x59, 0x3e, 0x59, 0x17, 0x37, 0x3f, - 0x98, 0xdd, 0x93, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, - 0x12, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, - 0x00, 0x00, 0x00, 0x07, 0x82, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x3a, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x3b, 0x0e, 0x00, 0x00, 0x07, 0x82, 0x00, 0x10, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x3a, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x07, - 0x82, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x10, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x33, 0x00, 0x00, 0x08, 0x82, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x3a, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3a, 0x80, 0x20, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x07, - 0x72, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0xf6, 0x0f, 0x10, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x32, 0x00, 0x00, 0x0a, 0x72, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x80, 0x41, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0xf6, 0x0f, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x96, 0x07, 0x10, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x0a, 0x72, 0x00, 0x10, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x56, 0x85, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x46, 0x02, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x07, - 0x72, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0xf6, 0x0f, 0x10, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x37, 0x00, 0x00, 0x09, 0x72, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x37, 0x00, 0x00, 0x0c, 0xd2, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xa6, 0x0a, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x06, 0x09, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x37, 0x00, 0x00, 0x0a, 0x72, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x56, 0x05, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x85, 0x20, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x03, 0x10, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x0a, 0x72, 0x00, 0x10, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x06, - 0xf2, 0xe0, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x05, 0x02, 0x00, - 0x46, 0x0e, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x01, - 0x53, 0x54, 0x41, 0x54, 0x94, 0x00, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00, - 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x90, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, - 0x0b, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0b, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00 - + 0x44, 0x58, 0x42, 0x43, 0x80, 0x35, 0x2d, 0xda, 0x88, 0xc1, 0xc4, 0x92, 0xe0, 0x96, 0x5c, 0x70, 0x90, 0x99, 0x77, + 0x52, 0x01, 0x00, 0x00, 0x00, 0xb0, 0x2d, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x48, 0x00, + 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x78, 0x01, 0x00, 0x00, 0x94, 0x01, 0x00, 0x00, 0x53, + 0x46, 0x49, 0x30, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x53, 0x47, 0x31, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x4f, 0x53, 0x47, 0x31, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x50, 0x53, 0x56, 0x30, 0x08, 0x01, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x41, 0x53, 0x48, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb, 0x25, 0x9c, 0x98, 0x0f, 0x25, 0x62, 0x80, 0xd8, 0x4b, 0xc7, + 0xc1, 0xd0, 0xd7, 0xfc, 0xe4, 0x44, 0x58, 0x49, 0x4c, 0x14, 0x2c, 0x00, 0x00, 0x60, 0x00, 0x05, 0x00, 0x05, 0x0b, + 0x00, 0x00, 0x44, 0x58, 0x49, 0x4c, 0x00, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xfc, 0x2b, 0x00, 0x00, 0x42, + 0x43, 0xc0, 0xde, 0x21, 0x0c, 0x00, 0x00, 0xfc, 0x0a, 0x00, 0x00, 0x0b, 0x82, 0x20, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x07, 0x81, 0x23, 0x91, 0x41, 0xc8, 0x04, 0x49, 0x06, 0x10, 0x32, 0x39, 0x92, 0x01, 0x84, + 0x0c, 0x25, 0x05, 0x08, 0x19, 0x1e, 0x04, 0x8b, 0x62, 0x80, 0x18, 0x45, 0x02, 0x42, 0x92, 0x0b, 0x42, 0xc4, 0x10, + 0x32, 0x14, 0x38, 0x08, 0x18, 0x4b, 0x0a, 0x32, 0x62, 0x88, 0x48, 0x90, 0x14, 0x20, 0x43, 0x46, 0x88, 0xa5, 0x00, + 0x19, 0x32, 0x42, 0xe4, 0x48, 0x0e, 0x90, 0x11, 0x23, 0xc4, 0x50, 0x41, 0x51, 0x81, 0x8c, 0xe1, 0x83, 0xe5, 0x8a, + 0x04, 0x31, 0x46, 0x06, 0x51, 0x18, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x1b, 0x8c, 0xe0, 0xff, 0xff, 0xff, 0xff, + 0x07, 0x40, 0x02, 0xa8, 0x0d, 0x86, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x03, 0x20, 0x01, 0xd5, 0x06, 0x62, 0xf8, 0xff, + 0xff, 0xff, 0xff, 0x01, 0x90, 0x00, 0x49, 0x18, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x13, 0x82, 0x60, 0x42, 0x20, + 0x4c, 0x08, 0x06, 0x00, 0x00, 0x00, 0x00, 0x89, 0x20, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x32, 0x22, 0x88, 0x09, + 0x20, 0x64, 0x85, 0x04, 0x13, 0x23, 0xa4, 0x84, 0x04, 0x13, 0x23, 0xe3, 0x84, 0xa1, 0x90, 0x14, 0x12, 0x4c, 0x8c, + 0x8c, 0x0b, 0x84, 0xc4, 0x4c, 0x10, 0xcc, 0xc1, 0x08, 0x40, 0x09, 0x00, 0x0a, 0xe6, 0x08, 0xc0, 0xa0, 0x0c, 0xc3, + 0x30, 0x10, 0x31, 0x03, 0x70, 0xd3, 0x70, 0xf9, 0x13, 0xf6, 0x10, 0x92, 0xbf, 0x12, 0xd2, 0x4a, 0x4c, 0x7e, 0x71, + 0xdb, 0xa8, 0x30, 0x0c, 0xc3, 0x18, 0xe6, 0x08, 0x10, 0x42, 0xee, 0x19, 0x2e, 0x7f, 0xc2, 0x1e, 0x42, 0xf2, 0x43, + 0xa0, 0x19, 0x16, 0x02, 0x05, 0x49, 0x61, 0x8e, 0x41, 0x51, 0x0c, 0xc3, 0x30, 0x86, 0x61, 0x30, 0x68, 0x29, 0x0b, + 0x30, 0x28, 0xc3, 0x30, 0x18, 0x86, 0x61, 0x20, 0xd4, 0xdc, 0x34, 0x5c, 0xfe, 0x84, 0x3d, 0x84, 0xe4, 0x77, 0x08, + 0x43, 0x34, 0x12, 0xe2, 0x34, 0x12, 0x22, 0x86, 0x61, 0x18, 0x0a, 0xf1, 0x0c, 0xca, 0x40, 0x50, 0x51, 0x8e, 0x41, + 0x19, 0x86, 0x61, 0x18, 0x86, 0x81, 0xa4, 0x32, 0x18, 0x83, 0x41, 0x54, 0x21, 0x86, 0x61, 0x18, 0xc8, 0x2a, 0x84, + 0x31, 0x18, 0x06, 0x61, 0x05, 0x31, 0x06, 0xc3, 0x30, 0x0c, 0xc3, 0x20, 0xed, 0xa8, 0xe1, 0xf2, 0x27, 0xec, 0x21, + 0x24, 0x9f, 0xdb, 0xa8, 0x62, 0x25, 0x26, 0xbf, 0xb8, 0x6d, 0x44, 0x18, 0x86, 0x61, 0x14, 0x82, 0x1b, 0x94, 0x81, + 0xba, 0xa3, 0x86, 0xcb, 0x9f, 0xb0, 0x87, 0x90, 0x7c, 0x6e, 0xa3, 0x8a, 0x95, 0x98, 0x7c, 0xe4, 0xb6, 0x11, 0x31, + 0x0c, 0xc3, 0x50, 0x88, 0x6f, 0x50, 0x06, 0x02, 0x4b, 0x61, 0x0c, 0x86, 0x61, 0x90, 0x38, 0x47, 0x10, 0x14, 0x43, + 0x19, 0x90, 0x61, 0x20, 0xa9, 0x1c, 0x08, 0x18, 0x46, 0x20, 0x8c, 0x99, 0xda, 0x60, 0x1c, 0xd8, 0x21, 0x1c, 0xe6, + 0x61, 0x1e, 0xdc, 0x80, 0x16, 0xca, 0x01, 0x1f, 0xe8, 0xa1, 0x1e, 0xe4, 0xa1, 0x1c, 0xe4, 0x80, 0x14, 0xf8, 0xc0, + 0x1e, 0xca, 0x61, 0x1c, 0xe8, 0xe1, 0x1d, 0xe4, 0x81, 0x0f, 0xcc, 0x81, 0x1d, 0xde, 0x21, 0x1c, 0xe8, 0x81, 0x0d, + 0xc0, 0x80, 0x0e, 0xfc, 0x00, 0x0c, 0xfc, 0x40, 0x0f, 0xf4, 0xa0, 0x1d, 0xd2, 0x01, 0x1e, 0xe6, 0xe1, 0x17, 0xe8, + 0x21, 0x1f, 0xe0, 0xa1, 0x1c, 0x50, 0x30, 0xcc, 0x24, 0x06, 0xe3, 0xc0, 0x0e, 0xe1, 0x30, 0x0f, 0xf3, 0xe0, 0x06, + 0xb4, 0x50, 0x0e, 0xf8, 0x40, 0x0f, 0xf5, 0x20, 0x0f, 0xe5, 0x20, 0x07, 0xa4, 0xc0, 0x07, 0xf6, 0x50, 0x0e, 0xe3, + 0x40, 0x0f, 0xef, 0x20, 0x0f, 0x7c, 0x60, 0x0e, 0xec, 0xf0, 0x0e, 0xe1, 0x40, 0x0f, 0x6c, 0x00, 0x06, 0x74, 0xe0, + 0x07, 0x60, 0xe0, 0x07, 0x48, 0x40, 0x53, 0x52, 0x67, 0x22, 0x83, 0x71, 0x60, 0x87, 0x70, 0x98, 0x87, 0x79, 0x70, + 0x03, 0x59, 0xb8, 0x05, 0x5a, 0x28, 0x07, 0x7c, 0xa0, 0x87, 0x7a, 0x90, 0x87, 0x72, 0x90, 0x03, 0x52, 0xe0, 0x03, + 0x7b, 0x28, 0x87, 0x71, 0xa0, 0x87, 0x77, 0x90, 0x07, 0x3e, 0x30, 0x07, 0x76, 0x78, 0x87, 0x70, 0xa0, 0x07, 0x36, + 0x00, 0x03, 0x3a, 0xf0, 0x03, 0x30, 0xf0, 0x03, 0x14, 0xa0, 0xc4, 0x9e, 0x91, 0x02, 0x11, 0xc0, 0x48, 0x68, 0x1a, + 0x8c, 0x61, 0x30, 0x8c, 0xc1, 0x18, 0x0c, 0x63, 0x18, 0x06, 0xc3, 0x18, 0x86, 0x81, 0xdc, 0x9b, 0xa4, 0x29, 0xa2, + 0x84, 0xc9, 0x67, 0x01, 0xe6, 0x59, 0x88, 0x88, 0x9d, 0x80, 0x89, 0x40, 0xc1, 0x40, 0xf0, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x13, 0x14, 0x72, 0xc0, 0x87, 0x74, 0x60, 0x87, 0x36, 0x68, 0x87, 0x79, 0x68, 0x03, 0x72, 0xc0, 0x87, 0x0d, + 0xaf, 0x50, 0x0e, 0x6d, 0xd0, 0x0e, 0x7a, 0x50, 0x0e, 0x6d, 0x00, 0x0f, 0x7a, 0x30, 0x07, 0x72, 0xa0, 0x07, 0x73, + 0x20, 0x07, 0x6d, 0x90, 0x0e, 0x71, 0xa0, 0x07, 0x73, 0x20, 0x07, 0x6d, 0x90, 0x0e, 0x78, 0xa0, 0x07, 0x73, 0x20, + 0x07, 0x6d, 0x90, 0x0e, 0x71, 0x60, 0x07, 0x7a, 0x30, 0x07, 0x72, 0xd0, 0x06, 0xe9, 0x30, 0x07, 0x72, 0xa0, 0x07, + 0x73, 0x20, 0x07, 0x6d, 0x90, 0x0e, 0x76, 0x40, 0x07, 0x7a, 0x60, 0x07, 0x74, 0xd0, 0x06, 0xe6, 0x10, 0x07, 0x76, + 0xa0, 0x07, 0x73, 0x20, 0x07, 0x6d, 0x60, 0x0e, 0x73, 0x20, 0x07, 0x7a, 0x30, 0x07, 0x72, 0xd0, 0x06, 0xe6, 0x60, + 0x07, 0x74, 0xa0, 0x07, 0x76, 0x40, 0x07, 0x6d, 0xe0, 0x0e, 0x78, 0xa0, 0x07, 0x71, 0x60, 0x07, 0x7a, 0x30, 0x07, + 0x72, 0xa0, 0x07, 0x76, 0x40, 0x07, 0x43, 0x9e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x86, 0x3c, 0x04, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x79, 0x16, 0x20, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xf2, 0x34, 0x40, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0xe4, 0x81, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xc8, 0x23, 0x01, 0x01, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x90, 0x87, 0x02, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x21, 0x8f, 0x05, 0x04, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x1e, 0x0c, + 0x08, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x3c, 0x1a, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x79, 0x3a, 0x20, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xf2, + 0x80, 0x01, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x79, 0xc4, 0x00, 0x08, 0x80, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x3c, 0x65, 0x00, 0x04, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x59, 0x20, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x32, 0x1e, 0x98, 0x14, 0x19, 0x11, 0x4c, 0x90, 0x8c, + 0x09, 0x26, 0x47, 0xc6, 0x04, 0x43, 0x1a, 0x4a, 0x60, 0x04, 0xa0, 0x1c, 0x8a, 0xa1, 0x08, 0x4a, 0xa2, 0x0c, 0x0a, + 0x33, 0xa0, 0x10, 0x0a, 0x82, 0xc8, 0x11, 0x00, 0x5a, 0x67, 0x00, 0xa8, 0x9d, 0x01, 0xa0, 0x77, 0x06, 0x80, 0xe2, + 0x19, 0x00, 0x00, 0x00, 0x00, 0x79, 0x18, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x1a, 0x03, 0x4c, 0x90, 0x46, 0x02, + 0x13, 0x44, 0x8f, 0x0c, 0x6f, 0xec, 0xed, 0x4d, 0x0c, 0x24, 0xc6, 0xe5, 0xc6, 0x45, 0x66, 0x06, 0x06, 0xc7, 0x25, + 0xc6, 0x06, 0x04, 0xa5, 0x46, 0x86, 0x2c, 0x2c, 0xe6, 0xa6, 0x4c, 0x26, 0x27, 0x65, 0x43, 0x10, 0x4c, 0x10, 0x06, + 0x67, 0x82, 0x30, 0x3c, 0x1b, 0x84, 0x81, 0x98, 0x20, 0x0c, 0xd0, 0x06, 0x61, 0x30, 0x28, 0x8c, 0xcd, 0x4d, 0x10, + 0x86, 0x68, 0xc3, 0x80, 0x24, 0xc4, 0x04, 0x61, 0x0d, 0x32, 0x02, 0x13, 0x84, 0x41, 0x9a, 0x20, 0x0c, 0xd3, 0x06, + 0x81, 0x70, 0x36, 0x24, 0xc4, 0xc2, 0x10, 0xc4, 0xd0, 0x10, 0xcf, 0x86, 0x64, 0x58, 0x18, 0x62, 0x18, 0x1a, 0xe2, + 0xd9, 0x90, 0x34, 0x0b, 0x43, 0x34, 0x43, 0x43, 0x3c, 0x13, 0x84, 0x81, 0xda, 0x90, 0x4c, 0x0b, 0x43, 0x4c, 0x43, + 0x43, 0x3c, 0x1b, 0x08, 0x28, 0x92, 0xa8, 0x09, 0x42, 0x1b, 0x68, 0x13, 0x04, 0x32, 0xc0, 0x36, 0x2c, 0x84, 0xc5, + 0x10, 0xc4, 0xd0, 0x5c, 0xd7, 0xf5, 0x6c, 0x58, 0x06, 0x8b, 0x21, 0x86, 0xa1, 0xb9, 0xae, 0xeb, 0xd9, 0x20, 0x60, + 0xd9, 0x04, 0xe1, 0x0d, 0xb6, 0x09, 0xc2, 0x50, 0x6d, 0x40, 0x88, 0x8d, 0x21, 0x88, 0x81, 0x03, 0x36, 0x04, 0xdd, + 0x04, 0x21, 0x0e, 0xb8, 0x0d, 0x08, 0xf1, 0x31, 0x04, 0x31, 0x10, 0xc0, 0x86, 0x00, 0x0c, 0x36, 0x10, 0x95, 0xe6, + 0x85, 0xc1, 0x04, 0x41, 0x00, 0x68, 0x0c, 0x4d, 0x35, 0x85, 0xa5, 0xb9, 0x4d, 0x10, 0x06, 0x6b, 0x82, 0x30, 0x5c, + 0x1b, 0x06, 0x33, 0x30, 0x83, 0x61, 0x83, 0x50, 0x06, 0x67, 0xb0, 0xa1, 0x18, 0x03, 0x32, 0x00, 0xc4, 0x00, 0x0d, + 0xaa, 0xb0, 0xb1, 0xd9, 0xb5, 0xb9, 0xa4, 0x91, 0x95, 0xb9, 0xd1, 0x4d, 0x09, 0x82, 0x2a, 0x64, 0x78, 0x2e, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x02, 0xa2, 0x09, 0x19, 0x9e, 0x8b, 0x5d, 0x18, 0x9b, 0x5d, 0x99, 0xdc, + 0x94, 0xc0, 0xa8, 0x43, 0x86, 0xe7, 0x32, 0x87, 0x16, 0x46, 0x56, 0x26, 0xd7, 0xf4, 0x46, 0x56, 0xc6, 0x36, 0x25, + 0x48, 0xca, 0x90, 0xe1, 0xb9, 0xc8, 0x95, 0xcd, 0xbd, 0xd5, 0xc9, 0x8d, 0x95, 0xcd, 0x4d, 0x09, 0xc2, 0xa0, 0x0e, + 0x19, 0x9e, 0x4b, 0x99, 0x1b, 0x9d, 0x5c, 0x1e, 0xd4, 0x5b, 0x9a, 0x1b, 0xdd, 0xdc, 0x94, 0x00, 0x0d, 0x00, 0x79, + 0x18, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x33, 0x08, 0x80, 0x1c, 0xc4, 0xe1, 0x1c, 0x66, 0x14, 0x01, 0x3d, 0x88, + 0x43, 0x38, 0x84, 0xc3, 0x8c, 0x42, 0x80, 0x07, 0x79, 0x78, 0x07, 0x73, 0x98, 0x71, 0x0c, 0xe6, 0x00, 0x0f, 0xed, + 0x10, 0x0e, 0xf4, 0x80, 0x0e, 0x33, 0x0c, 0x42, 0x1e, 0xc2, 0xc1, 0x1d, 0xce, 0xa1, 0x1c, 0x66, 0x30, 0x05, 0x3d, + 0x88, 0x43, 0x38, 0x84, 0x83, 0x1b, 0xcc, 0x03, 0x3d, 0xc8, 0x43, 0x3d, 0x8c, 0x03, 0x3d, 0xcc, 0x78, 0x8c, 0x74, + 0x70, 0x07, 0x7b, 0x08, 0x07, 0x79, 0x48, 0x87, 0x70, 0x70, 0x07, 0x7a, 0x70, 0x03, 0x76, 0x78, 0x87, 0x70, 0x20, + 0x87, 0x19, 0xcc, 0x11, 0x0e, 0xec, 0x90, 0x0e, 0xe1, 0x30, 0x0f, 0x6e, 0x30, 0x0f, 0xe3, 0xf0, 0x0e, 0xf0, 0x50, + 0x0e, 0x33, 0x10, 0xc4, 0x1d, 0xde, 0x21, 0x1c, 0xd8, 0x21, 0x1d, 0xc2, 0x61, 0x1e, 0x66, 0x30, 0x89, 0x3b, 0xbc, + 0x83, 0x3b, 0xd0, 0x43, 0x39, 0xb4, 0x03, 0x3c, 0xbc, 0x83, 0x3c, 0x84, 0x03, 0x3b, 0xcc, 0xf0, 0x14, 0x76, 0x60, + 0x07, 0x7b, 0x68, 0x07, 0x37, 0x68, 0x87, 0x72, 0x68, 0x07, 0x37, 0x80, 0x87, 0x70, 0x90, 0x87, 0x70, 0x60, 0x07, + 0x76, 0x28, 0x07, 0x76, 0xf8, 0x05, 0x76, 0x78, 0x87, 0x77, 0x80, 0x87, 0x5f, 0x08, 0x87, 0x71, 0x18, 0x87, 0x72, + 0x98, 0x87, 0x79, 0x98, 0x81, 0x2c, 0xee, 0xf0, 0x0e, 0xee, 0xe0, 0x0e, 0xf5, 0xc0, 0x0e, 0xec, 0x30, 0x03, 0x62, + 0xc8, 0xa1, 0x1c, 0xe4, 0xa1, 0x1c, 0xcc, 0xa1, 0x1c, 0xe4, 0xa1, 0x1c, 0xdc, 0x61, 0x1c, 0xca, 0x21, 0x1c, 0xc4, + 0x81, 0x1d, 0xca, 0x61, 0x06, 0xd6, 0x90, 0x43, 0x39, 0xc8, 0x43, 0x39, 0x98, 0x43, 0x39, 0xc8, 0x43, 0x39, 0xb8, + 0xc3, 0x38, 0x94, 0x43, 0x38, 0x88, 0x03, 0x3b, 0x94, 0xc3, 0x2f, 0xbc, 0x83, 0x3c, 0xfc, 0x82, 0x3b, 0xd4, 0x03, + 0x3b, 0xb0, 0xc3, 0x0c, 0xc4, 0x21, 0x07, 0x7c, 0x70, 0x03, 0x7a, 0x28, 0x87, 0x76, 0x80, 0x87, 0x19, 0xca, 0xc3, + 0x0e, 0xec, 0x60, 0x0f, 0xed, 0xe0, 0x06, 0xec, 0xf0, 0x0e, 0xef, 0x00, 0x0f, 0x33, 0x22, 0x88, 0x1c, 0xf0, 0xc1, + 0x0d, 0xc8, 0x41, 0x1c, 0xce, 0xc1, 0x0d, 0xec, 0x21, 0x1c, 0xe4, 0x81, 0x1d, 0xc2, 0x21, 0x1f, 0xde, 0xa1, 0x1e, + 0xe8, 0x61, 0x06, 0x13, 0x91, 0x03, 0x3e, 0xb8, 0x81, 0x38, 0xc8, 0x43, 0x39, 0x84, 0xc3, 0x3a, 0xb8, 0x81, 0x38, + 0xc8, 0x03, 0x00, 0x00, 0x00, 0x71, 0x20, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x06, 0x60, 0x70, 0xac, 0x09, 0x20, + 0x8d, 0x21, 0x40, 0xc3, 0xe5, 0x3b, 0x8f, 0x1f, 0x20, 0x0d, 0x10, 0x61, 0x7e, 0x71, 0xdb, 0x76, 0x00, 0x0d, 0x97, + 0xef, 0x3c, 0x7e, 0x80, 0x34, 0x40, 0x84, 0xf9, 0xc8, 0x6d, 0x9b, 0xc2, 0x36, 0x5c, 0xbe, 0xf3, 0xf8, 0x42, 0x40, + 0x15, 0x05, 0x11, 0x95, 0x0e, 0x30, 0x94, 0x84, 0x01, 0x08, 0x98, 0x5f, 0xdc, 0xb6, 0x2d, 0x6c, 0xc3, 0xe5, 0x3b, + 0x8f, 0x2f, 0x04, 0x54, 0x51, 0x10, 0x51, 0xe9, 0x00, 0x43, 0x49, 0x18, 0x80, 0x80, 0xf9, 0xc8, 0x6d, 0x5b, 0x83, + 0x34, 0x5c, 0xbe, 0xf3, 0xf8, 0x42, 0x44, 0x00, 0x13, 0x11, 0x02, 0xcd, 0xb0, 0x10, 0x96, 0xe0, 0x0c, 0x97, 0xef, + 0x3c, 0xfe, 0xe0, 0x4c, 0xb7, 0x5f, 0xdc, 0xb6, 0x11, 0x4c, 0xc3, 0xe5, 0x3b, 0x8f, 0x6f, 0x10, 0x53, 0x87, 0x30, + 0x44, 0x23, 0x21, 0x4e, 0x23, 0x99, 0x40, 0x35, 0x5c, 0xbe, 0xf3, 0xf8, 0x12, 0xc0, 0x3c, 0x0b, 0x51, 0x12, 0x15, + 0xb1, 0xf8, 0xc5, 0x6d, 0x1b, 0x83, 0x34, 0x5c, 0xbe, 0xf3, 0xf8, 0x13, 0x11, 0x4d, 0x08, 0x10, 0x61, 0x7e, 0x71, + 0xdb, 0x56, 0x50, 0x0d, 0x97, 0xef, 0x3c, 0xfe, 0x44, 0x5c, 0x13, 0x15, 0x11, 0xa5, 0x03, 0x0c, 0x7e, 0x71, 0xdb, + 0x36, 0x60, 0x0d, 0x97, 0xef, 0x3c, 0xfe, 0x44, 0x5c, 0x13, 0x15, 0x11, 0xec, 0xe4, 0x44, 0x84, 0x5f, 0xdc, 0xb6, + 0x05, 0x48, 0xc3, 0xe5, 0x3b, 0x8f, 0x3f, 0x1d, 0x11, 0x01, 0x0c, 0xe2, 0xe0, 0x23, 0xb7, 0x6d, 0x06, 0xcf, 0x70, + 0xf9, 0xce, 0xe3, 0x53, 0x0d, 0x10, 0x61, 0x7e, 0x71, 0xdb, 0x00, 0x00, 0x00, 0x61, 0x20, 0x00, 0x00, 0x14, 0x09, + 0x00, 0x00, 0x13, 0x04, 0xee, 0x10, 0x0b, 0x04, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, 0x34, 0x14, 0xd7, 0x0c, 0x40, + 0xa9, 0x94, 0x4b, 0x19, 0x95, 0x5d, 0x21, 0x95, 0x61, 0x40, 0xb9, 0x95, 0x5c, 0x11, 0x06, 0x14, 0x5f, 0x0d, 0x94, + 0x4d, 0x29, 0x15, 0x53, 0xc1, 0x14, 0x64, 0x40, 0xe9, 0x06, 0x14, 0x4e, 0x39, 0x55, 0x01, 0x21, 0x45, 0x50, 0x06, + 0x25, 0x30, 0x02, 0x50, 0x1e, 0x64, 0x8c, 0x11, 0xe8, 0xac, 0x39, 0x87, 0x60, 0x30, 0x46, 0x10, 0xf3, 0x60, 0x9f, + 0x7b, 0x33, 0x00, 0x63, 0x04, 0x6b, 0xad, 0xd6, 0xea, 0x37, 0x46, 0x00, 0xfb, 0xec, 0x5c, 0x7e, 0x63, 0x04, 0xb9, + 0x5e, 0xba, 0xf3, 0x37, 0x46, 0x80, 0xb3, 0xf7, 0x99, 0x7b, 0x63, 0x04, 0x20, 0x08, 0x82, 0xf0, 0x37, 0x46, 0xd0, + 0xf7, 0x2d, 0x8b, 0x6b, 0x63, 0x04, 0x7d, 0xdf, 0xb2, 0xb8, 0x2e, 0x8c, 0x11, 0x80, 0x20, 0x08, 0x82, 0xa0, 0x30, + 0x46, 0x10, 0xd7, 0xa3, 0xd9, 0x7e, 0x63, 0x04, 0x37, 0x3a, 0xcb, 0xf0, 0x37, 0x46, 0x90, 0xa2, 0x6e, 0x59, 0x7b, + 0x63, 0x04, 0x7b, 0xfa, 0xc7, 0xe5, 0x37, 0x46, 0x30, 0xcb, 0x30, 0x2f, 0x7f, 0x63, 0x04, 0xf0, 0xcd, 0xdf, 0xbd, + 0x37, 0x46, 0xc0, 0xfe, 0x75, 0xba, 0x7b, 0x63, 0x04, 0xf4, 0xfa, 0x82, 0xe9, 0x37, 0x46, 0x90, 0xca, 0xb1, 0x28, + 0x7f, 0x63, 0x04, 0x2e, 0xbe, 0xea, 0xff, 0x37, 0x46, 0xe0, 0xc2, 0xbb, 0x1d, 0x82, 0xc3, 0x18, 0x01, 0x1e, 0xb6, + 0xad, 0xfb, 0x8d, 0x11, 0xd0, 0x65, 0x8b, 0xb6, 0xde, 0x18, 0xc1, 0xde, 0xe3, 0x30, 0xfd, 0x8d, 0x11, 0x80, 0x6b, + 0xe8, 0xd3, 0xbf, 0x30, 0x46, 0x00, 0x82, 0x20, 0x08, 0x77, 0x63, 0x04, 0x3c, 0xbc, 0xea, 0x74, 0x37, 0x46, 0x70, + 0x8f, 0xf3, 0x2d, 0x66, 0x63, 0x04, 0xb5, 0x5a, 0xab, 0xed, 0x37, 0x46, 0xd0, 0xc7, 0xa2, 0x8b, 0x7f, 0x63, 0x04, + 0xb2, 0xe8, 0xf6, 0x34, 0x18, 0x8c, 0x11, 0xb8, 0x7d, 0x2c, 0xda, 0xbe, 0x30, 0x46, 0x00, 0x82, 0x20, 0x08, 0xff, + 0xc2, 0x18, 0x01, 0x08, 0x82, 0xa0, 0x0e, 0x06, 0x63, 0x04, 0x37, 0xde, 0x8e, 0x2d, 0x37, 0x46, 0x00, 0x82, 0x20, + 0x48, 0x7f, 0x63, 0x04, 0x20, 0x08, 0x82, 0x20, 0x18, 0x8c, 0x11, 0xb0, 0x6d, 0xfc, 0xca, 0xdb, 0x18, 0x81, 0xac, + 0x82, 0x6f, 0xf9, 0x8d, 0x11, 0xa4, 0xb4, 0x6a, 0xd3, 0xdf, 0x18, 0x81, 0x6b, 0xef, 0x2a, 0xde, 0x0b, 0x63, 0x04, + 0xa9, 0xb9, 0xd7, 0xec, 0x37, 0x46, 0x90, 0xf6, 0x7e, 0x5e, 0x7e, 0x63, 0x04, 0x2c, 0xc9, 0xc2, 0xbd, 0x2f, 0x8c, + 0x11, 0xe8, 0x6f, 0x5c, 0xe2, 0xbe, 0x30, 0x46, 0xb0, 0xef, 0xb0, 0xbb, 0xfb, 0xc2, 0x18, 0x81, 0xb9, 0xcf, 0xaa, + 0xfe, 0x0b, 0x63, 0x04, 0x6b, 0xd9, 0x93, 0x28, 0x18, 0x8c, 0x11, 0xb0, 0x2c, 0x7b, 0x96, 0xe0, 0x30, 0x46, 0x20, + 0xee, 0x60, 0x6e, 0x7e, 0x63, 0x04, 0x6d, 0x78, 0x93, 0xfa, 0x2f, 0x8c, 0x11, 0x90, 0x72, 0xe8, 0x8a, 0x60, 0x30, + 0x46, 0xa0, 0xae, 0x70, 0xaf, 0xfe, 0xc2, 0x18, 0xc1, 0xac, 0xff, 0x32, 0xde, 0x0b, 0x63, 0x04, 0x7e, 0x4e, 0xa2, + 0xf3, 0x2f, 0x8c, 0x11, 0xf8, 0x38, 0x59, 0xf7, 0xdf, 0x18, 0x41, 0xfb, 0xf3, 0x79, 0xfc, 0x8d, 0x11, 0xd4, 0x6f, + 0xbd, 0xab, 0xdf, 0x18, 0xc1, 0x3e, 0x92, 0x24, 0xed, 0x8d, 0x11, 0xfc, 0x2e, 0xfc, 0xe3, 0xde, 0x18, 0x41, 0x1b, + 0x97, 0xb5, 0xfd, 0x8d, 0x11, 0x9c, 0x6e, 0x8e, 0x97, 0xde, 0x18, 0xc1, 0x78, 0xe3, 0xb1, 0xea, 0x8d, 0x11, 0xf0, + 0x71, 0x0e, 0xfa, 0xde, 0x18, 0x81, 0x1c, 0xae, 0x7d, 0xfd, 0x8d, 0x11, 0xf0, 0xf8, 0x4b, 0xf7, 0xdf, 0x18, 0xc1, + 0xe8, 0xae, 0x7e, 0xfc, 0x0b, 0x63, 0x04, 0x72, 0x0f, 0xd6, 0xba, 0x2f, 0x8c, 0x11, 0xd0, 0x2e, 0xae, 0x82, 0xbf, + 0x30, 0x46, 0x60, 0xbe, 0x21, 0x99, 0x7f, 0x63, 0x04, 0x25, 0x4b, 0xe7, 0xa2, 0x2f, 0x8c, 0x11, 0xc4, 0x2f, 0x9a, + 0xb2, 0xbe, 0x30, 0x46, 0x30, 0x8f, 0x24, 0x0a, 0xfe, 0xc2, 0x18, 0x81, 0x4d, 0x96, 0x67, 0xfe, 0x8d, 0x11, 0x80, + 0x20, 0x08, 0xd2, 0xbf, 0x30, 0x46, 0xb0, 0xc7, 0x6a, 0xbc, 0x82, 0xc3, 0x18, 0x01, 0x08, 0x82, 0x20, 0xfa, 0x8d, + 0x11, 0xbc, 0x7b, 0x5a, 0xde, 0xdf, 0x18, 0x81, 0xdb, 0xc7, 0xa2, 0xed, 0x8d, 0x11, 0x98, 0xf7, 0xba, 0xca, 0xde, + 0x18, 0x01, 0x08, 0x82, 0x20, 0xfe, 0x8d, 0x00, 0x00, 0x00, 0x23, 0x06, 0x09, 0x00, 0x82, 0x60, 0x50, 0x06, 0xfe, + 0x50, 0x0e, 0xb6, 0xd0, 0x0f, 0xfd, 0x30, 0x0f, 0x23, 0x06, 0x09, 0x00, 0x82, 0x60, 0x50, 0x06, 0xff, 0x60, 0x0e, + 0xb7, 0xd0, 0x0f, 0xfd, 0x40, 0x0f, 0x23, 0x06, 0x09, 0x00, 0x82, 0x60, 0x50, 0x06, 0x20, 0x71, 0x0e, 0xb7, 0x90, + 0x0f, 0xf9, 0x50, 0x0f, 0x23, 0x06, 0x09, 0x00, 0x82, 0x60, 0x50, 0x06, 0x21, 0x81, 0x0e, 0xb8, 0xc0, 0x0f, 0xfc, + 0x60, 0x0f, 0x23, 0x06, 0x09, 0x00, 0x82, 0x60, 0x50, 0x06, 0x22, 0x91, 0x0e, 0xb9, 0x10, 0x12, 0x21, 0x71, 0x0f, + 0x23, 0x06, 0x09, 0x00, 0x82, 0x60, 0x50, 0x06, 0x23, 0xa1, 0x0e, 0xba, 0x10, 0x12, 0x21, 0x81, 0x0f, 0x23, 0x06, + 0x09, 0x00, 0x82, 0x60, 0x50, 0x06, 0x24, 0xb1, 0x0e, 0xbd, 0x20, 0x12, 0x22, 0x91, 0x0f, 0x23, 0x06, 0x09, 0x00, + 0x82, 0x60, 0x50, 0x06, 0x25, 0xc1, 0x0e, 0xbf, 0x30, 0x12, 0x23, 0xa1, 0x0f, 0x23, 0x06, 0x06, 0x00, 0x82, 0x60, + 0x40, 0xc8, 0x04, 0x39, 0x90, 0xc4, 0x88, 0x81, 0x01, 0x80, 0x20, 0x18, 0x10, 0x33, 0x51, 0x0e, 0x26, 0x31, 0x62, + 0x70, 0x00, 0x20, 0x08, 0x06, 0x60, 0xa0, 0x12, 0xf3, 0x30, 0x98, 0xc4, 0x68, 0x42, 0x20, 0x0c, 0x37, 0x10, 0x01, + 0x19, 0xcc, 0x32, 0x04, 0xed, 0x10, 0x8c, 0x26, 0x0c, 0xc3, 0x70, 0x43, 0x11, 0x90, 0xc1, 0x2c, 0x83, 0xd0, 0x0e, + 0xc1, 0x1d, 0x46, 0xdd, 0x61, 0x94, 0x09, 0xbd, 0x00, 0x1f, 0x13, 0x7c, 0x01, 0x3e, 0x87, 0x18, 0x75, 0x87, 0x51, + 0x46, 0x08, 0xf4, 0x31, 0x42, 0xa0, 0xcf, 0x68, 0x42, 0x03, 0x0c, 0x37, 0x04, 0x31, 0x01, 0x06, 0xb3, 0x0c, 0x43, + 0x14, 0x8c, 0x18, 0x1c, 0x00, 0x08, 0x82, 0x01, 0x18, 0xe4, 0x84, 0x48, 0x48, 0x2e, 0x31, 0x9a, 0x10, 0x04, 0xc3, + 0x0d, 0xc1, 0x4d, 0x80, 0xc1, 0x2c, 0x03, 0x51, 0x04, 0x23, 0x06, 0x0f, 0x00, 0x82, 0x60, 0xb0, 0x94, 0xc5, 0x3f, + 0x5c, 0xd6, 0x61, 0xb4, 0x43, 0x3b, 0xe0, 0x04, 0x4e, 0xa4, 0x44, 0x37, 0x9a, 0x10, 0x00, 0xa3, 0x09, 0x42, 0x30, + 0x9a, 0x30, 0x08, 0xa3, 0x09, 0xc4, 0x30, 0x62, 0xe0, 0x00, 0x20, 0x08, 0x06, 0x4d, 0x5a, 0xa0, 0x04, 0x18, 0x64, + 0x98, 0x4b, 0x10, 0x83, 0x10, 0xd4, 0xc3, 0x2c, 0x41, 0x3b, 0x8c, 0x18, 0x1c, 0x00, 0x08, 0x82, 0x01, 0x84, 0x16, + 0xfe, 0xc0, 0xf5, 0xc4, 0x68, 0x42, 0x00, 0x8c, 0x26, 0x08, 0xc1, 0x70, 0x83, 0x90, 0x81, 0xc1, 0x70, 0x83, 0x80, + 0x81, 0x41, 0x09, 0x81, 0xce, 0x32, 0x18, 0x47, 0x30, 0x62, 0xd0, 0x00, 0x20, 0x08, 0x06, 0x52, 0x5b, 0xac, 0x84, + 0x18, 0x8c, 0xc5, 0xe7, 0xd1, 0x04, 0x4d, 0xd0, 0x04, 0x4d, 0x8c, 0x26, 0x04, 0xc0, 0x68, 0x82, 0x10, 0x8c, 0x26, + 0x0c, 0xc2, 0x68, 0x02, 0x31, 0x8c, 0x18, 0x38, 0x00, 0x08, 0x82, 0x41, 0x43, 0x17, 0x33, 0xb1, 0x06, 0x64, 0x30, + 0x06, 0x39, 0x41, 0x0c, 0x42, 0x00, 0x12, 0xb3, 0x04, 0xed, 0x70, 0x8a, 0x51, 0x16, 0x78, 0xf2, 0xb1, 0x60, 0xa3, + 0x4f, 0x9d, 0x01, 0x5b, 0xc0, 0x05, 0x46, 0x59, 0x11, 0xc8, 0xc7, 0x82, 0x8f, 0x3e, 0x07, 0x19, 0x65, 0x01, 0x19, + 0xc8, 0xc7, 0x82, 0x30, 0xa0, 0x4f, 0xb5, 0xc1, 0x5c, 0xc0, 0x05, 0x46, 0x59, 0x11, 0xc8, 0xc7, 0x82, 0x32, 0xa0, + 0x8f, 0x29, 0x6b, 0x10, 0x1f, 0x7b, 0x02, 0xf9, 0x58, 0x90, 0x06, 0xf4, 0x31, 0xa3, 0x0d, 0xe2, 0x63, 0x4b, 0x20, + 0x1f, 0x0b, 0xd6, 0x80, 0x3e, 0x16, 0x10, 0xf2, 0x19, 0x31, 0x30, 0x00, 0x10, 0x04, 0x03, 0xca, 0x34, 0xc4, 0x62, + 0xba, 0x60, 0xa0, 0x11, 0x03, 0x03, 0x00, 0x41, 0x30, 0xa0, 0x50, 0xc3, 0x27, 0xa2, 0x0b, 0x06, 0xb2, 0xe0, 0x2c, + 0x40, 0x30, 0x62, 0x60, 0x00, 0x20, 0x08, 0x06, 0xd4, 0x6a, 0x9c, 0x45, 0x74, 0xc1, 0x40, 0x23, 0x06, 0x06, 0x00, + 0x82, 0x60, 0x40, 0xb5, 0xc6, 0x58, 0x3c, 0x17, 0x0c, 0x64, 0x01, 0x5b, 0x80, 0x60, 0xb8, 0x81, 0x08, 0xcc, 0x60, + 0x96, 0x21, 0x41, 0x82, 0x59, 0x02, 0xc5, 0xd2, 0xc0, 0x2d, 0x40, 0x30, 0x4b, 0x00, 0x0d, 0x54, 0x18, 0xb1, 0xe0, + 0xf0, 0x04, 0x32, 0x50, 0x61, 0xc4, 0x82, 0xe3, 0x13, 0xc8, 0x40, 0x85, 0x11, 0x0b, 0x0e, 0x58, 0x20, 0x03, 0x15, + 0x43, 0x2c, 0x38, 0x12, 0x62, 0x6e, 0x30, 0x17, 0x20, 0x18, 0x31, 0x38, 0x00, 0x10, 0x04, 0x03, 0xcb, 0x36, 0xe4, + 0x42, 0x70, 0x8d, 0x11, 0x83, 0x03, 0x00, 0x41, 0x30, 0xb0, 0x6e, 0x43, 0x2e, 0x02, 0xe1, 0x08, 0xc3, 0x46, 0x0c, + 0x0e, 0x00, 0x04, 0xc1, 0x00, 0xc3, 0x8d, 0xbf, 0x00, 0x83, 0xc0, 0x04, 0x5c, 0x80, 0xcf, 0x88, 0xc1, 0x01, 0x80, + 0x20, 0x18, 0x60, 0xba, 0xf1, 0x17, 0x5e, 0x60, 0xc1, 0x10, 0x9f, 0x11, 0x83, 0x03, 0x00, 0x41, 0x30, 0xc0, 0x78, + 0x63, 0x34, 0x02, 0x5d, 0x18, 0x6e, 0xc0, 0x2a, 0x33, 0x98, 0x65, 0x70, 0x96, 0x60, 0x96, 0x80, 0x19, 0xa8, 0x30, + 0xd6, 0x80, 0xe1, 0x96, 0x81, 0x0a, 0x63, 0x0d, 0x18, 0x6e, 0x19, 0xa8, 0x30, 0xd6, 0x80, 0xe1, 0x96, 0x81, 0x8a, + 0x61, 0x0d, 0x18, 0x3c, 0x58, 0xac, 0x0f, 0x42, 0x03, 0x04, 0x23, 0x06, 0x07, 0x00, 0x82, 0x60, 0x60, 0x91, 0x07, + 0x68, 0x08, 0xbc, 0x31, 0x62, 0x70, 0x00, 0x20, 0x08, 0x06, 0x56, 0x79, 0x80, 0x46, 0x20, 0x1c, 0x61, 0xd8, 0x88, + 0xc1, 0x01, 0x80, 0x20, 0x18, 0x60, 0xe6, 0xd1, 0x1a, 0x75, 0x10, 0x98, 0x60, 0x0e, 0xf0, 0x19, 0x31, 0x38, 0x00, + 0x10, 0x04, 0x03, 0x0c, 0x3d, 0x5a, 0x63, 0x0e, 0x02, 0x0b, 0x86, 0xf8, 0x8c, 0x18, 0x1c, 0x00, 0x08, 0x82, 0x01, + 0xa6, 0x1e, 0xb1, 0x11, 0xa0, 0x83, 0x05, 0x8f, 0x7c, 0x46, 0x0c, 0x1a, 0x00, 0x04, 0xc1, 0x40, 0x7a, 0x8f, 0xd6, + 0x20, 0x87, 0xf2, 0x40, 0x2c, 0xdb, 0xb0, 0x0d, 0xdb, 0xb0, 0x8d, 0xd1, 0x84, 0x00, 0x18, 0x4d, 0x10, 0x82, 0xd1, + 0x84, 0x41, 0xb0, 0xa1, 0x90, 0x8f, 0x0d, 0x86, 0x7c, 0x6c, 0x38, 0xe4, 0x63, 0x43, 0x05, 0x1f, 0x1b, 0x2a, 0xf8, + 0xd8, 0x50, 0xc1, 0xc7, 0x2a, 0xf8, 0x00, 0xc1, 0x70, 0x83, 0xf5, 0x06, 0x68, 0x30, 0xcb, 0xc0, 0x34, 0xc1, 0x2c, + 0x81, 0x33, 0x50, 0x61, 0xc0, 0x82, 0xa2, 0x34, 0x03, 0x15, 0x06, 0x2c, 0x28, 0x4a, 0x33, 0x50, 0x61, 0xc0, 0x82, + 0xa2, 0x34, 0x86, 0x06, 0xf5, 0x01, 0x82, 0xe1, 0x86, 0x34, 0x78, 0x03, 0x34, 0x98, 0x65, 0x50, 0x9e, 0x60, 0x96, + 0x00, 0x1a, 0xa8, 0x18, 0x78, 0x21, 0x91, 0x85, 0x67, 0xa0, 0xc2, 0xc0, 0x8f, 0x84, 0x79, 0x06, 0x2a, 0x0c, 0xfd, + 0x48, 0x98, 0x67, 0xa0, 0xc2, 0xe0, 0x8f, 0x84, 0x79, 0x8c, 0x17, 0xc0, 0x03, 0x04, 0x36, 0x0b, 0xee, 0x20, 0x1f, + 0x0b, 0xda, 0x81, 0x3e, 0x23, 0x06, 0x06, 0x00, 0x82, 0x60, 0x40, 0x99, 0x88, 0x78, 0x04, 0x17, 0x0c, 0x34, 0x62, + 0x70, 0x00, 0x20, 0x08, 0x06, 0xd6, 0x89, 0x8c, 0x47, 0xf0, 0x1f, 0x23, 0x06, 0x07, 0x00, 0x82, 0x60, 0x60, 0xa1, + 0xc8, 0x78, 0x04, 0x86, 0xc9, 0xc2, 0x3c, 0xc8, 0xc7, 0x02, 0x79, 0xa0, 0xcf, 0x88, 0x81, 0x01, 0x80, 0x20, 0x18, + 0x50, 0x2c, 0x82, 0x1e, 0xc1, 0x05, 0x03, 0x8d, 0x18, 0x1c, 0x00, 0x08, 0x82, 0x81, 0xd5, 0x22, 0xe9, 0x11, 0x94, + 0xc8, 0x88, 0xc1, 0x01, 0x80, 0x20, 0x18, 0x58, 0x2e, 0x92, 0x1e, 0x01, 0x34, 0x62, 0xd0, 0x00, 0x20, 0x08, 0x06, + 0x52, 0x8c, 0xbc, 0x87, 0x49, 0x9c, 0xc8, 0x11, 0xe0, 0x07, 0x7e, 0xe0, 0x07, 0x7e, 0x8c, 0x26, 0x04, 0x83, 0x49, + 0xad, 0x40, 0x1f, 0x93, 0x5c, 0x81, 0x3e, 0x26, 0xbd, 0x02, 0x7d, 0x46, 0x0c, 0x1c, 0x00, 0x04, 0xc1, 0xa0, 0xc1, + 0x91, 0xfb, 0x78, 0x09, 0x94, 0x38, 0x89, 0xfe, 0x18, 0x84, 0x80, 0x20, 0x8f, 0x59, 0x82, 0x76, 0x18, 0x6e, 0xc8, + 0x07, 0x16, 0x01, 0x83, 0x59, 0x06, 0xe9, 0x0a, 0x46, 0x0c, 0x1a, 0x00, 0x04, 0xc1, 0x40, 0xba, 0x91, 0xfa, 0x60, + 0x89, 0x16, 0x49, 0x09, 0x94, 0xf0, 0x0f, 0xff, 0xf0, 0x0f, 0xff, 0x18, 0x4d, 0x08, 0x80, 0xd1, 0x04, 0x21, 0x18, + 0x4d, 0x18, 0x84, 0xd1, 0x04, 0x62, 0x18, 0x31, 0x38, 0x00, 0x10, 0x04, 0x03, 0x2c, 0x47, 0x40, 0x84, 0xb8, 0x89, + 0x11, 0x83, 0x03, 0x00, 0x41, 0x30, 0xc0, 0x74, 0x24, 0x44, 0x08, 0x9c, 0x18, 0x31, 0x38, 0x00, 0x10, 0x04, 0x03, + 0x6c, 0x47, 0x44, 0x84, 0xc8, 0x89, 0x11, 0x03, 0x07, 0x00, 0x41, 0x30, 0x68, 0xc2, 0x04, 0x44, 0x72, 0x22, 0x26, + 0x60, 0xc2, 0x44, 0x06, 0x21, 0x20, 0xda, 0x63, 0xc4, 0xe0, 0x00, 0x40, 0x10, 0x0c, 0xc0, 0x20, 0x47, 0x44, 0x44, + 0x26, 0x64, 0x64, 0x34, 0x21, 0x00, 0x86, 0x1b, 0x82, 0x1b, 0x01, 0x83, 0x59, 0x06, 0x6a, 0x0a, 0x46, 0x0c, 0x1c, + 0x00, 0x04, 0xc1, 0xa0, 0x21, 0x93, 0x11, 0xd9, 0x09, 0x9a, 0x98, 0x89, 0x14, 0x31, 0x0a, 0xe2, 0x80, 0x8f, 0x59, + 0x82, 0x76, 0x18, 0x31, 0x38, 0x00, 0x10, 0x04, 0x83, 0xae, 0x47, 0x4a, 0xa4, 0x26, 0x70, 0x64, 0x34, 0x21, 0x08, + 0x46, 0x0c, 0x0e, 0x00, 0x04, 0xc1, 0x00, 0x0b, 0x13, 0x14, 0x09, 0x5c, 0xc3, 0x92, 0x80, 0x3e, 0x96, 0x08, 0xf4, + 0xb1, 0x64, 0xa0, 0xcf, 0x88, 0xc1, 0x02, 0x80, 0x20, 0x18, 0x68, 0x64, 0x82, 0x22, 0x83, 0x10, 0xc4, 0x07, 0x7c, + 0xbc, 0xc7, 0x70, 0x44, 0x20, 0x1b, 0xc2, 0x37, 0xcb, 0x50, 0x59, 0x81, 0x09, 0x6c, 0x01, 0x1f, 0x0b, 0xd8, 0x42, + 0x3e, 0x23, 0x06, 0x06, 0x00, 0x82, 0x60, 0x40, 0xad, 0x89, 0x8c, 0x04, 0x96, 0x16, 0x41, 0x7c, 0x2c, 0x70, 0x0b, + 0xf9, 0x58, 0x80, 0x1b, 0xf0, 0xb1, 0x00, 0xa1, 0x8f, 0x05, 0x8c, 0x7c, 0x4c, 0x60, 0xe4, 0x63, 0x03, 0x23, 0x9f, + 0x59, 0x02, 0x6b, 0xa0, 0xc2, 0x30, 0x2a, 0x8f, 0x1a, 0xa8, 0x30, 0x8c, 0xca, 0xa3, 0x06, 0x2a, 0x0c, 0xa3, 0xf2, + 0xa8, 0x11, 0x03, 0x03, 0x00, 0x41, 0x30, 0xa0, 0xec, 0x84, 0x4d, 0x86, 0x11, 0x03, 0x03, 0x00, 0x41, 0x30, 0xa0, + 0xee, 0xa4, 0x4d, 0x86, 0x11, 0x03, 0x03, 0x00, 0x41, 0x30, 0xa0, 0xf0, 0xc4, 0x4d, 0x86, 0xe1, 0x88, 0x81, 0x3d, + 0x88, 0x6f, 0x38, 0x62, 0x68, 0x0f, 0xe2, 0x1b, 0x8e, 0x18, 0xdc, 0x83, 0xf8, 0xa6, 0x1b, 0xf0, 0x22, 0x2f, 0x86, + 0xe9, 0x86, 0xbc, 0xd0, 0x8b, 0x61, 0xba, 0x41, 0x2f, 0xf6, 0x62, 0x18, 0x31, 0x38, 0x00, 0x10, 0x04, 0x03, 0xac, + 0x4f, 0xc8, 0x24, 0x89, 0x8f, 0x11, 0x83, 0x03, 0x00, 0x41, 0x30, 0xc0, 0xfc, 0xa4, 0x4c, 0x12, 0xf9, 0x18, 0x31, + 0x38, 0x00, 0x10, 0x04, 0x03, 0xec, 0x4f, 0xcc, 0x24, 0x99, 0x8f, 0x11, 0x03, 0x03, 0x00, 0x41, 0x30, 0xa0, 0x44, + 0x05, 0x4d, 0x86, 0x11, 0x03, 0x03, 0x00, 0x41, 0x30, 0xa0, 0x46, 0x25, 0x4d, 0x86, 0x11, 0x03, 0x03, 0x00, 0x41, + 0x30, 0xa0, 0x48, 0x45, 0x4d, 0x06, 0x1b, 0xec, 0x43, 0x3e, 0x36, 0xdc, 0x87, 0x7c, 0x6c, 0xc0, 0x0f, 0xf9, 0x8c, + 0x18, 0x18, 0x00, 0x08, 0x82, 0x01, 0x85, 0x2a, 0x6f, 0x32, 0x8c, 0x18, 0x18, 0x00, 0x08, 0x82, 0x01, 0x95, 0x2a, + 0x70, 0x32, 0x8c, 0x18, 0x18, 0x00, 0x08, 0x82, 0x01, 0xa5, 0x2a, 0x71, 0x32, 0xd8, 0xb0, 0x1f, 0xf2, 0xb1, 0x81, + 0x3f, 0xe4, 0x63, 0x43, 0x7f, 0xc8, 0xc7, 0xb0, 0xfe, 0x90, 0x8f, 0x61, 0xfe, 0x21, 0x1f, 0xc3, 0xfe, 0x43, 0x3e, + 0xf6, 0x1f, 0x43, 0x7c, 0x2c, 0x38, 0xe0, 0x63, 0x21, 0x42, 0xc4, 0xc7, 0x02, 0x04, 0x3e, 0x36, 0x22, 0x45, 0x7c, + 0x2c, 0x48, 0xe0, 0x63, 0xc5, 0x26, 0x1f, 0x23, 0x36, 0xf9, 0xd8, 0xb0, 0xc9, 0xc7, 0x06, 0x06, 0x3e, 0x36, 0x30, + 0xf0, 0xb1, 0x81, 0x81, 0xcf, 0x88, 0x81, 0x03, 0x80, 0x20, 0x18, 0x34, 0xe0, 0xf2, 0x27, 0xb7, 0x01, 0x1b, 0xaf, + 0x51, 0x2a, 0x83, 0x10, 0x8c, 0x02, 0x9b, 0xcc, 0x12, 0xb4, 0xc3, 0x88, 0xc1, 0x01, 0x80, 0x20, 0x18, 0x80, 0x01, + 0xae, 0x84, 0x4a, 0x6c, 0xbc, 0xca, 0x68, 0x42, 0x10, 0x0c, 0x37, 0x04, 0xb7, 0x02, 0x06, 0xb3, 0x0c, 0x58, 0x16, + 0x0c, 0x47, 0x98, 0xc6, 0x99, 0x10, 0xdf, 0x88, 0xc1, 0x01, 0x80, 0x20, 0x18, 0x80, 0x01, 0xaf, 0x94, 0x4a, 0x6d, + 0xc4, 0xca, 0x68, 0x42, 0x00, 0x0c, 0x37, 0x04, 0xba, 0x12, 0x06, 0x45, 0x04, 0x7c, 0xc1, 0x10, 0xc6, 0x1a, 0x33, + 0x02, 0x9f, 0xe9, 0x86, 0xd6, 0x08, 0x0e, 0x0b, 0x62, 0x44, 0x3e, 0x16, 0xd8, 0x08, 0x7c, 0xec, 0x35, 0x6e, 0x04, + 0x3e, 0x23, 0x06, 0x07, 0x00, 0x82, 0x60, 0xd0, 0x9d, 0xcb, 0xab, 0xfc, 0x46, 0xaf, 0x8c, 0x26, 0x04, 0x83, 0x11, + 0x01, 0x7d, 0x2c, 0xa8, 0x13, 0xf8, 0x58, 0x81, 0x23, 0xf2, 0xb1, 0x80, 0xa0, 0x8f, 0x05, 0x78, 0x02, 0x9f, 0xe1, + 0x08, 0xc2, 0x3d, 0x88, 0x6f, 0x38, 0xa2, 0x80, 0x0f, 0xe1, 0x2b, 0x21, 0xd8, 0xe1, 0x08, 0x22, 0x3e, 0x88, 0xaf, + 0x84, 0x60, 0x87, 0x23, 0x0c, 0xfa, 0x10, 0xbe, 0x0a, 0x84, 0xbd, 0x60, 0x88, 0x11, 0x03, 0x03, 0x00, 0x41, 0x30, + 0xa0, 0xf4, 0xe5, 0x5d, 0xa8, 0x11, 0x83, 0x03, 0x00, 0x41, 0x30, 0xb0, 0xf4, 0x05, 0x56, 0xd6, 0x63, 0x5e, 0x2e, + 0x30, 0xca, 0xf4, 0x23, 0xa0, 0xcf, 0x70, 0x04, 0x11, 0x10, 0xdf, 0x05, 0x43, 0xcc, 0x12, 0x6c, 0xc3, 0x0d, 0x62, + 0x40, 0x2f, 0x60, 0x30, 0xcb, 0xa0, 0x6d, 0xc1, 0x88, 0xc1, 0x01, 0x80, 0x20, 0x18, 0x74, 0xfd, 0x52, 0x2e, 0xf5, + 0x31, 0x2f, 0xa3, 0x09, 0x81, 0x30, 0x1c, 0x91, 0x1e, 0x01, 0xf1, 0x8d, 0x18, 0x1c, 0x00, 0x08, 0x82, 0x01, 0x18, + 0xfc, 0x0b, 0xba, 0xe0, 0x07, 0xbd, 0x8c, 0x26, 0x04, 0xc0, 0x70, 0x43, 0xd0, 0x2f, 0x61, 0x50, 0x44, 0xc0, 0x17, + 0x0c, 0x61, 0xef, 0x71, 0xc4, 0x67, 0xc4, 0xc0, 0x00, 0x40, 0x10, 0x0c, 0x28, 0x95, 0xf9, 0x97, 0x60, 0xc4, 0xe0, + 0x00, 0x40, 0x10, 0x0c, 0x2c, 0x95, 0x01, 0x97, 0xfd, 0x18, 0x99, 0x0b, 0x8c, 0x32, 0x15, 0x09, 0xe8, 0x33, 0x1c, + 0x41, 0x04, 0xc4, 0x77, 0xc1, 0x10, 0xb3, 0x04, 0xdb, 0x40, 0x87, 0x41, 0x0a, 0x18, 0xfb, 0x68, 0xec, 0x93, 0x0d, + 0x74, 0x18, 0xa0, 0x80, 0xb1, 0x8f, 0xc6, 0x3e, 0xd9, 0x40, 0xc7, 0xa0, 0x0b, 0x18, 0xa5, 0xc9, 0x58, 0x36, 0xd0, + 0x31, 0xa0, 0x01, 0x86, 0x68, 0x34, 0x96, 0x0d, 0x74, 0x0c, 0x76, 0x80, 0xd9, 0x98, 0x66, 0x63, 0xd9, 0x88, 0xc1, + 0x03, 0x80, 0x20, 0x18, 0x2c, 0x39, 0x33, 0x2f, 0x2b, 0xa2, 0x22, 0x05, 0x11, 0x2e, 0xe1, 0xc2, 0x32, 0x2c, 0xd3, + 0x2f, 0x31, 0x32, 0x9a, 0x10, 0x00, 0xa3, 0x09, 0x42, 0x30, 0x9a, 0x30, 0x08, 0x23, 0x06, 0x07, 0x00, 0x82, 0x60, + 0x00, 0x06, 0x34, 0xd3, 0x2f, 0x2d, 0xd2, 0x32, 0xa3, 0x09, 0x01, 0x30, 0xdc, 0x10, 0xc8, 0x0c, 0x18, 0xcc, 0x32, + 0x70, 0x5d, 0x30, 0x62, 0x60, 0x00, 0x20, 0x08, 0x06, 0x14, 0xcf, 0xc8, 0x8c, 0x31, 0x62, 0x60, 0x00, 0x20, 0x08, + 0x06, 0x54, 0xcf, 0xcc, 0x8c, 0x31, 0x62, 0x60, 0x00, 0x20, 0x08, 0x06, 0x94, 0xcf, 0xd0, 0x8c, 0x31, 0x1c, 0x31, + 0xf4, 0x08, 0xf1, 0x0d, 0x47, 0x0c, 0x3e, 0x42, 0x7c, 0xc3, 0x11, 0xc3, 0x8f, 0x10, 0xdf, 0x74, 0x83, 0x8f, 0xfc, + 0xc8, 0x30, 0xdd, 0xf0, 0x23, 0x60, 0x32, 0x4c, 0x37, 0x80, 0x49, 0x98, 0x0c, 0x96, 0x90, 0x09, 0x7c, 0x2c, 0x29, + 0x13, 0xf8, 0x58, 0x62, 0x26, 0xf0, 0xb1, 0x01, 0x4d, 0xe4, 0x63, 0x43, 0x9a, 0xc8, 0xc7, 0x06, 0x35, 0x91, 0xcf, + 0x88, 0x81, 0x01, 0x80, 0x20, 0x18, 0x50, 0x6b, 0x13, 0x33, 0xc3, 0x88, 0x81, 0x01, 0x80, 0x20, 0x18, 0x50, 0x6c, + 0x23, 0x33, 0xc3, 0x88, 0x81, 0x01, 0x80, 0x20, 0x18, 0x50, 0x6d, 0x33, 0x33, 0x83, 0x0d, 0xfc, 0x22, 0x1f, 0x1b, + 0xfa, 0x45, 0x3e, 0x36, 0xf8, 0x8b, 0x7c, 0x46, 0x0c, 0x0c, 0x00, 0x04, 0xc1, 0x80, 0x8a, 0x1b, 0x9c, 0x19, 0x46, + 0x0c, 0x0c, 0x00, 0x04, 0xc1, 0x80, 0x92, 0x9b, 0x9c, 0x19, 0x46, 0x0c, 0x0c, 0x00, 0x04, 0xc1, 0x80, 0x9a, 0x1b, + 0x9d, 0x19, 0x0c, 0x0b, 0x19, 0xf9, 0x18, 0x26, 0x32, 0xf2, 0x31, 0x6c, 0x64, 0xe4, 0x63, 0xc6, 0x10, 0x1f, 0x33, + 0x86, 0xf8, 0x98, 0x31, 0xc4, 0xc7, 0x06, 0x4c, 0x3e, 0x36, 0x60, 0xf2, 0xb1, 0x01, 0x93, 0x8f, 0x0d, 0x09, 0x7c, + 0x6c, 0x48, 0xe0, 0x63, 0x43, 0x02, 0x9f, 0x59, 0x82, 0x6e, 0xa0, 0xc2, 0x30, 0x38, 0x5a, 0xd8, 0x06, 0x2a, 0x0c, + 0x83, 0xa3, 0x85, 0x6d, 0xa0, 0xc2, 0x30, 0x38, 0x5a, 0xd8, 0x46, 0x0c, 0x1e, 0x00, 0x04, 0xc1, 0x60, 0x39, 0x9d, + 0xb0, 0xd1, 0x13, 0x3c, 0x99, 0x03, 0x39, 0x78, 0x99, 0x97, 0xd1, 0x1b, 0xbd, 0x59, 0x9b, 0x3f, 0x19, 0x4d, 0x08, + 0x80, 0xd1, 0x04, 0x21, 0x18, 0x4d, 0x18, 0x84, 0x59, 0x06, 0xef, 0x63, 0x83, 0x11, 0x03, 0x03, 0x00, 0x41, 0x30, + 0xa0, 0x4e, 0xa7, 0x6f, 0x86, 0x11, 0x03, 0x03, 0x00, 0x41, 0x30, 0xa0, 0x50, 0xc7, 0x6f, 0x86, 0x11, 0x03, 0x03, + 0x00, 0x41, 0x30, 0xa0, 0x52, 0xe7, 0x6f, 0x86, 0xe1, 0x88, 0x01, 0x55, 0x88, 0x6f, 0x38, 0x62, 0x48, 0x15, 0xe2, + 0x1b, 0x8e, 0x18, 0x54, 0x85, 0xf8, 0xa6, 0x1b, 0x52, 0x45, 0x55, 0x86, 0xe9, 0x06, 0x55, 0x59, 0x95, 0x61, 0xba, + 0x61, 0x55, 0x58, 0x65, 0xb0, 0xe4, 0x55, 0xe0, 0x63, 0x09, 0xac, 0xc0, 0xc7, 0x92, 0x58, 0x81, 0x8f, 0x0d, 0xb3, + 0x22, 0x1f, 0x1b, 0x68, 0x45, 0x3e, 0x36, 0xd4, 0x8a, 0x7c, 0x46, 0x0c, 0x0c, 0x00, 0x04, 0xc1, 0x80, 0xb2, 0x1d, + 0xbe, 0x19, 0x46, 0x0c, 0x0c, 0x00, 0x04, 0xc1, 0x80, 0xba, 0x9d, 0xbe, 0x19, 0x46, 0x0c, 0x0c, 0x00, 0x04, 0xc1, + 0x80, 0xc2, 0x1d, 0xbf, 0x19, 0x6c, 0x38, 0x1b, 0xf9, 0xd8, 0x80, 0x36, 0xf2, 0xb1, 0x21, 0x6d, 0xe4, 0x33, 0x62, + 0x60, 0x00, 0x20, 0x08, 0x06, 0x14, 0xef, 0x8c, 0xce, 0x30, 0x62, 0x60, 0x00, 0x20, 0x08, 0x06, 0x54, 0xef, 0x90, + 0xce, 0x30, 0x62, 0x60, 0x00, 0x20, 0x08, 0x06, 0x94, 0xef, 0x94, 0xce, 0x60, 0x18, 0xdb, 0xc8, 0xc7, 0xb0, 0xb6, + 0x91, 0x8f, 0x61, 0x6e, 0x23, 0x1f, 0x33, 0x86, 0xf8, 0x98, 0x31, 0xc4, 0xc7, 0x8c, 0x21, 0x3e, 0x36, 0x60, 0xf2, + 0xb1, 0x01, 0x93, 0x8f, 0x0d, 0x98, 0x7c, 0x6c, 0x48, 0xe0, 0x63, 0x43, 0x02, 0x1f, 0x1b, 0x12, 0xf8, 0xcc, 0x12, + 0x7c, 0x03, 0x15, 0x86, 0xe1, 0xb9, 0x42, 0x37, 0x50, 0x61, 0x18, 0x9e, 0x2b, 0x74, 0x03, 0x15, 0x86, 0xe1, 0xb9, + 0x42, 0x37, 0xcb, 0x00, 0x06, 0x61, 0xf0, 0x12, 0x23, 0x06, 0x0f, 0x00, 0x82, 0x60, 0xb0, 0xc8, 0x0f, 0xeb, 0x98, + 0xcb, 0xb8, 0xf8, 0x42, 0x2f, 0xe8, 0x8d, 0xde, 0x94, 0x4f, 0xf9, 0xd8, 0x8e, 0xba, 0x8c, 0x26, 0x04, 0xc0, 0x68, + 0x82, 0x10, 0x8c, 0x26, 0x0c, 0xc2, 0x68, 0x02, 0x31, 0xcc, 0x12, 0x88, 0xc1, 0x88, 0x41, 0x03, 0x80, 0x20, 0x18, + 0x48, 0xf4, 0x23, 0x3b, 0xeb, 0xa2, 0x3e, 0xe6, 0x52, 0x2e, 0xbb, 0xb3, 0x3b, 0xbb, 0xb3, 0x3b, 0xa3, 0x09, 0x01, + 0x30, 0x9a, 0x20, 0x04, 0xa3, 0x09, 0x83, 0x30, 0x9a, 0x40, 0x0c, 0xb3, 0x04, 0x62, 0x30, 0x50, 0x61, 0x48, 0x60, + 0x80, 0x84, 0xc1, 0x40, 0x85, 0x21, 0x81, 0x01, 0x12, 0x06, 0x03, 0x15, 0x86, 0x04, 0x06, 0x48, 0x18, 0x0c, 0x54, + 0x18, 0x12, 0x18, 0x20, 0x61, 0x30, 0xcb, 0x30, 0x06, 0x64, 0x10, 0x0e, 0x23, 0x06, 0x07, 0x00, 0x82, 0x60, 0xd0, + 0xe1, 0x0f, 0xf8, 0xc0, 0xcb, 0xfc, 0x8c, 0x26, 0x04, 0xc1, 0x88, 0xc1, 0x01, 0x80, 0x20, 0x18, 0x60, 0xfc, 0x33, + 0x3e, 0x41, 0xda, 0xcc, 0x12, 0x90, 0xc1, 0x40, 0x85, 0x21, 0x8c, 0x01, 0x7e, 0x89, 0x81, 0x21, 0x01, 0x7d, 0x0c, + 0x11, 0xe8, 0x63, 0xc8, 0x40, 0x9f, 0x11, 0x83, 0x03, 0x00, 0x41, 0x30, 0x00, 0x03, 0xff, 0x39, 0x9f, 0x7b, 0xd9, + 0x9f, 0xd1, 0x84, 0x40, 0x18, 0x6e, 0x08, 0xfa, 0x07, 0x0c, 0x66, 0x19, 0xca, 0xc0, 0x0c, 0x82, 0x11, 0x83, 0x03, + 0x00, 0x41, 0x30, 0xe8, 0x44, 0x48, 0x7d, 0xf4, 0xa5, 0x7f, 0x46, 0x13, 0x82, 0xc0, 0x02, 0x54, 0x90, 0x8f, 0x09, + 0xa8, 0x20, 0x1f, 0x1b, 0x50, 0x41, 0x3e, 0x23, 0x06, 0x0e, 0x00, 0x82, 0x60, 0xd0, 0xb8, 0x50, 0xfb, 0x94, 0x8c, + 0xbf, 0xf4, 0xcb, 0xfc, 0x0c, 0x42, 0x00, 0xe9, 0xce, 0x2c, 0x41, 0x3b, 0x0c, 0x37, 0x1c, 0xff, 0x03, 0x06, 0xb3, + 0x0c, 0x67, 0x80, 0x06, 0xc1, 0x88, 0xc1, 0x01, 0x80, 0x20, 0x18, 0x74, 0x28, 0x04, 0x3f, 0x20, 0x33, 0x42, 0xa3, + 0x09, 0x41, 0x60, 0xc1, 0x18, 0xc8, 0xc7, 0x84, 0x31, 0x90, 0x8f, 0x0d, 0x63, 0x20, 0x9f, 0x11, 0x03, 0x07, 0x00, + 0x41, 0x30, 0x68, 0x68, 0x68, 0x7e, 0x56, 0x86, 0x64, 0x46, 0x26, 0x7f, 0x06, 0x21, 0xb0, 0xc0, 0x67, 0x96, 0xa0, + 0x1d, 0x86, 0x1b, 0x9a, 0x11, 0x02, 0x83, 0x59, 0x86, 0x34, 0x50, 0x83, 0xc0, 0xce, 0x40, 0x16, 0xe2, 0x63, 0x67, + 0x20, 0x0b, 0xf1, 0xb1, 0x33, 0x90, 0x85, 0xf8, 0xd8, 0x10, 0x3a, 0xf2, 0xb1, 0x41, 0x74, 0xe4, 0x63, 0xc3, 0xe8, + 0xc8, 0xc7, 0x86, 0xdf, 0x81, 0x8f, 0x0d, 0xe0, 0x03, 0x1f, 0x1b, 0xc2, 0x07, 0x3e, 0x23, 0x06, 0x06, 0x00, 0x82, + 0x60, 0x40, 0xed, 0x50, 0x0c, 0x0d, 0x23, 0x06, 0x06, 0x00, 0x82, 0x60, 0x40, 0xf1, 0x90, 0x0c, 0x0d, 0x23, 0x06, + 0x06, 0x00, 0x82, 0x60, 0x40, 0xf5, 0xd0, 0x0c, 0x0d, 0x23, 0x06, 0x06, 0x00, 0x82, 0x60, 0x40, 0xf9, 0x10, 0x0d, + 0x0d, 0x23, 0x06, 0x06, 0x00, 0x82, 0x60, 0x40, 0xfd, 0x50, 0x0d, 0x0d, 0x23, 0x06, 0x06, 0x00, 0x82, 0x60, 0x40, + 0x81, 0x91, 0x0d, 0x0d, 0xc3, 0x11, 0xc3, 0xcf, 0x10, 0xdf, 0x70, 0xc4, 0x00, 0x36, 0xc4, 0x37, 0x1c, 0x31, 0x84, + 0x0d, 0xf1, 0x4d, 0x37, 0x80, 0x4d, 0xd8, 0x0c, 0xd3, 0x0d, 0x61, 0x23, 0x36, 0xc3, 0x74, 0x83, 0xd8, 0x8c, 0xcd, + 0x60, 0x89, 0xd9, 0xc0, 0xc7, 0x92, 0xb3, 0x81, 0x8f, 0x25, 0x68, 0x03, 0x1f, 0x1b, 0xd4, 0x46, 0x3e, 0x36, 0xac, + 0x8d, 0x7c, 0x6c, 0x60, 0x1b, 0xf9, 0x8c, 0x18, 0x18, 0x00, 0x08, 0x82, 0x01, 0xd5, 0x46, 0x33, 0x34, 0x8c, 0x18, + 0x18, 0x00, 0x08, 0x82, 0x01, 0xe5, 0x46, 0x34, 0x34, 0x8c, 0x18, 0x18, 0x00, 0x08, 0x82, 0x01, 0xf5, 0x46, 0x35, + 0x34, 0xd8, 0xe0, 0x3f, 0xf2, 0xb1, 0xe1, 0x7f, 0xe4, 0x63, 0x03, 0x08, 0xc9, 0x67, 0xc4, 0xc0, 0x00, 0x40, 0x10, + 0x0c, 0xa8, 0x39, 0xd2, 0xa1, 0x61, 0xc4, 0xc0, 0x00, 0x40, 0x10, 0x0c, 0x28, 0x3a, 0xda, 0xa1, 0x61, 0xc4, 0xc0, + 0x00, 0x40, 0x10, 0x0c, 0xa8, 0x3a, 0xe2, 0xa1, 0xc1, 0xb0, 0x11, 0x92, 0x8f, 0x61, 0x24, 0x24, 0x1f, 0xc3, 0x4a, + 0x48, 0x3e, 0x66, 0x0c, 0xf1, 0x31, 0x63, 0x88, 0x8f, 0x19, 0x43, 0x7c, 0x6c, 0xc0, 0xe4, 0x63, 0x03, 0x26, 0x1f, + 0x1b, 0x30, 0xf9, 0xd8, 0x90, 0xc0, 0xc7, 0x86, 0x04, 0x3e, 0x36, 0x24, 0xf0, 0x19, 0x31, 0x38, 0x00, 0x10, 0x04, + 0x83, 0xce, 0x8f, 0xcc, 0xc8, 0x6e, 0xf2, 0x68, 0x34, 0x21, 0x08, 0x2c, 0x28, 0xe4, 0x63, 0x85, 0x20, 0x1f, 0x2b, + 0x06, 0xf9, 0x8c, 0x18, 0x38, 0x00, 0x08, 0x82, 0x41, 0xa3, 0x4a, 0x69, 0x14, 0x3a, 0x7a, 0x93, 0x37, 0x6f, 0x34, + 0x08, 0x01, 0x2b, 0xd8, 0xd0, 0x2c, 0x41, 0x3b, 0x0c, 0x37, 0x8c, 0xc2, 0x1d, 0x81, 0x41, 0x91, 0x02, 0x0f, 0xe9, + 0x70, 0x43, 0x90, 0x47, 0x60, 0x30, 0xcb, 0xb0, 0x06, 0xa2, 0x10, 0x8c, 0x18, 0x1c, 0x00, 0x08, 0x82, 0x01, 0x18, + 0x94, 0x92, 0x1b, 0xf9, 0x8d, 0x1e, 0x8d, 0x26, 0x04, 0xc1, 0x70, 0x43, 0x40, 0x4a, 0x60, 0x30, 0xcb, 0xc0, 0x06, + 0xa1, 0x10, 0xcc, 0x32, 0xb4, 0x81, 0x1b, 0xb8, 0xc7, 0x88, 0xc1, 0x03, 0x80, 0x20, 0x18, 0x2c, 0xb1, 0xb4, 0x46, + 0xa6, 0x23, 0x3a, 0x7d, 0xc1, 0x17, 0x39, 0x94, 0x43, 0xa4, 0x44, 0x4a, 0x75, 0x94, 0x3a, 0xa3, 0x09, 0x01, 0x30, + 0x9a, 0x20, 0x04, 0xa3, 0x09, 0x83, 0x30, 0x4b, 0xf0, 0x06, 0x23, 0x06, 0x0d, 0x00, 0x82, 0x60, 0x20, 0xc9, 0x12, + 0x1c, 0xa9, 0x0e, 0x2a, 0x91, 0xce, 0xe8, 0xe4, 0x51, 0x1e, 0xe5, 0x51, 0x1e, 0x8d, 0x26, 0x04, 0xc0, 0x68, 0x82, + 0x10, 0x8c, 0x26, 0x0c, 0xc2, 0x2c, 0xc1, 0x1b, 0x0c, 0x54, 0x18, 0x4e, 0x1b, 0x18, 0x6e, 0x30, 0x50, 0x61, 0x38, + 0x6d, 0x60, 0xb8, 0xc1, 0x40, 0x85, 0xe1, 0xb4, 0x81, 0xe1, 0x06, 0xb3, 0x0c, 0x70, 0x10, 0x07, 0x7d, 0x31, 0x62, + 0x60, 0x00, 0x20, 0x08, 0x06, 0x14, 0x2e, 0xb9, 0xd2, 0x30, 0x62, 0x60, 0x00, 0x20, 0x08, 0x06, 0x54, 0x2e, 0xbd, + 0xd2, 0x30, 0x62, 0x60, 0x00, 0x20, 0x08, 0x06, 0x94, 0x2e, 0xc1, 0xd2, 0x30, 0x1c, 0x31, 0xe4, 0x0e, 0xf1, 0x0d, + 0x47, 0x0c, 0xba, 0x43, 0x7c, 0xc3, 0x11, 0xc3, 0xee, 0x10, 0xdf, 0x74, 0x83, 0xee, 0xec, 0xce, 0x30, 0xdd, 0xb0, + 0x3b, 0xbc, 0x33, 0x4c, 0x37, 0xf0, 0x4e, 0xef, 0x0c, 0x96, 0x80, 0x0f, 0x7c, 0x2c, 0x09, 0x1f, 0xf8, 0x58, 0x22, + 0x3e, 0xf0, 0xb1, 0x81, 0x7c, 0xe4, 0x63, 0x43, 0xf9, 0xc8, 0xc7, 0x06, 0xf3, 0x91, 0xcf, 0x88, 0x81, 0x01, 0x80, + 0x20, 0x18, 0x50, 0xe7, 0xd4, 0x4a, 0xc3, 0x88, 0x81, 0x01, 0x80, 0x20, 0x18, 0x50, 0xe8, 0xe4, 0x4a, 0xc3, 0x88, + 0x81, 0x01, 0x80, 0x20, 0x18, 0x50, 0xe9, 0xf4, 0x4a, 0x83, 0x0d, 0x78, 0x24, 0x1f, 0x1b, 0xf2, 0x48, 0x3e, 0x36, + 0xe8, 0x91, 0x7c, 0x46, 0x0c, 0x0c, 0x00, 0x04, 0xc1, 0x80, 0x6a, 0x27, 0x5a, 0x1a, 0x46, 0x0c, 0x0c, 0x00, 0x04, + 0xc1, 0x80, 0x72, 0xa7, 0x5a, 0x1a, 0x46, 0x0c, 0x0c, 0x00, 0x04, 0xc1, 0x80, 0x7a, 0x27, 0x5b, 0x1a, 0x0c, 0xeb, + 0x23, 0xf9, 0x18, 0xe6, 0x47, 0xf2, 0x31, 0xec, 0x8f, 0xe4, 0x63, 0xc6, 0x10, 0x1f, 0x33, 0x86, 0xf8, 0x98, 0x31, + 0xc4, 0xc7, 0x06, 0x4c, 0x3e, 0x36, 0x60, 0xf2, 0xb1, 0x01, 0x93, 0x8f, 0x0d, 0x09, 0x7c, 0x6c, 0x48, 0xe0, 0x63, + 0x43, 0x02, 0x9f, 0x59, 0x82, 0x38, 0x18, 0xa8, 0x30, 0x0c, 0x38, 0x70, 0x85, 0x37, 0x18, 0xa8, 0x30, 0x0c, 0x38, + 0x70, 0x85, 0x37, 0x18, 0xa8, 0x30, 0x0c, 0x38, 0x70, 0x85, 0x37, 0x18, 0x31, 0x38, 0x00, 0x10, 0x04, 0x03, 0x28, + 0xa4, 0x6e, 0xa9, 0x7e, 0xec, 0x69, 0x34, 0x21, 0x00, 0x86, 0x1b, 0x82, 0xf8, 0x01, 0x83, 0xd1, 0x84, 0x21, 0x18, + 0x6e, 0x08, 0xe2, 0x07, 0x0c, 0x6a, 0x08, 0x74, 0x96, 0x01, 0x14, 0xe4, 0x20, 0x30, 0x9d, 0x28, 0x8d, 0xf8, 0x98, + 0x4e, 0x94, 0x46, 0x7c, 0x4c, 0x27, 0x4a, 0x23, 0x3e, 0xc6, 0x0c, 0xf0, 0x31, 0x66, 0x80, 0x8f, 0x31, 0x03, 0x7c, + 0x66, 0x19, 0xe6, 0x00, 0x14, 0xe0, 0x63, 0x38, 0xc2, 0x88, 0x25, 0xe1, 0x9b, 0x65, 0xb0, 0x03, 0x3a, 0x08, 0x86, + 0x23, 0x8e, 0x58, 0x22, 0xbe, 0x59, 0x86, 0x3a, 0xb8, 0x83, 0xc0, 0x62, 0x29, 0x8a, 0x8f, 0x05, 0x09, 0x7d, 0x46, + 0x0c, 0x0e, 0x00, 0x04, 0xc1, 0x00, 0x6b, 0x29, 0x79, 0x62, 0xa1, 0x60, 0x96, 0xe0, 0x0e, 0xac, 0x85, 0xa8, 0xf8, + 0x58, 0xc0, 0xd0, 0x67, 0xc4, 0xe0, 0x00, 0x40, 0x10, 0x0c, 0x30, 0x98, 0xaa, 0xa7, 0x17, 0x0a, 0x66, 0x09, 0xee, + 0x60, 0xa0, 0xc3, 0x10, 0xec, 0x00, 0xa9, 0x03, 0x30, 0x0c, 0xe8, 0x60, 0x38, 0xc2, 0xd1, 0x25, 0xe1, 0x9b, 0x65, + 0xd0, 0x03, 0x3c, 0x08, 0x86, 0x23, 0x1e, 0x5d, 0x22, 0xbe, 0x59, 0x86, 0x3c, 0xd8, 0x83, 0xc0, 0x74, 0x29, 0x8b, + 0x8f, 0x05, 0x11, 0x7d, 0x46, 0x0c, 0x0e, 0x00, 0x04, 0xc1, 0x00, 0xb3, 0xa9, 0x7d, 0x2a, 0x82, 0x59, 0x82, 0x3d, + 0x30, 0x1b, 0xe2, 0xe2, 0x63, 0x01, 0x45, 0x9f, 0x11, 0x83, 0x03, 0x00, 0x41, 0x30, 0xc0, 0x72, 0xca, 0x9f, 0x90, + 0x60, 0x96, 0x60, 0x0f, 0x06, 0x3a, 0x0c, 0x41, 0x0f, 0x90, 0x3c, 0x90, 0xf0, 0x60, 0x38, 0xc2, 0x1a, 0x27, 0xe1, + 0x9b, 0x65, 0xf0, 0x03, 0x3e, 0x08, 0x86, 0x23, 0xae, 0x71, 0x22, 0xbe, 0x59, 0x86, 0x3e, 0xf8, 0x83, 0xc0, 0xc6, + 0x29, 0x0c, 0xe2, 0x63, 0x41, 0x46, 0x9f, 0x11, 0x83, 0x03, 0x00, 0x41, 0x30, 0xc0, 0x7e, 0x8a, 0xa4, 0x8a, 0x60, + 0x96, 0xe0, 0x0f, 0xec, 0x87, 0xc8, 0x20, 0x3e, 0x16, 0x70, 0xf4, 0x19, 0x31, 0x38, 0x00, 0x10, 0x04, 0x03, 0x4c, + 0xac, 0x4e, 0x0a, 0x09, 0x66, 0x09, 0xfe, 0x60, 0xa0, 0xc3, 0x10, 0xfc, 0x00, 0xe9, 0x03, 0x89, 0x0f, 0x46, 0x0c, + 0x0c, 0x00, 0x04, 0xc1, 0x80, 0x32, 0x2b, 0x9e, 0x0a, 0x2c, 0x10, 0x03, 0xf9, 0x98, 0x20, 0x06, 0xf2, 0xb1, 0x41, + 0x0c, 0xe4, 0x63, 0x83, 0x1b, 0xc0, 0xc7, 0x06, 0x37, 0x80, 0x8f, 0x0d, 0x6e, 0x00, 0x9f, 0x59, 0x02, 0x50, 0x18, + 0xe8, 0x30, 0x48, 0x26, 0x0e, 0x8c, 0x3f, 0x50, 0x05, 0x39, 0x18, 0xe8, 0x30, 0x48, 0x26, 0x0e, 0x8c, 0x3f, 0x50, + 0x05, 0x39, 0x18, 0xe8, 0x30, 0x48, 0x26, 0x0e, 0x8c, 0x3f, 0x50, 0x05, 0x39, 0x98, 0x6e, 0xa0, 0x83, 0x21, 0x1d, + 0xa6, 0x1b, 0xe8, 0x60, 0x50, 0x87, 0xe9, 0x06, 0x3a, 0x18, 0xd6, 0x61, 0xc4, 0xe0, 0x00, 0x40, 0x10, 0x0c, 0xba, + 0xb7, 0xba, 0xa9, 0x33, 0x52, 0xab, 0xd1, 0x84, 0x20, 0xb0, 0x22, 0x90, 0x8f, 0x15, 0x82, 0x7c, 0xac, 0x18, 0xe4, + 0x33, 0x4b, 0x10, 0x0a, 0x03, 0x15, 0x86, 0x01, 0x0a, 0xe8, 0x18, 0xac, 0xc1, 0x40, 0x85, 0x61, 0x80, 0x82, 0x3a, + 0x06, 0x6b, 0x30, 0x50, 0x61, 0x18, 0xa0, 0xc0, 0x8e, 0xc1, 0x1a, 0x8c, 0x18, 0x38, 0x00, 0x08, 0x82, 0x41, 0xe3, + 0x57, 0x3d, 0x55, 0x47, 0x6e, 0xd4, 0x46, 0x63, 0x35, 0x08, 0x01, 0x68, 0xa8, 0xd4, 0x2c, 0x41, 0x3b, 0x8c, 0x18, + 0x1c, 0x00, 0x08, 0x82, 0x41, 0x77, 0x57, 0x3f, 0xf5, 0x46, 0x73, 0x35, 0x9a, 0x10, 0x00, 0xc3, 0x11, 0x41, 0x1e, + 0x05, 0xdf, 0x2c, 0xc3, 0x28, 0xa4, 0x42, 0x30, 0xcb, 0x40, 0x0a, 0xa5, 0xf0, 0x2b, 0x23, 0x06, 0x0f, 0x00, 0x82, + 0x60, 0xb0, 0x88, 0x16, 0x4f, 0xd9, 0xd1, 0x1c, 0xb9, 0x49, 0x9b, 0xa8, 0x94, 0x4a, 0xd5, 0x55, 0x5d, 0x99, 0x95, + 0x1e, 0x8d, 0x26, 0x04, 0xc0, 0x68, 0x82, 0x10, 0x8c, 0x26, 0x0c, 0xc2, 0x68, 0x02, 0x31, 0xcc, 0x12, 0x98, 0xc2, + 0x88, 0x41, 0x03, 0x80, 0x20, 0x18, 0x48, 0xa4, 0x25, 0x56, 0x7b, 0xa4, 0x57, 0x76, 0x54, 0x47, 0x6b, 0xb5, 0x56, + 0x6b, 0xb5, 0x56, 0xa3, 0x09, 0x01, 0x30, 0x9a, 0x20, 0x04, 0xa3, 0x09, 0x83, 0x30, 0x9a, 0x40, 0x0c, 0xb3, 0x04, + 0xa6, 0x30, 0x50, 0x61, 0x48, 0xa4, 0x80, 0x94, 0xc2, 0x40, 0x85, 0x21, 0x91, 0x02, 0x52, 0x0a, 0x03, 0x15, 0x86, + 0x44, 0x0a, 0x48, 0x29, 0x0c, 0x54, 0x18, 0x12, 0x29, 0x20, 0xa5, 0x30, 0xdc, 0x80, 0x27, 0xa3, 0x15, 0x06, 0xd3, + 0x0d, 0xa9, 0x54, 0x04, 0xd3, 0x0d, 0xaa, 0x54, 0x08, 0xd3, 0x0d, 0xab, 0x54, 0x0c, 0xc3, 0x0d, 0x7d, 0x72, 0x5a, + 0x60, 0x30, 0xcb, 0x80, 0x0a, 0xa7, 0x10, 0x8c, 0x18, 0x1c, 0x00, 0x08, 0x82, 0x41, 0xd7, 0x5a, 0x75, 0x55, 0x4a, + 0xa8, 0x35, 0x9a, 0x10, 0x04, 0xb3, 0x04, 0xa8, 0x30, 0x50, 0x61, 0x08, 0xa7, 0xc0, 0x98, 0xc2, 0x40, 0x85, 0x41, + 0x9c, 0x02, 0x63, 0x0a, 0x03, 0x15, 0x86, 0x71, 0x0a, 0x8c, 0x29, 0x8c, 0x18, 0x38, 0x00, 0x08, 0x82, 0x41, 0x93, + 0x5b, 0x78, 0x05, 0x4b, 0xa9, 0x84, 0x4a, 0x7e, 0x35, 0x08, 0xc1, 0x52, 0x56, 0xb3, 0x04, 0xed, 0x30, 0x62, 0x70, + 0x00, 0x20, 0x08, 0x06, 0x60, 0x10, 0x5b, 0x7a, 0xa5, 0x4a, 0xa6, 0x35, 0x9a, 0x10, 0x04, 0xc3, 0x0d, 0xc1, 0x6b, + 0x81, 0xc1, 0x2c, 0x83, 0x2a, 0xbc, 0x42, 0x30, 0x62, 0xb0, 0x00, 0x20, 0x08, 0x06, 0x9a, 0x6d, 0xe9, 0x95, 0x7b, + 0xb4, 0x07, 0x7b, 0x8c, 0x95, 0x58, 0x85, 0xd5, 0x70, 0x44, 0x20, 0x52, 0xca, 0x37, 0xcb, 0xb0, 0x0a, 0xea, 0x10, + 0x8c, 0x18, 0x2c, 0x00, 0x08, 0x82, 0x81, 0x86, 0x5b, 0x7c, 0xb5, 0x1b, 0xba, 0x91, 0x1b, 0x65, 0x45, 0x56, 0x63, + 0x35, 0x62, 0xb0, 0x00, 0x20, 0x08, 0x06, 0x5a, 0x6e, 0xf5, 0x15, 0x8f, 0xec, 0x88, 0x8e, 0x98, 0x55, 0x59, 0x91, + 0xd5, 0x70, 0x84, 0x10, 0x10, 0xdf, 0x2c, 0x03, 0x2b, 0xb4, 0x42, 0x30, 0x62, 0x70, 0x00, 0x20, 0x08, 0x06, 0x18, + 0x6f, 0x8d, 0x96, 0x40, 0x56, 0x46, 0x04, 0xf4, 0x99, 0x25, 0x70, 0x05, 0x2b, 0x88, 0xf8, 0x8c, 0x18, 0x1c, 0x00, + 0x08, 0x82, 0x01, 0xf6, 0x5b, 0xa6, 0xd5, 0x4b, 0x81, 0x05, 0x09, 0x7c, 0x2c, 0x50, 0xe8, 0x33, 0x4b, 0xe0, 0x0a, + 0x03, 0x15, 0x86, 0xc2, 0x0a, 0x42, 0x2b, 0x58, 0xa0, 0x1f, 0xf2, 0x31, 0x41, 0x3f, 0xe4, 0x63, 0x83, 0x7e, 0xc8, + 0xc7, 0x86, 0xb5, 0x92, 0xcf, 0x88, 0x01, 0x02, 0x80, 0x20, 0x18, 0x88, 0xc1, 0x78, 0xc9, 0xd6, 0x5a, 0x0d, 0xc1, + 0x88, 0x01, 0x02, 0x80, 0x20, 0x18, 0x88, 0x01, 0x79, 0xcd, 0xd6, 0x5a, 0x0d, 0x81, 0x19, 0x6b, 0x25, 0x9f, 0x11, + 0x03, 0x04, 0x00, 0x41, 0x30, 0x10, 0x03, 0xf3, 0xaa, 0xad, 0xb5, 0x32, 0x82, 0x11, 0x03, 0x04, 0x00, 0x41, 0x30, + 0x10, 0x83, 0xf3, 0xb2, 0xad, 0xb5, 0x32, 0x02, 0x4b, 0xd6, 0x4a, 0x3e, 0x23, 0x06, 0x08, 0x00, 0x82, 0x60, 0x20, + 0x06, 0xe9, 0x85, 0x5b, 0x6b, 0x95, 0x04, 0x23, 0x06, 0x08, 0x00, 0x82, 0x60, 0x20, 0x06, 0xea, 0x95, 0x5b, 0x6b, + 0x95, 0x04, 0xc3, 0x11, 0x47, 0x3b, 0x09, 0xdf, 0x70, 0x44, 0xe1, 0x4e, 0xc2, 0x37, 0x1c, 0x31, 0xbc, 0x93, 0xf0, + 0x0d, 0x47, 0x28, 0xf0, 0x44, 0x7c, 0xc3, 0x11, 0x48, 0x3c, 0x11, 0xdf, 0x70, 0x84, 0x21, 0x4f, 0xc4, 0x77, 0xc6, + 0x10, 0x67, 0x0c, 0x71, 0xc6, 0x10, 0x67, 0x0c, 0x71, 0xc6, 0x10, 0x67, 0x0c, 0x61, 0xc6, 0x10, 0x02, 0x33, 0x86, + 0x10, 0x98, 0x31, 0x84, 0xe0, 0x06, 0xc3, 0x6e, 0x30, 0xec, 0x06, 0xc3, 0x46, 0x0c, 0x0c, 0x00, 0x04, 0xc1, 0x80, + 0x1a, 0x31, 0xfc, 0xca, 0x46, 0x0c, 0x0c, 0x00, 0x04, 0xc1, 0x80, 0x22, 0xb1, 0xfc, 0xba, 0x46, 0x0c, 0x0c, 0x00, + 0x04, 0xc1, 0x80, 0x2a, 0x31, 0xfd, 0xaa, 0x46, 0x0c, 0x0c, 0x00, 0x04, 0xc1, 0x80, 0x32, 0x31, 0xf6, 0x1a, 0x46, + 0x0c, 0x0c, 0x00, 0x04, 0xc1, 0x80, 0x3a, 0xb1, 0xf6, 0x1a, 0x46, 0x0c, 0x0c, 0x00, 0x04, 0xc1, 0x80, 0x42, 0x31, + 0xf7, 0x1a, 0x6c, 0xa0, 0x2d, 0xf9, 0xd8, 0x50, 0x5b, 0xf2, 0xb1, 0xc1, 0xb6, 0xe4, 0x33, 0x62, 0x60, 0x00, 0x20, + 0x08, 0x06, 0x14, 0x8b, 0xcd, 0xd7, 0x30, 0x62, 0x60, 0x00, 0x20, 0x08, 0x06, 0x54, 0x8b, 0xd1, 0xd7, 0x30, 0x62, + 0x60, 0x00, 0x20, 0x08, 0x06, 0x94, 0x8b, 0xd5, 0xd7, 0x60, 0xc3, 0x23, 0x1f, 0x1b, 0x1e, 0xf9, 0xd8, 0xf0, 0xc8, + 0xc7, 0x86, 0xbc, 0x92, 0xcf, 0x88, 0x01, 0x02, 0x80, 0x20, 0x18, 0x88, 0x41, 0x8b, 0xf1, 0x57, 0x5e, 0x0d, 0xc1, + 0x88, 0x01, 0x02, 0x80, 0x20, 0x18, 0x88, 0x81, 0x8b, 0xf5, 0x57, 0x5e, 0x0d, 0x81, 0x19, 0xaf, 0x25, 0x9f, 0x11, + 0x03, 0x04, 0x00, 0x41, 0x30, 0x10, 0x03, 0x18, 0xfb, 0xaf, 0xd7, 0x32, 0x82, 0x11, 0x03, 0x04, 0x00, 0x41, 0x30, + 0x10, 0x83, 0x18, 0x03, 0xb1, 0xd7, 0x32, 0x02, 0x4b, 0x5e, 0x4b, 0x3e, 0x23, 0x06, 0x08, 0x00, 0x82, 0x60, 0x20, + 0x06, 0x33, 0x26, 0x62, 0xaf, 0x95, 0x04, 0x23, 0x06, 0x08, 0x00, 0x82, 0x60, 0x20, 0x06, 0x34, 0x36, 0x62, 0xaf, + 0x95, 0x04, 0x06, 0x27, 0xbe, 0x25, 0x9f, 0x11, 0x03, 0x04, 0x00, 0x41, 0x30, 0x10, 0x03, 0x1b, 0x2b, 0x31, 0xdf, + 0x82, 0x93, 0x60, 0xc4, 0x00, 0x01, 0x40, 0x10, 0x0c, 0xc4, 0xe0, 0xc6, 0x4c, 0xcc, 0xb7, 0xe0, 0x24, 0xb0, 0x39, + 0xf1, 0x2d, 0xf9, 0x8c, 0x18, 0x20, 0x00, 0x08, 0x82, 0x81, 0x18, 0xe4, 0x18, 0x8a, 0xf9, 0xd6, 0x9c, 0x04, 0x23, + 0x06, 0x08, 0x00, 0x82, 0x60, 0x20, 0x06, 0x3a, 0x96, 0x62, 0xbe, 0x35, 0x27, 0x81, 0xd9, 0x89, 0x6f, 0xc9, 0x67, + 0xc4, 0x00, 0x01, 0x40, 0x10, 0x0c, 0xc4, 0x80, 0xc7, 0x56, 0xcc, 0xb7, 0xec, 0x24, 0x18, 0x31, 0x40, 0x00, 0x10, + 0x04, 0x03, 0x31, 0xe8, 0x31, 0x16, 0xf3, 0x2d, 0x3b, 0x09, 0x86, 0x23, 0x0e, 0xb0, 0x12, 0xbe, 0xe1, 0x88, 0x22, + 0xac, 0x84, 0x6f, 0x38, 0x62, 0x10, 0x2b, 0xe1, 0x1b, 0x8e, 0x50, 0xc6, 0x8a, 0xf8, 0x86, 0x23, 0x10, 0xb2, 0x22, + 0xbe, 0xe1, 0x08, 0xa3, 0xac, 0x88, 0xef, 0x8c, 0x21, 0xce, 0x18, 0xe2, 0x8c, 0x21, 0xce, 0x18, 0xe2, 0x8c, 0x21, + 0xce, 0x18, 0xc2, 0x8c, 0x21, 0x04, 0x66, 0x0c, 0x21, 0x30, 0x63, 0x08, 0xc1, 0x0d, 0x86, 0xdd, 0x60, 0xd8, 0x0d, + 0x86, 0x8d, 0x18, 0x18, 0x00, 0x08, 0x82, 0x01, 0x65, 0x67, 0x6b, 0x96, 0x8d, 0x18, 0x18, 0x00, 0x08, 0x82, 0x01, + 0x75, 0x67, 0x6c, 0x76, 0x8d, 0x18, 0x18, 0x00, 0x08, 0x82, 0x01, 0x85, 0x67, 0x6d, 0x56, 0x8d, 0x18, 0x18, 0x00, + 0x08, 0x82, 0x01, 0x95, 0x67, 0x3f, 0x36, 0x8c, 0x18, 0x18, 0x00, 0x08, 0x82, 0x01, 0xa5, 0x67, 0x60, 0x36, 0x8c, + 0x18, 0x18, 0x00, 0x08, 0x82, 0x01, 0xb5, 0x67, 0x61, 0x36, 0xd8, 0x70, 0x62, 0xf2, 0xb1, 0x01, 0xc5, 0xe4, 0x63, + 0x43, 0x8a, 0xc9, 0x67, 0xc4, 0xc0, 0x00, 0x40, 0x10, 0x0c, 0xa8, 0x3f, 0x33, 0xb3, 0x61, 0xc4, 0xc0, 0x00, 0x40, + 0x10, 0x0c, 0x28, 0x50, 0x3b, 0xb3, 0x61, 0xc4, 0xc0, 0x00, 0x40, 0x10, 0x0c, 0xa8, 0x50, 0x43, 0xb3, 0xc1, 0x86, + 0x47, 0x3e, 0x36, 0x3c, 0xf2, 0xb1, 0xe1, 0x91, 0x8f, 0x0d, 0xff, 0x25, 0x9f, 0x11, 0x03, 0x04, 0x00, 0x41, 0x30, + 0x10, 0x03, 0x50, 0x7b, 0xb3, 0xff, 0x1a, 0x82, 0x11, 0x03, 0x04, 0x00, 0x41, 0x30, 0x10, 0x83, 0x50, 0x83, 0xb3, + 0xff, 0x1a, 0x02, 0x33, 0xfe, 0x4b, 0x3e, 0x23, 0x06, 0x08, 0x00, 0x82, 0x60, 0x20, 0x06, 0xa3, 0x26, 0x67, 0xff, + 0x65, 0x04, 0x23, 0x06, 0x08, 0x00, 0x82, 0x60, 0x20, 0x06, 0xa4, 0x36, 0x67, 0xff, 0x65, 0x04, 0x46, 0x07, 0x74, + 0x20, 0x1f, 0x93, 0x03, 0x39, 0x90, 0x8f, 0x05, 0x02, 0x7c, 0x46, 0x0c, 0x0c, 0x00, 0x04, 0xc1, 0x80, 0x72, 0xb5, + 0x33, 0x0b, 0x0c, 0x41, 0xe4, 0x63, 0x86, 0x21, 0x1f, 0x0b, 0x04, 0xf8, 0x8c, 0x18, 0x18, 0x00, 0x08, 0x82, 0x01, + 0x25, 0x6b, 0x6b, 0x16, 0x0c, 0x47, 0x04, 0xaf, 0x15, 0x7c, 0x66, 0x08, 0xf4, 0x99, 0x6e, 0x90, 0xad, 0x40, 0xb0, + 0xe0, 0x91, 0x8f, 0x09, 0x8d, 0x7c, 0x46, 0x0c, 0x10, 0x00, 0x04, 0xc1, 0x40, 0x0c, 0x64, 0x2d, 0xd4, 0xf8, 0x4b, + 0x20, 0x85, 0x11, 0x03, 0x04, 0x00, 0x41, 0x30, 0x10, 0x83, 0x59, 0x13, 0x35, 0xfe, 0x12, 0x82, 0x11, 0x03, 0x04, + 0x00, 0x41, 0x30, 0x10, 0x03, 0x5a, 0x1b, 0x35, 0xfe, 0x22, 0x4c, 0x61, 0xc4, 0x00, 0x01, 0x40, 0x10, 0x0c, 0xc4, + 0xa0, 0xd6, 0x48, 0x8d, 0xbf, 0x88, 0x60, 0xc4, 0x00, 0x01, 0x40, 0x10, 0x0c, 0xc4, 0xc0, 0xd6, 0x4a, 0x8d, 0xbf, + 0x0c, 0x54, 0x18, 0x31, 0x40, 0x00, 0x10, 0x04, 0x03, 0x31, 0xb8, 0x35, 0x53, 0xe3, 0x2f, 0x23, 0xb0, 0xa2, 0x90, + 0x8f, 0x11, 0x84, 0x7c, 0x6c, 0x18, 0xe4, 0x63, 0x03, 0x22, 0x1f, 0x1b, 0x0e, 0xf9, 0xd8, 0x60, 0xc8, 0xc7, 0x06, + 0x11, 0x93, 0xcf, 0x88, 0x01, 0x02, 0x80, 0x20, 0x18, 0x88, 0xc1, 0xaf, 0xb9, 0x9a, 0x88, 0x0d, 0xc1, 0x88, 0x01, + 0x02, 0x80, 0x20, 0x18, 0x88, 0x01, 0xb8, 0xbd, 0x9a, 0x88, 0x0d, 0x81, 0x19, 0x22, 0x26, 0x9f, 0x11, 0x03, 0x04, + 0x00, 0x41, 0x30, 0x10, 0x03, 0x71, 0x8b, 0x35, 0x11, 0x33, 0x82, 0x11, 0x03, 0x04, 0x00, 0x41, 0x30, 0x10, 0x83, + 0x71, 0x93, 0x35, 0x11, 0x33, 0x02, 0x4b, 0x44, 0x4c, 0x3e, 0x23, 0x06, 0x08, 0x00, 0x82, 0x60, 0x20, 0x06, 0xe5, + 0x46, 0x6b, 0x22, 0x96, 0x04, 0x23, 0x06, 0x08, 0x00, 0x82, 0x60, 0x20, 0x06, 0xe6, 0x56, 0x6b, 0x22, 0x96, 0x04, + 0x76, 0x88, 0x98, 0x7c, 0x46, 0x0c, 0x10, 0x00, 0x04, 0xc1, 0x40, 0x0c, 0xd0, 0xed, 0xd6, 0x44, 0xac, 0x08, 0x46, + 0x0c, 0x10, 0x00, 0x04, 0xc1, 0x40, 0x0c, 0xd2, 0x0d, 0xd7, 0x44, 0x6c, 0x08, 0x4c, 0x11, 0x31, 0xf9, 0x8c, 0x18, + 0x20, 0x00, 0x08, 0x82, 0x81, 0x18, 0xac, 0x9b, 0xae, 0x89, 0x18, 0x12, 0x8c, 0x18, 0x20, 0x00, 0x08, 0x82, 0x81, + 0x18, 0xb0, 0xdb, 0xae, 0x89, 0x98, 0x11, 0x58, 0x23, 0x62, 0xf2, 0x19, 0x31, 0x40, 0x00, 0x10, 0x04, 0x03, 0x31, + 0x70, 0xb7, 0x5e, 0x13, 0xb1, 0x25, 0x18, 0x31, 0x40, 0x00, 0x10, 0x04, 0x03, 0x31, 0x78, 0x37, 0x5f, 0x13, 0xb1, + 0x24, 0x18, 0x31, 0x38, 0x00, 0x10, 0x04, 0x03, 0x8c, 0xde, 0x76, 0xed, 0x90, 0xaf, 0x11, 0x83, 0x03, 0x00, 0x41, + 0x30, 0xc0, 0xea, 0x8d, 0xd7, 0x8a, 0xf9, 0x1a, 0x31, 0x38, 0x00, 0x10, 0x04, 0x03, 0xcc, 0xde, 0x7a, 0x6d, 0xa0, + 0x2f, 0x1b, 0x4a, 0x4c, 0x3e, 0x23, 0x06, 0x08, 0x00, 0x82, 0x60, 0x20, 0x06, 0xf4, 0x36, 0x6e, 0x25, 0x36, 0x04, + 0x23, 0x06, 0x08, 0x00, 0x82, 0x60, 0x20, 0x06, 0xf5, 0x46, 0x6e, 0x25, 0x36, 0x04, 0x66, 0x94, 0x98, 0x7c, 0x46, + 0x0c, 0x10, 0x00, 0x04, 0xc1, 0x40, 0x0c, 0xee, 0xcd, 0xdc, 0x4a, 0xcc, 0x08, 0x46, 0x0c, 0x10, 0x00, 0x04, 0xc1, + 0x40, 0x0c, 0xf0, 0xed, 0xdc, 0x4a, 0xcc, 0x08, 0x2c, 0x29, 0x31, 0xf9, 0x8c, 0x18, 0x20, 0x00, 0x08, 0x82, 0x81, + 0x18, 0xe8, 0x5b, 0xba, 0x95, 0x58, 0x12, 0x8c, 0x18, 0x20, 0x00, 0x08, 0x82, 0x81, 0x18, 0xec, 0x9b, 0xba, 0x95, + 0x58, 0x12, 0xd8, 0x21, 0x32, 0xf1, 0xb1, 0x42, 0x64, 0xe2, 0x63, 0x83, 0xc8, 0xc4, 0xc7, 0x86, 0xd4, 0x90, 0x8f, + 0x0d, 0xaa, 0x21, 0x1f, 0x1b, 0x56, 0x43, 0x3e, 0x36, 0xa0, 0x0c, 0x7c, 0x6c, 0x40, 0x19, 0xf8, 0xd8, 0x80, 0x32, + 0xf0, 0x19, 0x31, 0x58, 0x00, 0x10, 0x04, 0x03, 0x0d, 0xe5, 0xd8, 0x6d, 0x10, 0x82, 0x5a, 0xa3, 0xb5, 0x59, 0x1b, + 0x4d, 0x88, 0x8d, 0xc1, 0x84, 0x50, 0x83, 0x8f, 0xcd, 0x85, 0xa8, 0xc1, 0xc7, 0x84, 0x80, 0x3e, 0x23, 0x06, 0x07, + 0x00, 0x82, 0x60, 0x80, 0xb9, 0x5c, 0xbd, 0x05, 0x2c, 0x36, 0x62, 0x70, 0x00, 0x20, 0x08, 0x06, 0xd8, 0xcb, 0xd1, + 0x5b, 0x50, 0x8c, 0x26, 0xdc, 0x46, 0x60, 0xc2, 0xcc, 0xc8, 0xc7, 0x86, 0x99, 0x91, 0x8f, 0x11, 0x33, 0x23, 0x1f, + 0x73, 0x86, 0xf8, 0x98, 0x33, 0xc4, 0xc7, 0x9c, 0x21, 0x3e, 0x76, 0x0c, 0xf2, 0x31, 0x64, 0x90, 0x8f, 0x25, 0x83, + 0x7c, 0x6c, 0x48, 0xe0, 0x63, 0x43, 0x02, 0x1f, 0x1b, 0x12, 0xf8, 0xcc, 0x12, 0xa8, 0xc3, 0x2c, 0x03, 0x2c, 0xc4, + 0x02, 0x28, 0x8d, 0x18, 0x3c, 0x00, 0x08, 0x82, 0xc1, 0x32, 0x76, 0xfd, 0x86, 0x63, 0x34, 0xf6, 0x42, 0x2e, 0xb4, + 0x6e, 0xeb, 0x66, 0x73, 0x36, 0x77, 0x72, 0x3b, 0x36, 0x9a, 0x10, 0x00, 0xa3, 0x09, 0x42, 0x30, 0x9a, 0x30, 0x08, + 0xb3, 0x04, 0xb2, 0x30, 0x62, 0xd0, 0x00, 0x20, 0x08, 0x06, 0x12, 0xd9, 0x89, 0x1c, 0x8f, 0xe9, 0x9c, 0x8d, 0xd5, + 0xd8, 0xca, 0xad, 0xdc, 0xca, 0xad, 0xdc, 0x68, 0x42, 0x00, 0x8c, 0x26, 0x08, 0xc1, 0x68, 0xc2, 0x20, 0xcc, 0x12, + 0xc8, 0xc2, 0x40, 0x85, 0xe1, 0xc0, 0x82, 0x11, 0x0b, 0x03, 0x15, 0x86, 0x03, 0x0b, 0x46, 0x2c, 0x0c, 0x54, 0x18, + 0x0e, 0x2c, 0x18, 0xb1, 0x30, 0xcb, 0x30, 0x0b, 0xb4, 0xf0, 0x42, 0x23, 0x06, 0x06, 0x00, 0x82, 0x60, 0x40, 0xa9, + 0x1d, 0xd8, 0x0d, 0x23, 0x06, 0x06, 0x00, 0x82, 0x60, 0x40, 0xad, 0x5d, 0xd8, 0x0d, 0x23, 0x06, 0x06, 0x00, 0x82, + 0x60, 0x40, 0xb1, 0x9d, 0xd8, 0x0d, 0xc3, 0x11, 0xc3, 0x9a, 0x11, 0xdf, 0x70, 0xc4, 0xc0, 0x66, 0xc4, 0x37, 0x1c, + 0x31, 0xb4, 0x19, 0xf1, 0x4d, 0x37, 0xb0, 0x59, 0x9b, 0x0d, 0xd3, 0x0d, 0x6d, 0xe6, 0x66, 0xc3, 0x74, 0x83, 0x9b, + 0xbd, 0xd9, 0x60, 0x89, 0x9c, 0xc1, 0xc7, 0x92, 0x39, 0x83, 0x8f, 0x25, 0x74, 0x06, 0x1f, 0x1b, 0xec, 0x4c, 0x3e, + 0x36, 0xdc, 0x99, 0x7c, 0x6c, 0xc0, 0x33, 0xf9, 0x8c, 0x18, 0x18, 0x00, 0x08, 0x82, 0x01, 0x95, 0x77, 0x3f, 0x37, + 0x8c, 0x18, 0x18, 0x00, 0x08, 0x82, 0x01, 0xa5, 0x77, 0x60, 0x37, 0x8c, 0x18, 0x18, 0x00, 0x08, 0x82, 0x01, 0xb5, + 0x77, 0x61, 0x37, 0xd8, 0xa0, 0x72, 0xf2, 0xb1, 0x61, 0xe5, 0xe4, 0x63, 0x03, 0xcb, 0xc9, 0x67, 0xc4, 0xc0, 0x00, + 0x40, 0x10, 0x0c, 0xa8, 0xbf, 0x33, 0xbb, 0x61, 0xc4, 0xc0, 0x00, 0x40, 0x10, 0x0c, 0x28, 0xd0, 0x3b, 0xbb, 0x61, + 0xc4, 0xc0, 0x00, 0x40, 0x10, 0x0c, 0xa8, 0xd0, 0x43, 0xbb, 0xc1, 0xb0, 0x97, 0x93, 0x8f, 0x61, 0x30, 0x27, 0x1f, + 0xc3, 0x62, 0x4e, 0x3e, 0x66, 0x0c, 0xf1, 0x31, 0x63, 0x88, 0x8f, 0x19, 0x43, 0x7c, 0x6c, 0xc0, 0xe4, 0x63, 0x03, + 0x26, 0x1f, 0x1b, 0x30, 0xf9, 0xd8, 0x90, 0xc0, 0xc7, 0x86, 0x04, 0x3e, 0x36, 0x24, 0xf0, 0x99, 0x25, 0xa0, 0x85, + 0x81, 0x0a, 0xc3, 0x98, 0x05, 0x57, 0x90, 0x85, 0x81, 0x0a, 0xc3, 0x98, 0x05, 0x57, 0x90, 0x85, 0x81, 0x0a, 0xc3, + 0x98, 0x05, 0x57, 0x90, 0x85, 0x11, 0x83, 0x03, 0x00, 0x41, 0x30, 0x80, 0x66, 0x2f, 0xed, 0x4e, 0x0d, 0xf5, 0x46, + 0x13, 0x02, 0x60, 0xb8, 0x21, 0x18, 0x35, 0x30, 0x18, 0x4d, 0x18, 0x82, 0xe1, 0x86, 0x60, 0xd4, 0xc0, 0xa0, 0x86, + 0x40, 0x67, 0x19, 0xc6, 0xa1, 0x16, 0x02, 0x63, 0x9f, 0x1b, 0x8a, 0x8f, 0xb1, 0xcf, 0x0d, 0xc5, 0xc7, 0xd8, 0xe7, + 0x86, 0xe2, 0x63, 0xcc, 0x00, 0x1f, 0x63, 0x06, 0xf8, 0x18, 0x33, 0xc0, 0x67, 0x96, 0xc1, 0x16, 0xc6, 0x41, 0x94, + 0x86, 0x23, 0x8c, 0xb1, 0x13, 0xbe, 0x59, 0x86, 0x5c, 0xb8, 0x85, 0x60, 0x38, 0xe2, 0x18, 0x3b, 0xe2, 0x9b, 0x65, + 0xc0, 0x05, 0x5d, 0x08, 0x6c, 0xec, 0xa2, 0xf8, 0x58, 0x90, 0xd0, 0x67, 0xc4, 0xe0, 0x00, 0x40, 0x10, 0x0c, 0xb0, + 0xdf, 0x23, 0x3d, 0x5f, 0x0b, 0x66, 0x09, 0x74, 0xc1, 0x7e, 0x8d, 0x8a, 0x8f, 0x05, 0x0c, 0x7d, 0x46, 0x0c, 0x0e, + 0x00, 0x04, 0xc1, 0x00, 0x13, 0xbf, 0xd3, 0x0b, 0xb7, 0x60, 0x96, 0x40, 0x17, 0x06, 0x3a, 0x0c, 0x21, 0x17, 0x10, + 0x5c, 0x20, 0xed, 0xe0, 0x16, 0x86, 0x23, 0x1c, 0xb6, 0x13, 0xbe, 0x59, 0x86, 0x5e, 0xd8, 0x85, 0x60, 0x38, 0xe2, + 0x61, 0x3b, 0xe2, 0x9b, 0x65, 0xe0, 0x05, 0x5f, 0x08, 0x8c, 0xed, 0xb2, 0xf8, 0x58, 0x10, 0xd1, 0x67, 0xc4, 0xe0, + 0x00, 0x40, 0x10, 0x0c, 0x30, 0xf4, 0x6b, 0xbd, 0x22, 0x98, 0x25, 0xf0, 0x05, 0x43, 0x37, 0x2e, 0x3e, 0x16, 0x50, + 0xf4, 0x19, 0x31, 0x38, 0x00, 0x10, 0x04, 0x03, 0x6c, 0xfd, 0x60, 0x0f, 0x09, 0x66, 0x09, 0x7c, 0x61, 0xa0, 0xc3, + 0x10, 0x7a, 0x01, 0xe1, 0x05, 0x69, 0x17, 0x86, 0x23, 0xac, 0xba, 0x13, 0xbe, 0x59, 0x86, 0x70, 0xf8, 0x85, 0x60, + 0x38, 0xe2, 0xaa, 0x3b, 0xe2, 0x9b, 0x65, 0x00, 0x07, 0x71, 0x08, 0xac, 0xee, 0xc2, 0x20, 0x3e, 0x16, 0x64, 0xf4, + 0x19, 0x31, 0x38, 0x00, 0x10, 0x04, 0x03, 0x2c, 0xfe, 0x6c, 0xaf, 0x08, 0x66, 0x09, 0xc4, 0xc1, 0xe2, 0x8d, 0x0c, + 0xe2, 0x63, 0x01, 0x47, 0x9f, 0x11, 0x83, 0x03, 0x00, 0x41, 0x30, 0xc0, 0xe8, 0x2f, 0xf7, 0x90, 0x60, 0x96, 0x40, + 0x1c, 0x06, 0x3a, 0x0c, 0x21, 0x1c, 0x10, 0x70, 0x90, 0x7e, 0x61, 0xc4, 0xc0, 0x00, 0x40, 0x10, 0x0c, 0x28, 0xfc, + 0x73, 0xbf, 0xc0, 0x02, 0x31, 0x90, 0x8f, 0x09, 0x62, 0x20, 0x1f, 0x1b, 0xc4, 0x40, 0x3e, 0x36, 0xb8, 0x01, 0x7c, + 0x6c, 0x70, 0x03, 0xf8, 0xd8, 0xe0, 0x06, 0xf0, 0x99, 0x25, 0x18, 0x87, 0x81, 0x0e, 0x03, 0x0d, 0x03, 0x5a, 0x30, + 0xc4, 0x41, 0x15, 0x6a, 0x61, 0xa0, 0xc3, 0x40, 0xc3, 0x80, 0x16, 0x0c, 0x71, 0x50, 0x85, 0x5a, 0x18, 0xe8, 0x30, + 0xd0, 0x30, 0xa0, 0x05, 0x43, 0x1c, 0x54, 0xa1, 0x16, 0x46, 0x0c, 0x16, 0x00, 0x04, 0xc1, 0x40, 0xfb, 0xbf, 0xf1, + 0xbb, 0x21, 0x1b, 0xaa, 0x21, 0xd6, 0x5b, 0x3d, 0xd5, 0x1b, 0x8e, 0x08, 0xd6, 0x4e, 0xf9, 0x66, 0x19, 0xc8, 0x41, + 0x1d, 0x82, 0xd1, 0x04, 0x37, 0x11, 0x86, 0x1b, 0x82, 0xfe, 0x03, 0x83, 0x59, 0x86, 0x72, 0x30, 0x87, 0xc0, 0x0e, + 0x3c, 0x88, 0x8f, 0x1d, 0x78, 0x10, 0x1f, 0x3b, 0xf0, 0x20, 0x3e, 0xd6, 0x2a, 0x83, 0x7c, 0xcc, 0x55, 0x06, 0xf9, + 0xd8, 0xab, 0x0c, 0xf2, 0xb1, 0x81, 0x85, 0xe0, 0x63, 0x03, 0x0b, 0xc1, 0xc7, 0x06, 0x16, 0x82, 0xcf, 0x2c, 0x81, + 0x3a, 0x8c, 0x18, 0x2c, 0x00, 0x08, 0x82, 0x81, 0xc6, 0x82, 0x01, 0xfc, 0xbd, 0x90, 0x0b, 0xb5, 0x50, 0xee, 0xe1, + 0xde, 0xed, 0x8d, 0x18, 0x2c, 0x00, 0x08, 0x82, 0x81, 0xd6, 0x82, 0x41, 0xfc, 0x89, 0x42, 0x28, 0x80, 0x82, 0xee, + 0xe5, 0x1e, 0xee, 0x8d, 0x18, 0x2c, 0x00, 0x08, 0x82, 0x81, 0xe6, 0x82, 0x81, 0xfc, 0x49, 0x11, 0xb4, 0x7b, 0xba, + 0x97, 0x7b, 0xc3, 0x11, 0x83, 0x40, 0x7c, 0xb3, 0x0c, 0xe7, 0x80, 0x0e, 0xc1, 0x88, 0xc1, 0x01, 0x80, 0x20, 0x18, + 0x60, 0x31, 0x18, 0xe0, 0xdf, 0x90, 0x7b, 0x56, 0x04, 0xf4, 0x99, 0x25, 0x48, 0x07, 0x33, 0x8a, 0xf8, 0x8c, 0x18, + 0x1c, 0x00, 0x08, 0x82, 0x01, 0x46, 0x83, 0xc1, 0xfe, 0xc9, 0x5c, 0x60, 0x81, 0x01, 0x1f, 0x0b, 0x0e, 0xfa, 0xcc, + 0x12, 0xa4, 0xc3, 0x40, 0x85, 0xa1, 0x9c, 0x83, 0x80, 0x0e, 0x16, 0x6c, 0xf2, 0x31, 0x61, 0x93, 0x8f, 0x0d, 0x9b, + 0x7c, 0x6c, 0x00, 0x3f, 0xf9, 0x8c, 0x18, 0x20, 0x00, 0x08, 0x82, 0x81, 0x18, 0xe0, 0x60, 0x70, 0x82, 0x01, 0xf8, + 0x0d, 0xc1, 0x88, 0x01, 0x02, 0x80, 0x20, 0x18, 0x88, 0x41, 0x0e, 0x06, 0x28, 0x18, 0x80, 0xdf, 0x10, 0x98, 0x01, + 0x7e, 0xf2, 0x19, 0x31, 0x40, 0x00, 0x10, 0x04, 0x03, 0x31, 0xd8, 0xc1, 0x40, 0x05, 0x03, 0xf0, 0x33, 0x82, 0x11, + 0x03, 0x04, 0x00, 0x41, 0x30, 0x10, 0x03, 0x1e, 0x0c, 0x56, 0x30, 0x00, 0x3f, 0x23, 0xb0, 0x04, 0xfc, 0xe4, 0x33, + 0x62, 0x80, 0x00, 0x20, 0x08, 0x06, 0x62, 0xe0, 0x83, 0x41, 0x0b, 0x06, 0xe0, 0x97, 0x04, 0x23, 0x06, 0x08, 0x00, + 0x82, 0x60, 0x20, 0x06, 0x3f, 0x18, 0xb8, 0x60, 0x00, 0x7e, 0x49, 0x30, 0x1c, 0x71, 0x88, 0x9d, 0xf0, 0x0d, 0x47, + 0x14, 0x63, 0x27, 0x7c, 0xc3, 0x11, 0x03, 0xd9, 0x09, 0xdf, 0x70, 0x84, 0x52, 0x76, 0xc4, 0x37, 0x1c, 0x81, 0x98, + 0x1d, 0xf1, 0x0d, 0x47, 0x18, 0x67, 0x47, 0x7c, 0x67, 0x0c, 0x71, 0xc6, 0x10, 0x67, 0x0c, 0x71, 0xc6, 0x10, 0x67, + 0x0c, 0x71, 0xc6, 0x10, 0x66, 0x0c, 0x21, 0x30, 0x63, 0x08, 0x81, 0x19, 0x43, 0x08, 0x6e, 0x30, 0xec, 0x06, 0xc3, + 0x6e, 0x30, 0x6c, 0xc4, 0xc0, 0x00, 0x40, 0x10, 0x0c, 0x28, 0x3c, 0x0c, 0xda, 0x30, 0xc8, 0x46, 0x0c, 0x0c, 0x00, + 0x04, 0xc1, 0x80, 0xca, 0xc3, 0xc0, 0x0d, 0x83, 0x6b, 0xc4, 0xc0, 0x00, 0x40, 0x10, 0x0c, 0x28, 0x3d, 0x0c, 0xde, + 0x30, 0xa8, 0x46, 0x0c, 0x0c, 0x00, 0x04, 0xc1, 0x80, 0xda, 0xc3, 0x20, 0x0c, 0x83, 0x61, 0xc4, 0xc0, 0x00, 0x40, + 0x10, 0x0c, 0x28, 0x3e, 0x0c, 0xc4, 0x30, 0x18, 0x46, 0x0c, 0x0c, 0x00, 0x04, 0xc1, 0x80, 0xea, 0xc3, 0x60, 0x0c, + 0x83, 0xc1, 0x86, 0x14, 0x0c, 0xe4, 0x63, 0x83, 0x0a, 0x06, 0xf2, 0xb1, 0x61, 0x05, 0x03, 0xf9, 0x8c, 0x18, 0x18, + 0x00, 0x08, 0x82, 0x01, 0x15, 0x8a, 0x01, 0x1a, 0x06, 0xc3, 0x88, 0x81, 0x01, 0x80, 0x20, 0x18, 0x50, 0xa2, 0x18, + 0xa4, 0x61, 0x30, 0x8c, 0x18, 0x18, 0x00, 0x08, 0x82, 0x01, 0x35, 0x8a, 0x81, 0x1a, 0x06, 0x83, 0x0d, 0x8f, 0x7c, + 0x6c, 0x78, 0xe4, 0x63, 0xc3, 0x23, 0x1f, 0x1b, 0xdc, 0x4f, 0x3e, 0x23, 0x06, 0x08, 0x00, 0x82, 0x60, 0x20, 0x06, + 0xa2, 0x18, 0xc4, 0x61, 0xe0, 0x7e, 0x43, 0x30, 0x62, 0x80, 0x00, 0x20, 0x08, 0x06, 0x62, 0x30, 0x8a, 0x81, 0x1c, + 0x06, 0xee, 0x37, 0x04, 0x66, 0x90, 0x60, 0x20, 0x9f, 0x11, 0x03, 0x04, 0x00, 0x41, 0x30, 0x10, 0x83, 0x52, 0x0c, + 0xe8, 0x30, 0x20, 0xc1, 0xc0, 0x08, 0x46, 0x0c, 0x10, 0x00, 0x04, 0xc1, 0x40, 0x0c, 0x4c, 0x31, 0xa8, 0xc3, 0x80, + 0x04, 0x03, 0x23, 0xb0, 0x84, 0x04, 0x03, 0xf9, 0x8c, 0x18, 0x20, 0x00, 0x08, 0x82, 0x81, 0x18, 0xa0, 0x62, 0x70, + 0x87, 0x01, 0x09, 0x06, 0x49, 0x30, 0x62, 0x80, 0x00, 0x20, 0x08, 0x06, 0x62, 0x90, 0x8a, 0x01, 0x1e, 0x06, 0x24, + 0x18, 0x24, 0x81, 0xc5, 0xc2, 0x0c, 0x06, 0xf2, 0x19, 0x31, 0x40, 0x00, 0x10, 0x04, 0x03, 0x31, 0x58, 0xc5, 0x40, + 0x0f, 0x83, 0x19, 0x0c, 0x62, 0x21, 0x18, 0x31, 0x40, 0x00, 0x10, 0x04, 0x03, 0x31, 0x60, 0xc5, 0x60, 0x0f, 0x83, + 0x19, 0x0c, 0x62, 0x21, 0x30, 0x5a, 0x98, 0xc1, 0x40, 0x3e, 0x23, 0x06, 0x08, 0x00, 0x82, 0x60, 0x20, 0x06, 0xae, + 0x18, 0xf4, 0x61, 0x30, 0x83, 0x01, 0x2d, 0x04, 0x23, 0x06, 0x08, 0x00, 0x82, 0x60, 0x20, 0x06, 0xaf, 0x18, 0xf8, + 0x61, 0x30, 0x83, 0x01, 0x2d, 0x04, 0x76, 0x0b, 0x33, 0x18, 0xc8, 0x67, 0xc4, 0x00, 0x01, 0x40, 0x10, 0x0c, 0xc4, + 0x20, 0x16, 0x03, 0x50, 0x0c, 0x66, 0x30, 0xb8, 0x85, 0x60, 0xc4, 0x00, 0x01, 0x40, 0x10, 0x0c, 0xc4, 0x40, 0x16, + 0x83, 0x50, 0x0c, 0x66, 0x30, 0xb8, 0x85, 0x60, 0x38, 0xe2, 0xa8, 0x3d, 0xe1, 0x1b, 0x8e, 0x28, 0x6c, 0x4f, 0xf8, + 0x86, 0x23, 0x86, 0xdb, 0x13, 0xbe, 0xe1, 0x08, 0x05, 0xf7, 0x88, 0x6f, 0x38, 0x02, 0xc9, 0x3d, 0xe2, 0x1b, 0x8e, + 0x30, 0x74, 0x8f, 0xf8, 0xce, 0x18, 0xe2, 0x8c, 0x21, 0xce, 0x18, 0xe2, 0x8c, 0x21, 0xce, 0x18, 0xe2, 0x8c, 0x21, + 0xcc, 0x18, 0x42, 0x60, 0xc6, 0x10, 0x02, 0x33, 0x86, 0x10, 0xdc, 0x60, 0xd8, 0x0d, 0x86, 0xdd, 0x60, 0xd8, 0x88, + 0x81, 0x01, 0x80, 0x20, 0x18, 0x50, 0xeb, 0x18, 0x80, 0x63, 0x90, 0x8d, 0x18, 0x18, 0x00, 0x08, 0x82, 0x01, 0xc5, + 0x8e, 0x41, 0x38, 0x06, 0xd7, 0x88, 0x81, 0x01, 0x80, 0x20, 0x18, 0x50, 0xed, 0x18, 0x88, 0x63, 0x50, 0x8d, 0x18, + 0x18, 0x00, 0x08, 0x82, 0x01, 0xe5, 0x8e, 0x01, 0x2d, 0x06, 0xc3, 0x88, 0x81, 0x01, 0x80, 0x20, 0x18, 0x50, 0xef, + 0x18, 0xd4, 0x62, 0x30, 0x8c, 0x18, 0x18, 0x00, 0x08, 0x82, 0x01, 0x05, 0x8f, 0x81, 0x2d, 0x06, 0x83, 0x0d, 0x7c, + 0x18, 0xc8, 0xc7, 0x86, 0x3e, 0x0c, 0xe4, 0x63, 0x83, 0x1f, 0x06, 0xf2, 0x19, 0x31, 0x30, 0x00, 0x10, 0x04, 0x03, + 0x8a, 0x1e, 0x83, 0x5d, 0x0c, 0x86, 0x11, 0x03, 0x03, 0x00, 0x41, 0x30, 0xa0, 0xea, 0x31, 0xe0, 0xc5, 0x60, 0x18, + 0x31, 0x30, 0x00, 0x10, 0x04, 0x03, 0xca, 0x1e, 0x83, 0x5e, 0x0c, 0x06, 0x1b, 0x1e, 0xf9, 0xd8, 0xf0, 0xc8, 0xc7, + 0x86, 0x47, 0x3e, 0x36, 0xd0, 0x61, 0x20, 0x9f, 0x11, 0x03, 0x04, 0x00, 0x41, 0x30, 0x10, 0x83, 0x7a, 0x0c, 0xc8, + 0x31, 0xa0, 0xc3, 0x60, 0x08, 0x46, 0x0c, 0x10, 0x00, 0x04, 0xc1, 0x40, 0x0c, 0xec, 0x31, 0x28, 0xc7, 0x80, 0x0e, + 0x83, 0x21, 0x30, 0x83, 0x0e, 0x03, 0xf9, 0x8c, 0x18, 0x20, 0x00, 0x08, 0x82, 0x81, 0x18, 0xe0, 0x63, 0x70, 0x8e, + 0x01, 0x1d, 0x06, 0x46, 0x30, 0x62, 0x80, 0x00, 0x20, 0x08, 0x06, 0x62, 0x90, 0x8f, 0x01, 0x3a, 0x06, 0x74, 0x18, + 0x18, 0x81, 0xd1, 0x01, 0x1d, 0xc8, 0xc7, 0xe4, 0x40, 0x0e, 0xe4, 0x63, 0x81, 0x00, 0x9f, 0x11, 0x03, 0x03, 0x00, + 0x41, 0x30, 0xa0, 0x46, 0x32, 0xe0, 0xc5, 0x20, 0x30, 0x04, 0x91, 0x8f, 0x19, 0x86, 0x7c, 0x2c, 0x10, 0xe0, 0x33, + 0x62, 0x60, 0x00, 0x20, 0x08, 0x06, 0xd4, 0x49, 0x06, 0xe0, 0x18, 0x04, 0xc3, 0x11, 0x01, 0x09, 0x06, 0xc1, 0x67, + 0x86, 0x40, 0x9f, 0xe9, 0x86, 0x13, 0x0c, 0x02, 0xc1, 0x82, 0x47, 0x3e, 0x26, 0x34, 0xf2, 0x19, 0x31, 0x40, 0x00, + 0x10, 0x04, 0x03, 0x31, 0x38, 0xc9, 0xc0, 0x1e, 0x83, 0x38, 0x0c, 0x04, 0x52, 0x18, 0x31, 0x40, 0x00, 0x10, 0x04, + 0x03, 0x31, 0x40, 0xc9, 0xe0, 0x1e, 0x83, 0x38, 0x0c, 0x84, 0x60, 0xc4, 0x00, 0x01, 0x40, 0x10, 0x0c, 0xc4, 0x20, + 0x25, 0x03, 0x7c, 0x0c, 0xe2, 0x30, 0x20, 0x4c, 0x61, 0xc4, 0x00, 0x01, 0x40, 0x10, 0x0c, 0xc4, 0x40, 0x25, 0x83, + 0x7c, 0x0c, 0xe2, 0x30, 0x20, 0x82, 0x11, 0x03, 0x04, 0x00, 0x41, 0x30, 0x10, 0x83, 0x95, 0x0c, 0xf4, 0x31, 0x88, + 0xc3, 0xc0, 0x40, 0x85, 0x11, 0x03, 0x04, 0x00, 0x41, 0x30, 0x10, 0x03, 0x96, 0x0c, 0xf6, 0x31, 0x88, 0xc3, 0xc0, + 0x08, 0xac, 0x28, 0xe4, 0x63, 0x04, 0x21, 0x1f, 0x1b, 0x06, 0xf9, 0xd8, 0x80, 0xc8, 0xc7, 0x86, 0x43, 0x3e, 0x36, + 0x18, 0xf2, 0xb1, 0xe1, 0x0e, 0x03, 0xf9, 0x8c, 0x18, 0x20, 0x00, 0x08, 0x82, 0x81, 0x18, 0xd0, 0x64, 0x30, 0x92, + 0xc1, 0x1d, 0x06, 0x43, 0x30, 0x62, 0x80, 0x00, 0x20, 0x08, 0x06, 0x62, 0x50, 0x93, 0x01, 0x49, 0x06, 0x77, 0x18, + 0x0c, 0x81, 0x19, 0x77, 0x18, 0xc8, 0x67, 0xc4, 0x00, 0x01, 0x40, 0x10, 0x0c, 0xc4, 0xe0, 0x26, 0x03, 0x93, 0x0c, + 0xee, 0x30, 0x30, 0x82, 0x11, 0x03, 0x04, 0x00, 0x41, 0x30, 0x10, 0x03, 0x9c, 0x0c, 0x4e, 0x32, 0xb8, 0xc3, 0xc0, + 0x08, 0x2c, 0xb9, 0xc3, 0x40, 0x3e, 0x23, 0x06, 0x08, 0x00, 0x82, 0x60, 0x20, 0x06, 0x3a, 0x19, 0xa4, 0x64, 0x70, + 0x87, 0x41, 0x12, 0x8c, 0x18, 0x20, 0x00, 0x08, 0x82, 0x81, 0x18, 0xec, 0x64, 0xa0, 0x92, 0xc1, 0x1d, 0x06, 0x49, + 0x60, 0xc7, 0x1d, 0x06, 0xf2, 0x19, 0x31, 0x40, 0x00, 0x10, 0x04, 0x03, 0x31, 0xe8, 0xc9, 0x80, 0x25, 0x83, 0x3b, + 0x0c, 0x8a, 0x60, 0xc4, 0x00, 0x01, 0x40, 0x10, 0x0c, 0xc4, 0xc0, 0x27, 0x83, 0x96, 0x0c, 0xee, 0x30, 0x18, 0x02, + 0x53, 0xee, 0x30, 0x90, 0xcf, 0x88, 0x01, 0x02, 0x80, 0x20, 0x18, 0x88, 0x01, 0x58, 0x06, 0x2f, 0x19, 0xdc, 0x61, + 0x80, 0x04, 0x23, 0x06, 0x08, 0x00, 0x82, 0x60, 0x20, 0x06, 0x61, 0x19, 0xc0, 0x64, 0x70, 0x87, 0x81, 0x11, 0x58, + 0x73, 0x87, 0x81, 0x7c, 0x46, 0x0c, 0x10, 0x00, 0x04, 0xc1, 0x40, 0x0c, 0xc6, 0x32, 0x90, 0xc9, 0xe0, 0x0e, 0x83, + 0x25, 0x18, 0x31, 0x40, 0x00, 0x10, 0x04, 0x03, 0x31, 0x20, 0xcb, 0x60, 0x26, 0x83, 0x3b, 0x0c, 0x92, 0x60, 0xc4, + 0xe0, 0x00, 0x40, 0x10, 0x0c, 0xb0, 0xb4, 0x0c, 0x60, 0x32, 0x38, 0xce, 0x30, 0x18, 0x31, 0x38, 0x00, 0x10, 0x04, + 0x03, 0x4c, 0x2d, 0x83, 0x98, 0x0c, 0x0a, 0x34, 0x0c, 0x46, 0x0c, 0x0e, 0x00, 0x04, 0xc1, 0x00, 0x5b, 0xcb, 0x40, + 0x26, 0x83, 0x21, 0x0d, 0x03, 0x1b, 0xf4, 0x30, 0x90, 0xcf, 0x88, 0x01, 0x02, 0x80, 0x20, 0x18, 0x88, 0x41, 0x5a, + 0x06, 0x38, 0x19, 0xe8, 0x61, 0x30, 0x04, 0x23, 0x06, 0x08, 0x00, 0x82, 0x60, 0x20, 0x06, 0x6a, 0x19, 0xe4, 0x64, + 0xa0, 0x87, 0xc1, 0x10, 0x98, 0xa1, 0x87, 0x81, 0x7c, 0x46, 0x0c, 0x10, 0x00, 0x04, 0xc1, 0x40, 0x0c, 0xd8, 0x32, + 0xd8, 0xc9, 0x40, 0x0f, 0x03, 0x23, 0x18, 0x31, 0x40, 0x00, 0x10, 0x04, 0x03, 0x31, 0x68, 0xcb, 0x80, 0x27, 0x03, + 0x3d, 0x0c, 0x8c, 0xc0, 0x12, 0x3d, 0x0c, 0xe4, 0x33, 0x62, 0x80, 0x00, 0x20, 0x08, 0x06, 0x62, 0xf0, 0x96, 0x81, + 0x4f, 0x06, 0x7a, 0x18, 0x24, 0xc1, 0x88, 0x01, 0x02, 0x80, 0x20, 0x18, 0x88, 0x01, 0x5c, 0x06, 0x3f, 0x19, 0xe8, + 0x61, 0x90, 0x04, 0x76, 0xdc, 0x55, 0x7c, 0xac, 0xb8, 0xab, 0xf8, 0xd8, 0x70, 0x57, 0xf1, 0xb1, 0xc1, 0x7f, 0xe4, + 0x63, 0xc3, 0xff, 0xc8, 0xc7, 0x06, 0x10, 0x92, 0x8f, 0x0d, 0x7d, 0x05, 0x1f, 0x1b, 0xfa, 0x0a, 0x3e, 0x36, 0xf4, + 0x15, 0x7c, 0x46, 0x0c, 0x16, 0x00, 0x04, 0xc1, 0x40, 0xeb, 0xcb, 0x20, 0x2c, 0x83, 0x41, 0x08, 0x54, 0x32, 0x48, + 0xc9, 0x00, 0x25, 0x83, 0xd1, 0x04, 0x13, 0x1a, 0x4c, 0xb0, 0xc7, 0x00, 0x3e, 0x46, 0x17, 0xf7, 0x18, 0xc0, 0xc7, + 0x84, 0x80, 0x3e, 0x23, 0x06, 0x07, 0x00, 0x82, 0x60, 0x80, 0x8d, 0x66, 0xa0, 0x96, 0x41, 0x10, 0x8a, 0xc1, 0x88, + 0xc1, 0x01, 0x80, 0x20, 0x18, 0x60, 0xa4, 0x19, 0xa4, 0x65, 0x10, 0x14, 0xa3, 0x09, 0x2c, 0x14, 0x98, 0x80, 0x5a, + 0xf2, 0xb1, 0x01, 0xb5, 0xe4, 0x63, 0x04, 0x6a, 0xc9, 0xc7, 0x9c, 0x21, 0x3e, 0xe6, 0x0c, 0xf1, 0x31, 0x67, 0x88, + 0x8f, 0x1d, 0x83, 0x7c, 0x0c, 0x19, 0xe4, 0x63, 0xc9, 0x20, 0x1f, 0x1b, 0x12, 0xf8, 0xd8, 0x90, 0xc0, 0xc7, 0x86, + 0x04, 0x3e, 0xb3, 0x04, 0xea, 0x30, 0xd0, 0x62, 0xc0, 0x93, 0x2b, 0xc0, 0x4c, 0x39, 0x18, 0xe9, 0x80, 0xb6, 0x81, + 0x2a, 0xa0, 0x6d, 0x30, 0x0e, 0x03, 0x2d, 0x06, 0x3c, 0xb9, 0x02, 0xcc, 0x94, 0x83, 0x91, 0x0e, 0x68, 0x1b, 0xa8, + 0x02, 0xda, 0x06, 0xe3, 0x30, 0xd0, 0x62, 0xc0, 0x93, 0x2b, 0xc0, 0x4c, 0x39, 0x18, 0xe9, 0x80, 0xb6, 0x81, 0x2a, + 0xa0, 0x6d, 0x30, 0x0e, 0x85, 0x5b, 0x81, 0x54, 0x6e, 0x0d, 0x52, 0xba, 0x55, 0xc8, 0x70, 0x03, 0xaa, 0xc5, 0x66, + 0x10, 0x06, 0xd3, 0x0d, 0xe3, 0x25, 0x04, 0xd3, 0x0d, 0xe3, 0x45, 0x08, 0xd3, 0x0d, 0xe3, 0x65, 0x0c, 0xc3, 0x0d, + 0xaa, 0x56, 0x9b, 0x41, 0x18, 0x4c, 0x37, 0xec, 0x62, 0x40, 0x04, 0xd3, 0x0d, 0xbc, 0x18, 0x10, 0xc2, 0x74, 0x43, + 0x2f, 0x06, 0xc4, 0x30, 0xdc, 0xf0, 0x6a, 0xb9, 0x19, 0x80, 0xc1, 0x2c, 0x03, 0x3b, 0xac, 0x43, 0x30, 0x62, 0x70, + 0x00, 0x20, 0x08, 0x06, 0xdd, 0x6f, 0x06, 0xa7, 0x19, 0xdc, 0x62, 0xa0, 0x9b, 0xc1, 0x68, 0x42, 0x10, 0xcc, 0x12, + 0xb0, 0xc3, 0x40, 0x85, 0x21, 0xac, 0x03, 0xa3, 0x0e, 0x03, 0x15, 0x06, 0xb1, 0x0e, 0x8c, 0x3a, 0x0c, 0x54, 0x18, + 0xc6, 0x3a, 0x30, 0xea, 0x30, 0x62, 0x70, 0x00, 0x20, 0x08, 0x06, 0x98, 0x79, 0x06, 0xad, 0x19, 0x0c, 0xe4, 0x18, + 0x8c, 0x18, 0x1c, 0x00, 0x08, 0x82, 0x01, 0x76, 0x9e, 0x81, 0x6b, 0x06, 0x43, 0x39, 0x06, 0x23, 0x06, 0x07, 0x00, + 0x82, 0x60, 0x80, 0xa1, 0x67, 0xf0, 0x9a, 0xc1, 0x60, 0x8e, 0xc1, 0x88, 0x81, 0x03, 0x80, 0x20, 0x18, 0x34, 0xee, + 0x19, 0xb4, 0x66, 0x50, 0x8e, 0x81, 0x2f, 0x06, 0xbd, 0x18, 0xcc, 0x66, 0x30, 0x08, 0x01, 0x7c, 0xe9, 0x65, 0x30, + 0x4b, 0xd0, 0x0e, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; diff --git a/OptiScaler/shaders/dlssnr/precompile/dlssnr.hlsl b/OptiScaler/shaders/dlssnr/precompile/dlssnr.hlsl index bf8b46be8..0280b6e68 100644 --- a/OptiScaler/shaders/dlssnr/precompile/dlssnr.hlsl +++ b/OptiScaler/shaders/dlssnr/precompile/dlssnr.hlsl @@ -18,6 +18,8 @@ cbuffer Params : register(b0) float gCompareSplit; // where the wipe cuts, 0..1 float gCompareZoom; // side by side: 1 fits the frame, 2 fills the half uint gCompareSwap; // put the edited frame on the other side + uint gTransfer; // 0 Classic, 1 Matched residual + uint gHdrLift; // 0 H0, 1 H1; ignored unless gTransfer == 1 }; // Colours outside the AP1 gamut are impossible on any display and read as sparkle where a bright @@ -86,7 +88,7 @@ float3 HueOkLab(float3 incorrect, float3 correct) Texture2D gSource : register(t0); // encode: the frame. resolve: the proxy. Texture2D gModel : register(t1); // resolve: what the model returned. Texture2D gOriginal : register(t2); // resolve: the untouched frame. -Texture2D gMotion : register(t3); // resolve, accumulating: the game's motion vectors. +Texture2D gProxy : register(t3); // resolve: colorCopy (full-res encoded proxy) RWTexture2D gTarget : register(u0); // encode: the proxy. resolve: the frame. RWTexture2D gKeep : register(u1); // encode: the untouched copy. resolve: the edit history. SamplerState gLinear : register(s0); // so the edit can be read at a different size @@ -107,6 +109,45 @@ float3 SrgbToLinear(float3 v) return lerp(v / 12.92, pow((v + 0.055) / 1.055, 2.4), step(0.04045, v)); } +float3 DecodeRgb(float3 rgb) +{ + return gPassthrough != 0 ? rgb : SrgbToLinear(rgb); +} + +float3 CubeScaleResidual(float3 P, float3 T) +{ + if (gPassthrough != 0) + return T; + float3 d = T - P; + float alpha = 1.0; + [unroll] for (int c = 0; c < 3; ++c) + { + if (d[c] > 1e-6) + alpha = min(alpha, (1.0 - P[c]) / d[c]); + else if (d[c] < -1e-6) + alpha = min(alpha, (0.0 - P[c]) / d[c]); + } + return P + saturate(alpha) * d; +} + +// Live UpgradeToneMap body, parameterized. Empty-model gate is the caller's job. +float3 ComposeUpgrade(float3 H, float3 proxyRgb, float3 modelRgb) +{ + float originalLuma = dot(H, kLuma); + float proxyLuma = dot(proxyRgb, kLuma); + float modelLuma = dot(modelRgb, kLuma); + float ratio; + if (originalLuma < proxyLuma) + ratio = originalLuma / max(proxyLuma, 1e-6); + else + ratio = (modelLuma + max(0.0, originalLuma - proxyLuma)) / modelLuma; + float3 upgraded = lerp(H, HueOkLab(modelRgb * ratio, modelRgb), gTransferStrength); + float upgradedLuma = dot(upgraded, kLuma); + const float kRatioFloor = 1.0 / 512.0; + float lumaRatio = clamp((upgradedLuma + kRatioFloor) / (originalLuma + kRatioFloor), 0.0, gMaxRatio); + return lerp(H * lumaRatio, upgraded, gColourStrength); +} + // The edit at an arbitrary position, exactly as the resolve computes its own. float3 EditAt(float2 uvq) { @@ -134,7 +175,54 @@ void CSMain(uint3 id : SV_DispatchThreadID) if (gMode == 2) { - gTarget[id.xy] = gSource.SampleLevel(gLinear, uv, 0); + if (gTransfer == 0) + { + gTarget[id.xy] = gSource.SampleLevel(gLinear, uv, 0); + return; + } + + uint srcW, srcH; + gSource.GetDimensions(srcW, srcH); + if (srcW == gWidth && srcH == gHeight) + { + gTarget[id.xy] = gSource.Load(int3(id.xy, 0)); + return; + } + + const float x0 = ((float) id.x * (float) srcW) / (float) gWidth; + const float x1 = ((float) (id.x + 1) * (float) srcW) / (float) gWidth; + const float y0 = ((float) id.y * (float) srcH) / (float) gHeight; + const float y1 = ((float) (id.y + 1) * (float) srcH) / (float) gHeight; + const float area = (x1 - x0) * (y1 - y0); + + const int i0 = (int) floor(x0); + const int i1 = (int) ceil(x1) - 1; + const int j0 = (int) floor(y0); + const int j1 = (int) ceil(y1) - 1; + + float3 acc = 0.0; + for (int j = j0; j <= j1; ++j) + { + const int jj = clamp(j, 0, (int) srcH - 1); + const float t0y = (float) j; + const float aY = max(y0, t0y); + const float bY = min(y1, t0y + 1.0); + const float wy = max(bY - aY, 0.0); + for (int i = i0; i <= i1; ++i) + { + const int ii = clamp(i, 0, (int) srcW - 1); + const float t0x = (float) i; + const float aX = max(x0, t0x); + const float bX = min(x1, t0x + 1.0); + const float w = max(bX - aX, 0.0) * wy; + acc += gSource.Load(int3(ii, jj, 0)).rgb * w; + } + } + + const int acx = clamp((int) floor(((float) id.x + 0.5) * (float) srcW / (float) gWidth), 0, (int) srcW - 1); + const int acy = clamp((int) floor(((float) id.y + 0.5) * (float) srcH / (float) gHeight), 0, (int) srcH - 1); + const float a = gSource.Load(int3(acx, acy, 0)).a; + gTarget[id.xy] = float4(acc / area, a); return; } @@ -219,127 +307,92 @@ void CSMain(uint3 id : SV_DispatchThreadID) // Sampled rather than loaded: when the model ran at a reduced resolution these are smaller than the // frame, and its edit is enlarged here while the frame underneath stays untouched. - float4 proxySample = gSource.SampleLevel(gLinear, cmpUv, 0); - float4 modelSample = gModel.SampleLevel(gLinear, cmpUv, 0); - - // Nothing was encoded on the way in, so nothing is decoded here either. - float3 proxy = gPassthrough != 0 ? proxySample.rgb : SrgbToLinear(proxySample.rgb); - float3 model = gPassthrough != 0 ? modelSample.rgb : SrgbToLinear(modelSample.rgb); + float3 p = DecodeRgb(gSource.SampleLevel(gLinear, cmpUv, 0).rgb); + float3 m = DecodeRgb(gModel.SampleLevel(gLinear, cmpUv, 0).rgb); float4 originalSample = gCompareMode == 1 ? gOriginal.SampleLevel(gLinear, cmpUv, 0) : gOriginal.Load(int3(id.xy, 0)); - - // All three pictures have to share a scale before their luminances can be compared. The proxy and - // the model come back from an sRGB decode, so they sit in 0..1 where 1 is the white point; the - // frame is raw linear and runs well past that. Comparing them unnormalised is a real bug and it - // reads exactly like the model has stopped adding detail: with the frame several times larger, - // the shadow branch never fires, every pixel takes the highlight branch, and the clamp flattens - // the result to a near-constant scale. Colour still moves, because that comes from the model's - // own hue, which is what makes the failure so confusing to look at. const float normScale = gPassthrough != 0 ? 1.0 : max(gWhitePoint, 1e-4); - float3 original = originalSample.rgb / normScale; - - float originalLuma = dot(original, kLuma); - float proxyLuma = dot(proxy, kLuma); + float3 H = originalSample.rgb / normScale; if (gDebugView == 1) { - gTarget[id.xy] = float4(proxy * gWhitePoint, originalSample.a); + gTarget[id.xy] = float4(p * gWhitePoint, originalSample.a); return; } - if (gDebugView == 2) { - gTarget[id.xy] = float4(model * gWhitePoint, originalSample.a); + gTarget[id.xy] = float4(m * gWhitePoint, originalSample.a); return; } - - float3 edit = model - proxy; - - // Coring was tried here and removed: the per-frame churn's amplitude overlaps the real detail's, - // so an amplitude threshold cannot separate them -- it only relocated the noise to the threshold. - if (gDebugView == 3) { - // Amplified and centred on grey, so both directions of the edit are visible at once. - float3 shown = saturate(0.5 + edit * 20.0); + float3 shown = saturate(0.5 + (m - p) * 20.0); gTarget[id.xy] = float4(SrgbToLinear(shown) * gWhitePoint, originalSample.a); return; } - // The edit, averaged over time. The model re-decides a measurable fraction of its answer every - // frame even on a static scene; blending each frame's edit with its own reprojected history keeps - // the consistent part -- the detail -- and cancels the part that re-randomises. NVIDIA's own - // motion vectors carry the history to where the surface is now. + if (gDebugView == 4 || gDebugView == 5) + { + float3 shown = 0.0; + if (gTransfer == 1) + { + float4 eSample = gCompareMode == 1 ? gProxy.SampleLevel(gLinear, cmpUv, 0) + : gProxy.Load(int3(id.xy, 0)); + float3 P = DecodeRgb(eSample.rgb); + uint srcW, srcH; + gSource.GetDimensions(srcW, srcH); + bool sameRate = (srcW == gWidth && srcH == gHeight); + float3 T = sameRate ? m : CubeScaleResidual(P, P + (m - p)); + shown = (gDebugView == 4 ? P : T) * gWhitePoint; + } + gTarget[id.xy] = float4(shown, originalSample.a); + return; + } - // The composition. The model's answer is not treated as a difference to add onto the frame -- it - // is a complete picture in its own right, and it is brought back by rescaling it to sit where the - // original's luminance says it should. Adding a difference is what let colour run away: nothing - // bounded where the sum landed, so a warm subject could arrive green. Here both ends of every - // blend are well-formed pictures, so everything between them is one too. - float modelLuma = dot(model, kLuma); - float3 upgraded; + if (gTransferStrength == 0) + { + float4 o = gCompareMode == 1 ? gOriginal.SampleLevel(gLinear, cmpUv, 0) + : gOriginal.Load(int3(id.xy, 0)); + float3 rgb = o.rgb; + if (outsideFrame) + rgb = 0.0; + if (onDivider) + rgb = gWhitePoint; + gTarget[id.xy] = float4(rgb, o.a); + return; + } - if (modelLuma <= 1e-5) + float3 result; + if (gTransfer == 0) { - // The model can return an empty frame for an input it cannot read. Rescaling that collapses - // the picture to black, so the frame is handed back untouched instead. - upgraded = original; + if (dot(m, kLuma) <= 1e-5) + result = H * normScale; + else + result = ComposeUpgrade(H, p, m) * normScale; } else { - float ratio; - - if (originalLuma < proxyLuma) - { - // Below what the proxy showed: the frame's own luminance is the target. - ratio = originalLuma / max(proxyLuma, 1e-6); - } + float4 eSample = gCompareMode == 1 ? gProxy.SampleLevel(gLinear, cmpUv, 0) + : gProxy.Load(int3(id.xy, 0)); + float3 P = DecodeRgb(eSample.rgb); + uint srcW, srcH; + gSource.GetDimensions(srcW, srcH); + bool sameRate = (srcW == gWidth && srcH == gHeight); + float3 T = sameRate ? m : CubeScaleResidual(P, P + (m - p)); + + if (dot(m, kLuma) <= 1e-5) + result = H * normScale; + else if (gHdrLift == 1) + result = lerp(H, H + (T - P), gTransferStrength) * normScale; else - { - // Above it, the difference is headroom the proxy could not represent -- brightness the - // frame really has and the model never saw. It is handed back on top of the model's own - // answer rather than scaled away, which is what kept highlights from being muted. - ratio = (modelLuma + max(0.0, originalLuma - proxyLuma)) / modelLuma; - } - - upgraded = lerp(original, HueOkLab(model * ratio, model), gTransferStrength); + result = ComposeUpgrade(H, P, T) * normScale; } - // Detail strength decides how much of the model's picture is reached at all; colour strength - // decides whether its colour comes with it. At 0 the frame keeps the game's own hue exactly and - // only its light carries the model's verdict; at 1 the model's colour arrives as well. - float upgradedLuma = dot(upgraded, kLuma); - - // A ratio against a dark pixel is unbounded, and clamping it is not the same as taming it. - // - // In linear light divided by paper white a shadowed pixel sits around a thousandth, so a tiny - // absolute edit from the model becomes an enormous ratio, hits the clamp, and doubles that - // pixel's brightness. The next frame it lands slightly differently and the pixel drops back. - // That is the boiling: patches of lighter colour crawling over otherwise still geometry, worst - // where the picture is darkest. - // - // Adding the same floor above and below leaves bright pixels alone -- where luminance is far - // larger than the floor the term vanishes -- while making the ratio fall smoothly to one as - // luminance approaches zero. No edit at all is the right answer for a pixel with no light in it. - const float kRatioFloor = 1.0 / 512.0; - float lumaRatio = clamp((upgradedLuma + kRatioFloor) / (originalLuma + kRatioFloor), 0.0, gMaxRatio); - float3 result = lerp(original * lumaRatio, upgraded, gColourStrength); - - // Back out of the normalised space the composition worked in. - result *= normScale; - - // The side being shown untouched takes the frame as it arrived, past every step above. if (showOriginal) result = originalSample.rgb; - - // The letterbox. The sampler clamps rather than wrapping, so without this the bars would be the - // frame's edge row smeared down the screen. if (outsideFrame) - result = float3(0.0, 0.0, 0.0); - - // A hairline so the two sides are never mistaken for one picture. + result = 0.0; if (onDivider) - result = float3(gWhitePoint, gWhitePoint, gWhitePoint); - - gTarget[id.xy] = float4(max(result, float3(0.0, 0.0, 0.0)), originalSample.a); + result = gWhitePoint; + gTarget[id.xy] = float4(max(result, 0.0), originalSample.a); } diff --git a/OptiScaler/shaders/output_scaling/OS_Dx12.cpp b/OptiScaler/shaders/output_scaling/OS_Dx12.cpp index da1c6e006..e9bea734f 100644 --- a/OptiScaler/shaders/output_scaling/OS_Dx12.cpp +++ b/OptiScaler/shaders/output_scaling/OS_Dx12.cpp @@ -77,8 +77,12 @@ bool OS_Dx12::Dispatch(ID3D12GraphicsCommandList* InCmdList, ID3D12Resource* InR const auto dstW = (uint32_t) dstDesc.Width; const auto dstH = (uint32_t) dstDesc.Height; - FsrEasuCon(fsr1Constants.const0, fsr1Constants.const1, fsr1Constants.const2, fsr1Constants.const3, srcW, srcH, - srcW, srcH, dstW, dstH); + FsrEasuCon(fsr1Constants.const0, fsr1Constants.const1, fsr1Constants.const2, fsr1Constants.const3, srcW, srcH, srcW, + srcH, dstW, dstH); + // vrperfkit's CSO only runs EASU inside SquaredRadius; tiles outside fall back to bilinear. + // The struct default is zero, which makes every tile bilinear -- "FSR1" was a no-op. UINT_MAX + // disables the circle so the whole destination uses EASU. + fsr1Constants.squaredRadius = 0xFFFFFFFFu; constants.srcWidth = srcW; constants.srcHeight = srcH; @@ -87,7 +91,7 @@ bool OS_Dx12::Dispatch(ID3D12GraphicsCommandList* InCmdList, ID3D12Resource* InR // fsr upscaling bool createdConstantsBuffer = false; - if (Config::Instance()->OutputScalingDownscaler.value_or_default() == Scaler::FSR1) + if (_scaler == Scaler::FSR1) { createdConstantsBuffer = CreateConstantsBuffer(_device, _constantBuffer, fsr1Constants, currentHeap.GetCbvCPU(0)); @@ -123,7 +127,12 @@ bool OS_Dx12::Dispatch(ID3D12GraphicsCommandList* InCmdList, ID3D12Resource* InR } OS_Dx12::OS_Dx12(std::string InName, ID3D12Device* InDevice, bool InUpsample) - : Shader_Dx12(InName, InDevice), _upsample(InUpsample) + : OS_Dx12(std::move(InName), InDevice, InUpsample, Config::Instance()->OutputScalingDownscaler.value_or_default()) +{ +} + +OS_Dx12::OS_Dx12(std::string InName, ID3D12Device* InDevice, bool InUpsample, Scaler kernel) + : Shader_Dx12(InName, InDevice), _upsample(InUpsample), _scaler(kernel) { if (InDevice == nullptr) { @@ -148,15 +157,13 @@ OS_Dx12::OS_Dx12(std::string InName, ID3D12Device* InDevice, bool InUpsample) InDevice->CreateCommittedResource(&heapProps, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&_constantBuffer)); - auto downscalerConfig = Config::Instance()->OutputScalingDownscaler.value_or_default(); - const void* csoData = nullptr; size_t csoSize = 0; const char* sourceCode = nullptr; std::string name = "OS: "; - if (downscalerConfig == Scaler::FSR1) + if (_scaler == Scaler::FSR1) { csoData = fsr_easu_cso; csoSize = sizeof(fsr_easu_cso); @@ -175,7 +182,7 @@ OS_Dx12::OS_Dx12(std::string InName, ID3D12Device* InDevice, bool InUpsample) InNumThreadsY = 8; InNumThreadsX = 8; - switch (downscalerConfig) + switch (_scaler) { case Scaler::CatmullRom: csoData = bcds_catmull_cso; diff --git a/OptiScaler/shaders/output_scaling/OS_Dx12.h b/OptiScaler/shaders/output_scaling/OS_Dx12.h index 0298fac2c..dcd4e56dc 100644 --- a/OptiScaler/shaders/output_scaling/OS_Dx12.h +++ b/OptiScaler/shaders/output_scaling/OS_Dx12.h @@ -2,15 +2,19 @@ #include #include +#include #include #include +enum class Scaler : uint32_t; + #define OS_NUM_OF_HEAPS 2 class OS_Dx12 : public Shader_Dx12 { private: bool _upsample = false; + Scaler _scaler; FrameDescriptorHeap _frameHeaps[OS_NUM_OF_HEAPS]; @@ -31,6 +35,7 @@ class OS_Dx12 : public Shader_Dx12 bool CanRender() const { return _init && _buffer != nullptr; } OS_Dx12(std::string InName, ID3D12Device* InDevice, bool InUpsample); + OS_Dx12(std::string InName, ID3D12Device* InDevice, bool InUpsample, Scaler kernel); ~OS_Dx12(); }; diff --git a/OptiScaler/upscalers/IFeature.cpp b/OptiScaler/upscalers/IFeature.cpp index 8164ca175..ae6037ed3 100644 --- a/OptiScaler/upscalers/IFeature.cpp +++ b/OptiScaler/upscalers/IFeature.cpp @@ -2,6 +2,98 @@ #include #include "IFeature.h" +DlssNr::Mode IFeature::NREffectiveMode() const +{ + auto mode = DlssNr::ConfiguredMode(); + + if (!Config::Instance()->DlssNrEnabled.value_or_default()) + return DlssNr::Mode::PostProcess; + + // The multi-pass stage lives in IFeature_Dx12's pipeline and nowhere else. + // On D3D11 and Vulkan the model still runs, but only after the upscaler. + if (Api() != API::DX12) + return DlssNr::Mode::PostProcess; + + // Only DLSS and Ray Reconstruction reach NRPrepareForCreate, so only they + // can have their target held at 1:1. Returning anything else for an + // upscaler that never records a built mode would ask for a rebuild every + // frame, for ever. + const auto upscaler = GetUpscalerType(); + + if (upscaler != Upscaler::DLSS && upscaler != Upscaler::DLSSD) + return DlssNr::Mode::PostProcess; + + if (!DlssNr::UsesTwoFeatures(mode)) + return mode; + + const bool rrActive = upscaler == Upscaler::DLSSD; + const bool wantsRr = Config::Instance()->DlssNrFeature1Pipeline.value_or_default() == + (uint32_t) DlssNr::Feature1Pipeline::RayReconstruction; + + if (wantsRr != rrActive) + { + static bool warned = false; + + if (!warned) + { + warned = true; + LOG_WARN("DLSS-NR: multi-pass is set to the {} pipeline but the active upscaler is {}; using " + "the conventional ordering instead", + wantsRr ? "Ray Reconstruction" : "Super Resolution", rrActive ? "RR" : "not RR"); + } + + return DlssNr::Mode::PostProcess; + } + + return mode; +} + +bool IFeature::NRApplyFeature1Hold() +{ + if (!NRUsesTwoFeatures()) + return false; + + if (_nrSourceWidth == 0 || _nrSourceHeight == 0) + return false; + + if (_targetWidth != _nrSourceWidth || _targetHeight != _nrSourceHeight) + { + LOG_DEBUG("DLSS-NR multi-pass: restoring the first pass's 1:1 target, {}x{} rather than {}x{}", _nrSourceWidth, + _nrSourceHeight, _targetWidth, _targetHeight); + } + + _targetWidth = _nrSourceWidth; + _targetHeight = _nrSourceHeight; + + return true; +} + +// Settle which arrangement this feature is being built for, and apply what that +// decides. +// +// Deliberately NOT part of SetInitParameters. That runs from a feature's +// constructor -- DLSSFeature's, with DLSSFeatureDx12 not yet built -- where +// Api() and GetUpscalerType() are still pure virtual. Calling them there +// aborts the process. +// +// Called from ProcessInitParams instead, which runs from InitInternal with the +// object complete, and before the create flags are assembled from _initFlags. +void IFeature::NRPrepareForCreate() +{ + _nrModeAtCreate = NREffectiveMode(); + + if (NRUsesTwoFeatures() && _nrSourceWidth != 0 && _nrSourceHeight != 0) + { + _renderWidth = _nrSourceWidth; + _renderHeight = _nrSourceHeight; + _targetWidth = _nrSourceWidth; + _targetHeight = _nrSourceHeight; + + LOG_INFO("DLSS-NR multi-pass: Feature 1 runs 1:1 at {}x{}, spatial enlarge to {}x{}", _renderWidth, + _renderHeight, _displayWidth, _displayHeight); + } +} + void IFeature::SetHandle(unsigned int InHandleId) { _handle = new NVSDK_NGX_Handle { InHandleId }; @@ -84,6 +176,11 @@ bool IFeature::SetInitParameters(NVSDK_NGX_Parameter* InParameters) _initFlags.AutoExposure = _featureFlags & NVSDK_NGX_DLSS_Feature_Flags_AutoExposure; } + // What the game's own frame is. A plain copy, deliberately: this runs + // from a feature's constructor, before the most-derived class exists, + // so Api() and GetUpscalerType() are still pure virtual. + _nrGameIsHdr = _initFlags.IsHdr; + LOG_INFO("Init Flag AutoExposure: {}", _initFlags.AutoExposure); LOG_INFO("Init Flag DepthInverted: {}", _initFlags.DepthInverted); LOG_INFO("Init Flag IsHdr: {}", _initFlags.IsHdr); @@ -149,6 +246,12 @@ bool IFeature::SetInitParameters(NVSDK_NGX_Parameter* InParameters) _renderHeight = height; } + // The game's own render resolution, before the multi-pass hold can pin + // _renderWidth. Another plain copy: the hold itself needs to know which + // arrangement is in force, which cannot be asked from a constructor. + _nrSourceWidth = _renderWidth; + _nrSourceHeight = _renderHeight; + _perfQualityValue = (NVSDK_NGX_PerfQuality_Value) pqValue; LOG_INFO("Render Resolution: {0}x{1}, Display Resolution {2}x{3}, Quality: {4}", _renderWidth, _renderHeight, diff --git a/OptiScaler/upscalers/IFeature.h b/OptiScaler/upscalers/IFeature.h index 262a5c13a..57363cb8f 100644 --- a/OptiScaler/upscalers/IFeature.h +++ b/OptiScaler/upscalers/IFeature.h @@ -1,6 +1,7 @@ #pragma once #include "SysUtils.h" +#include #include #include @@ -91,6 +92,20 @@ class IFeature unsigned int _displayWidth = 0; unsigned int _displayHeight = 0; + // The game's own render resolution, recorded before the multi-pass hold can + // pin _renderWidth/_renderHeight to 1:1. Zero until SetInitParameters has run. + unsigned int _nrSourceWidth = 0; + unsigned int _nrSourceHeight = 0; + + // The arrangement this feature was actually built for. Target size is latched + // at creation, so a placement change has to rebuild rather than take effect live. + DlssNr::Mode _nrModeAtCreate = DlssNr::Mode::PostProcess; + + // Whether the game's own image is linear HDR, recorded in the constructor + // before any later create-time decision. Distinct from IsHdr(), which reports + // the flag this feature was created with. + bool _nrGameIsHdr = false; + long _frameCount = 0; bool _featureFrozen = false; bool _moduleLoaded = false; @@ -159,6 +174,47 @@ class IFeature virtual bool LowResMV() { return _initFlags.LowResMV; } virtual bool SharpenEnabled() { return _initFlags.SharpenEnabled; } + // The Neural Rendering arrangement actually in force for this feature. + // + // Off, a non-Dx12 API, or an upscaler that is not DLSS / Ray Reconstruction + // always reports PostProcess. Multi-pass also needs the configured first-pass + // pipeline to match the live upscaler; a mismatch falls back rather than + // holding a feature at 1:1 with no enlarge to follow. + DlssNr::Mode NREffectiveMode() const; + + bool NRUsesTwoFeatures() const { return DlssNr::UsesTwoFeatures(NREffectiveMode()); } + + // True when the arrangement changed since this feature was built. Engines + // rebuild on a resolution change, and the game's resolutions have not moved, + // so nothing else notices. Left unchecked, the pipeline starts routing a + // display-sized feature into a render-resolution buffer. + bool NRNeedsRebuild() const { return _nrModeAtCreate != NREffectiveMode(); } + + // What the pipeline and the seam must branch on, rather than the configured + // mode: the two agree only after a rebuild. + DlssNr::Mode NRBuiltMode() const { return _nrModeAtCreate; } + + // Settle which arrangement this feature is being built for, and apply the + // 1:1 hold. Must be called from ProcessInitParams, never from a constructor: + // it asks Api() and GetUpscalerType(), which are still pure virtual while + // the base classes are being built. + void NRPrepareForCreate(); + + // Re-apply the multi-pass 1:1 hold to the target resolution. + // + // ProcessInitParams recomputes the target afterwards -- to the display size, + // or to that times the Output Scaling ratio -- and either branch silently + // undoes the hold. Returns true when it took ownership of the target, in + // which case the caller must not apply the Output Scaling multiplier. + bool NRApplyFeature1Hold(); + + unsigned int NRSourceWidth() const { return _nrSourceWidth != 0 ? _nrSourceWidth : _renderWidth; } + unsigned int NRSourceHeight() const { return _nrSourceHeight != 0 ? _nrSourceHeight : _renderHeight; } + + // What the colour path must branch on: whether the frame the model is about + // to see is linear HDR. Distinct from IsHdr(). + bool NRGameIsHdr() const { return _nrGameIsHdr; } + virtual bool CallsUpscalerEndByItself() { return false; } IFeature(unsigned int InHandleId, NVSDK_NGX_Parameter* InParameters) { SetHandle(InHandleId); } diff --git a/OptiScaler/upscalers/IFeature_Dx11wDx12.cpp b/OptiScaler/upscalers/IFeature_Dx11wDx12.cpp index 8c81090e5..f4c6a0417 100644 --- a/OptiScaler/upscalers/IFeature_Dx11wDx12.cpp +++ b/OptiScaler/upscalers/IFeature_Dx11wDx12.cpp @@ -413,8 +413,8 @@ bool IFeature_Dx11wDx12::Evaluate(ID3D11DeviceContext* InDeviceContext, NVSDK_NG if (!reportedNrOffer) { reportedNrOffer = true; - LOG_INFO("DLSS-NR: the D3D11 bridge reached the hand-off (upscale ok: {}, enabled: {})", - dx12EvalResult, Config::Instance()->DlssNrEnabled.value_or_default()); + LOG_INFO("DLSS-NR: the D3D11 bridge reached the hand-off (upscale ok: {}, enabled: {})", dx12EvalResult, + Config::Instance()->DlssNrEnabled.value_or_default()); } if (dx12EvalResult && Config::Instance()->DlssNrEnabled.value_or_default()) diff --git a/OptiScaler/upscalers/IFeature_Dx12.cpp b/OptiScaler/upscalers/IFeature_Dx12.cpp index 0273356e1..94c259e9c 100644 --- a/OptiScaler/upscalers/IFeature_Dx12.cpp +++ b/OptiScaler/upscalers/IFeature_Dx12.cpp @@ -5,6 +5,29 @@ #include "IFeature_Dx12.h" #include "State.h" +#include + +// After a multi-pass hold, TargetWidth is the first pass's 1:1 size. Output +// Scaling still works at display (times its multiplier) so the FSR1 enlarge +// can write a display-sized image into that buffer instead of R-to-R. +static void OutputScalingWorkSize(IFeature* feature, unsigned int& width, unsigned int& height) +{ + width = feature->TargetWidth(); + height = feature->TargetHeight(); + + if (feature->NRBuiltMode() != DlssNr::Mode::MultiPass) + return; + + float ssMulti = Config::Instance()->OutputScalingMultiplier.value_or_default(); + + if (ssMulti < 0.5f) + ssMulti = 0.5f; + else if (ssMulti > 3.0f) + ssMulti = 3.0f; + + width = static_cast(feature->DisplayWidth() * ssMulti); + height = static_cast(feature->DisplayHeight() * ssMulti); +} void IFeature_Dx12::ResourceBarrier(ID3D12GraphicsCommandList* InCommandList, ID3D12Resource* InResource, D3D12_RESOURCE_STATES InBeforeState, D3D12_RESOURCE_STATES InAfterState) const @@ -33,7 +56,10 @@ bool IFeature_Dx12::Init(ID3D12Device* InDevice, ID3D12GraphicsCommandList* InCo if (!Config::Instance()->OverlayMenu.value_or_default() && (Imgui == nullptr || Imgui.get() == nullptr)) Imgui = std::make_unique(Util::GetProcessWindow(), InDevice); - OutputScaler = std::make_unique("Output Scaling", InDevice, (TargetWidth() < DisplayWidth())); + unsigned int osWidth = 0; + unsigned int osHeight = 0; + OutputScalingWorkSize(this, osWidth, osHeight); + OutputScaler = std::make_unique("Output Scaling", InDevice, (osWidth < DisplayWidth())); RCAS = std::make_unique("RCAS", InDevice); Bias = std::make_unique("Bias", InDevice); // TODO: not needed on DLSS/DLSSD Magnifier = std::make_unique("Magnifier", InDevice); @@ -52,6 +78,18 @@ bool IFeature_Dx12::Evaluate(ID3D12GraphicsCommandList* InCommandList, NVSDK_NGX return false; } + // A placement change needs the feature rebuilt before anything else happens. + // The target resolution is fixed at creation, and nothing else here notices: + // engines rebuild on a resolution change, and the game's resolutions have not + // moved. Reported as success: this is one deliberate frame during a mode + // change, not an upscaler that has gone wrong. + if (NRNeedsRebuild()) + { + LOG_INFO("DLSS-NR placement changed; rebuilding the upscaler feature for it"); + State::Instance().changeBackend[Handle()->Id] = true; + return true; + } + if (Config::Instance()->OverrideSharpness.value_or_default()) _sharpness = Config::Instance()->Sharpness.value_or_default(); else @@ -98,37 +136,94 @@ bool IFeature_Dx12::Evaluate(ID3D12GraphicsCommandList* InCommandList, NVSDK_NGX // Order is important as that's the order of shader dispatch std::vector pipeline; - if (useOutputScaling) + // Multi-pass: the model on the first pass's 1:1 result, then FSR1 EASU + // enlarges to display. Pushed first so it sits closest to the upscaler -- + // the model wants the frame before Output Scaling or RCAS has been over it. + if (NRBuiltMode() == DlssNr::Mode::MultiPass) { + if (MultiPassScaler == nullptr) + MultiPassScaler = std::make_unique("DLSS-NR Enlarge", Device, true, Scaler::FSR1); + pipeline.push_back( { // Setup [&](ID3D12Resource* nextOutput) -> ID3D12Resource* { - if (OutputScaler->CreateBufferResource(Device, nextOutput, TargetWidth(), TargetHeight(), - D3D12_RESOURCE_STATE_UNORDERED_ACCESS)) + if (MultiPassScaler->CreateBufferResource(Device, nextOutput, RenderWidth(), RenderHeight(), + D3D12_RESOURCE_STATE_UNORDERED_ACCESS)) { - OutputScaler->SetBufferState(InCommandList, D3D12_RESOURCE_STATE_UNORDERED_ACCESS); - return OutputScaler->Buffer(); + MultiPassScaler->SetBufferState(InCommandList, D3D12_RESOURCE_STATE_UNORDERED_ACCESS); + return MultiPassScaler->Buffer(); } + return nullptr; }, // Dispatch [&](ID3D12Resource* input, ID3D12Resource* output) -> bool { - LOG_DEBUG("Scaling output..."); - OutputScaler->SetBufferState(InCommandList, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); - - if (!OutputScaler->Dispatch(InCommandList, input, output)) + if (paramDepth != nullptr && paramMotion != nullptr) { - Config::Instance()->OutputScalingEnabled.set_volatile_value(false); - State::Instance().changeBackend[Handle()->Id] = true; - return false; + DlssNrFrameInfo frame {}; + frame.DepthInverted = DepthInverted(); + frame.ColourIsLinearHdr = NRGameIsHdr(); + frame.Reset = FrameCount() <= 1; + + int reset = 0; + if (InParameters->Get(NVSDK_NGX_Parameter_Reset, &reset) == NVSDK_NGX_Result_Success) + frame.Reset = frame.Reset || reset != 0; + + if (InParameters->Get(NVSDK_NGX_Parameter_MV_Scale_X, &frame.MvScaleX) != + NVSDK_NGX_Result_Success) + frame.MvScaleX = 1.0f; + + if (InParameters->Get(NVSDK_NGX_Parameter_MV_Scale_Y, &frame.MvScaleY) != + NVSDK_NGX_Result_Success) + frame.MvScaleY = 1.0f; + + DlssNr::Run(InCommandList, input, paramDepth, paramMotion, input, frame, + State::Instance().currentCommandQueue); } - return true; + + MultiPassScaler->SetBufferState(InCommandList, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); + return MultiPassScaler->Dispatch(InCommandList, input, output); } }); } + if (useOutputScaling) + { + pipeline.push_back({ // Setup + [&](ID3D12Resource* nextOutput) -> ID3D12Resource* + { + unsigned int osWidth = 0; + unsigned int osHeight = 0; + OutputScalingWorkSize(this, osWidth, osHeight); + + if (OutputScaler->CreateBufferResource(Device, nextOutput, osWidth, osHeight, + D3D12_RESOURCE_STATE_UNORDERED_ACCESS)) + { + OutputScaler->SetBufferState(InCommandList, D3D12_RESOURCE_STATE_UNORDERED_ACCESS); + return OutputScaler->Buffer(); + } + return nullptr; + }, + + // Dispatch + [&](ID3D12Resource* input, ID3D12Resource* output) -> bool + { + LOG_DEBUG("Scaling output..."); + OutputScaler->SetBufferState(InCommandList, + D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); + + if (!OutputScaler->Dispatch(InCommandList, input, output)) + { + Config::Instance()->OutputScalingEnabled.set_volatile_value(false); + State::Instance().changeBackend[Handle()->Id] = true; + return false; + } + return true; + } }); + } + _actualSharpness = _sharpness; if (useRcas) { @@ -326,6 +421,7 @@ IFeature_Dx12::~IFeature_Dx12() Imgui.reset(); OutputScaler.reset(); + MultiPassScaler.reset(); RCAS.reset(); Bias.reset(); } diff --git a/OptiScaler/upscalers/IFeature_Dx12.h b/OptiScaler/upscalers/IFeature_Dx12.h index 4993ddd37..cabd7145b 100644 --- a/OptiScaler/upscalers/IFeature_Dx12.h +++ b/OptiScaler/upscalers/IFeature_Dx12.h @@ -31,6 +31,7 @@ class IFeature_Dx12 : public virtual IFeature ID3D12Device* Device = nullptr; static inline std::unique_ptr Imgui = nullptr; std::unique_ptr OutputScaler = nullptr; + std::unique_ptr MultiPassScaler = nullptr; std::unique_ptr RCAS = nullptr; std::unique_ptr Bias = nullptr; std::unique_ptr Magnifier = nullptr; diff --git a/OptiScaler/upscalers/IFeature_VkwDx12.cpp b/OptiScaler/upscalers/IFeature_VkwDx12.cpp index c4c40f3ef..050f1dbc8 100644 --- a/OptiScaler/upscalers/IFeature_VkwDx12.cpp +++ b/OptiScaler/upscalers/IFeature_VkwDx12.cpp @@ -2104,8 +2104,8 @@ bool IFeature_VkwDx12::Evaluate(VkCommandBuffer InCmdBuffer, NVSDK_NGX_Parameter if (!reportedNrOffer) { reportedNrOffer = true; - LOG_INFO("DLSS-NR: the Vulkan bridge reached the hand-off (upscale ok: {}, enabled: {})", - dx12EvalResult, Config::Instance()->DlssNrEnabled.value_or_default()); + LOG_INFO("DLSS-NR: the Vulkan bridge reached the hand-off (upscale ok: {}, enabled: {})", dx12EvalResult, + Config::Instance()->DlssNrEnabled.value_or_default()); } if (dx12EvalResult && Config::Instance()->DlssNrEnabled.value_or_default()) diff --git a/OptiScaler/upscalers/dlss/DLSSFeature.cpp b/OptiScaler/upscalers/dlss/DLSSFeature.cpp index 13410372d..584ac018c 100644 --- a/OptiScaler/upscalers/dlss/DLSSFeature.cpp +++ b/OptiScaler/upscalers/dlss/DLSSFeature.cpp @@ -34,6 +34,11 @@ void DLSSFeature::ProcessInitParams(NVSDK_NGX_Parameter* InParameters) { LOG_FUNC(); + // Settle the Neural Rendering arrangement first: it can hold the target at + // 1:1. Here rather than in SetInitParameters because that runs from a + // constructor, where the virtuals this needs do not exist yet. + NRPrepareForCreate(); + // Create flags ----------------------------- unsigned int featureFlags = 0; @@ -58,7 +63,16 @@ void DLSSFeature::ProcessInitParams(NVSDK_NGX_Parameter* InParameters) InParameters->Set(NVSDK_NGX_Parameter_DLSS_Feature_Create_Flags, featureFlags); // Resolution ----------------------------- - if (Config::Instance()->OutputScalingEnabled.value_or_default() && (LowResMV() || RenderWidth() == DisplayWidth())) + // Multi-pass holds this feature at 1:1. Checked before the branches below + // because both of them recompute the target -- to the display size, or to + // that times the Output Scaling ratio -- and either would silently undo + // the hold. + if (NRApplyFeature1Hold()) + { + LOG_DEBUG("DLSS-NR multi-pass: this feature stays at {}x{}", TargetWidth(), TargetHeight()); + } + else if (Config::Instance()->OutputScalingEnabled.value_or_default() && + (LowResMV() || RenderWidth() == DisplayWidth())) { LOG_DEBUG("Output Scaling is active"); diff --git a/OptiScaler/upscalers/dlssd/DLSSDFeature.cpp b/OptiScaler/upscalers/dlssd/DLSSDFeature.cpp index 74b600286..0e2ac6f86 100644 --- a/OptiScaler/upscalers/dlssd/DLSSDFeature.cpp +++ b/OptiScaler/upscalers/dlssd/DLSSDFeature.cpp @@ -32,6 +32,11 @@ void DLSSDFeature::ProcessEvaluateParams(NVSDK_NGX_Parameter* InParameters) void DLSSDFeature::ProcessInitParams(NVSDK_NGX_Parameter* InParameters) { + // Settle the Neural Rendering arrangement first: it can hold the target at + // 1:1. Here rather than in SetInitParameters because that runs from a + // constructor, where the virtuals this needs do not exist yet. + NRPrepareForCreate(); + // Create flags ----------------------------- unsigned int featureFlags = 0; @@ -56,7 +61,12 @@ void DLSSDFeature::ProcessInitParams(NVSDK_NGX_Parameter* InParameters) InParameters->Set(NVSDK_NGX_Parameter_DLSS_Feature_Create_Flags, featureFlags); // Resolution ----------------------------- - if (Config::Instance()->OutputScalingEnabled.value_or_default() && (LowResMV() || RenderWidth() == DisplayWidth())) + if (NRApplyFeature1Hold()) + { + LOG_DEBUG("DLSS-NR multi-pass: this feature stays at {}x{}", TargetWidth(), TargetHeight()); + } + else if (Config::Instance()->OutputScalingEnabled.value_or_default() && + (LowResMV() || RenderWidth() == DisplayWidth())) { float ssMulti = Config::Instance()->OutputScalingMultiplier.value_or_default();