diff --git a/OptiScaler/OptiScaler.vcxproj b/OptiScaler/OptiScaler.vcxproj index 5a36d05d..9de465cb 100644 --- a/OptiScaler/OptiScaler.vcxproj +++ b/OptiScaler/OptiScaler.vcxproj @@ -518,6 +518,9 @@ copy NUL "$(SolutionDir)x64\Release\a\!! EXTRACT ALL FILES TO GAME FOLDER !!" /Y + + + @@ -819,6 +822,7 @@ copy NUL "$(SolutionDir)x64\Release\a\!! EXTRACT ALL FILES TO GAME FOLDER !!" /Y + diff --git a/OptiScaler/OptiScaler.vcxproj.filters b/OptiScaler/OptiScaler.vcxproj.filters index 33b8a434..823faad9 100644 --- a/OptiScaler/OptiScaler.vcxproj.filters +++ b/OptiScaler/OptiScaler.vcxproj.filters @@ -188,6 +188,15 @@ Config + + Header Files + + + Header Files + + + Header Files + Header Files @@ -1280,6 +1289,9 @@ Source Files + + Source Files + Source Files diff --git a/OptiScaler/shaders/auto_exposure/AE_Common.h b/OptiScaler/shaders/auto_exposure/AE_Common.h new file mode 100644 index 00000000..95d90298 --- /dev/null +++ b/OptiScaler/shaders/auto_exposure/AE_Common.h @@ -0,0 +1,122 @@ +#pragma once + +#include "SysUtils.h" + +static std::string aeShader = R"( +// Auto exposure from the log average luminance of the renderSize part of color, same as FSR2. +// FSR3 clamps the log luma instead of the luma, which makes every pixel darker than 1 count as 1 +// and caps the exposure way too low for typical pre-exposed input. + +cbuffer Params : register(b0) +{ + uint2 RenderSize; + uint GroupCountX; + uint GroupCount; + float DeltaTime; // seconds + uint Reset; + uint PassIndex; + float PreExposure; +}; + +Texture2D Color : register(t0); +// [0] smoothed log luma carried between frames, [1..] per group log luma sums +RWStructuredBuffer Data : register(u0); +RWTexture2D Exposure : register(u1); + +#define THREADS 256 +#define TILE 32 + +static const float Epsilon = 6.10e-05f; +static const float Fp16Max = 65504.0f; + +groupshared float Shared[THREADS]; + +float LogLuma(uint2 pos) +{ + // Exposure is applied after FSR removes pre-exposure, so it has to be measured without it too + const float3 rgb = max(0.0f, Color.Load(int3(pos, 0)).rgb) / PreExposure; + const float luma = min(dot(rgb, float3(0.2126f, 0.7152f, 0.0722f)), Fp16Max); + return log(max(Epsilon, luma)); +} + +float GroupSum(float value, uint tid) +{ + Shared[tid] = value; + GroupMemoryBarrierWithGroupSync(); + + [unroll] + for (uint stride = THREADS / 2; stride > 0; stride >>= 1) + { + if (tid < stride) + Shared[tid] += Shared[tid + stride]; + + GroupMemoryBarrierWithGroupSync(); + } + + return Shared[0]; +} + +float ComputeAutoExposureFromLavg(float Lavg) +{ + Lavg = exp(Lavg); + + const float S = 100.0f; // ISO arithmetic speed + const float K = 12.5f; + const float ExposureISO100 = log2((Lavg * S) / K); + + const float q = 0.65f; + const float Lmax = (78.0f / (q * S)) * pow(2.0f, ExposureISO100); + + return 1.0f / Lmax; +} + +[numthreads(16, 16, 1)] +void CSMain(uint3 gid : SV_GroupID, uint3 gtid : SV_GroupThreadID, uint tid : SV_GroupIndex) +{ + if (PassIndex == 0) + { + float sum = 0.0f; + + [unroll] + for (uint y = 0; y < TILE; y += 16) + { + [unroll] + for (uint x = 0; x < TILE; x += 16) + { + const uint2 pos = gid.xy * TILE + gtid.xy + uint2(x, y); + + if (all(pos < RenderSize)) + sum += LogLuma(pos); + } + } + + const float total = GroupSum(sum, tid); + + if (tid == 0) + Data[1 + gid.y * GroupCountX + gid.x] = total; + } + else + { + float sum = 0.0f; + + for (uint i = tid; i < GroupCount; i += THREADS) + sum += Data[1 + i]; + + const float total = GroupSum(sum, tid); + + if (tid == 0) + { + float logLuma = total / (float(RenderSize.x) * float(RenderSize.y)); + + if (Reset == 0) + { + const float prevLogLuma = Data[0]; + logLuma = prevLogLuma + (logLuma - prevLogLuma) * (1.0f - exp(-DeltaTime)); + } + + Data[0] = logLuma; + Exposure[uint2(0, 0)] = ComputeAutoExposureFromLavg(logLuma); + } + } +} +)"; diff --git a/OptiScaler/shaders/auto_exposure/AE_Dx12.cpp b/OptiScaler/shaders/auto_exposure/AE_Dx12.cpp new file mode 100644 index 00000000..169df5dc --- /dev/null +++ b/OptiScaler/shaders/auto_exposure/AE_Dx12.cpp @@ -0,0 +1,215 @@ +#include "pch.h" +#include "AE_Dx12.h" + +#include "AE_Common.h" +#include "precompile/AE_Shader.h" + +#include + +using Microsoft::WRL::ComPtr; + +static constexpr uint32_t AE_THREADS = 256; +static constexpr uint32_t AE_TILE = 32; + +bool AE_Dx12::CreateRootSignature(ID3D12Device* InDevice) +{ + CD3DX12_DESCRIPTOR_RANGE1 ranges[2]; + ranges[0].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0); + ranges[1].Init(D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 2, 0); + + // Root constants, so every dispatch has its own values without a constant buffer per frame + CD3DX12_ROOT_PARAMETER1 rootParameters[2]; + rootParameters[0].InitAsConstants(sizeof(InternalConstants) / sizeof(uint32_t), 0); + rootParameters[1].InitAsDescriptorTable(_countof(ranges), ranges); + + CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC rootSigDesc {}; + rootSigDesc.Init_1_1(_countof(rootParameters), rootParameters); + + ComPtr errorBlob; + ComPtr signatureBlob; + + auto hr = D3D12SerializeVersionedRootSignature(&rootSigDesc, &signatureBlob, &errorBlob); + + if (FAILED(hr)) + { + LOG_ERROR("[{0}] D3D12SerializeVersionedRootSignature error {1:x}", _name, hr); + return false; + } + + hr = InDevice->CreateRootSignature(0, signatureBlob->GetBufferPointer(), signatureBlob->GetBufferSize(), + IID_PPV_ARGS(&_rootSignature)); + + if (FAILED(hr)) + { + LOG_ERROR("[{0}] CreateRootSignature error {1:x}", _name, hr); + return false; + } + + return true; +} + +bool AE_Dx12::EnsureDataBuffer(uint32_t InElements) +{ + if (_data != nullptr && _dataElements >= InElements) + return true; + + // Previous frames can still be using it + DeferredRelease::Release(_data); + _dataElements = 0; + _historyValid = false; + + auto heapProps = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT); + auto desc = CD3DX12_RESOURCE_DESC::Buffer(InElements * sizeof(float), D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS); + + auto hr = _device->CreateCommittedResource(&heapProps, D3D12_HEAP_FLAG_NONE, &desc, + D3D12_RESOURCE_STATE_UNORDERED_ACCESS, nullptr, IID_PPV_ARGS(&_data)); + + if (hr != S_OK) + { + LOG_ERROR("[{0}] CreateCommittedResource error {1:x}", _name, (unsigned int) hr); + return false; + } + + _data->SetName(L"AE_Data"); + _dataElements = InElements; + + return true; +} + +bool AE_Dx12::Dispatch(ID3D12GraphicsCommandList* InCmdList, ID3D12Resource* InColor, uint32_t InRenderWidth, + uint32_t InRenderHeight, float InPreExposure, float InDeltaTimeSeconds, bool InReset) +{ + if (!_init || _device == nullptr || InCmdList == nullptr || InColor == nullptr || InRenderWidth == 0 || + InRenderHeight == 0) + return false; + + LOG_DEBUG("[{0}] Start!", _name); + + const uint32_t groupCountX = (InRenderWidth + AE_TILE - 1) / AE_TILE; + const uint32_t groupCountY = (InRenderHeight + AE_TILE - 1) / AE_TILE; + const uint32_t groupCount = groupCountX * groupCountY; + + if (!EnsureDataBuffer(groupCount + 1)) + return false; + + ScopedGpuTime_Dx12 scopedGpuTime(GpuTime.get(), InCmdList); + + _counter++; + _counter = _counter % AE_NUM_OF_HEAPS; + FrameDescriptorHeap& currentHeap = _frameHeaps[_counter]; + + CreateShaderResourceView(_device, InColor, currentHeap.GetSrvCPU(0)); + + D3D12_UNORDERED_ACCESS_VIEW_DESC dataUav {}; + dataUav.Format = DXGI_FORMAT_UNKNOWN; + dataUav.ViewDimension = D3D12_UAV_DIMENSION_BUFFER; + dataUav.Buffer.NumElements = _dataElements; + dataUav.Buffer.StructureByteStride = sizeof(float); + _device->CreateUnorderedAccessView(_data, nullptr, &dataUav, currentHeap.GetUavCPU(0)); + + CreateUnorderedAccessView(_device, _exposure, currentHeap.GetUavCPU(1), 0); + + SetBufferState(InCmdList, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, _exposure, &_exposureState); + + // Previous pass 1 may still be reading the sums + auto dataBarrier = CD3DX12_RESOURCE_BARRIER::UAV(_data); + InCmdList->ResourceBarrier(1, &dataBarrier); + + ID3D12DescriptorHeap* heaps[] = { currentHeap.GetHeapCSU() }; + InCmdList->SetDescriptorHeaps(_countof(heaps), heaps); + + InCmdList->SetComputeRootSignature(_rootSignature); + InCmdList->SetPipelineState(_pipelineState); + InCmdList->SetComputeRootDescriptorTable(1, currentHeap.GetTableGPUStart()); + + InternalConstants constants {}; + constants.RenderWidth = InRenderWidth; + constants.RenderHeight = InRenderHeight; + constants.GroupCountX = groupCountX; + constants.GroupCount = groupCount; + constants.DeltaTime = std::clamp(InDeltaTimeSeconds, 0.0f, 1.0f); + constants.PreExposure = InPreExposure > 0.0f ? InPreExposure : 1.0f; + // Without history the scene luma is taken as is instead of adapting from a stale value + constants.Reset = (InReset || !_historyValid) ? 1 : 0; + + // Per tile log luma sums + constants.PassIndex = 0; + InCmdList->SetComputeRoot32BitConstants(0, sizeof(constants) / sizeof(uint32_t), &constants, 0); + InCmdList->Dispatch(groupCountX, groupCountY, 1); + + InCmdList->ResourceBarrier(1, &dataBarrier); + + // Average, temporal adaptation and exposure + constants.PassIndex = 1; + InCmdList->SetComputeRoot32BitConstants(0, sizeof(constants) / sizeof(uint32_t), &constants, 0); + InCmdList->Dispatch(1, 1, 1); + + SetBufferState(InCmdList, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, _exposure, &_exposureState); + + _historyValid = true; + + return true; +} + +AE_Dx12::AE_Dx12(std::string InName, ID3D12Device* InDevice) : Shader_Dx12(InName, InDevice) +{ + if (InDevice == nullptr) + { + LOG_ERROR("InDevice is nullptr!"); + return; + } + + LOG_DEBUG("{0} start!", _name); + + if (!CreateRootSignature(InDevice)) + { + LOG_ERROR("Failed to create root signature"); + return; + } + + if (!CreateComputePipeline(InDevice, &_pipelineState, AE_cso, sizeof(AE_cso), aeShader.c_str())) + { + LOG_ERROR("[{0}] Failed to create compute pipeline", _name); + return; + } + + auto heapProps = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT); + auto desc = CD3DX12_RESOURCE_DESC::Tex2D(DXGI_FORMAT_R32_FLOAT, 1, 1, 1, 1, 1, 0, + D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS); + + auto hr = InDevice->CreateCommittedResource(&heapProps, D3D12_HEAP_FLAG_NONE, &desc, _exposureState, nullptr, + IID_PPV_ARGS(&_exposure)); + + if (hr != S_OK) + { + LOG_ERROR("[{0}] CreateCommittedResource error {1:x}", _name, (unsigned int) hr); + return; + } + + _exposure->SetName(L"AE_Exposure"); + + ScopedSkipHeapCapture skipHeapCapture {}; + + for (int i = 0; i < AE_NUM_OF_HEAPS; i++) + { + if (!_frameHeaps[i].Initialize(InDevice, 1, 2, 0)) + { + LOG_ERROR("[{0}] Failed to init heap", _name); + return; + } + } + + _init = true; +} + +AE_Dx12::~AE_Dx12() +{ + if (State::Instance().isShuttingDown) + return; + + for (int i = 0; i < AE_NUM_OF_HEAPS; i++) + _frameHeaps[i].ReleaseHeaps(); + + DeferredRelease::Release(_data); + DeferredRelease::Release(_exposure); +} diff --git a/OptiScaler/shaders/auto_exposure/AE_Dx12.h b/OptiScaler/shaders/auto_exposure/AE_Dx12.h new file mode 100644 index 00000000..13a3d40f --- /dev/null +++ b/OptiScaler/shaders/auto_exposure/AE_Dx12.h @@ -0,0 +1,49 @@ +#pragma once +#include +#include +#include +#include + +#define AE_NUM_OF_HEAPS 2 + +// Computes an FSR2 style auto exposure from the renderSize part of color into a 1x1 R32_FLOAT texture +class AE_Dx12 : public Shader_Dx12 +{ + private: + struct InternalConstants + { + uint32_t RenderWidth; + uint32_t RenderHeight; + uint32_t GroupCountX; + uint32_t GroupCount; + float DeltaTime; + uint32_t Reset; + uint32_t PassIndex; + float PreExposure; + }; + + FrameDescriptorHeap _frameHeaps[AE_NUM_OF_HEAPS]; + + // [0] smoothed log luma, [1..] per group sums + ID3D12Resource* _data = nullptr; + uint32_t _dataElements = 0; + + ID3D12Resource* _exposure = nullptr; + D3D12_RESOURCE_STATES _exposureState = D3D12_RESOURCE_STATE_UNORDERED_ACCESS; + + bool _historyValid = false; + + bool CreateRootSignature(ID3D12Device* InDevice); + bool EnsureDataBuffer(uint32_t InElements); + + public: + // Leaves Exposure() in NON_PIXEL_SHADER_RESOURCE + bool Dispatch(ID3D12GraphicsCommandList* InCmdList, ID3D12Resource* InColor, uint32_t InRenderWidth, + uint32_t InRenderHeight, float InPreExposure, float InDeltaTimeSeconds, bool InReset); + + ID3D12Resource* Exposure() { return _exposure; } + + AE_Dx12(std::string InName, ID3D12Device* InDevice); + + ~AE_Dx12(); +}; diff --git a/OptiScaler/shaders/auto_exposure/precompile/AE.hlsl b/OptiScaler/shaders/auto_exposure/precompile/AE.hlsl new file mode 100644 index 00000000..94a9a35f --- /dev/null +++ b/OptiScaler/shaders/auto_exposure/precompile/AE.hlsl @@ -0,0 +1,116 @@ +// Auto exposure from the log average luminance of the renderSize part of color, same as FSR2. +// FSR3 clamps the log luma instead of the luma, which makes every pixel darker than 1 count as 1 +// and caps the exposure way too low for typical pre-exposed input. + +cbuffer Params : register(b0) +{ + uint2 RenderSize; + uint GroupCountX; + uint GroupCount; + float DeltaTime; // seconds + uint Reset; + uint PassIndex; + float PreExposure; +}; + +Texture2D Color : register(t0); +// [0] smoothed log luma carried between frames, [1..] per group log luma sums +RWStructuredBuffer Data : register(u0); +RWTexture2D Exposure : register(u1); + +#define THREADS 256 +#define TILE 32 + +static const float Epsilon = 6.10e-05f; +static const float Fp16Max = 65504.0f; + +groupshared float Shared[THREADS]; + +float LogLuma(uint2 pos) +{ + // Exposure is applied after FSR removes pre-exposure, so it has to be measured without it too + const float3 rgb = max(0.0f, Color.Load(int3(pos, 0)).rgb) / PreExposure; + const float luma = min(dot(rgb, float3(0.2126f, 0.7152f, 0.0722f)), Fp16Max); + return log(max(Epsilon, luma)); +} + +float GroupSum(float value, uint tid) +{ + Shared[tid] = value; + GroupMemoryBarrierWithGroupSync(); + + [unroll] + for (uint stride = THREADS / 2; stride > 0; stride >>= 1) + { + if (tid < stride) + Shared[tid] += Shared[tid + stride]; + + GroupMemoryBarrierWithGroupSync(); + } + + return Shared[0]; +} + +float ComputeAutoExposureFromLavg(float Lavg) +{ + Lavg = exp(Lavg); + + const float S = 100.0f; // ISO arithmetic speed + const float K = 12.5f; + const float ExposureISO100 = log2((Lavg * S) / K); + + const float q = 0.65f; + const float Lmax = (78.0f / (q * S)) * pow(2.0f, ExposureISO100); + + return 1.0f / Lmax; +} + +[numthreads(16, 16, 1)] +void CSMain(uint3 gid : SV_GroupID, uint3 gtid : SV_GroupThreadID, uint tid : SV_GroupIndex) +{ + if (PassIndex == 0) + { + float sum = 0.0f; + + [unroll] + for (uint y = 0; y < TILE; y += 16) + { + [unroll] + for (uint x = 0; x < TILE; x += 16) + { + const uint2 pos = gid.xy * TILE + gtid.xy + uint2(x, y); + + if (all(pos < RenderSize)) + sum += LogLuma(pos); + } + } + + const float total = GroupSum(sum, tid); + + if (tid == 0) + Data[1 + gid.y * GroupCountX + gid.x] = total; + } + else + { + float sum = 0.0f; + + for (uint i = tid; i < GroupCount; i += THREADS) + sum += Data[1 + i]; + + const float total = GroupSum(sum, tid); + + if (tid == 0) + { + float logLuma = total / (float(RenderSize.x) * float(RenderSize.y)); + + if (Reset == 0) + { + const float prevLogLuma = Data[0]; + logLuma = prevLogLuma + (logLuma - prevLogLuma) * (1.0f - exp(-DeltaTime)); + } + + Data[0] = logLuma; + Exposure[uint2(0, 0)] = ComputeAutoExposureFromLavg(logLuma); + } + } +} diff --git a/OptiScaler/shaders/auto_exposure/precompile/AE_Shader.cso b/OptiScaler/shaders/auto_exposure/precompile/AE_Shader.cso new file mode 100644 index 00000000..5f0c3efa Binary files /dev/null and b/OptiScaler/shaders/auto_exposure/precompile/AE_Shader.cso differ diff --git a/OptiScaler/shaders/auto_exposure/precompile/AE_Shader.h b/OptiScaler/shaders/auto_exposure/precompile/AE_Shader.h new file mode 100644 index 00000000..a3e8855c --- /dev/null +++ b/OptiScaler/shaders/auto_exposure/precompile/AE_Shader.h @@ -0,0 +1,270 @@ +#pragma once + +inline static const unsigned char AE_cso[] = { + 0x44, 0x58, 0x42, 0x43, 0xd2, 0x6a, 0x7a, 0xda, 0xbb, 0xd6, 0x4d, 0xc4, 0xb8, 0x47, 0xc3, 0x30, 0x1f, 0x66, 0xb0, + 0x6d, 0x01, 0x00, 0x00, 0x00, 0xb4, 0x13, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x48, 0x00, + 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x3c, 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, 0xb0, 0x00, 0x00, 0x00, 0x34, 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, + 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 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, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 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, 0x08, 0x00, 0x00, 0x00, 0x00, 0x43, 0x53, 0x4d, 0x61, 0x69, 0x6e, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x48, 0x41, 0x53, 0x48, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc2, 0x0c, 0x61, 0x0f, + 0xc3, 0xec, 0x9f, 0x9f, 0x6a, 0x57, 0x95, 0xca, 0xfe, 0x32, 0x60, 0xf8, 0x44, 0x58, 0x49, 0x4c, 0x70, 0x12, 0x00, + 0x00, 0x60, 0x00, 0x05, 0x00, 0x9c, 0x04, 0x00, 0x00, 0x44, 0x58, 0x49, 0x4c, 0x00, 0x01, 0x00, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x58, 0x12, 0x00, 0x00, 0x42, 0x43, 0xc0, 0xde, 0x21, 0x0c, 0x00, 0x00, 0x93, 0x04, 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, 0x0b, 0x00, 0x00, 0x00, + 0x1b, 0x8c, 0xe0, 0xff, 0xff, 0xff, 0xff, 0x07, 0x40, 0x02, 0xa8, 0x0d, 0x84, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x03, + 0x20, 0x6d, 0x30, 0x86, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x09, 0xa8, 0x36, 0x18, 0xc4, 0xff, 0xff, 0xff, 0xff, + 0x0f, 0x00, 0x03, 0x48, 0x00, 0x00, 0x49, 0x18, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x13, 0x82, 0x60, 0x42, 0x20, + 0x4c, 0x08, 0x86, 0x09, 0x01, 0x01, 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, 0xdc, 0xc1, 0x0c, 0x40, 0x02, 0x0a, 0x00, 0x83, 0x08, 0xc1, 0x30, 0x02, 0x50, + 0x82, 0x81, 0x88, 0x39, 0x02, 0x30, 0x28, 0x83, 0x61, 0x18, 0x74, 0x14, 0xc1, 0x30, 0x28, 0x29, 0x03, 0x60, 0x00, + 0xb4, 0xcc, 0x11, 0x20, 0xd4, 0xdc, 0x33, 0x5c, 0xfe, 0x84, 0x3d, 0x84, 0xe4, 0x87, 0x40, 0x33, 0x2c, 0x04, 0x0a, + 0x9c, 0xb2, 0x0c, 0xc6, 0x63, 0x18, 0x06, 0x00, 0x00, 0x40, 0x43, 0x50, 0x21, 0x00, 0x03, 0x00, 0x48, 0xba, 0x69, + 0xb8, 0xfc, 0x09, 0x7b, 0x08, 0xc9, 0x5f, 0x09, 0x69, 0x25, 0x26, 0xbf, 0xb8, 0x6d, 0x54, 0x00, 0x00, 0x00, 0x18, + 0x45, 0xa1, 0x8c, 0xc7, 0x30, 0x0c, 0xc3, 0x30, 0x0c, 0xaa, 0x0a, 0x02, 0x18, 0x00, 0x00, 0x00, 0x00, 0x40, 0x57, + 0x19, 0x06, 0xc3, 0xa0, 0xec, 0xa8, 0xe1, 0xf2, 0x27, 0xec, 0x21, 0x24, 0x9f, 0xdb, 0xa8, 0x62, 0x25, 0x26, 0xbf, + 0xb8, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x14, 0x62, 0x33, 0x1e, 0x83, 0xb8, 0xa3, 0x86, 0xcb, 0x9f, 0xb0, 0x87, 0x90, + 0x7c, 0x6e, 0xa3, 0x8a, 0x95, 0x98, 0x7c, 0xe4, 0xb6, 0x11, 0x61, 0x18, 0x86, 0x51, 0x08, 0xcf, 0x78, 0x0c, 0xfa, + 0xe6, 0x08, 0x82, 0x62, 0x3c, 0x46, 0x63, 0x18, 0x21, 0x89, 0x45, 0x19, 0x8c, 0xc7, 0x30, 0x00, 0x00, 0x00, 0x1a, + 0x22, 0x4b, 0x41, 0x19, 0x8f, 0x61, 0x90, 0x39, 0x10, 0x30, 0x8c, 0x40, 0x00, 0x33, 0xb5, 0xc1, 0x38, 0xb0, 0x43, + 0x38, 0xcc, 0xc3, 0x3c, 0xb8, 0x01, 0x2d, 0x94, 0x03, 0x3e, 0xd0, 0x43, 0x3d, 0xc8, 0x43, 0x39, 0xc8, 0x01, 0x29, + 0xf0, 0x81, 0x3d, 0x94, 0xc3, 0x38, 0xd0, 0xc3, 0x3b, 0xc8, 0x03, 0x1f, 0x98, 0x03, 0x3b, 0xbc, 0x43, 0x38, 0xd0, + 0x03, 0x1b, 0x80, 0x01, 0x1d, 0xf8, 0x01, 0x18, 0xf8, 0x81, 0x1e, 0xe8, 0x41, 0x3b, 0xa4, 0x03, 0x3c, 0xcc, 0xc3, + 0x2f, 0xd0, 0x43, 0x3e, 0xc0, 0x43, 0x39, 0xa0, 0xc0, 0x98, 0x49, 0x0c, 0xc6, 0x81, 0x1d, 0xc2, 0x61, 0x1e, 0xe6, + 0xc1, 0x0d, 0x68, 0xa1, 0x1c, 0xf0, 0x81, 0x1e, 0xea, 0x41, 0x1e, 0xca, 0x41, 0x0e, 0x48, 0x81, 0x0f, 0xec, 0xa1, + 0x1c, 0xc6, 0x81, 0x1e, 0xde, 0x41, 0x1e, 0xf8, 0xc0, 0x1c, 0xd8, 0xe1, 0x1d, 0xc2, 0x81, 0x1e, 0xd8, 0x00, 0x0c, + 0xe8, 0xc0, 0x0f, 0xc0, 0xc0, 0x0f, 0x90, 0x90, 0xaa, 0xb4, 0xce, 0xf4, 0x8d, 0x03, 0x3b, 0x84, 0xc3, 0x3c, 0xcc, + 0x83, 0x1b, 0xc8, 0xc2, 0x2d, 0xcc, 0x02, 0x3d, 0xc8, 0x43, 0x3d, 0x8c, 0x03, 0x3d, 0xd4, 0x83, 0x3c, 0x94, 0x03, + 0x39, 0x88, 0x42, 0x3d, 0x98, 0x83, 0x39, 0x94, 0x83, 0x3c, 0xf0, 0x81, 0x39, 0xb0, 0xc3, 0x3b, 0x84, 0x03, 0x3d, + 0xf8, 0x01, 0x0a, 0x00, 0x6a, 0x67, 0xc2, 0xc6, 0x81, 0x1d, 0xc2, 0x61, 0x1e, 0xe6, 0xc1, 0x0d, 0x64, 0xe1, 0x16, + 0x68, 0xa1, 0x1c, 0xf0, 0x81, 0x1e, 0xea, 0x41, 0x1e, 0xca, 0x41, 0x0e, 0x48, 0x81, 0x0f, 0xcc, 0x81, 0x1d, 0xde, + 0x21, 0x1c, 0xe8, 0xc1, 0x0f, 0x50, 0x00, 0xd0, 0x3b, 0x8c, 0x20, 0x18, 0x67, 0xa4, 0x40, 0x04, 0x30, 0x12, 0x3a, + 0x62, 0xc3, 0x00, 0x0c, 0x03, 0x20, 0x79, 0x8e, 0x00, 0x14, 0xa6, 0x00, 0x06, 0x11, 0x80, 0x01, 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, 0x3a, 0x0f, 0x24, 0x90, 0x21, 0x23, 0x45, 0x44, 0x00, 0x76, 0x00, 0xf0, 0x90, + 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x21, 0xcf, 0x01, 0x04, 0x40, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x9e, 0x03, 0x08, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x86, 0x3c, 0x09, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x79, 0x16, 0x20, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xf2, 0x40, 0x40, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0xe4, 0x91, 0x80, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xc8, 0x53, 0x01, 0x01, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x90, 0xe7, 0x02, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x21, 0x4f, 0x06, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x1e, 0x0e, + 0x08, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x3c, 0x1f, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x79, 0xc4, 0x00, 0x08, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, + 0x3c, 0x64, 0x00, 0x04, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x1e, 0x33, 0x00, 0x02, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x2c, 0x10, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x32, 0x1e, 0x98, 0x18, + 0x19, 0x11, 0x4c, 0x90, 0x8c, 0x09, 0x26, 0x47, 0xc6, 0x04, 0x43, 0x32, 0x4a, 0x60, 0x04, 0xa0, 0x24, 0x8a, 0xa1, + 0x08, 0x0a, 0xa3, 0x10, 0x0a, 0xa8, 0x40, 0x28, 0x1c, 0x01, 0x20, 0x7a, 0x04, 0xa0, 0x40, 0x08, 0x9e, 0x01, 0xa0, + 0x79, 0x06, 0x80, 0xdc, 0x19, 0x00, 0x62, 0x67, 0x00, 0x00, 0x79, 0x18, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x00, 0x1a, + 0x03, 0x4c, 0x90, 0x46, 0x02, 0x13, 0x44, 0x8f, 0x0c, 0x6f, 0xec, 0xed, 0x4d, 0x0c, 0x24, 0xc6, 0x25, 0xc7, 0x45, + 0xc6, 0x06, 0x46, 0xc6, 0x25, 0xe6, 0x06, 0x04, 0x45, 0x26, 0x86, 0x4c, 0x06, 0xc7, 0xec, 0x46, 0xe6, 0x26, 0x65, + 0x43, 0x10, 0x4c, 0x10, 0x0c, 0x68, 0x82, 0x60, 0x44, 0x1b, 0x84, 0x81, 0x98, 0x20, 0x18, 0xd2, 0x06, 0x61, 0x30, + 0x28, 0x8c, 0xcd, 0x4d, 0x10, 0x8c, 0x69, 0xc3, 0x80, 0x24, 0xc4, 0x04, 0x81, 0x0d, 0x3e, 0x02, 0x13, 0x04, 0x83, + 0xda, 0x20, 0x10, 0xc6, 0x86, 0x84, 0x58, 0x18, 0x82, 0x18, 0x1a, 0xc2, 0xd9, 0x10, 0x3c, 0x13, 0x04, 0x37, 0xf0, + 0x26, 0x08, 0x46, 0x35, 0x41, 0x08, 0x83, 0x6c, 0x82, 0x60, 0x58, 0x1b, 0x84, 0x81, 0xda, 0xb0, 0x10, 0x11, 0x43, + 0x10, 0x83, 0x34, 0x4d, 0x53, 0x35, 0x41, 0x80, 0x03, 0x6e, 0xc3, 0x32, 0x5c, 0x0c, 0x31, 0x0c, 0xcd, 0x34, 0x4d, + 0xce, 0x06, 0xc1, 0xc2, 0x26, 0x08, 0x73, 0xd0, 0x4d, 0x10, 0x8c, 0x6b, 0x03, 0x42, 0x68, 0x0c, 0x41, 0x0c, 0x1b, + 0xb0, 0x21, 0xe0, 0x36, 0x10, 0x50, 0xd6, 0x01, 0x13, 0x84, 0x22, 0xa0, 0x31, 0x34, 0xd5, 0x14, 0x96, 0xe6, 0x36, + 0x41, 0xa0, 0x83, 0x6d, 0x82, 0x60, 0x60, 0x1b, 0x06, 0x31, 0x10, 0x83, 0x61, 0x03, 0x41, 0x84, 0x01, 0x35, 0x06, + 0x1b, 0x8a, 0x0f, 0x0c, 0x00, 0x8f, 0x0c, 0x58, 0x98, 0xb1, 0xbd, 0x85, 0xd1, 0xf1, 0x79, 0x6b, 0x73, 0x4b, 0x83, + 0x7b, 0xa3, 0x2b, 0x73, 0xa3, 0x03, 0x19, 0x43, 0x0b, 0x93, 0x63, 0x34, 0x95, 0xd6, 0x06, 0xc7, 0x56, 0x06, 0x32, + 0xf4, 0x32, 0xb4, 0xb2, 0x02, 0x42, 0x25, 0x14, 0x14, 0xb4, 0x21, 0x40, 0x83, 0x09, 0x02, 0x1d, 0x68, 0x1b, 0x86, + 0x33, 0x48, 0x03, 0x35, 0xd8, 0x30, 0x98, 0xc1, 0x1a, 0xa8, 0xc1, 0x86, 0x81, 0x0d, 0xd8, 0x40, 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, 0xba, 0x3a, 0x64, 0x78, 0x2e, + 0x65, 0x6e, 0x74, 0x72, 0x79, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x53, 0x02, 0x32, 0x00, 0x00, 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, 0x45, 0x00, 0x00, 0x00, 0x05, 0xf0, 0x04, 0x7e, 0x53, 0x74, 0x58, + 0x5e, 0x26, 0x03, 0x81, 0x33, 0x68, 0xb0, 0x19, 0xb4, 0x00, 0x83, 0x63, 0x4d, 0x00, 0x69, 0x4c, 0xa1, 0x19, 0x2e, + 0xdf, 0x79, 0xfc, 0x00, 0x88, 0x22, 0x84, 0x88, 0xec, 0x00, 0x1a, 0x2e, 0xdf, 0x79, 0xfc, 0x00, 0x69, 0x80, 0x08, + 0xf3, 0x8b, 0xdb, 0xb6, 0x07, 0x6a, 0xb8, 0x7c, 0xe7, 0xf1, 0x03, 0xaa, 0x28, 0x88, 0xa8, 0x74, 0x80, 0xc1, 0x2f, + 0x6e, 0xdb, 0x1c, 0xaa, 0xe1, 0xf2, 0x9d, 0xc7, 0x0f, 0xa8, 0xa2, 0x20, 0x22, 0x76, 0x72, 0x22, 0xc2, 0x2f, 0x6e, + 0xdb, 0x16, 0xb6, 0xe1, 0xf2, 0x9d, 0xc7, 0x17, 0x02, 0xaa, 0x28, 0x88, 0xa8, 0x74, 0x80, 0xa1, 0x24, 0x0c, 0x40, + 0xc0, 0xfc, 0xe2, 0xb6, 0x8d, 0x61, 0x1b, 0x2e, 0xdf, 0x79, 0x7c, 0x21, 0xa0, 0x8a, 0x82, 0x88, 0x4a, 0x07, 0x18, + 0x4a, 0xc2, 0x00, 0x04, 0xcc, 0x47, 0x6e, 0xdb, 0x1a, 0xa4, 0xe1, 0xf2, 0x9d, 0xc7, 0x17, 0x22, 0x02, 0x98, 0x88, + 0x10, 0x68, 0x86, 0x85, 0xb0, 0x04, 0x67, 0xb8, 0x7c, 0xe7, 0xf1, 0x07, 0x67, 0xba, 0xfd, 0xe2, 0xb6, 0x8d, 0x40, + 0x0c, 0x86, 0xcb, 0x77, 0x1e, 0xbf, 0x58, 0x80, 0x69, 0x22, 0x1a, 0x62, 0x68, 0x8f, 0x88, 0x00, 0x06, 0x71, 0x10, + 0x1b, 0x30, 0x72, 0xa8, 0xc7, 0x47, 0x6e, 0xdb, 0x04, 0xa2, 0xe1, 0xf2, 0x9d, 0xc7, 0x37, 0x22, 0x87, 0x7a, 0xc4, + 0xc1, 0x47, 0x6e, 0xdb, 0x10, 0xaa, 0xe1, 0xf2, 0x9d, 0xc7, 0x9f, 0x88, 0x6b, 0xa2, 0x22, 0xa2, 0x74, 0x80, 0xc1, + 0x2f, 0x6e, 0xdb, 0x0c, 0xac, 0xe1, 0xf2, 0x9d, 0xc7, 0x9f, 0x88, 0x6b, 0xa2, 0x22, 0x82, 0x9d, 0x9c, 0x88, 0xf0, + 0x8b, 0xdb, 0xb6, 0x81, 0x6c, 0xb8, 0x7c, 0xe7, 0xf1, 0xa7, 0x23, 0x22, 0x80, 0x41, 0x1c, 0xc4, 0x06, 0x8c, 0x1c, + 0xea, 0xf1, 0x91, 0xdb, 0xb6, 0x82, 0x67, 0xb8, 0x7c, 0xe7, 0xf1, 0xa9, 0x06, 0x88, 0x30, 0xbf, 0xb8, 0x6d, 0x00, + 0x00, 0x00, 0x00, 0x61, 0x20, 0x00, 0x00, 0x8e, 0x02, 0x00, 0x00, 0x13, 0x04, 0x74, 0x10, 0x0b, 0x04, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0xd4, 0x94, 0x47, 0x11, 0x8c, 0x00, 0x94, 0x00, 0x19, 0x65, 0x18, 0x50, 0x80, 0x04, 0x05, + 0x1c, 0x50, 0x88, 0x01, 0xa5, 0x18, 0x50, 0x0a, 0xe5, 0x1b, 0x50, 0xbc, 0x01, 0xa5, 0x52, 0xa0, 0x01, 0x33, 0x00, + 0x65, 0x54, 0x76, 0xe5, 0x52, 0x72, 0x45, 0x18, 0x50, 0x6e, 0x85, 0x54, 0x80, 0x02, 0x05, 0x18, 0x50, 0x10, 0x04, + 0x8c, 0x11, 0xd4, 0x6a, 0xad, 0xd6, 0xdf, 0x18, 0x01, 0x08, 0x82, 0x20, 0x0c, 0x06, 0x63, 0x04, 0x7b, 0xac, 0xc6, + 0xfb, 0x37, 0x46, 0x00, 0x82, 0x20, 0x88, 0x7f, 0x63, 0x04, 0x7b, 0xac, 0xc6, 0xfb, 0x2f, 0x8c, 0x11, 0xe0, 0x30, + 0x2f, 0xce, 0xdf, 0x18, 0x01, 0x28, 0xdb, 0xff, 0xc9, 0x8d, 0x11, 0x80, 0x20, 0xfe, 0xdf, 0x63, 0x30, 0x46, 0x80, + 0xb3, 0xf7, 0x99, 0x7b, 0x63, 0x04, 0xb9, 0x5e, 0xba, 0xf3, 0x37, 0x46, 0x00, 0xfb, 0xec, 0x5c, 0x7e, 0x33, 0x00, + 0x23, 0x00, 0x64, 0x0f, 0x75, 0x04, 0x02, 0x60, 0x44, 0x46, 0x34, 0xd4, 0x11, 0x08, 0x80, 0x11, 0x19, 0x10, 0x00, + 0x23, 0x06, 0x09, 0x00, 0x82, 0x60, 0x20, 0x06, 0x7b, 0x60, 0x95, 0x01, 0x1e, 0xe0, 0xc1, 0x1b, 0x8c, 0x18, 0x24, + 0x00, 0x08, 0x82, 0x81, 0x18, 0xf0, 0xc1, 0x65, 0x06, 0x78, 0x80, 0x07, 0x70, 0x30, 0x62, 0x90, 0x00, 0x20, 0x08, + 0x06, 0x62, 0xd0, 0x07, 0x18, 0x1a, 0xe4, 0x41, 0x1e, 0xc4, 0xc1, 0x88, 0x41, 0x02, 0x80, 0x20, 0x18, 0x88, 0x81, + 0x1f, 0x64, 0x6a, 0xa0, 0x07, 0x7a, 0x20, 0x07, 0x23, 0x06, 0x06, 0x00, 0x82, 0x60, 0x70, 0xa8, 0x42, 0x18, 0xec, + 0xc1, 0x88, 0x81, 0x01, 0x80, 0x20, 0x18, 0x1c, 0xab, 0x20, 0x06, 0x7d, 0x30, 0x62, 0x50, 0x00, 0x20, 0x08, 0x06, + 0x89, 0x2a, 0xa0, 0xc1, 0x88, 0xc1, 0x01, 0x80, 0x20, 0x18, 0x7c, 0xa3, 0xf0, 0x11, 0x7f, 0x30, 0x9a, 0x10, 0x08, + 0xc3, 0x0d, 0x01, 0x28, 0x80, 0xc1, 0x88, 0xc1, 0x01, 0x80, 0x20, 0x18, 0x7c, 0xa6, 0x20, 0x06, 0x47, 0x28, 0xcc, + 0x32, 0x04, 0x9b, 0x30, 0x62, 0x60, 0x00, 0x20, 0x08, 0x06, 0x07, 0x2c, 0xa4, 0xc1, 0x28, 0x8c, 0x18, 0x18, 0x00, + 0x08, 0x82, 0xc1, 0x11, 0x0b, 0x6a, 0x30, 0x0a, 0x95, 0xb0, 0xc1, 0x55, 0xd2, 0x06, 0x57, 0x83, 0x00, 0x55, 0x08, + 0x30, 0x9a, 0x70, 0x00, 0xa3, 0x09, 0x48, 0x30, 0xdc, 0x40, 0x08, 0x64, 0x30, 0xdc, 0x40, 0x08, 0x64, 0x50, 0x42, + 0xa0, 0xb3, 0x0c, 0xc2, 0x10, 0x8c, 0x18, 0x34, 0x00, 0x08, 0x82, 0x41, 0x65, 0x0b, 0x6b, 0x40, 0xb5, 0xc2, 0x61, + 0xc0, 0x01, 0x1c, 0xc0, 0x01, 0x1c, 0x8c, 0x26, 0x04, 0xc0, 0x68, 0x82, 0x10, 0x8c, 0x26, 0x0c, 0xc2, 0x88, 0xc1, + 0x01, 0x80, 0x20, 0x18, 0x48, 0xbb, 0x30, 0x07, 0xdd, 0x30, 0x62, 0x70, 0x00, 0x20, 0x08, 0x06, 0x12, 0x2f, 0xd0, + 0x81, 0x37, 0x8c, 0x18, 0x1c, 0x00, 0x08, 0x82, 0x81, 0xd4, 0x0b, 0x75, 0xf0, 0x0d, 0x23, 0x06, 0x07, 0x00, 0x82, + 0x60, 0xc0, 0xe9, 0x42, 0x1d, 0x68, 0xb5, 0x30, 0x9a, 0x10, 0x0c, 0x56, 0x04, 0xf4, 0xb1, 0x42, 0xa0, 0x8f, 0x15, + 0x03, 0x7d, 0x46, 0x0c, 0x16, 0x00, 0x04, 0xc1, 0xe0, 0x0a, 0x07, 0x3b, 0x18, 0x84, 0xe0, 0x0c, 0xd0, 0x20, 0x0d, + 0x46, 0x0c, 0x0e, 0x00, 0x04, 0xc1, 0x40, 0x22, 0x07, 0x3b, 0x08, 0xd6, 0x60, 0xc4, 0xe0, 0x00, 0x40, 0x10, 0x0c, + 0xa4, 0x72, 0xe8, 0x83, 0x36, 0x08, 0x46, 0x0c, 0x0c, 0x00, 0x04, 0xc1, 0x60, 0x41, 0x07, 0x3e, 0x08, 0x2c, 0x80, + 0x03, 0xf9, 0xcc, 0x12, 0x0c, 0x03, 0x15, 0x80, 0x20, 0xd0, 0x42, 0x50, 0x19, 0x2e, 0xc0, 0x70, 0x43, 0x80, 0x91, + 0x41, 0x05, 0x96, 0xce, 0x32, 0x10, 0x45, 0x30, 0x62, 0xd0, 0x00, 0x20, 0x08, 0x06, 0xd5, 0x3a, 0x80, 0x42, 0x1a, + 0x88, 0xc3, 0xb0, 0x95, 0x42, 0x29, 0x94, 0x42, 0x29, 0x8c, 0x26, 0x04, 0xc0, 0x68, 0x82, 0x10, 0x8c, 0x26, 0x0c, + 0xc2, 0x88, 0xc1, 0x01, 0x80, 0x20, 0x18, 0x48, 0xf0, 0x80, 0x0a, 0x72, 0x30, 0x8c, 0x18, 0x1c, 0x00, 0x08, 0x82, + 0x81, 0x14, 0x0f, 0xa9, 0x30, 0x07, 0xc3, 0x88, 0xc1, 0x01, 0x80, 0x20, 0x18, 0x48, 0xf2, 0xa0, 0x0a, 0x74, 0x30, + 0x8c, 0x18, 0x1c, 0x00, 0x08, 0x82, 0x01, 0xf7, 0x0e, 0xaa, 0xf0, 0x06, 0xea, 0x30, 0x9a, 0x10, 0x0c, 0x56, 0x04, + 0xf4, 0xb1, 0x42, 0xa0, 0x8f, 0x15, 0x03, 0x7d, 0x46, 0x0c, 0x16, 0x00, 0x04, 0xc1, 0xe0, 0xb2, 0x87, 0x55, 0x18, + 0x84, 0x80, 0x0f, 0xfa, 0xc0, 0x0f, 0x46, 0x0c, 0x0e, 0x00, 0x04, 0xc1, 0x40, 0xca, 0x87, 0x55, 0x08, 0x40, 0x61, + 0xc4, 0xe0, 0x00, 0x40, 0x10, 0x0c, 0x24, 0x7d, 0x90, 0x05, 0x51, 0x08, 0x46, 0x0c, 0x0c, 0x00, 0x04, 0xc1, 0x60, + 0xe9, 0x87, 0x58, 0x08, 0x2c, 0x28, 0x05, 0xf9, 0x58, 0x50, 0xc1, 0x67, 0x96, 0xa0, 0x18, 0xa8, 0x00, 0x04, 0x82, + 0x0d, 0x86, 0x72, 0x03, 0x77, 0x80, 0xe1, 0x86, 0xa0, 0x0d, 0xc8, 0xa0, 0xda, 0x20, 0xd0, 0x59, 0x06, 0xe3, 0x08, + 0x46, 0x0c, 0x1a, 0x00, 0x04, 0xc1, 0xa0, 0x0a, 0x09, 0x5b, 0xf8, 0x03, 0x7c, 0x90, 0x83, 0x61, 0x17, 0x76, 0x61, + 0x17, 0x76, 0x61, 0x34, 0x21, 0x00, 0x46, 0x13, 0x84, 0x60, 0x34, 0x61, 0x10, 0x46, 0x0c, 0x0e, 0x00, 0x04, 0xc1, + 0x40, 0x32, 0x09, 0x5f, 0x40, 0x85, 0x61, 0xc4, 0xe0, 0x00, 0x40, 0x10, 0x0c, 0xa4, 0x93, 0xf8, 0x85, 0x54, 0x18, + 0x46, 0x0c, 0x0e, 0x00, 0x04, 0xc1, 0x40, 0x42, 0x09, 0x70, 0x50, 0x85, 0x61, 0xc4, 0xe0, 0x00, 0x40, 0x10, 0x0c, + 0xb8, 0x92, 0x00, 0x87, 0x52, 0x00, 0x89, 0xd1, 0x84, 0x60, 0xb0, 0x22, 0xa0, 0x8f, 0x15, 0x02, 0x7d, 0xac, 0x18, + 0xe8, 0x33, 0x62, 0xb0, 0x00, 0x20, 0x08, 0x06, 0x17, 0x4b, 0x84, 0xc3, 0x20, 0x04, 0xb2, 0x30, 0x0b, 0xb4, 0x30, + 0x62, 0x70, 0x00, 0x20, 0x08, 0x06, 0xd2, 0x4b, 0x84, 0x43, 0x60, 0x0b, 0x23, 0x06, 0x07, 0x00, 0x82, 0x60, 0x20, + 0xc1, 0x04, 0x3a, 0xe0, 0x42, 0x30, 0x62, 0x60, 0x00, 0x20, 0x08, 0x06, 0xcb, 0x4c, 0x9c, 0x43, 0x60, 0xc1, 0x2e, + 0xc8, 0xc7, 0x82, 0x0a, 0x3e, 0xb3, 0x04, 0xc7, 0x40, 0x05, 0x20, 0x18, 0x6c, 0x50, 0xd4, 0x1a, 0x54, 0x3a, 0xcb, + 0x80, 0x24, 0xc1, 0x88, 0x41, 0x03, 0x80, 0x20, 0x18, 0x54, 0x35, 0xa1, 0x0e, 0xb3, 0xc0, 0x12, 0x6d, 0x70, 0xbd, + 0xc3, 0x3b, 0xbc, 0xc3, 0x3b, 0x8c, 0x26, 0x04, 0xc0, 0x68, 0x82, 0x10, 0x8c, 0x26, 0x0c, 0xc2, 0x88, 0xc1, 0x01, + 0x80, 0x20, 0x18, 0x48, 0x3a, 0x21, 0x0f, 0xbc, 0x30, 0x8c, 0x18, 0x1c, 0x00, 0x08, 0x82, 0x81, 0xb4, 0x13, 0xf3, + 0xd0, 0x0b, 0xc3, 0x88, 0xc1, 0x01, 0x80, 0x20, 0x18, 0x48, 0x3c, 0x41, 0x0f, 0xbe, 0x30, 0x8c, 0x18, 0x1c, 0x00, + 0x08, 0x82, 0x01, 0x97, 0x13, 0xf4, 0x90, 0x0b, 0x34, 0x31, 0x9a, 0x10, 0x0c, 0x56, 0x04, 0xf4, 0xb1, 0x42, 0xa0, + 0x8f, 0x15, 0x03, 0x7d, 0x46, 0x0c, 0x16, 0x00, 0x04, 0xc1, 0xe0, 0x02, 0x8b, 0x7a, 0x18, 0x84, 0xc0, 0x1c, 0xce, + 0x01, 0x1d, 0x46, 0x0c, 0x0e, 0x00, 0x04, 0xc1, 0x40, 0x1a, 0x8b, 0x7a, 0x08, 0xd4, 0x61, 0xc4, 0xe0, 0x00, 0x40, + 0x10, 0x0c, 0x24, 0xb2, 0xe0, 0x07, 0x76, 0x08, 0x46, 0x0c, 0x0c, 0x00, 0x04, 0xc1, 0x60, 0x39, 0x8b, 0x7d, 0x08, + 0x2c, 0x78, 0x07, 0xf9, 0x58, 0x30, 0xc1, 0x67, 0x96, 0x20, 0x19, 0xa8, 0x00, 0x04, 0x04, 0x0d, 0x8e, 0x15, 0x0c, + 0x70, 0xf1, 0x13, 0xe2, 0x30, 0x6c, 0x40, 0x04, 0xc2, 0x00, 0x8c, 0x18, 0x18, 0x00, 0x08, 0x82, 0x41, 0x76, 0x16, + 0x24, 0xf1, 0x13, 0xc3, 0x0d, 0xe3, 0xb0, 0x0f, 0x64, 0x30, 0xcb, 0xa0, 0x2c, 0x41, 0x91, 0x03, 0x3f, 0xc0, 0x0a, + 0x86, 0xb9, 0x10, 0x8b, 0x80, 0x02, 0x60, 0x50, 0x01, 0x0c, 0x0b, 0x04, 0xf8, 0x0c, 0x1b, 0x10, 0x47, 0x30, 0x00, + 0xb3, 0x04, 0xcb, 0x88, 0x81, 0x01, 0x80, 0x20, 0x18, 0x64, 0x6d, 0xa1, 0x12, 0x65, 0x31, 0xdc, 0x90, 0x0e, 0x20, + 0x41, 0x06, 0xb3, 0x0c, 0x4c, 0x13, 0x94, 0x3a, 0x84, 0x04, 0xac, 0x60, 0xc8, 0x0b, 0xb4, 0x08, 0x28, 0x00, 0x06, + 0x2d, 0xc0, 0xb0, 0x40, 0x80, 0xcf, 0xb0, 0x01, 0xd1, 0x04, 0x03, 0x30, 0x4b, 0xd0, 0x8c, 0x18, 0x18, 0x00, 0x08, + 0x82, 0x41, 0x36, 0x17, 0x30, 0xb1, 0x16, 0xc3, 0x0d, 0xef, 0x60, 0x16, 0x64, 0x30, 0xcb, 0xe0, 0x3c, 0x41, 0xc1, + 0xc3, 0x59, 0xc0, 0x0a, 0x86, 0xbf, 0x70, 0x8b, 0x80, 0x02, 0x60, 0x50, 0x04, 0x0c, 0x0b, 0x04, 0xf8, 0x0c, 0x1b, + 0x10, 0x53, 0x30, 0x00, 0xb3, 0x04, 0xcf, 0x88, 0x81, 0x01, 0x80, 0x20, 0x18, 0x64, 0x79, 0x61, 0x13, 0x71, 0x31, + 0xdc, 0x50, 0x0f, 0x6b, 0x41, 0x06, 0xb3, 0x0c, 0x50, 0x14, 0x94, 0x3d, 0xb0, 0x05, 0xac, 0x60, 0x28, 0x0d, 0xba, + 0x08, 0x28, 0x00, 0x06, 0x5d, 0xc0, 0xb0, 0x40, 0x80, 0xcf, 0xb0, 0x01, 0x91, 0x05, 0x03, 0x30, 0x4b, 0x10, 0x8d, + 0x18, 0x18, 0x00, 0x08, 0x82, 0x41, 0xf6, 0x17, 0x3c, 0x71, 0x17, 0xc3, 0x0d, 0xfb, 0x10, 0x13, 0x64, 0x30, 0xcb, + 0x20, 0x4d, 0x41, 0xf1, 0x83, 0x4c, 0xc0, 0x0a, 0x86, 0xd5, 0xd0, 0x8b, 0x80, 0x02, 0x60, 0x50, 0x07, 0x0c, 0x0b, + 0x04, 0xf8, 0x0c, 0x1b, 0x10, 0x5f, 0x30, 0x00, 0xb3, 0x04, 0xd3, 0x88, 0x81, 0x01, 0x80, 0x20, 0x18, 0x64, 0xa5, + 0x21, 0x16, 0x7d, 0x31, 0xdc, 0x10, 0x12, 0x79, 0x41, 0x06, 0xb3, 0x0c, 0x54, 0x15, 0x94, 0x48, 0xe8, 0x05, 0xac, + 0x60, 0x88, 0x0d, 0xd0, 0x08, 0x28, 0x00, 0x06, 0x8d, 0x01, 0x30, 0x2c, 0x10, 0xe0, 0x33, 0x6c, 0x40, 0x94, 0x41, + 0x30, 0x00, 0xb3, 0x04, 0xd5, 0x88, 0x81, 0x01, 0x80, 0x20, 0x18, 0x64, 0xab, 0x81, 0x16, 0xa3, 0x31, 0xdc, 0x70, + 0x12, 0xa1, 0x41, 0x06, 0xb3, 0x0c, 0xd6, 0x15, 0x14, 0x4a, 0x88, 0x06, 0xac, 0x60, 0xb8, 0x0d, 0xd3, 0x08, 0x28, + 0x00, 0x06, 0xa5, 0x01, 0x30, 0x2c, 0x10, 0xe0, 0x33, 0x6c, 0x40, 0xac, 0x41, 0x30, 0x00, 0xb3, 0x04, 0xd7, 0x88, + 0x81, 0x01, 0x80, 0x20, 0x18, 0x64, 0xb1, 0xe1, 0x16, 0xa9, 0x31, 0xdc, 0xd0, 0x12, 0xaa, 0x01, 0x06, 0xb3, 0x0c, + 0x58, 0x16, 0x50, 0x4d, 0x00, 0x83, 0x6e, 0x02, 0x18, 0x16, 0x08, 0xf0, 0x19, 0x36, 0x20, 0x72, 0x22, 0x18, 0x80, + 0x59, 0x82, 0x6c, 0xa0, 0x22, 0x0c, 0x10, 0x8c, 0x65, 0xae, 0x11, 0x03, 0x03, 0x00, 0x41, 0x30, 0xc8, 0x6c, 0x63, + 0x2e, 0x5c, 0x63, 0x96, 0x41, 0x9b, 0x83, 0x80, 0x74, 0x02, 0x18, 0x23, 0x06, 0x07, 0x00, 0x82, 0x60, 0xf0, 0xd5, + 0x46, 0x5c, 0xd8, 0x04, 0x6c, 0x8c, 0x26, 0x04, 0x42, 0x05, 0x36, 0x21, 0x85, 0x13, 0xb4, 0x01, 0x15, 0x08, 0x30, + 0x62, 0xd0, 0x00, 0x20, 0x08, 0x06, 0x64, 0x80, 0x1b, 0x7e, 0xd1, 0x13, 0x41, 0x6d, 0x18, 0x62, 0x21, 0x16, 0x62, + 0x31, 0x1a, 0xb3, 0x04, 0x73, 0x30, 0x9a, 0x40, 0x13, 0xc3, 0x70, 0x43, 0x4e, 0x04, 0x64, 0x30, 0xcb, 0xc0, 0x7d, + 0xc1, 0x2c, 0x41, 0x37, 0x50, 0x01, 0x2c, 0x9d, 0xa9, 0x70, 0x03, 0x15, 0xc6, 0xd2, 0xd9, 0x09, 0x57, 0x81, 0x6e, + 0xc0, 0x88, 0x01, 0x02, 0x80, 0x20, 0x18, 0x98, 0x01, 0x6f, 0x90, 0x86, 0x58, 0x04, 0xba, 0x31, 0x9a, 0x10, 0x00, + 0x16, 0x14, 0xf0, 0xa9, 0x22, 0x35, 0x60, 0xc4, 0xe0, 0x00, 0x40, 0x10, 0x0c, 0xbe, 0xf1, 0xf8, 0x0b, 0xb2, 0xf0, + 0x8d, 0xd1, 0x84, 0x60, 0x18, 0x6e, 0x18, 0x02, 0x32, 0x98, 0x65, 0xe8, 0xbc, 0x60, 0x96, 0xe0, 0x1b, 0xa8, 0x00, + 0x74, 0x65, 0x53, 0xbc, 0x15, 0x0c, 0xf3, 0x21, 0x1e, 0x65, 0x31, 0x6c, 0x40, 0x04, 0xc2, 0x00, 0x8c, 0x18, 0x18, + 0x00, 0x08, 0x82, 0x41, 0xa6, 0x1e, 0xa7, 0x21, 0x1e, 0xc3, 0x0d, 0x66, 0xe1, 0x17, 0x64, 0x30, 0xcb, 0x00, 0x06, + 0x61, 0x10, 0xd4, 0x59, 0xfc, 0x05, 0xac, 0x60, 0xb0, 0x8f, 0xf2, 0x08, 0x28, 0x00, 0x06, 0x15, 0xc0, 0xb0, 0x40, + 0x80, 0xcf, 0xb0, 0x01, 0x71, 0x04, 0x03, 0x30, 0x4b, 0x10, 0x06, 0x23, 0x06, 0x06, 0x00, 0x82, 0x60, 0x90, 0xc1, + 0x47, 0x6b, 0xa0, 0xc7, 0x70, 0x03, 0x5b, 0x8c, 0x06, 0x19, 0xcc, 0x32, 0x88, 0xc1, 0x18, 0x04, 0xd5, 0x16, 0xa4, + 0x01, 0x2b, 0x18, 0xf8, 0x63, 0x3d, 0x02, 0x0a, 0x80, 0x41, 0x0b, 0x30, 0x2c, 0x10, 0xe0, 0x33, 0x6c, 0x40, 0x34, + 0xc1, 0x00, 0xcc, 0x12, 0x8c, 0xc1, 0x88, 0x81, 0x01, 0x80, 0x20, 0x18, 0x64, 0xf6, 0x31, 0x1b, 0xee, 0x31, 0xdc, + 0x20, 0x17, 0xe9, 0x41, 0x06, 0xb3, 0x0c, 0x64, 0x50, 0x06, 0x41, 0xcd, 0x85, 0x7a, 0xc0, 0x0a, 0x06, 0x11, 0x89, + 0x8f, 0x80, 0x02, 0x60, 0x50, 0x04, 0x0c, 0x0b, 0x04, 0xf8, 0x0c, 0x1b, 0x10, 0x53, 0x30, 0x00, 0xb3, 0x04, 0x65, + 0x30, 0x62, 0x60, 0x00, 0x20, 0x08, 0x06, 0x19, 0x7f, 0xe4, 0x06, 0x7d, 0x0c, 0x37, 0xe0, 0x85, 0x7b, 0x90, 0xc1, + 0x2c, 0x83, 0x19, 0x9c, 0x41, 0x50, 0x79, 0xf1, 0x1e, 0xb0, 0x82, 0x01, 0x45, 0xee, 0x23, 0xa0, 0x00, 0x18, 0x74, + 0x01, 0xc3, 0x02, 0x01, 0x3e, 0xc3, 0x06, 0x44, 0x16, 0x0c, 0xc0, 0x2c, 0xc1, 0x19, 0x8c, 0x18, 0x18, 0x00, 0x08, + 0x82, 0x41, 0x26, 0x22, 0xbf, 0xa1, 0x1f, 0xc3, 0x0d, 0x7e, 0x41, 0x1b, 0x64, 0x30, 0xcb, 0x80, 0x06, 0x69, 0x10, + 0xd4, 0x5f, 0xd4, 0x06, 0xac, 0x60, 0x70, 0x91, 0xfe, 0x08, 0x28, 0x00, 0x06, 0x75, 0xc0, 0xb0, 0x40, 0x80, 0xcf, + 0xb0, 0x01, 0xf1, 0x05, 0x03, 0x30, 0x4b, 0x90, 0x06, 0x23, 0x06, 0x06, 0x00, 0x82, 0x60, 0x90, 0xa1, 0x48, 0x79, + 0x80, 0xc8, 0x70, 0x03, 0x69, 0xf0, 0x07, 0x19, 0xcc, 0x32, 0xa8, 0xc1, 0x1a, 0x04, 0x55, 0x1a, 0xfd, 0x01, 0x2b, + 0x18, 0x68, 0x64, 0x44, 0x02, 0x0a, 0x80, 0x41, 0x63, 0x00, 0x0c, 0x0b, 0x04, 0xf8, 0x0c, 0x1b, 0x10, 0x65, 0x10, + 0x0c, 0xc0, 0x2c, 0xc1, 0x1a, 0x8c, 0x18, 0x18, 0x00, 0x08, 0x82, 0x41, 0xe6, 0x22, 0xeb, 0x61, 0x22, 0xc3, 0x0d, + 0xaa, 0x41, 0x22, 0x64, 0x30, 0xcb, 0xc0, 0x06, 0x6d, 0x10, 0xd4, 0x6a, 0x94, 0x08, 0xac, 0x60, 0xd0, 0x91, 0x14, + 0x09, 0x28, 0x00, 0x06, 0xa5, 0x01, 0x30, 0x2c, 0x10, 0xe0, 0x33, 0x6c, 0x40, 0xac, 0x41, 0x30, 0x00, 0xb3, 0x04, + 0x6d, 0x30, 0x62, 0x60, 0x00, 0x20, 0x08, 0x06, 0x19, 0x8d, 0xc4, 0x07, 0x8b, 0x0c, 0x37, 0xc0, 0x46, 0x8b, 0x80, + 0xc1, 0x2c, 0x83, 0x1b, 0xbc, 0x41, 0x40, 0xb8, 0x01, 0x0c, 0xd2, 0x0d, 0x60, 0x58, 0x20, 0xc0, 0x67, 0xd8, 0x80, + 0xe0, 0x8d, 0x60, 0x00, 0x66, 0x09, 0xde, 0x60, 0xa0, 0x22, 0x0c, 0x10, 0x37, 0x90, 0xa1, 0x36, 0x18, 0x31, 0x30, + 0x00, 0x10, 0x04, 0x83, 0x2c, 0x47, 0xec, 0x23, 0x46, 0x66, 0x19, 0xe0, 0x60, 0x0e, 0x02, 0xea, 0x0d, 0x60, 0x8c, + 0x18, 0x1c, 0x00, 0x08, 0x82, 0xc1, 0x87, 0x23, 0xf4, 0x91, 0x1b, 0x33, 0x32, 0x9a, 0x10, 0x00, 0x17, 0x00, 0x35, + 0x9a, 0x30, 0x04, 0x17, 0x00, 0x65, 0xc1, 0x20, 0x1f, 0x3b, 0x02, 0xfa, 0x8c, 0x18, 0x1c, 0x00, 0x08, 0x82, 0xc1, + 0xf7, 0x23, 0xfb, 0x01, 0x1e, 0x3b, 0x32, 0x9a, 0x10, 0x04, 0xc3, 0x0d, 0x01, 0x8f, 0x80, 0xc1, 0x2c, 0x43, 0x1c, + 0xc8, 0x41, 0x30, 0x62, 0x80, 0x00, 0x20, 0x08, 0x06, 0x66, 0xf0, 0x23, 0x27, 0x52, 0x1e, 0x3d, 0xd2, 0x23, 0xa3, + 0x09, 0x01, 0x60, 0x46, 0x10, 0x9f, 0x11, 0x83, 0x03, 0x00, 0x41, 0x30, 0xe0, 0xcc, 0x24, 0x44, 0xcc, 0x23, 0x4c, + 0x46, 0x13, 0x02, 0xc0, 0x02, 0xfb, 0x90, 0xcf, 0x88, 0x81, 0x01, 0x80, 0x20, 0x18, 0x2c, 0x6f, 0x82, 0x22, 0x81, + 0xe5, 0x47, 0x10, 0x1f, 0x0b, 0x0c, 0xf9, 0x58, 0x80, 0xc0, 0x67, 0x96, 0x40, 0x0e, 0x06, 0x2a, 0x00, 0x21, 0x0e, + 0x38, 0x38, 0x18, 0x31, 0x68, 0x00, 0x10, 0x04, 0x03, 0x32, 0x58, 0x93, 0x18, 0x81, 0x0f, 0x34, 0x41, 0x93, 0xa0, + 0x3e, 0xea, 0xa3, 0x3e, 0x6c, 0xc4, 0x02, 0xff, 0x90, 0xcf, 0x88, 0x81, 0x01, 0x80, 0x20, 0x18, 0x2c, 0x75, 0xe2, + 0x22, 0x81, 0x05, 0x21, 0x22, 0x9f, 0x11, 0x03, 0x03, 0x00, 0x41, 0x30, 0x58, 0xee, 0x64, 0x45, 0x82, 0x11, 0x03, + 0x03, 0x00, 0x41, 0x30, 0x58, 0xf0, 0x24, 0x46, 0x02, 0x2b, 0x91, 0x80, 0x3e, 0x23, 0x06, 0x0e, 0x00, 0x82, 0x60, + 0x00, 0xe5, 0xc9, 0x8e, 0xdc, 0x87, 0x9b, 0xb8, 0x49, 0x8c, 0x04, 0x41, 0x10, 0xfc, 0xc8, 0x2c, 0xc1, 0x1c, 0x60, + 0x40, 0x0c, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x5b, 0x06, 0x79, 0x08, 0xd8, 0x60, 0xcb, 0x80, 0x0f, 0x01, + 0x1b, 0x6c, 0x19, 0xf2, 0x21, 0x60, 0x83, 0x2d, 0xc3, 0x3e, 0x04, 0x6c, 0xb0, 0x65, 0x10, 0x89, 0x80, 0x0d, 0xb6, + 0x0c, 0x23, 0x11, 0xb0, 0xc1, 0x96, 0xa1, 0x24, 0x02, 0x36, 0xd8, 0x32, 0xb0, 0x44, 0xc0, 0x06, 0x5b, 0x86, 0x96, + 0x08, 0xd8, 0x60, 0xcb, 0xf0, 0x12, 0x01, 0x1b, 0x6c, 0x19, 0x6c, 0x22, 0x60, 0x83, 0x2d, 0xc3, 0x4d, 0x04, 0x6c, + 0xb0, 0x65, 0xc8, 0x89, 0x80, 0x0d, 0xb6, 0x0c, 0x60, 0x11, 0xb0, 0xc1, 0x96, 0x21, 0x2c, 0x02, 0x36, 0xd8, 0x32, + 0x8c, 0x45, 0xc0, 0x06, 0x5b, 0x06, 0xb5, 0x08, 0xd8, 0x60, 0xcb, 0xb0, 0x16, 0x01, 0x1b, 0x6c, 0x19, 0xda, 0x22, + 0x60, 0x83, 0x2d, 0x03, 0x5d, 0x04, 0x6c, 0xb0, 0x65, 0xa8, 0x8b, 0x80, 0x0d, 0xb6, 0x0c, 0x77, 0x11, 0xb0, 0xc1, + 0x96, 0x81, 0x2f, 0x02, 0x36, 0xd8, 0x32, 0xf4, 0x45, 0xc0, 0x06, 0x5b, 0x86, 0xbf, 0x08, 0xd8, 0x60, 0xcb, 0x40, + 0x1a, 0x01, 0x1b, 0x6c, 0x19, 0x7c, 0x23, 0x60, 0x83, 0x2d, 0x03, 0x79, 0x04, 0x6c, 0xb0, 0x65, 0x28, 0x8f, 0x80, + 0x0d, 0xb6, 0x0c, 0xe7, 0x11, 0xb0, 0xc1, 0x96, 0xc1, 0x3d, 0x02, 0x36, 0xd8, 0x32, 0xbc, 0x47, 0xc0, 0x06, 0x5b, + 0x86, 0xf8, 0x08, 0xd8, 0x60, 0xcb, 0x80, 0x1f, 0x01, 0x1b, 0x6c, 0x19, 0xf2, 0x23, 0x60, 0x83, 0x2d, 0xc3, 0x7e, + 0x04, 0x6c, 0xb0, 0x65, 0x10, 0x91, 0x80, 0x0d, 0xb6, 0x0c, 0x23, 0x12, 0xb0, 0xc1, 0x96, 0xa1, 0x44, 0x02, 0x36, + 0xd8, 0x32, 0xb0, 0x48, 0xc0, 0x06, 0x5b, 0x86, 0x16, 0x09, 0xd8, 0x60, 0xcb, 0xf0, 0x22, 0x01, 0x1b, 0x6c, 0x19, + 0x6c, 0x24, 0x60, 0x83, 0x2d, 0xc3, 0x8d, 0x04, 0x6c, 0xb0, 0x65, 0xc8, 0x91, 0x80, 0x0d, 0xb6, 0x0c, 0x60, 0x12, + 0xb0, 0xc1, 0x96, 0x21, 0x4c, 0x02, 0x36, 0xd8, 0x32, 0x8c, 0x49, 0xc0, 0x06, 0x5b, 0x06, 0x34, 0x09, 0xd8, 0x60, + 0xcb, 0x90, 0x26, 0x01, 0x1b, 0x6c, 0x19, 0xd6, 0x24, 0x60, 0x83, 0x2d, 0x03, 0x9c, 0x04, 0x6c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; diff --git a/OptiScaler/upscalers/ffx/FFXFeature_Dx12.cpp b/OptiScaler/upscalers/ffx/FFXFeature_Dx12.cpp index 1f6cac64..e61dd06b 100644 --- a/OptiScaler/upscalers/ffx/FFXFeature_Dx12.cpp +++ b/OptiScaler/upscalers/ffx/FFXFeature_Dx12.cpp @@ -38,57 +38,6 @@ bool FFXFeatureDx12::InitInternal(ID3D12GraphicsCommandList* InCommandList, NVSD return InitFFX(InParameters); } -bool CreateBufferResourceWithSize(ID3D12Device* device, ID3D12Resource* source, D3D12_RESOURCE_STATES state, - ID3D12Resource** target, UINT width, UINT height) -{ - if (device == nullptr || source == nullptr) - return false; - - auto inDesc = source->GetDesc(); - - if (*target != nullptr) - { - auto bufDesc = (*target)->GetDesc(); - - if (bufDesc.Width != width || bufDesc.Height != height || bufDesc.Format != inDesc.Format || - bufDesc.Flags != inDesc.Flags) - { - // Previous frames can still be using it - DeferredRelease::Release(*target); - } - else - { - return true; - } - } - - D3D12_HEAP_PROPERTIES heapProperties; - D3D12_HEAP_FLAGS heapFlags; - HRESULT hr = source->GetHeapProperties(&heapProperties, &heapFlags); - - if (hr != S_OK) - { - LOG_ERROR("GetHeapProperties result: {:X}", (UINT64) hr); - return false; - } - - inDesc.Width = width; - inDesc.Height = height; - - hr = device->CreateCommittedResource(&heapProperties, D3D12_HEAP_FLAG_NONE, &inDesc, state, nullptr, - IID_PPV_ARGS(target)); - - if (hr != S_OK) - { - LOG_ERROR("CreateCommittedResource result: {:X}", (UINT64) hr); - return false; - } - - LOG_DEBUG("Created new one: {}x{}", inDesc.Width, inDesc.Height); - - return true; -} - bool FFXFeatureDx12::EvaluateInternal(ID3D12GraphicsCommandList* InCommandList, NVSDK_NGX_Parameter* InParameters) { LOG_FUNC(); @@ -161,70 +110,6 @@ bool FFXFeatureDx12::EvaluateInternal(ID3D12GraphicsCommandList* InCommandList, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); } - // WAR for FSR 4's autoexposure shader reading entire underlying resource - // instead of what's specified by renderSize or color's FfxApiResourceDescription. - // Only linear has this issue - if (Version().major >= 4 && AutoExposure() && !Config::Instance()->FsrNonLinearPQ.value_or_default() && - !Config::Instance()->FsrNonLinearSRGB.value_or_default() && - !Config::Instance()->FsrNonLinearColorSpace.value_or_default()) - { - D3D12_RESOURCE_DESC desc = paramColor->GetDesc(); - - const auto diffW = static_cast(desc.Width) - static_cast(params.renderSize.width); - const auto diffH = static_cast(desc.Height) - static_cast(params.renderSize.height); - - // Seemingly it may only be happening when both axes are 1 pixel too big - // But let's be conservative here and also trigger when just one axis is too big - const bool validW = (diffW >= 0 && diffW <= 2); - const bool validH = (diffH >= 0 && diffH <= 2); - const bool isPadded = (diffW > 0 || diffH > 0); - - if (validW && validH && isPadded) - { - static size_t count = 0; - const size_t index = count % 2; - - CreateBufferResourceWithSize(Device, paramColor, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, - &smallerColor[index], params.renderSize.width, params.renderSize.height); - - if (smallerColor[index]) - { - ResourceBarrier(InCommandList, paramColor, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, - D3D12_RESOURCE_STATE_COPY_SOURCE); - - ResourceBarrier(InCommandList, smallerColor[index], D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, - D3D12_RESOURCE_STATE_COPY_DEST); - - D3D12_BOX srcBox {}; - srcBox.left = 0; - srcBox.top = 0; - srcBox.right = params.renderSize.width; - srcBox.bottom = params.renderSize.height; - srcBox.front = 0; - srcBox.back = 1; - - D3D12_TEXTURE_COPY_LOCATION dstLocation {}; - dstLocation.pResource = smallerColor[index]; - dstLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; - dstLocation.SubresourceIndex = 0; - - D3D12_TEXTURE_COPY_LOCATION srcLocation {}; - srcLocation.pResource = paramColor; - srcLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; - srcLocation.SubresourceIndex = 0; - - InCommandList->CopyTextureRegion(&dstLocation, 0, 0, 0, &srcLocation, &srcBox); - - ResourceBarrier(InCommandList, smallerColor[index], D3D12_RESOURCE_STATE_COPY_DEST, - D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); - - paramColor = smallerColor[index]; - } - - count++; - } - } - params.color = ffxApiGetResourceDX12(paramColor, FFX_API_RESOURCE_STATE_COMPUTE_READ); } else @@ -579,6 +464,19 @@ bool FFXFeatureDx12::EvaluateInternal(ID3D12GraphicsCommandList* InCommandList, params.upscaleSize.height = TargetHeight(); } + if (_customAutoExposure) + { + if (_autoExposure->Dispatch(InCommandList, paramColor, params.renderSize.width, params.renderSize.height, + params.preExposure, params.frameTimeDelta / 1000.0f, params.reset)) + { + params.exposure = ffxApiGetResourceDX12(_autoExposure->Exposure(), FFX_API_RESOURCE_STATE_COMPUTE_READ); + } + else + { + LOG_ERROR("Auto exposure dispatch failed"); + } + } + LOG_DEBUG("Dispatch!!"); auto result = FfxApiProxy::D3D12_Dispatch(&_context, ¶ms.header); @@ -663,12 +561,36 @@ bool FFXFeatureDx12::InitFFX(const NVSDK_NGX_Parameter* InParameters) State::Instance().ffxUpscalerVersionsDx12.ids.size()) Config::Instance()->FfxUpscalerIndex.set_volatile_value(0); + parse_version( + State::Instance().ffxUpscalerVersionsDx12.names[Config::Instance()->FfxUpscalerIndex.value_or_default()]); + ffxOverrideVersion override = { 0 }; override.header.type = FFX_API_DESC_TYPE_OVERRIDE_VERSION; override.versionId = State::Instance().ffxUpscalerVersionsDx12.ids[Config::Instance()->FfxUpscalerIndex.value_or_default()]; backendDesc.header.pNext = &override.header; + // FSR4's own auto exposure reads the whole color texture instead of just renderSize. On UE the padding + // outside the view can hold garbage that makes it think the scene is very bright and black out the frame. + // Run our own auto exposure instead and hand FSR4 the result as a regular exposure texture. + // Only linear input is affected, color space changes recreate the feature. + _customAutoExposure = false; + auto& cfg = *Config::Instance(); + + if (Version().major >= 4 && AutoExposure() && !cfg.FsrNonLinearPQ.value_or_default() && + !cfg.FsrNonLinearSRGB.value_or_default() && !cfg.FsrNonLinearColorSpace.value_or_default()) + { + if (_autoExposure == nullptr) + _autoExposure = std::make_unique("AutoExposure", Device); + + if (_autoExposure->IsInit()) + { + _customAutoExposure = true; + _contextDesc.flags &= ~FFX_UPSCALE_ENABLE_AUTO_EXPOSURE; + LOG_INFO("Using OptiScaler auto exposure instead of FSR's"); + } + } + LOG_DEBUG("_createContext!"); { @@ -683,10 +605,7 @@ bool FFXFeatureDx12::InitFFX(const NVSDK_NGX_Parameter* InParameters) } } - auto version = - State::Instance().ffxUpscalerVersionsDx12.names[Config::Instance()->FfxUpscalerIndex.value_or_default()]; _name = "FSR"; - parse_version(version); } SetInit(true); diff --git a/OptiScaler/upscalers/ffx/FFXFeature_Dx12.h b/OptiScaler/upscalers/ffx/FFXFeature_Dx12.h index 885085b4..debcac0f 100644 --- a/OptiScaler/upscalers/ffx/FFXFeature_Dx12.h +++ b/OptiScaler/upscalers/ffx/FFXFeature_Dx12.h @@ -4,11 +4,13 @@ #include "dx12/ffx_api_dx12.h" #include "proxies/FfxApi_Proxy.h" +#include class FFXFeatureDx12 : public FFXFeature, public IFeature_Dx12 { private: - ID3D12Resource* smallerColor[2] {}; + std::unique_ptr _autoExposure; + bool _customAutoExposure = false; NVSDK_NGX_Parameter* SetParameters(NVSDK_NGX_Parameter* InParameters); @@ -44,8 +46,5 @@ class FFXFeatureDx12 : public FFXFeature, public IFeature_Dx12 if (_context != nullptr) FfxApiProxy::D3D12_DestroyContext(&_context, NULL); - - SAFE_RELEASE(smallerColor[0]); - SAFE_RELEASE(smallerColor[1]); } };