Compare commits

..

19 Commits

Author SHA1 Message Date
riperiperi
bebd8eb822 Vulkan: Cache delegate for EndRenderPass (#6132)
This prevents a small allocation each time this method is called. This is a top 3 SOH allocation during gameplay in most games, and eliminating it is pretty free.
2024-01-16 13:22:20 +01:00
gdkchan
f4b74e9ce1 Fix vertex buffer size when switching between inline and state draw parameters (#6101)
* Fix vertex buffer size when switching between inline and state draw parameters

* Format whitespace
2024-01-14 09:37:19 +01:00
Isaac Marovitz
4e19b36ad7 CI: Dependabot Groups (#6110)
* CI: Dependabot Groups

* NUnit

* Limit of 10

* Whoops

* Missing wildcard

* Remove Ryujank group
2024-01-13 15:28:57 +01:00
gdkchan
b16923a902 Revert Apple hypervisor force ordered memory change (#6068) 2024-01-13 11:58:09 +01:00
TSRBerry
7e58b21f3d Fix Amiibo regression and some minor code improvements (#6107)
* Remove redundant code and fix small issues

* Log amiibo exceptions

* Add more checks when getting Amiibo data

* Fall back to online data if local file is inaccessible

* Make dotnet format happy
2024-01-13 11:45:38 +01:00
Isaac Marovitz
4fbc978e73 Switch to Microsoft.IdentityModel.JsonWebTokens (#6108)
* Switch to `Microsoft.IdentityModel.JsonWebTokens`

* Formatting
2024-01-13 11:39:00 +01:00
Isaac Marovitz
1a45dc8df8 Ava UI: RTL Language Support (#5619)
* Add Hebrew locale files to ItemGroups

* Align all windows RTL for testing

This should be controlled with a binding that selects the appropriate layout based on current language

* Update FlowDirection as Locale changes

* Fix Settings NavigationViewItem FlowDirection

* Fix remaining text

* Fix input menu directionality

* Fix RTL not rendering

* Fix rebase errors
2024-01-13 01:42:42 +01:00
Isaac Marovitz
f037fcba9a Ava UI: Better Controller Applet (#5756)
* Start work on better Controller Applet

* Don’t require title

* UI improvements

* Border around TBD area

* Formatting

* Better SVGs

* Add missing margin

* Use Locale

* Rename function

* Make buttons ourselves

* Make the buttons do shit

* Formatting

* Adjust SVGs

* Fix Open Settings Window

* Make field readonly

* Final tweaks

* Update src/Ryujinx.Ava/UI/Applet/AvaHostUiHandler.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update src/Ryujinx.Ava/UI/Applet/ControllerAppletDialog.axaml.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Apply suggestions from code review

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update src/Ryujinx.Ava/UI/Applet/ControllerAppletDialog.axaml.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Move icons to Ava project

* Reorder arguments

* Try to focus Settings Window

* Fix icons

Project shenangians

* Add ContentDialogHelper.ShowWindowAsync method

* Fix closed SettingsWindow reference in MainWindow

* Fix SettingsWindow dialog

* Suggestion

---------

Co-authored-by: Ac_K <Acoustik666@gmail.com>
Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>
2024-01-13 01:41:57 +01:00
gdkchan
59a0c7cfd8 Fix PPTC version string for firmware titles (#6071) 2024-01-04 00:08:10 +01:00
gdkchan
6f50b9bdb0 Add support for PermissionLocked attribute added on firmware 17.0.0 (#6072)
* Update MemoryState enum and add new flags

* Add support for new PermissionLocked attribute added on firmware 17.0.0

* Format whitespace
2024-01-04 00:05:14 +01:00
AxesP
f11d663df7 Local Amiibo.json should be used if connection failed (#3681)
* Local Amiibo.json should be used if connection failed

Currently Ryujinx is not loading any Amiibo if connection fails, even if the Amiibo.json exists.
This fix will use the local file and show a Dialog if connection fails.

* using local Amiibo.json & fixed Amiibo.json date comparison

Using local Amiibo.json when connection fails and comparison without milliseconds for LastModified that comes from https://amiibo.ryujinx.org/ and the local one (The JSON file has milliseconds on it, those will cause an error when comparing the date from the header because the header one doesn't has milliseconds on it). Both changes made for Avalonia UI.

* Fixed date comparison

Same date comparison fix, but made for normal UI (Not for AvaloniaUI).
This error can be prevented if the file in https://amiibo.ryujinx.org/ did not have the date with milliseconds.

* Securely trying to get a list of Amiibo (For normal UI)

* Securely trying to get a list of Amiibo (Change for AvaloniaUI)

* Date comparison reverted

* Apply suggestions from code review

* Use fallback amiibo.json if remote file is not valid (Normal UI)

* Use fallback amiibo.json if remote file is not valid (Avalonia UI)

* Code styles corrected.

* Code styles corrected in AmiiboWindowViewModel.

* Readded Ryujinx.Common.Logging using.

* Fixed using order.

---------

Co-authored-by: Ac_K <Acoustik666@gmail.com>
2023-12-28 02:43:17 +01:00
Isaac Marovitz
19a949d0bf Ava UI: Fix crash when clicking on a cheat's name (#5860)
* Fix crash

* Remove nullable

* Hide BuildId for child nodes

* Fix warning

* Fix charset
2023-12-25 06:57:14 +01:00
dependabot[bot]
feec5ef7b3 ci: bump actions/upload-artifact from 3 to 4 (#6050)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 3 to 4.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-25 06:33:10 +01:00
Mary Guillemard
9864675a0b Revert "ci: bump actions/github-script from 6 to 7 (#5966)"
This reverts commit 0531c16326.
2023-12-11 21:51:40 +01:00
shinra-electric
06bff0159c Correctly set filetypes in Info.plist (#6023)
Currently the filetype association is not working. 

This should hopefully fix it.
2023-12-11 21:49:07 +01:00
Mary Guillemard
04ed8c1f83 infra: Fix labeler.yml after labeler@v5 update 2023-12-11 21:43:05 +01:00
rmg-x
ad8d5b9b56 Ava UI: Fix temporary volume not being set after unmute (#5833) 2023-12-11 21:26:11 +01:00
gdkchan
1df6c07f78 Implement support for multi-range buffers using Vulkan sparse mappings (#5427)
* Pass MultiRange to BufferManager

* Implement support for multi-range buffers using Vulkan sparse mappings

* Use multi-range for remaining buffers, delete old methods

* Assume that more buffers are contiguous

* Dispose multi-range buffers after they are removed from the list

* Properly init BufferBounds for constant and storage buffers

* Do not try reading zero bytes data from an unmapped address on the shader cache + PR feedback

* Fix misaligned sparse buffer offsets

* Null check can be simplified

* PR feedback
2023-12-04 20:30:19 +01:00
dependabot[bot]
0531c16326 ci: bump actions/github-script from 6 to 7 (#5966)
Bumps [actions/github-script](https://github.com/actions/github-script) from 6 to 7.
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](https://github.com/actions/github-script/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/github-script
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-04 20:29:41 +01:00
85 changed files with 2982 additions and 672 deletions

View File

@@ -13,7 +13,7 @@ updates:
- package-ecosystem: nuget
directory: /
open-pull-requests-limit: 5
open-pull-requests-limit: 10
schedule:
interval: daily
labels:
@@ -22,3 +22,19 @@ updates:
- marysaka
commit-message:
prefix: nuget
groups:
Avalonia:
patterns:
- "*Avalonia*"
Silk.NET:
patterns:
- "Silk.NET*"
OpenTK:
patterns:
- "OpenTK*"
SixLabors:
patterns:
- "SixLabors*"
NUnit:
patterns:
- "NUnit*"

42
.github/labeler.yml vendored
View File

@@ -1,33 +1,35 @@
audio: 'src/Ryujinx.Audio*/**'
audio:
- changed-files:
- any-glob-to-any-file: 'src/Ryujinx.Audio*/**'
cpu:
- 'src/ARMeilleure/**'
- 'src/Ryujinx.Cpu/**'
- 'src/Ryujinx.Memory/**'
- changed-files:
- any-glob-to-any-file: ['src/ARMeilleure/**', 'src/Ryujinx.Cpu/**', 'src/Ryujinx.Memory/**']
gpu:
- 'src/Ryujinx.Graphics.*/**'
- 'src/Spv.Generator/**'
- 'src/Ryujinx.ShaderTools/**'
- changed-files:
- any-glob-to-any-file: ['src/Ryujinx.Graphics.*/**', 'src/Spv.Generator/**', 'src/Ryujinx.ShaderTools/**']
'graphics-backend:opengl':
- changed-files:
- any-glob-to-any-file: 'src/Ryujinx.Graphics.OpenGL/**'
'graphics-backend:opengl': 'src/Ryujinx.Graphics.OpenGL/**'
'graphics-backend:vulkan':
- 'src/Ryujinx.Graphics.Vulkan/**'
- 'src/Spv.Generator/**'
- changed-files:
- any-glob-to-any-file: ['src/Ryujinx.Graphics.Vulkan/**', 'src/Spv.Generator/**']
gui:
- 'src/Ryujinx/**'
- 'src/Ryujinx.Ui.Common/**'
- 'src/Ryujinx.Ui.LocaleGenerator/**'
- 'src/Ryujinx.Ava/**'
- changed-files:
- any-glob-to-any-file: ['src/Ryujinx/**', 'src/Ryujinx.Ui.Common/**', 'src/Ryujinx.Ui.LocaleGenerator/**', 'src/Ryujinx.Ava/**']
horizon:
- 'src/Ryujinx.HLE/**'
- 'src/Ryujinx.Horizon*/**'
- changed-files:
- any-glob-to-any-file: ['src/Ryujinx.HLE/**', 'src/Ryujinx.Horizon/**']
kernel: 'src/Ryujinx.HLE/HOS/Kernel/**'
kernel:
- changed-files:
- any-glob-to-any-file: 'src/Ryujinx.HLE/HOS/Kernel/**'
infra:
- '.github/**'
- 'distribution/**'
- 'Directory.Packages.props'
- changed-files:
- any-glob-to-any-file: ['.github/**', 'distribution/**', 'Directory.Packages.props']

View File

@@ -79,21 +79,21 @@ jobs:
if: github.event_name == 'pull_request' && matrix.os == 'ubuntu-latest'
- name: Upload Ryujinx artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }}
path: publish
if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest'
- name: Upload Ryujinx.Headless.SDL2 artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: sdl2-ryujinx-headless-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }}
path: publish_sdl2_headless
if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest'
- name: Upload Ryujinx.Ava artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: ava-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }}
path: publish_ava
@@ -144,14 +144,14 @@ jobs:
./distribution/macos/create_macos_build_headless.sh . publish_tmp_headless publish_headless ./distribution/macos/entitlements.xml "${{ env.RYUJINX_BASE_VERSION }}" "${{ steps.git_short_hash.outputs.result }}" "${{ matrix.configuration }}" "-p:ExtraDefineConstants=DISABLE_UPDATER"
- name: Upload Ryujinx.Ava artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: ava-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-macos_universal
path: "publish_ava/*.tar.gz"
if: github.event_name == 'pull_request'
- name: Upload Ryujinx.Headless.SDL2 artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: sdl2-ryujinx-headless-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-macos_universal
path: "publish_headless/*.tar.gz"

View File

@@ -63,7 +63,7 @@ jobs:
- name: Upload report
if: failure()
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: dotnet-format
path: ./*-report.json

View File

@@ -21,6 +21,7 @@
<PackageVersion Include="LibHac" Version="0.19.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.7.0" />
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="7.2.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" />
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
@@ -46,9 +47,8 @@
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" />
<PackageVersion Include="SPB" Version="0.0.4-build28" />
<PackageVersion Include="System.Drawing.Common" Version="8.0.0" />
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="7.0.3" />
<PackageVersion Include="System.IO.Hashing" Version="8.0.0" />
<PackageVersion Include="System.Management" Version="8.0.0" />
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
</ItemGroup>
</Project>
</Project>

View File

@@ -10,14 +10,25 @@
<string>Ryujinx</string>
<key>CFBundleIconFile</key>
<string>Ryujinx.icns</string>
<key>CFBundleTypeExtensions</key>
<array>
<string>nca</string>
<string>nro</string>
<string>nso</string>
<string>nsp</string>
<string>xci</string>
</array>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>nca</string>
<string>nro</string>
<string>nso</string>
<string>nsp</string>
<string>xci</string>
</array>
<key>CFBundleTypeName</key>
<string>Nintendo Switch File</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSHandlerRank</key>
<string>Default</string>
</dict>
</array>
<key>CFBundleIdentifier</key>
<string>org.ryujinx.Ryujinx</string>
<key>CFBundleInfoDictionaryVersion</key>

View File

@@ -1088,10 +1088,11 @@ namespace Ryujinx.Ava
case KeyboardHotkeyState.ToggleMute:
if (Device.IsAudioMuted())
{
Device.SetVolume(ConfigurationState.Instance.System.AudioVolume);
Device.SetVolume(_viewModel.VolumeBeforeMute);
}
else
{
_viewModel.VolumeBeforeMute = Device.GetVolume();
Device.SetVolume(0);
}

View File

@@ -0,0 +1,155 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 27.9.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 1000 355.6" style="enable-background:new 0 0 1000 355.6;" xml:space="preserve">
<style type="text/css">
.st0{fill:#00BBDB;stroke:#000000;}
.st1{fill:#333333;stroke:#000000;stroke-width:0.93;}
.st2{fill:#333333;stroke:#000000;stroke-width:0.96;}
.st3{fill:#333333;stroke:#000000;stroke-width:0.85;}
.st4{fill:#1A1A1A;stroke:#000000;}
.st5{fill:#333333;stroke:#000000;}
.st6{fill:#1A1A1A;stroke:#1A1A1A;}
.st7{fill:#1A1A1A;stroke:#4D4D4D;}
.st8{stroke:#4D4D4D;}
.st9{stroke:#000000;}
</style>
<g id="g344">
<path id="path66-7" d="M906.6,6.5v7.9c0,3.6-2.9,6.4-6.5,6.5H71.2c-3.6,0-6.4-2.9-6.5-6.5V6.5c0-3.6,2.9-6.4,6.5-6.5L207,0
c4.9,0,9.6,1.2,14,3.4l13,6.7h79.2l13-6.7c4.3-2.2,9.1-3.4,13.9-3.4l269.7,0c4.9,0,9.6,1.2,14,3.4l13,6.7H716l13-6.7
c4.3-2.2,9.1-3.4,13.9-3.4l157.2,0C903.7,0,906.6,2.9,906.6,6.5L906.6,6.5L906.6,6.5z M65.7,14.4c0,3,2.4,5.5,5.5,5.5h828.9
c3,0,5.5-2.4,5.5-5.5V6.5c0-3-2.4-5.5-5.5-5.5H742.9c-4.7,0-9.3,1.1-13.5,3.3l-13.1,6.8c-0.1,0-0.2,0.1-0.2,0.1h-79.5
c-0.1,0-0.2,0-0.2-0.1l-13.1-6.8c-4.2-2.2-8.8-3.3-13.5-3.3H340.1c-4.7,0-9.3,1.1-13.5,3.3l-13.1,6.8c-0.1,0-0.2,0.1-0.2,0.1h-79.5
c-0.1,0-0.2,0-0.2-0.1l-13.1-6.8C216.3,2.1,211.7,1,207,1L71.2,1c-3,0-5.5,2.4-5.5,5.5L65.7,14.4L65.7,14.4z"/>
<path id="path68-5" d="M858.9,20.3v11.2c0,0.3-0.2,0.5-0.5,0.5H72c-0.3,0-0.5-0.2-0.5-0.5V20.3c0-0.3,0.2-0.5,0.5-0.5h786.4
C858.7,19.8,858.9,20,858.9,20.3z M857.9,31V20.8H72.5V31H857.9z"/>
<path id="path70-3" d="M1000,37.5v106.2c0,117-94.8,211.9-211.9,211.9l0,0H220.9c-116.8,0-211.8-95.1-211.8-211.9V37.5
c0-3.6,2.9-6.5,6.5-6.5h978C997.1,31,1000,33.9,1000,37.5L1000,37.5L1000,37.5z M10.1,143.7c0,116.3,94.6,210.9,210.8,210.9h567.2
C904.4,354.6,999,260,999,143.7V37.5c0-3-2.4-5.5-5.5-5.5h-978c-3,0-5.5,2.4-5.5,5.5v106.2L10.1,143.7L10.1,143.7z"/>
<path id="path98" d="M717.1,6.5v4.2c0,0.6-0.4,1-1,1l0,0h-79.5c-0.6,0-1-0.4-1-1V6.5c0-3.8,3.1-6.9,7-7h67.6
C714-0.5,717.1,2.6,717.1,6.5z M715.1,9.7V6.5c0-2.7-2.2-5-5-5h-67.6c-2.7,0-5,2.2-5,5v3.2H715.1z"/>
<path id="path100" d="M314.3,6.5v4.2c0,0.6-0.4,1-1,1h-79.5c-0.6,0-1-0.4-1-1V6.5c0-3.8,3.1-6.9,7-7h67.6
C311.2-0.5,314.3,2.6,314.3,6.5z M312.3,9.7V6.5c0-2.7-2.2-5-5-5h-67.6c-2.7,0-5,2.2-5,5v3.2H312.3z"/>
<path id="path1144" class="st0" d="M997.9,162.4c-3,26.2-8.5,49.9-21,75.1c-10.9,21.9-25.7,41.1-42.5,57.5
c-33.2,32.3-77.8,53.7-126.7,59c-3.4,0.4-30.5,0.1-72.2,0.3c-158.5,1.1-520.6,0.9-534.4-0.5c-17.7-1.8-36.5-6.1-52.4-12
c-7.5-2.8-15.8-7.1-23.1-10.7C61.8,299.6,21.2,238.8,12,168.6C9.6,150.4,8.7,35.5,11,33.1c0.1-0.1,2.3-0.9,7-1
c11.1-0.4,36-0.4,79.4-1.3c32.7-0.7,76.3,0,132.5-0.1c70.3-0.2,160.2,1.6,274.8,1.6c484.5,0,491.2-1.4,492.4,0.9
c0.2,0.3,1.7,2.5,1.8,5.3c1.1,21.9-0.5,119.2-0.7,120.6L997.9,162.4L997.9,162.4z"/>
<polygon id="polygon80" class="st1" points="470.2,195 470.2,168.3 448.1,181.7 "/>
<polygon id="polygon82" class="st2" points="627.3,181.3 605.6,194.1 605.6,168.5 "/>
<polygon id="polygon84" class="st1" points="538.1,271.6 551.5,249.7 524.7,249.7 "/>
<polygon id="polygon86" class="st3" points="551.6,114.8 524.1,114.8 537.9,89.2 "/>
<path id="path102" d="M139.3,338.3c0,0.3-0.1,0.5-0.3,0.7l-3.4,3.4c-2,2-5.1,2.6-7.8,1.5C50.1,309.1,0.1,231.9,0,146.7l0-51.2
c0-3.8,3.1-6.9,7-7h2.6c0.6,0,1,0.4,1,1v54.2C10.5,228.2,61,304.5,138.7,337.4c0.3,0.1,0.5,0.4,0.6,0.7L139.3,338.3L139.3,338.3z
M2,146.7c0.1,84.4,49.7,160.9,126.7,195.4c1.9,0.8,4.1,0.4,5.5-1.1l2.4-2.4C58.8,305,8.5,228.3,8.6,143.6V90.4H7c-2.7,0-5,2.2-5,5
L2,146.7L2,146.7z"/>
<path id="path104" d="M116.1,60v46.9c0,2.2-1.8,4-4,4h-11.7c-2.2,0-4-1.8-4-4V60c0-2.2,1.8-4,4-4h11.7
C114.3,56,116.1,57.8,116.1,60z M98.5,106.9c0,1.1,0.9,2,2,2h11.7c1.1,0,2-0.9,2-2V60c0-1.1-0.9-2-2-2h-11.7c-1.1,0-2,0.9-2,2
V106.9z"/>
<path id="path106" d="M502.9,181.7c0,21.2-17.2,38.5-38.5,38.5s-38.5-17.2-38.5-38.5s17.2-38.5,38.5-38.5S502.9,160.4,502.9,181.7
L502.9,181.7z M428,181.7c0,20.1,16.3,36.5,36.5,36.5s36.5-16.3,36.5-36.5s-16.3-36.5-36.5-36.5S428,161.5,428,181.7L428,181.7z"/>
<path id="path108" d="M649.8,181.7c0,21.2-17.2,38.5-38.5,38.5s-38.5-17.2-38.5-38.5s17.2-38.5,38.5-38.5S649.8,160.4,649.8,181.7
L649.8,181.7z M574.9,181.7c0,20.1,16.3,36.5,36.5,36.5s36.5-16.3,36.5-36.5s-16.3-36.5-36.5-36.5l0,0
C591.2,145.2,574.9,161.5,574.9,181.7L574.9,181.7z"/>
<path id="path110" d="M576.3,108.2c0,21.2-17.2,38.5-38.5,38.5s-38.5-17.2-38.5-38.5s17.2-38.5,38.5-38.5l0,0
C559.1,69.8,576.3,87,576.3,108.2z M501.4,108.2c0,20.1,16.3,36.5,36.5,36.5s36.5-16.3,36.5-36.5s-16.3-36.5-36.5-36.5l0,0
C517.7,71.8,501.4,88.1,501.4,108.2L501.4,108.2L501.4,108.2z"/>
<path id="path112" d="M576.3,255.1c0,21.2-17.2,38.5-38.5,38.5s-38.5-17.2-38.5-38.5s17.2-38.5,38.5-38.5l0,0
C559.1,216.7,576.3,233.9,576.3,255.1z M501.4,255.1c0,20.1,16.3,36.5,36.5,36.5s36.5-16.3,36.5-36.5s-16.3-36.5-36.5-36.5l0,0
C517.7,218.7,501.4,235,501.4,255.1L501.4,255.1L501.4,255.1z"/>
<path id="path114" d="M753.7,105.5v45c0,5.5-4.4,9.9-9.9,9.9h-45c-5.5,0-9.9-4.4-9.9-9.9v-45c0-5.5,4.4-9.9,9.9-9.9h45
C749.3,95.6,753.7,100.1,753.7,105.5z M690.9,150.5c0,4.4,3.6,7.9,7.9,7.9h45c4.4,0,7.9-3.6,7.9-7.9v-45c0-4.4-3.6-7.9-7.9-7.9h-45
c-4.4,0-7.9,3.6-7.9,7.9V150.5z"/>
<path id="path116" d="M741.7,128c0,11.3-9.2,20.4-20.4,20.4s-20.4-9.2-20.4-20.4s9.2-20.4,20.4-20.4l0,0
C732.6,107.6,741.7,116.7,741.7,128z M702.8,128c0,10.2,8.3,18.4,18.4,18.4s18.4-8.3,18.4-18.4s-8.3-18.4-18.4-18.4
S702.9,117.8,702.8,128z"/>
<path id="path118" d="M260.2,244.8v12.3c0,0.6-0.4,1-1,1l0,0c-39.6-1.7-71.3-33.4-73-73c0-0.3,0.1-0.5,0.3-0.7s0.4-0.3,0.7-0.3
h12.3c3.5,0,6.4,2.6,6.9,6c3.7,24.7,23.1,44.2,47.9,47.9C257.6,238.4,260.2,241.3,260.2,244.8L260.2,244.8z M258.2,256v-11.2
c0-2.5-1.8-4.6-4.3-4.9c-25.6-3.9-45.7-24-49.6-49.6c-0.3-2.5-2.4-4.3-4.9-4.3h-11.2C190.3,223.4,220.8,253.9,258.2,256L258.2,256z
"/>
<path id="path120" d="M338.9,185L338.9,185c-1.7,39.6-33.4,71.3-73,73c-0.6,0-1-0.4-1-1v-12.3c0-3.5,2.6-6.4,6-6.9
c24.7-3.7,44.2-23.1,47.9-47.9c0.5-3.4,3.4-6,6.9-6H338C338.5,184,338.9,184.5,338.9,185L338.9,185z M266.9,256
c37.4-2.2,67.8-32.6,70-70h-11.2c-2.5,0-4.6,1.8-4.9,4.3c-3.9,25.6-24,45.7-49.6,49.6c-2.5,0.3-4.3,2.4-4.3,4.9V256L266.9,256z"/>
<path id="path122" d="M338.9,178.3c0,0.6-0.4,1-1,1h-12.3c-3.5,0-6.4-2.6-6.9-6c-3.7-24.7-23.1-44.2-47.9-47.9
c-3.4-0.5-6-3.4-6-6.9v-12.3c0-0.6,0.4-1,1-1l0,0C305.5,107,337.2,138.7,338.9,178.3L338.9,178.3L338.9,178.3z M266.9,118.6
c0,2.5,1.8,4.6,4.3,4.9c25.6,3.9,45.7,24,49.6,49.6c0.3,2.5,2.4,4.3,4.9,4.3h11.2c-2.2-37.4-32.6-67.8-70-70V118.6z"/>
<path id="path124" d="M260.2,106.3v12.3c0,3.5-2.6,6.4-6,6.9c-24.7,3.7-44.2,23.1-47.9,47.9c-0.5,3.4-3.4,6-6.9,6h-12.3
c-0.3,0-0.5-0.1-0.7-0.3s-0.3-0.5-0.3-0.7c1.7-39.6,33.4-71.3,73-73C259.7,105.3,260.2,105.7,260.2,106.3L260.2,106.3L260.2,106.3z
M188.2,177.3h11.2c2.5,0,4.6-1.8,4.9-4.3c3.9-25.6,24-45.7,49.6-49.6c2.5-0.3,4.3-2.4,4.3-4.9v-11.2
C220.8,109.5,190.3,140,188.2,177.3L188.2,177.3L188.2,177.3z"/>
<path id="path126" d="M339,181.7c0,1.2,0,2.3-0.1,3.4c0,0.5-0.5,0.9-1,0.9h-12.3c-2.5,0-4.6,1.8-4.9,4.3
c-3.9,25.6-24,45.7-49.6,49.6c-2.5,0.3-4.3,2.4-4.3,4.9v12.3c0,0.5-0.4,1-0.9,1c-1.1,0.1-2.2,0.1-3.4,0.1s-2.3,0-3.4-0.1
c-0.5,0-0.9-0.5-0.9-1v-12.3c0-2.5-1.8-4.6-4.3-4.9c-25.6-3.9-45.7-24-49.6-49.6c-0.3-2.5-2.4-4.3-4.9-4.3h-12.3
c-0.5,0-1-0.4-1-0.9c-0.1-2.3-0.1-4.5,0-6.8c0-0.5,0.5-0.9,1-0.9h12.3c2.5,0,4.6-1.8,4.9-4.3c3.9-25.6,24-45.7,49.6-49.6
c2.5-0.3,4.3-2.4,4.3-4.9v-12.3c0-0.5,0.4-1,0.9-1c1.1-0.1,2.2-0.1,3.4-0.1s2.3,0,3.4,0.1c0.5,0,0.9,0.5,0.9,1v12.3
c0,2.5,1.8,4.6,4.3,4.9c25.6,3.9,45.7,24,49.6,49.6c0.3,2.5,2.4,4.3,4.9,4.3h12.3c0.5,0,1,0.4,1,0.9C339,179.4,339,180.5,339,181.7
L339,181.7z M337,184c0.1-1.5,0.1-3.1,0-4.7h-11.3c-3.5,0-6.4-2.6-6.9-6c-3.7-24.7-23.1-44.2-47.9-47.9c-3.4-0.5-6-3.4-6-6.9v-11.3
h-4.6v11.4c0,3.5-2.6,6.4-6,6.9c-24.7,3.7-44.2,23.1-47.9,47.9c-0.5,3.4-3.4,6-6.9,6h-11.3c-0.1,1.5-0.1,3.1,0,4.7h11.3
c3.5,0,6.4,2.6,6.9,6c3.7,24.7,23.1,44.2,47.9,47.9c3.4,0.5,6,3.4,6,6.9v11.3h4.6v-11.3c0-3.5,2.6-6.4,6-6.9
c24.7-3.7,44.2-23.1,47.9-47.9c0.5-3.4,3.4-6,6.9-6L337,184L337,184z"/>
<path id="path179" class="st4" d="M574.3,261.6c-3.2,14.7-12.7,24.9-27.1,29c-3.4,1-6.3,1.2-11.7,0.9c-6.5-0.3-7.8-0.7-13.5-3.5
c-22.9-11.4-27.9-40.7-10-58.6c19.4-19.4,51.9-12,60.8,13.9C574.5,248.5,575.3,257.1,574.3,261.6L574.3,261.6L574.3,261.6z
M538,249c-7.3,0-13.4,0.2-13.4,0.4c0,0.9,13.2,23.3,13.5,23c0.7-0.7,13.1-22.2,13.2-22.8C551.3,249.2,545.4,249,538,249L538,249
L538,249z"/>
<path id="path181" class="st4" d="M500.6,187.1c-0.9,6.9-5,15-10.4,20.5c-14.5,14.6-36.9,14.6-51.4-0.1c-22.9-23-7-62.3,25.4-62.3
c10.4,0,18.8,3.5,26,10.7C498.4,164.3,502.1,175.2,500.6,187.1L500.6,187.1L500.6,187.1z M469.9,168.5
c-2.4,0.9-22.4,12.8-22.4,13.3c0,0.4,9,5.9,19.9,12l3.6,2v-13.9C471,169.7,470.9,168.1,469.9,168.5L469.9,168.5L469.9,168.5z"/>
<path id="path185" class="st4" d="M647,190.4c-3.5,12.9-13.7,23.1-26.6,26.5c-22.7,5.9-45.2-11.6-45.3-35.2
c0-7.4,0.9-11.3,4.5-18.1c8.8-16.9,30.4-23.9,47.9-15.4C643,155.8,651.4,173.9,647,190.4L647,190.4L647,190.4z M616.9,174.2
c-6.3-3.6-11.6-6.5-11.8-6.3s-0.3,6.4-0.1,13.8l0.2,13.5l11.6-6.7c6.4-3.7,11.6-6.9,11.6-7.2C628.4,181.1,623.2,177.8,616.9,174.2
L616.9,174.2z"/>
<path id="path187" class="st4" d="M574.2,111.7c0,0.3-0.3,1.6-0.5,2.9c-2.2,11.2-9.7,20.9-20.1,26.2c-5.6,2.8-12.2,4.2-17.9,3.8
c-14.8-1.1-27.7-11.3-32.5-25.6c-2.9-8.8-2.2-18,2.2-26.7s12.1-15.5,21.3-18.6c15.3-5.3,32.2,0.7,41.6,14.7c4,6,6.3,13.4,6.2,20.2
C574.3,109.9,574.3,111.4,574.2,111.7L574.2,111.7L574.2,111.7z M551.1,112.4c-0.4-0.7-1.6-3-2.8-5.1c-4.8-8.7-9.2-15.6-10-16.2
c-0.4-0.3-0.5-0.3-0.9,0c-1.3,0.9-13.5,21.5-13.5,22.9c0,0.3,0.1,0.6,0.2,0.7c0.5,0.4,4.8,0.6,13.9,0.6c9.1,0,13-0.2,13.5-0.6
C551.9,114.3,551.8,113.8,551.1,112.4L551.1,112.4L551.1,112.4z"/>
<path id="path189" class="st5" d="M739.1,131.6c-0.2,1-1,3-1.6,4.4c-1,2.1-1.6,2.9-3.5,4.8c-1.9,1.9-2.7,2.5-4.8,3.5
c-5.5,2.7-10.6,2.7-16.1,0c-2.1-1-2.8-1.6-4.8-3.5c-3.6-3.6-5.3-7.7-5.3-12.7c0-8.4,5.7-15.7,13.9-17.8c2-0.5,6.2-0.5,8.3,0
c6.3,1.5,11.5,6.3,13.4,12.7C739.4,125.4,739.6,129.2,739.1,131.6L739.1,131.6L739.1,131.6z"/>
<path id="path191" class="st4" d="M751.3,152.4c-0.4,1.6-1.3,3-2.6,4.1c-2.1,1.8-0.9,1.7-27.4,1.7s-25.3,0.1-27.5-1.9
c-0.7-0.6-1.5-1.7-1.9-2.5l-0.7-1.5v-48.3l0.9-1.8c0.6-1.3,1.2-2,2.1-2.7c2.3-1.8,1.3-1.7,27.8-1.6c23.8,0.1,23.9,0.1,25.1,0.6
c1.6,0.7,3.1,2.3,3.9,3.9l0.7,1.3l0,23.8C751.6,145.6,751.5,151.4,751.3,152.4L751.3,152.4L751.3,152.4z M741.6,125.1
c-1.5-10.6-10.7-18.1-21.4-17.5c-3.4,0.2-5.3,0.7-8.1,2c-9.3,4.6-13.7,15.5-10.2,25.3c1,2.8,2.2,4.7,4.3,7c2.8,3,6.3,5.1,10.3,6
c2.2,0.5,7.2,0.5,9.4,0c3.9-0.9,7.4-3,10.2-6c2.1-2.2,3.3-4.2,4.3-6.7C741.7,131.9,742.1,128.5,741.6,125.1L741.6,125.1
L741.6,125.1z"/>
<path id="path1082" class="st6" d="M330.4,183.8c-6,0-6.5,0.1-7.9,0.7c-2.1,1.1-3.4,2.9-3.9,5.6c-2.7,15.1-10.1,27.5-21.9,36.5
c-6.5,5-15.1,8.8-23.2,10.4c-4.3,0.8-5.7,1.4-7,3c-1.5,1.8-1.8,3.3-1.8,10v5.9h-4.2v-6.3c0-6.1,0-6.3-0.9-8
c-1.2-2.4-2.7-3.3-7.3-4.2c-23.2-4.6-41-22.4-45.4-45.6c-0.8-4.2-1.6-5.6-3.8-6.9c-1.5-0.9-1.6-0.9-8.1-1l-6.6-0.1v-4.2h6.3
c6.2,0,6.3,0,8-0.9c2.6-1.3,3.4-2.6,4.4-7.6c3.8-19.4,17-35.1,35.4-42.3c2.7-1,5.9-1.9,12-3.3c2.6-0.6,4.2-1.7,5.2-3.6
c0.6-1.1,0.7-2,0.8-7.9l0.1-6.6h4.2v6.3c0,6.1,0,6.3,0.9,8c1.1,2.2,2.9,3.4,5.9,3.9c23.8,4.1,42.3,22.2,46.8,46
c0.8,4.2,1.7,5.7,4.1,7c1.5,0.8,1.9,0.8,8,0.9l6.4,0.1v4.2L330.4,183.8L330.4,183.8z"/>
<path id="path1084" class="st7" d="M257.3,255.8c-0.5-0.1-1.9-0.3-3.2-0.4c-3.9-0.4-10.1-1.7-14.8-3.3c-24.1-8-42.7-28.1-48.9-52.7
c-0.8-3.1-1.6-7.6-1.9-11.3l-0.2-2h5.9c8.5,0,9.1,0.4,10.4,6.5c3.7,18.4,15.2,33.7,31.9,41.9c5.1,2.6,10.1,4.1,17,5.5
c2.4,0.5,4,1.8,4.4,3.7c0.2,0.7,0.3,3.8,0.3,6.8v5.5L257.3,255.8L257.3,255.8z"/>
<path id="path1086" class="st7" d="M336.4,189.8c-3,27-21.4,50.8-46.9,60.9c-6,2.4-13.4,4.2-18.7,4.7c-1.3,0.1-2.7,0.3-3.1,0.4
l-0.8,0.2l0.1-6.3c0.1-5.8,0.2-6.4,0.8-7.2c1.2-1.6,2.2-2,6.2-2.9c5.8-1.2,9.4-2.4,14.8-5.2c5.7-2.9,9.8-5.7,14.2-9.9
c9.1-8.6,15.2-19.7,17.5-31.7c0.7-3.6,1.2-4.6,2.7-5.8c0.8-0.6,1.4-0.7,7.2-0.8c5.9-0.1,6.3-0.1,6.3,0.5
C336.7,187,336.6,188.4,336.4,189.8L336.4,189.8L336.4,189.8z"/>
<path id="path1088" class="st7" d="M257.7,119.9c-0.9,2.4-1.6,2.8-6.5,3.8c-18.2,3.7-33.5,15.4-41.6,32c-2.4,4.8-3.7,8.6-4.7,13.5
c-1,4.7-1.6,6.2-2.9,7.1c-1.1,0.7-1.4,0.7-7.4,0.7c-3.5,0-6.3-0.1-6.3-0.3s0.2-1.9,0.5-4c1.5-12,5.6-22.9,12.4-32.8
c3-4.4,5.6-7.4,9.9-11.6c9.6-9.3,20.7-15.5,33.5-18.8c3.6-0.9,10.9-2.1,12.9-2.1c0.6,0,0.6,0.3,0.6,5.6
C258.1,116.9,258,119.1,257.7,119.9L257.7,119.9L257.7,119.9z"/>
<path id="path1090" class="st7" d="M330.5,177.3c-5.8-0.1-6.4-0.2-7.2-0.8c-1.6-1.2-2-2.2-2.9-6.3c-4.7-23.5-23.3-41.9-47-46.4
c-3.5-0.7-4.5-1.1-5.6-2.7c-0.6-0.8-0.7-1.4-0.8-7.2l-0.1-6.3l1.7,0.2c10.5,1.2,18,3.3,26.4,7.5c22.6,11.2,38.1,32.9,41.3,58
c0.3,2.1,0.5,3.9,0.5,4S333.9,177.3,330.5,177.3L330.5,177.3L330.5,177.3z"/>
<path id="path1092" class="st4" d="M113.5,108c-0.6,0.5-1.8,0.7-7.2,0.7c-4.5,0-6.7-0.2-7-0.5c-0.4-0.4-0.5-6.4-0.5-24.6
c0-21.3,0.1-24.2,0.7-24.8c0.6-0.5,1.8-0.7,7-0.7c6.1,0,6.4,0,7,1c0.6,0.8,0.7,3.9,0.7,24.6S114,107.4,113.5,108z"/>
<path id="path1094" class="st8" d="M134.2,340.6c-2.8,2.4-3.7,2.2-12.6-2.3c-21.7-10.8-39.6-23.6-56.5-40.5
c-31.4-31.4-52.2-71.6-59.8-115.3c-2.6-15.1-2.7-16.5-2.9-54L2.3,93.7l1.2-1.4c0.9-1,1.7-1.4,3-1.6l1.8-0.2l0.2,33.8
c0.2,36.2,0.3,38.8,2.7,53.3c6.2,38.4,22,72.9,47.3,103.3c5.7,6.8,18.3,19.5,25.2,25.2c10,8.4,21.7,16.5,32.4,22.5
c5.4,3.1,18.7,9.6,19.5,9.6C136.4,338.3,136,339.1,134.2,340.6L134.2,340.6L134.2,340.6z"/>
<path id="path1150" class="st9" d="M616,21.1c-216,0-634,0-526-0.1c108-0.1,614.3-0.3,722.4-0.1c29.2,0,43.2,0.1,45,0.1
C862.4,21.1,773.6,21.1,616,21.1L616,21.1L616,21.1z"/>
<path id="path1152" class="st4" d="M903.2,19c-2.1,1.5-44.2,0.8-417.6,0.8S70.3,20.6,68.2,19.1c-2-1.4-2.2-3.4-2.2-8.5
c0-1.8-0.1-3.8,0.2-5.4c0.3-1.4,2-3.2,2.4-3.5c0.5-0.5,4.3-0.3,16.4-0.4c11.3-0.1,29,0.2,55.3,0.2c44.7,0,61.8-0.6,69.8-0.1
c4.4,0.3,6.1,1.2,8.1,1.9c3.1,1.2,8.2,3.9,10.9,5.6l5,3.2l40.4-0.4c37.6-0.4,39,0.2,41.7-1.6c1.5-1.1,6.1-3,9.3-4.6l5.8-3l7.9-1.3
l19.8,0.1l15.8,0.1l97.9,0.2c93.1,0.2,126.7-1.2,141.1,0.3c4.2,0.4,6.5,1.6,8.5,2.3c2.7,0.9,4.9,2.1,7.8,3.9l6.1,3.7l15.6-0.4
l25.1-0.1c26.5-0.1,40.4-0.6,40.9-1.3c0.4-0.6,4-2.7,8.5-4.3l9-3.3l9-1.1l15.9,0.2l57.9,0.2c61.9,0.2,84.5-0.4,85.5,0.5
c0.9,0.8,2.4,4.5,2.4,8.4C905.8,15.4,905.1,17.6,903.2,19L903.2,19L903.2,19z"/>
<path id="path1154" class="st4" d="M464.9,30.8l-5.2,0l-387-0.4v-8.5h392.2l393-1.1l0.2,5.3l-0.2,4.9L464.9,30.8L464.9,30.8z"/>
<path id="path1156" class="st0" d="M273.3,9.7h-38.6v-3c0-1.7,0.9-3.6,2.1-4.3c2.8-1.7,70.3-1.7,73.1,0c1.2,0.7,2.1,2.6,2.1,4.3v3
H273.3L273.3,9.7z"/>
<path id="path1158" class="st0" d="M676.2,9.7h-38.7v-3c0-1.8,0.9-3.6,2.1-4.3c2.8-1.7,70.3-1.7,73.2,0c1.2,0.7,2.1,2.6,2.1,4.3v3
H676.2L676.2,9.7z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,341 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 27.9.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 1000 1000.2" style="enable-background:new 0 0 1000 1000.2;" xml:space="preserve">
<style type="text/css">
.st0{fill:#00BBDB;stroke:#000000;}
.st1{fill:#333333;stroke:#000000;stroke-width:0.93;}
.st2{fill:#333333;stroke:#000000;stroke-width:0.96;}
.st3{fill:#333333;stroke:#000000;stroke-width:0.85;}
.st4{fill:#1A1A1A;stroke:#000000;}
.st5{fill:#333333;stroke:#000000;}
.st6{fill:#1A1A1A;stroke:#1A1A1A;}
.st7{fill:#1A1A1A;stroke:#4D4D4D;}
.st8{stroke:#4D4D4D;}
.st9{stroke:#000000;}
.st10{opacity:0.1;}
.st11{fill:#FF5F55;}
.st12{fill:#FF5F53;}
.st13{fill:#FFFFFF;}
.st14{fill:#999595;stroke:#000000;stroke-width:2.39;stroke-linecap:round;stroke-linejoin:round;}
.st15{fill:#3A3D40;stroke:#000000;stroke-width:2.73;stroke-linecap:round;stroke-linejoin:round;}
</style>
<g id="layer1">
<g id="g344">
<path id="path66-7" d="M349.1,906.6h-7.9c-3.6,0-6.4-2.9-6.5-6.5V71.2c0-3.6,2.9-6.4,6.5-6.5h7.9c3.6,0,6.4,2.9,6.5,6.5V207
c0,4.9-1.2,9.6-3.4,14l-6.7,13v79.2l6.7,13c2.2,4.3,3.4,9.1,3.4,13.9v269.7c0,4.9-1.2,9.6-3.4,14l-6.7,13V716l6.7,13
c2.2,4.3,3.4,9.1,3.4,13.9v157.2C355.6,903.7,352.7,906.6,349.1,906.6L349.1,906.6L349.1,906.6z M341.2,65.7c-3,0-5.5,2.4-5.5,5.5
v828.9c0,3,2.4,5.5,5.5,5.5h7.9c3,0,5.5-2.4,5.5-5.5V742.9c0-4.7-1.1-9.3-3.3-13.5l-6.8-13.1c0-0.1-0.1-0.2-0.1-0.2v-79.5
c0-0.1,0-0.2,0.1-0.2l6.8-13.1c2.2-4.2,3.3-8.8,3.3-13.5V340.1c0-4.7-1.1-9.3-3.3-13.5l-6.8-13.1c0-0.1-0.1-0.2-0.1-0.2v-79.5
c0-0.1,0-0.2,0.1-0.2l6.8-13.1c2.2-4.2,3.3-8.8,3.3-13.5V71.2c0-3-2.4-5.5-5.5-5.5L341.2,65.7L341.2,65.7z"/>
<path id="path68-5" d="M335.3,858.9h-11.2c-0.3,0-0.5-0.2-0.5-0.5V72c0-0.3,0.2-0.5,0.5-0.5h11.2c0.3,0,0.5,0.2,0.5,0.5v786.4
C335.8,858.7,335.6,858.9,335.3,858.9z M324.6,857.9h10.2V72.5h-10.2V857.9z"/>
<path id="path70-3" d="M318.1,1000H211.9C94.9,1000,0,905.2,0,788.1l0,0V220.9C0,104.1,95.1,9.1,211.9,9.1h106.2
c3.6,0,6.5,2.9,6.5,6.5v978C324.6,997.1,321.7,1000,318.1,1000L318.1,1000L318.1,1000z M211.9,10.1C95.6,10.1,1,104.7,1,220.9
v567.2C1,904.4,95.6,999,211.9,999h106.2c3,0,5.5-2.4,5.5-5.5v-978c0-3-2.4-5.5-5.5-5.5H211.9L211.9,10.1L211.9,10.1z"/>
<path id="path98" d="M349.1,717.1h-4.2c-0.6,0-1-0.4-1-1l0,0v-79.5c0-0.6,0.4-1,1-1h4.2c3.8,0,6.9,3.1,7,7v67.6
C356.1,714,353,717.1,349.1,717.1z M345.9,715.1h3.2c2.7,0,5-2.2,5-5v-67.6c0-2.7-2.2-5-5-5h-3.2V715.1z"/>
<path id="path100" d="M349.1,314.3h-4.2c-0.6,0-1-0.4-1-1v-79.5c0-0.6,0.4-1,1-1h4.2c3.8,0,6.9,3.1,7,7v67.6
C356.1,311.2,353,314.3,349.1,314.3z M345.9,312.3h3.2c2.7,0,5-2.2,5-5v-67.6c0-2.7-2.2-5-5-5h-3.2V312.3z"/>
<path id="path1144" class="st0" d="M193.2,997.9c-26.2-3-49.9-8.5-75.1-21C96.2,966,77,951.2,60.7,934.4
C28.4,901.1,7,856.6,1.7,807.7c-0.4-3.4-0.1-30.5-0.3-72.2C0.3,576.9,0.4,214.9,1.8,201c1.8-17.7,6.1-36.5,12-52.4
c2.8-7.5,7.1-15.8,10.7-23.1C55.9,61.8,116.8,21.2,187,12c18.2-2.4,133.1-3.3,135.5-0.9c0.1,0.1,0.9,2.3,1,7
c0.4,11.1,0.4,36,1.3,79.4c0.7,32.7,0,76.3,0.1,132.5c0.2,70.3-1.6,160.2-1.6,274.8c0,484.5,1.4,491.2-0.9,492.4
c-0.3,0.2-2.5,1.7-5.3,1.8c-21.9,1.1-119.2-0.5-120.6-0.7L193.2,997.9L193.2,997.9z"/>
<polygon id="polygon80" class="st1" points="173.9,448.1 160.6,470.2 187.3,470.2 "/>
<polygon id="polygon82" class="st2" points="187.1,605.6 174.3,627.3 161.5,605.6 "/>
<polygon id="polygon84" class="st1" points="105.9,524.7 84,538.1 105.9,551.5 "/>
<polygon id="polygon86" class="st3" points="266.4,537.9 240.8,551.6 240.8,524.1 "/>
<path id="path102" d="M17.3,139.3c-0.3,0-0.5-0.1-0.7-0.3l-3.4-3.4c-2-2-2.6-5.1-1.5-7.8C46.5,50.1,123.7,0.1,208.9,0h51.2
c3.8,0,6.9,3.1,7,7v2.6c0,0.6-0.4,1-1,1h-54.2C127.4,10.5,51.1,61,18.2,138.7c-0.1,0.3-0.4,0.5-0.7,0.6L17.3,139.3L17.3,139.3z
M208.9,2C124.5,2.1,48,51.7,13.5,128.7c-0.8,1.9-0.4,4.1,1.1,5.5l2.4,2.4C50.6,58.8,127.3,8.5,212,8.6h53.2V7c0-2.7-2.2-5-5-5
L208.9,2L208.9,2z"/>
<path id="path104" d="M295.6,116.1h-46.9c-2.2,0-4-1.8-4-4v-11.7c0-2.2,1.8-4,4-4h46.9c2.2,0,4,1.8,4,4v11.7
C299.6,114.3,297.8,116.1,295.6,116.1z M248.7,98.5c-1.1,0-2,0.9-2,2v11.7c0,1.1,0.9,2,2,2h46.9c1.1,0,2-0.9,2-2v-11.7
c0-1.1-0.9-2-2-2H248.7z"/>
<path id="path106" d="M173.9,502.9c-21.2,0-38.5-17.2-38.5-38.5s17.2-38.5,38.5-38.5s38.5,17.2,38.5,38.5S195.2,502.9,173.9,502.9
L173.9,502.9z M173.9,428c-20.1,0-36.5,16.3-36.5,36.5s16.3,36.5,36.5,36.5s36.5-16.3,36.5-36.5S194.1,428,173.9,428L173.9,428z"
/>
<path id="path108" d="M173.9,649.8c-21.2,0-38.5-17.2-38.5-38.5s17.2-38.5,38.5-38.5s38.5,17.2,38.5,38.5S195.2,649.8,173.9,649.8
L173.9,649.8z M173.9,574.9c-20.1,0-36.5,16.3-36.5,36.5s16.3,36.5,36.5,36.5s36.5-16.3,36.5-36.5l0,0
C210.4,591.2,194.1,574.9,173.9,574.9L173.9,574.9z"/>
<path id="path110" d="M247.4,576.3c-21.2,0-38.5-17.2-38.5-38.5s17.2-38.5,38.5-38.5s38.5,17.2,38.5,38.5l0,0
C285.8,559.1,268.6,576.3,247.4,576.3z M247.4,501.4c-20.1,0-36.5,16.3-36.5,36.5s16.3,36.5,36.5,36.5s36.5-16.3,36.5-36.5l0,0
C283.8,517.7,267.5,501.4,247.4,501.4L247.4,501.4L247.4,501.4z"/>
<path id="path112" d="M100.5,576.3c-21.2,0-38.5-17.2-38.5-38.5s17.2-38.5,38.5-38.5s38.5,17.2,38.5,38.5l0,0
C138.9,559.1,121.7,576.3,100.5,576.3z M100.5,501.4c-20.1,0-36.5,16.3-36.5,36.5s16.3,36.5,36.5,36.5s36.5-16.3,36.5-36.5l0,0
C136.9,517.7,120.6,501.4,100.5,501.4L100.5,501.4L100.5,501.4z"/>
<path id="path114" d="M250.1,753.7h-45c-5.5,0-9.9-4.4-9.9-9.9v-45c0-5.5,4.4-9.9,9.9-9.9h45c5.5,0,9.9,4.4,9.9,9.9v45
C260,749.3,255.5,753.7,250.1,753.7z M205.1,690.9c-4.4,0-7.9,3.6-7.9,7.9v45c0,4.4,3.6,7.9,7.9,7.9h45c4.4,0,7.9-3.6,7.9-7.9v-45
c0-4.4-3.6-7.9-7.9-7.9H205.1z"/>
<path id="path116" d="M227.6,741.7c-11.3,0-20.4-9.2-20.4-20.4s9.2-20.4,20.4-20.4s20.4,9.2,20.4,20.4l0,0
C248,732.6,238.9,741.7,227.6,741.7z M227.6,702.8c-10.2,0-18.4,8.3-18.4,18.4s8.3,18.4,18.4,18.4s18.4-8.3,18.4-18.4
S237.8,702.9,227.6,702.8z"/>
<path id="path118" d="M110.8,260.2H98.5c-0.6,0-1-0.4-1-1l0,0c1.7-39.6,33.4-71.3,73-73c0.3,0,0.5,0.1,0.7,0.3s0.3,0.4,0.3,0.7
v12.3c0,3.5-2.6,6.4-6,6.9c-24.7,3.7-44.2,23.1-47.9,47.9C117.2,257.6,114.3,260.2,110.8,260.2L110.8,260.2z M99.6,258.2h11.2
c2.5,0,4.6-1.8,4.9-4.3c3.9-25.6,24-45.7,49.6-49.6c2.5-0.3,4.3-2.4,4.3-4.9v-11.2C132.2,190.3,101.7,220.8,99.6,258.2L99.6,258.2
z"/>
<path id="path120" d="M170.6,338.9L170.6,338.9c-39.6-1.7-71.3-33.4-73-73c0-0.6,0.4-1,1-1h12.3c3.5,0,6.4,2.6,6.9,6
c3.7,24.7,23.1,44.2,47.9,47.9c3.4,0.5,6,3.4,6,6.9V338C171.6,338.5,171.1,338.9,170.6,338.9L170.6,338.9z M99.6,266.9
c2.2,37.4,32.6,67.8,70,70v-11.2c0-2.5-1.8-4.6-4.3-4.9c-25.6-3.9-45.7-24-49.6-49.6c-0.3-2.5-2.4-4.3-4.9-4.3H99.6L99.6,266.9z"
/>
<path id="path122" d="M177.3,338.9c-0.6,0-1-0.4-1-1v-12.3c0-3.5,2.6-6.4,6-6.9c24.7-3.7,44.2-23.1,47.9-47.9c0.5-3.4,3.4-6,6.9-6
h12.3c0.6,0,1,0.4,1,1l0,0C248.6,305.5,216.9,337.2,177.3,338.9L177.3,338.9L177.3,338.9z M237,266.9c-2.5,0-4.6,1.8-4.9,4.3
c-3.9,25.6-24,45.7-49.6,49.6c-2.5,0.3-4.3,2.4-4.3,4.9v11.2c37.4-2.2,67.8-32.6,70-70H237z"/>
<path id="path124" d="M249.3,260.2H237c-3.5,0-6.4-2.6-6.9-6c-3.7-24.7-23.1-44.2-47.9-47.9c-3.4-0.5-6-3.4-6-6.9v-12.3
c0-0.3,0.1-0.5,0.3-0.7s0.5-0.3,0.7-0.3c39.6,1.7,71.3,33.4,73,73C250.3,259.7,249.9,260.2,249.3,260.2L249.3,260.2L249.3,260.2z
M178.3,188.2v11.2c0,2.5,1.8,4.6,4.3,4.9c25.6,3.9,45.7,24,49.6,49.6c0.3,2.5,2.4,4.3,4.9,4.3h11.2
C246.1,220.8,215.6,190.3,178.3,188.2L178.3,188.2L178.3,188.2z"/>
<path id="path126" d="M173.9,339c-1.2,0-2.3,0-3.4-0.1c-0.5,0-0.9-0.5-0.9-1v-12.3c0-2.5-1.8-4.6-4.3-4.9
c-25.6-3.9-45.7-24-49.6-49.6c-0.3-2.5-2.4-4.3-4.9-4.3H98.5c-0.5,0-1-0.4-1-0.9c-0.1-1.1-0.1-2.2-0.1-3.4s0-2.3,0.1-3.4
c0-0.5,0.5-0.9,1-0.9h12.3c2.5,0,4.6-1.8,4.9-4.3c3.9-25.6,24-45.7,49.6-49.6c2.5-0.3,4.3-2.4,4.3-4.9v-12.3c0-0.5,0.4-1,0.9-1
c2.3-0.1,4.5-0.1,6.8,0c0.5,0,0.9,0.5,0.9,1v12.3c0,2.5,1.8,4.6,4.3,4.9c25.6,3.9,45.7,24,49.6,49.6c0.3,2.5,2.4,4.3,4.9,4.3h12.3
c0.5,0,1,0.4,1,0.9c0.1,1.1,0.1,2.2,0.1,3.4s0,2.3-0.1,3.4c0,0.5-0.5,0.9-1,0.9H237c-2.5,0-4.6,1.8-4.9,4.3
c-3.9,25.6-24,45.7-49.6,49.6c-2.5,0.3-4.3,2.4-4.3,4.9v12.3c0,0.5-0.4,1-0.9,1C176.2,339,175.1,339,173.9,339L173.9,339z
M171.6,337c1.5,0.1,3.1,0.1,4.7,0v-11.3c0-3.5,2.6-6.4,6-6.9c24.7-3.7,44.2-23.1,47.9-47.9c0.5-3.4,3.4-6,6.9-6h11.3v-4.6H237
c-3.5,0-6.4-2.6-6.9-6c-3.7-24.7-23.1-44.2-47.9-47.9c-3.4-0.5-6-3.4-6-6.9v-11.3c-1.5-0.1-3.1-0.1-4.7,0v11.3
c0,3.5-2.6,6.4-6,6.9c-24.7,3.7-44.2,23.1-47.9,47.9c-0.5,3.4-3.4,6-6.9,6H99.4v4.6h11.3c3.5,0,6.4,2.6,6.9,6
c3.7,24.7,23.1,44.2,47.9,47.9c3.4,0.5,6,3.4,6,6.9L171.6,337L171.6,337z"/>
<path id="path179" class="st4" d="M94,574.3c-14.7-3.2-24.9-12.7-29-27.1c-1-3.4-1.2-6.3-0.9-11.7c0.3-6.5,0.7-7.8,3.5-13.5
c11.4-22.9,40.7-27.9,58.6-10c19.4,19.4,12,51.9-13.9,60.8C107.1,574.5,98.5,575.3,94,574.3L94,574.3L94,574.3z M106.6,538
c0-7.3-0.2-13.4-0.4-13.4c-0.9,0-23.3,13.2-23,13.5c0.7,0.7,22.2,13.1,22.8,13.2C106.4,551.3,106.6,545.4,106.6,538L106.6,538
L106.6,538z"/>
<path id="path181" class="st4" d="M168.5,500.6c-6.9-0.9-15-5-20.5-10.4c-14.6-14.5-14.6-36.9,0.1-51.4c23-22.9,62.3-7,62.3,25.4
c0,10.4-3.5,18.8-10.7,26C191.3,498.4,180.4,502.1,168.5,500.6L168.5,500.6L168.5,500.6z M187.1,469.9
c-0.9-2.4-12.8-22.4-13.3-22.4c-0.4,0-5.9,9-12,19.9l-2,3.6h13.9C185.9,471,187.5,470.9,187.1,469.9L187.1,469.9L187.1,469.9z"/>
<path id="path185" class="st4" d="M165.2,647c-12.9-3.5-23.1-13.7-26.5-26.6c-5.9-22.7,11.6-45.2,35.2-45.3
c7.4,0,11.3,0.9,18.1,4.5c16.9,8.8,23.9,30.4,15.4,47.9C199.8,643,181.7,651.4,165.2,647L165.2,647L165.2,647z M181.4,616.9
c3.6-6.3,6.5-11.6,6.3-11.8s-6.4-0.3-13.8-0.1l-13.5,0.2l6.7,11.6c3.7,6.4,6.9,11.6,7.2,11.6C174.5,628.4,177.8,623.2,181.4,616.9
L181.4,616.9z"/>
<path id="path187" class="st4" d="M243.9,574.2c-0.3,0-1.6-0.3-2.9-0.5c-11.2-2.2-20.9-9.7-26.2-20.1c-2.8-5.6-4.2-12.2-3.8-17.9
c1.1-14.8,11.3-27.7,25.6-32.5c8.8-2.9,18-2.2,26.7,2.2s15.5,12.1,18.6,21.3c5.3,15.3-0.7,32.2-14.7,41.6c-6,4-13.4,6.3-20.2,6.2
C245.7,574.3,244.2,574.3,243.9,574.2L243.9,574.2L243.9,574.2z M243.2,551.1c0.7-0.4,3-1.6,5.1-2.8c8.7-4.8,15.6-9.2,16.2-10
c0.3-0.4,0.3-0.5,0-0.9c-0.9-1.3-21.5-13.5-22.9-13.5c-0.3,0-0.6,0.1-0.7,0.2c-0.4,0.5-0.6,4.8-0.6,13.9c0,9.1,0.2,13,0.6,13.5
C241.3,551.9,241.8,551.8,243.2,551.1L243.2,551.1L243.2,551.1z"/>
<path id="path189" class="st5" d="M224,739.1c-1-0.2-3-1-4.4-1.6c-2.1-1-2.9-1.6-4.8-3.5c-1.9-1.9-2.5-2.7-3.5-4.8
c-2.7-5.5-2.7-10.6,0-16.1c1-2.1,1.6-2.8,3.5-4.8c3.6-3.6,7.7-5.3,12.7-5.3c8.4,0,15.7,5.7,17.8,13.9c0.5,2,0.5,6.2,0,8.3
c-1.5,6.3-6.3,11.5-12.7,13.4C230.2,739.4,226.4,739.6,224,739.1L224,739.1L224,739.1z"/>
<path id="path191" class="st4" d="M203.2,751.3c-1.6-0.4-3-1.3-4.1-2.6c-1.8-2.1-1.7-0.9-1.7-27.4s-0.1-25.3,1.9-27.5
c0.6-0.7,1.7-1.5,2.5-1.9l1.5-0.7h48.3l1.8,0.9c1.3,0.6,2,1.2,2.7,2.1c1.8,2.3,1.7,1.3,1.6,27.8c-0.1,23.8-0.1,23.9-0.6,25.1
c-0.7,1.6-2.3,3.1-3.9,3.9l-1.3,0.7l-23.8,0C210,751.6,204.2,751.5,203.2,751.3L203.2,751.3L203.2,751.3z M230.5,741.6
c10.6-1.5,18.1-10.7,17.5-21.4c-0.2-3.4-0.7-5.3-2-8.1c-4.6-9.3-15.5-13.7-25.3-10.2c-2.8,1-4.7,2.2-7,4.3c-3,2.8-5.1,6.3-6,10.3
c-0.5,2.2-0.5,7.2,0,9.4c0.9,3.9,3,7.4,6,10.2c2.2,2.1,4.2,3.3,6.7,4.3C223.7,741.7,227.1,742.1,230.5,741.6L230.5,741.6
L230.5,741.6z"/>
<path id="path1082" class="st6" d="M171.8,330.4c0-6-0.1-6.5-0.7-7.9c-1.1-2.1-2.9-3.4-5.6-3.9c-15.1-2.7-27.5-10.1-36.5-21.9
c-5-6.5-8.8-15.1-10.4-23.2c-0.8-4.3-1.4-5.7-3-7c-1.8-1.5-3.3-1.8-10-1.8h-5.9v-4.2h6.3c6.1,0,6.3,0,8-0.9
c2.4-1.2,3.3-2.7,4.2-7.3c4.6-23.2,22.4-41,45.6-45.4c4.2-0.8,5.6-1.6,6.9-3.8c0.9-1.5,0.9-1.6,1-8.1l0.1-6.6h4.2v6.3
c0,6.2,0,6.3,0.9,8c1.3,2.6,2.6,3.4,7.6,4.4c19.4,3.8,35.1,17,42.3,35.4c1,2.7,1.9,5.9,3.3,12c0.6,2.6,1.7,4.2,3.6,5.2
c1.1,0.6,2,0.7,7.9,0.8l6.6,0.1v4.2h-6.3c-6.1,0-6.3,0-8,0.9c-2.2,1.1-3.4,2.9-3.9,5.9c-4.1,23.8-22.2,42.3-46,46.8
c-4.2,0.8-5.7,1.7-7,4.1c-0.8,1.5-0.8,1.9-0.9,8l-0.1,6.4h-4.2L171.8,330.4L171.8,330.4z"/>
<path id="path1084" class="st7" d="M99.8,257.3c0.1-0.5,0.3-1.9,0.4-3.2c0.4-3.9,1.7-10.1,3.3-14.8c8-24.1,28.1-42.7,52.7-48.9
c3.1-0.8,7.6-1.6,11.3-1.9l2-0.2v5.9c0,8.5-0.4,9.1-6.5,10.4c-18.4,3.7-33.7,15.2-41.9,31.9c-2.6,5.1-4.1,10.1-5.5,17
c-0.5,2.4-1.8,4-3.7,4.4c-0.7,0.2-3.8,0.3-6.8,0.3h-5.5L99.8,257.3L99.8,257.3z"/>
<path id="path1086" class="st7" d="M165.8,336.4c-27-3-50.8-21.4-60.9-46.9c-2.4-6-4.2-13.4-4.7-18.7c-0.1-1.3-0.3-2.7-0.4-3.1
l-0.2-0.8l6.3,0.1c5.8,0.1,6.4,0.2,7.2,0.8c1.6,1.2,2,2.2,2.9,6.2c1.2,5.8,2.4,9.4,5.2,14.8c2.9,5.7,5.7,9.8,9.9,14.2
c8.6,9.1,19.7,15.2,31.7,17.5c3.6,0.7,4.6,1.2,5.8,2.7c0.6,0.8,0.7,1.4,0.8,7.2c0.1,5.9,0.1,6.3-0.5,6.3
C168.6,336.7,167.2,336.6,165.8,336.4L165.8,336.4L165.8,336.4z"/>
<path id="path1088" class="st7" d="M235.7,257.7c-2.4-0.9-2.8-1.6-3.8-6.5c-3.7-18.2-15.4-33.5-32-41.6c-4.8-2.4-8.6-3.7-13.5-4.7
c-4.7-1-6.2-1.6-7.1-2.9c-0.7-1.1-0.7-1.4-0.7-7.4c0-3.5,0.1-6.3,0.3-6.3s1.9,0.2,4,0.5c12,1.5,22.9,5.6,32.8,12.4
c4.4,3,7.4,5.6,11.6,9.9c9.3,9.6,15.5,20.7,18.8,33.5c0.9,3.6,2.1,10.9,2.1,12.9c0,0.6-0.3,0.6-5.6,0.6
C238.7,258.1,236.5,258,235.7,257.7L235.7,257.7L235.7,257.7z"/>
<path id="path1090" class="st7" d="M178.3,330.5c0.1-5.8,0.2-6.4,0.8-7.2c1.2-1.6,2.2-2,6.3-2.9c23.5-4.7,41.9-23.3,46.4-47
c0.7-3.5,1.1-4.5,2.7-5.6c0.8-0.6,1.4-0.7,7.2-0.8l6.3-0.1l-0.2,1.7c-1.2,10.5-3.3,18-7.5,26.4c-11.2,22.6-32.9,38.1-58,41.3
c-2.1,0.3-3.9,0.5-4,0.5S178.3,333.9,178.3,330.5L178.3,330.5L178.3,330.5z"/>
<path id="path1092" class="st4" d="M247.6,113.5c-0.5-0.6-0.7-1.8-0.7-7.2c0-4.5,0.2-6.7,0.5-7c0.4-0.4,6.4-0.5,24.6-0.5
c21.3,0,24.2,0.1,24.8,0.7c0.5,0.6,0.7,1.8,0.7,7c0,6.1,0,6.4-1,7c-0.8,0.6-3.9,0.7-24.6,0.7S248.2,114,247.6,113.5z"/>
<path id="path1094" class="st8" d="M15,134.2c-2.3-2.8-2.2-3.7,2.3-12.6C28,99.9,40.9,82,57.8,65.1C89.1,33.7,129.4,12.8,173,5.3
c15.1-2.6,16.5-2.7,54-2.9l34.8-0.2l1.4,1.2c1,0.9,1.4,1.7,1.6,3l0.2,1.8l-33.8,0.2C195,8.6,192.5,8.8,178,11.1
c-38.4,6.2-72.9,22-103.3,47.3c-6.8,5.7-19.5,18.3-25.2,25.2c-8.4,10-16.5,21.7-22.5,32.4c-3.1,5.4-9.6,18.7-9.6,19.5
C17.3,136.4,16.5,136,15,134.2L15,134.2L15,134.2z"/>
<path id="path1150" class="st9" d="M334.4,616c0-216,0-634,0.1-526c0.1,108,0.3,614.3,0.1,722.4c0,29.2-0.1,43.2-0.1,45
C334.5,862.4,334.5,773.6,334.4,616L334.4,616L334.4,616z"/>
<path id="path1152" class="st4" d="M336.6,903.2c-1.5-2.1-0.8-44.2-0.8-417.6S335,70.3,336.5,68.2c1.4-2,3.4-2.2,8.5-2.2
c1.8,0,3.8-0.1,5.4,0.2c1.4,0.3,3.2,2,3.5,2.4c0.5,0.5,0.3,4.3,0.4,16.4c0.1,11.3-0.2,29-0.2,55.3c0,44.7,0.6,61.8,0.1,69.8
c-0.3,4.4-1.2,6.1-1.9,8.1c-1.2,3.1-3.9,8.2-5.6,10.9l-3.2,5l0.4,40.4c0.4,37.6-0.2,39,1.6,41.7c1.1,1.5,3,6.1,4.6,9.3l3,5.8
l1.3,7.9l-0.1,19.8l-0.1,15.8l-0.2,97.9c-0.2,93.1,1.2,126.7-0.3,141.1c-0.4,4.2-1.6,6.5-2.3,8.5c-0.9,2.7-2.1,4.9-3.9,7.8
l-3.7,6.1l0.4,15.6l0.1,25.1c0.1,26.5,0.6,40.4,1.3,40.9c0.6,0.4,2.7,4,4.3,8.5l3.3,9l1.1,9l-0.2,15.9l-0.2,57.9
c-0.2,61.9,0.4,84.5-0.5,85.5c-0.8,0.9-4.5,2.4-8.4,2.4C340.2,905.8,338,905.1,336.6,903.2L336.6,903.2L336.6,903.2z"/>
<path id="path1154" class="st4" d="M325.2,464.9V72.7h8.5v392.2l1.1,393l-5.3,0.2l-4.9-0.2L325.2,464.9L325.2,464.9z"/>
<path id="path1156" class="st0" d="M345.9,273.3v-38.6h3c1.7,0,3.6,0.9,4.3,2.1c1.7,2.8,1.7,70.3,0,73.1c-0.7,1.2-2.6,2.1-4.3,2.1
h-3V273.3L345.9,273.3z"/>
<path id="path1158" class="st0" d="M345.9,676.2v-38.7h3c1.8,0,3.6,0.9,4.3,2.1c1.7,2.8,1.7,70.3,0,73.2c-0.7,1.2-2.6,2.1-4.3,2.1
h-3V676.2L345.9,676.2z"/>
</g>
<g id="g315">
<g id="g64" class="st10">
<path id="path36" class="st11" d="M654.6,233.9v79.5h-4.2c-3.3,0-6-2.7-6-6v-67.6c0-3.3,2.7-6,6-6h4.2V233.9z"/>
<path id="path38" class="st11" d="M654.6,636.6v79.5h-4.2c-3.3,0-6-2.7-6-6v-67.6c0-3.3,2.7-6,6-6L654.6,636.6L654.6,636.6z"/>
<path id="path40" class="st11" d="M985.7,134.9l-3.4,3.4C949.1,60.3,872.5,9.6,787.6,9.6h-54.2V7c0-3.3,2.7-6,6-6h51.2
C875.4,1,952.3,50.8,987,128.2C988,130.5,987.5,133.1,985.7,134.9L985.7,134.9L985.7,134.9z"/>
<path id="path42" class="st11" d="M736.2,94.5V82.8c0-1.6-1.3-3-3-3h-11.7c-1.6,0-3,1.3-3,3l0,0v11.7c0,1.6-1.3,3-3,3h-11.7
c-1.6,0-3,1.3-3,3l0,0v11.7c0,1.6,1.3,3,3,3h11.7c1.6,0,3,1.3,3,3l0,0v11.7c0,1.6,1.3,3,3,3h11.7c1.6,0,3-1.3,3-3l0,0v-11.7
c0-1.6,1.3-3,3-3h11.7c1.6,0,3-1.3,3-3l0,0v-11.7c0-1.6-1.3-3-3-3h-11.7C737.5,97.5,736.2,96.1,736.2,94.5L736.2,94.5z"/>
<circle id="circle44" class="st11" cx="825.6" cy="333.9" r="37.5"/>
<circle id="circle46" class="st11" cx="825.6" cy="187.1" r="37.5"/>
<circle id="circle48" class="st11" cx="899" cy="260.5" r="37.5"/>
<circle id="circle50" class="st11" cx="752.2" cy="260.5" r="37.5"/>
<circle id="circle52" class="st11" cx="771.7" cy="721.3" r="27.9"/>
<path id="path54" class="st11" d="M822.3,460.3v12.3c0,3-2.2,5.5-5.2,5.9c-25.2,3.7-45,23.5-48.7,48.7c-0.4,2.9-2.9,5.1-5.9,5.2
h-12.3C751.9,493.3,783.2,462,822.3,460.3L822.3,460.3L822.3,460.3z"/>
<path id="path56" class="st11" d="M822.3,598.8v12.3c-39.1-1.7-70.3-33-72.1-72h12.3c3,0,5.5,2.2,5.9,5.2
c3.7,25.2,23.5,45,48.7,48.7C820.1,593.3,822.3,595.8,822.3,598.8L822.3,598.8L822.3,598.8z"/>
<path id="path58" class="st11" d="M901,539c-1.7,39.1-33,70.3-72.1,72v-12.3c0-3,2.2-5.5,5.2-5.9c25.2-3.7,45-23.5,48.7-48.7
c0.4-2.9,2.9-5.1,5.9-5.2L901,539L901,539z"/>
<path id="path60" class="st11" d="M901,532.4h-12.3c-3,0-5.5-2.2-5.9-5.2c-3.7-25.2-23.5-45-48.7-48.7c-2.9-0.4-5.1-2.9-5.2-5.9
v-12.3C868,462,899.3,493.3,901,532.4L901,532.4L901,532.4z"/>
<path id="path62" class="st11" d="M901.1,535.7c0,1.1,0,2.2-0.1,3.3h-12.3c-3,0-5.5,2.2-5.9,5.2c-3.7,25.2-23.5,45-48.7,48.7
c-2.9,0.4-5.1,2.9-5.2,5.9v12.3c-1.1,0.1-2.2,0.1-3.3,0.1s-2.2,0-3.3-0.1v-12.3c0-3-2.2-5.5-5.2-5.9c-25.2-3.7-45-23.5-48.7-48.7
c-0.4-2.9-2.9-5.1-5.9-5.2h-12.3c-0.1-1.1-0.1-2.2-0.1-3.3s0-2.2,0.1-3.3h12.3c3,0,5.5-2.2,5.9-5.2c3.7-25.2,23.5-45,48.7-48.7
c2.9-0.4,5.1-2.9,5.2-5.9v-12.3c1.1-0.1,2.2-0.1,3.3-0.1s2.2,0,3.3,0.1v12.3c0,3,2.2,5.5,5.2,5.9c25.2,3.7,45,23.5,48.7,48.7
c0.4,2.9,2.9,5.1,5.9,5.2H901C901,533.5,901.1,534.6,901.1,535.7L901.1,535.7z"/>
</g>
<path id="path72" d="M658.3,906.6h-7.9c-3.6,0-6.4-2.9-6.5-6.5V742.9c0-4.9,1.2-9.6,3.4-13.9l6.7-13v-79.2l-6.7-13
c-2.2-4.3-3.4-9.1-3.4-14V340.1c0-4.9,1.2-9.6,3.4-13.9l6.7-13V234l-6.7-13c-2.2-4.3-3.4-9.1-3.4-14V71.2c0-3.6,2.9-6.4,6.5-6.5
h7.9c3.6,0,6.4,2.9,6.5,6.5c1.1,276.3,1.2,552.6,0,828.9C664.7,903.7,661.8,906.6,658.3,906.6L658.3,906.6L658.3,906.6z
M650.4,65.7c-3,0-5.5,2.4-5.5,5.5V207c0,4.7,1.1,9.3,3.3,13.5l6.8,13.1c0,0.1,0.1,0.1,0.1,0.2v79.5c0,0.1,0,0.2-0.1,0.2
l-6.8,13.1c-2.2,4.2-3.3,8.8-3.3,13.5v269.7c0,4.7,1.1,9.3,3.3,13.5l6.8,13.1c0,0.1,0.1,0.1,0.1,0.2v79.5c0,0.1,0,0.2-0.1,0.2
l-6.8,13.1c-2.2,4.2-3.3,8.8-3.3,13.5v157.2c0,3,2.4,5.5,5.5,5.5h7.9c3,0,5.5-2.4,5.5-5.5V71.2c0-3-2.4-5.5-5.5-5.5L650.4,65.7
L650.4,65.7z"/>
<path id="path74" d="M675.5,858.9h-11.3c-0.3,0-0.5-0.2-0.5-0.5l0,0L664.5,72c0-0.3,0.2-0.5,0.5-0.5h11.3c0.3,0-0.3,0.2-0.3,0.5
v786.4C676,858.7,675.8,858.9,675.5,858.9z M665.6,857.9h9.4l0.8-785.4h-10.3l1,8.8c-0.6,256.1,4.5,511.8-1.1,768.3
C665.3,852.3,665.6,855.1,665.6,857.9L665.6,857.9z"/>
<path id="path76" d="M787.4,1000.2H681.2c-3.6,0-6.5-2.9-6.5-6.5v-978c0-3.6,2.9-6.5,6.5-6.5h106.2
c116.8,0,211.9,95.1,211.9,211.9v567.2C999.3,905.3,904.5,1000.2,787.4,1000.2L787.4,1000.2z M681.2,10.4c-3,0-5.5,2.4-5.5,5.5
v978c0,3,2.4,5.5,5.5,5.5h106.2c116.3,0,210.9-94.6,210.9-210.9V221.1c0-116.3-94.6-210.9-210.9-210.9L681.2,10.4z"/>
<path id="path128" d="M654.6,314.3h-4.2c-3.8,0-6.9-3.1-7-7v-67.6c0-3.8,3.1-6.9,7-7h4.2c0.6,0,1,0.4,1,1l0,0v79.5
C655.6,313.9,655.1,314.3,654.6,314.3L654.6,314.3z M650.4,234.8c-2.7,0-5,2.2-5,5v67.6c0,2.7,2.2,5,5,5h3.2v-77.5h-3.2V234.8z"/>
<path id="path130" d="M654.6,717.1h-4.2c-3.8,0-6.9-3.1-7-7v-67.6c0-3.8,3.1-6.9,7-7h4.2c0.6,0,1,0.4,1,1l0,0V716
C655.6,716.6,655.1,717.1,654.6,717.1L654.6,717.1z M650.4,637.6c-2.7,0-5,2.2-5,5v67.6c0,2.7,2.2,5,5,5h3.2v-77.5L650.4,637.6
L650.4,637.6z"/>
<path id="path1240" class="st12" d="M805.5,998.7c26.6-2.4,50.5-9.2,75.9-21.8c5.1-2.5,10-5.2,14.9-8
c19.1-11.3,36.3-27.1,49.6-41.7c25.5-28,45.5-70.1,51.4-116.5c0.1-0.7,0.4-4.3,0.4-6.8c-0.1-6.9,0.7-20,0.8-38.3
c0.7-77.5,1.1-244,1.1-376.4c0-101.2-0.3-182.5-1-188.9c-2.7-26.1-9.3-49.1-20.8-72.1c-31.8-63.9-93-107.4-164-116.7
c-11.4-1.5-60.7-1.6-96.6-1.3c-7.7,0.1-14.1-0.1-20.1,0.1c-7.5,0.2-12.9-0.1-16.2,0.2c-1.7,0.2-3,0.8-3.2,1
c-0.1,0.1-1.4,1.4-1.5,3.3c-0.3,5.2-0.3,17.1-0.4,38c0,4.2-0.1,8.8-0.1,13.8c0,4.9,0.1,10.1,0.1,15.8c-0.1,8.8,0.1,18.6,0.1,29.2
c0,9.8-0.2,20.5,0,32c0.2,9,0.2,18.4,0.2,28.4c-0.1,15.1,0.6,31.1,0.3,49c-0.2,15,0.1,30,0,46.9c-0.1,11.1,0.1,22.6,0,34.6
c-0.1,7.2,0,14.5-0.1,22.1c-0.5,52.1,0,112.2,0,180.3c0,254.3-0.3,376.1,0,435.2c0,7.7-0.2,14.5-0.1,20.2c0,2.2,0,4.3,0,6.2
c0,3.3,0,6.6,0,9.3c0,2.9-0.1,5.8,0,8c0.1,5,0.1,8.1,0.3,10c0.3,2.8,1.3,3.6,1.6,3.8c0.2,0.2,1.3,0.9,3.1,1.1
c4.5,0.4,14.6,0.3,27.1,0.2c9.9-0.1,21.3-0.2,32.8-0.2C772.7,998.8,805.8,999,805.5,998.7L805.5,998.7L805.5,998.7z"/>
<path id="path78" d="M771.7,763.2c-23.1,0-41.9-18.7-41.9-41.9s18.7-41.9,41.9-41.9c23.2,0,41.9,18.7,41.9,41.9l0,0
C813.5,744.4,794.8,763.1,771.7,763.2z M771.7,680.4c-22.6,0-40.9,18.3-40.9,40.9s18.3,40.9,40.9,40.9c22.6,0,40.9-18.3,40.9-40.9
l0,0C812.5,698.7,794.3,680.4,771.7,680.4z"/>
<path id="path88" class="st13" d="M838.9,203.2h-5.5l-7.6-10.9l-8,10.9h-5.4l10.6-16.3l-9.8-15.6h5.2l7.4,10.6l7.3-10.6h5
l-9.8,15.4L838.9,203.2L838.9,203.2z"/>
<path id="path90" class="st13" d="M765.9,244.5l-11.6,20.6v11.4h-4.4V265l-11.6-20.5h5.3l6.4,11.7l2.1,3l2.4-2.6l6.4-12.1H765.9
L765.9,244.5z"/>
<path id="path92" class="st13" d="M912.6,276.5h-4.7l-2.2-7l-12.4,0.3l-3.2,6.7h-4.5l9.9-35.2l6.7,3.2L912.6,276.5z M903.9,265.2
l-4.9-14.6l-4.6,14.8L903.9,265.2L903.9,265.2z"/>
<path id="path132" d="M982.3,139.3h-0.2c-0.3-0.1-0.6-0.3-0.7-0.6C948.4,61,872.1,10.5,787.6,10.6h-54.2c-0.6,0-1-0.4-1-1l0,0V7
c0-3.8,3.1-6.9,7-7h51.2C875.8,0.1,953,50.1,987.9,127.8c1.2,2.6,0.6,5.7-1.5,7.8L983,139C982.8,139.2,982.5,139.3,982.3,139.3
L982.3,139.3z M734.4,8.6h53.2c84.7-0.1,161.3,50.2,195,128l2.4-2.4l0,0c1.5-1.4,1.9-3.6,1.1-5.5C951.5,51.7,875,2.1,790.6,2
h-51.2c-2.7,0-5,2.2-5,5V8.6z"/>
<path id="path134" d="M733.2,133.7h-11.7c-2.2,0-4-1.8-4-4V118c0-1.1-0.9-2-2-2h-11.7c-2.2,0-4-1.8-4-4v-11.7c0-2.2,1.8-4,4-4
h11.7c1.1,0,2-0.9,2-2V82.8c0-2.2,1.8-4,4-4h11.7c2.2,0,4,1.8,4,4v11.7c0,1.1,0.9,2,2,2h11.7c2.2,0,4,1.8,4,4v11.7
c0,2.2-1.8,4-4,4h-11.7c-1.1,0-2,0.9-2,2v11.7C737.2,131.9,735.4,133.7,733.2,133.7z M703.9,98.5c-1.1,0-2,0.9-2,2v11.7
c0,1.1,0.9,2,2,2h11.7c2.2,0,4,1.8,4,4v11.7c0,1.1,0.9,2,2,2h11.7c1.1,0,2-0.9,2-2v-11.7c0-2.2,1.8-4,4-4H751c1.1,0,2-0.9,2-2
v-11.7c0-1.1-0.9-2-2-2h-11.7c-2.2,0-4-1.8-4-4V82.8c0-1.1-0.9-2-2-2h-11.7c-1.1,0-2,0.9-2,2v11.7c0,2.2-1.8,4-4,4H703.9z"/>
<path id="path136" d="M825.6,372.4c-21.2,0-38.5-17.2-38.5-38.5s17.2-38.5,38.5-38.5c21.3,0,38.5,17.2,38.5,38.5
C864,355.2,846.8,372.4,825.6,372.4z M825.6,297.5c-20.1,0-36.5,16.3-36.5,36.5s16.3,36.5,36.5,36.5c20.2,0,36.5-16.3,36.5-36.5
C862,313.8,845.7,297.5,825.6,297.5z"/>
<path id="path138" d="M825.6,225.5c-21.2,0-38.5-17.2-38.5-38.5s17.2-38.5,38.5-38.5c21.3,0,38.5,17.2,38.5,38.5
C864,208.3,846.8,225.5,825.6,225.5z M825.6,150.6c-20.1,0-36.5,16.3-36.5,36.5s16.3,36.5,36.5,36.5c20.2,0,36.5-16.3,36.5-36.5
C862,166.9,845.7,150.6,825.6,150.6z"/>
<path id="path140" d="M899,299c-21.2,0-38.5-17.2-38.5-38.5S877.7,222,899,222c21.3,0,38.5,17.2,38.5,38.5S920.3,298.9,899,299z
M899,224c-20.1,0-36.5,16.3-36.5,36.5S878.8,297,899,297c20.2,0,36.5-16.3,36.5-36.5S919.2,224,899,224z"/>
<path id="path142" d="M752.2,299c-21.2,0-38.5-17.2-38.5-38.5s17.2-38.5,38.5-38.5c21.3,0,38.5,17.2,38.5,38.5
C790.6,281.7,773.4,298.9,752.2,299z M752.2,224c-20.1,0-36.5,16.3-36.5,36.5s16.2,36.2,36.4,36.2s36.6-16,36.6-36.2
C788.6,240.4,772.3,224,752.2,224z"/>
<path id="path144" d="M771.7,750.2c-16,0-28.9-13-28.9-28.9s13-28.9,28.9-28.9s28.9,13,28.9,28.9l0,0
C800.6,737.3,787.7,750.2,771.7,750.2z M771.7,694.3c-14.9,0-26.9,12.1-26.9,26.9s12.1,26.9,26.9,26.9s26.9-12.1,26.9-26.9
S786.6,694.4,771.7,694.3z"/>
<path id="path146" d="M762.5,533.4h-12.3c-0.6,0-1-0.4-1-1l0,0c1.7-39.6,33.4-71.3,73-73c0.3,0,0.5,0.1,0.7,0.3
c0.2,0.2,0.3,0.4,0.3,0.7v12.3c0,3.5-2.6,6.4-6,6.9c-24.7,3.7-44.2,23.1-47.9,47.9C768.9,530.8,766,533.4,762.5,533.4z
M751.3,531.4h11.2c2.5,0,4.6-1.8,4.9-4.3c3.9-25.6,24-45.7,49.6-49.6c2.5-0.3,4.3-2.4,4.3-4.9v-11.2
C783.9,463.5,753.4,494,751.3,531.4z"/>
<path id="path148" d="M822.3,612.1C822.3,612.1,822.2,612.1,822.3,612.1c-39.6-1.7-71.3-33.4-73-73c0-0.6,0.4-1,1-1h12.3
c3.5,0,6.4,2.6,6.9,6c3.7,24.7,23.1,44.2,47.9,47.9c3.4,0.5,6,3.4,6,6.9v12.3C823.3,611.6,822.8,612.1,822.3,612.1L822.3,612.1
L822.3,612.1z M751.3,540c2.2,37.4,32.6,67.8,70,70v-11.2c0-2.5-1.8-4.6-4.3-4.9c-25.6-3.9-45.7-24-49.6-49.6
c-0.3-2.5-2.4-4.3-4.9-4.3H751.3z"/>
<path id="path150" d="M828.9,612.1c-0.6,0-1-0.4-1-1l0,0v-12.3c0-3.5,2.6-6.4,6-6.9c24.7-3.7,44.2-23.1,47.9-47.9
c0.5-3.4,3.4-6,6.9-6H901c0.6,0,1,0.4,1,1l0,0C900.2,578.7,868.6,610.3,828.9,612.1C829,612.1,829,612.1,828.9,612.1L828.9,612.1
L828.9,612.1z M888.7,540c-2.5,0-4.6,1.8-4.9,4.3c-3.9,25.6-24,45.7-49.6,49.6c-2.5,0.3-4.3,2.4-4.3,4.9V610
c37.4-2.2,67.8-32.6,70-70H888.7z"/>
<path id="path152" d="M901,533.4h-12.3c-3.5,0-6.4-2.6-6.9-6c-3.7-24.7-23.1-44.2-47.9-47.9c-3.4-0.5-6-3.4-6-6.9v-12.3
c0-0.3,0.1-0.5,0.3-0.7c0.2-0.2,0.5-0.3,0.7-0.3c39.6,1.7,71.3,33.4,73,73C902,532.9,901.6,533.3,901,533.4L901,533.4L901,533.4z
M829.9,461.4v11.2c0,2.5,1.8,4.6,4.3,4.9c25.6,3.9,45.7,24,49.6,49.6c0.3,2.5,2.4,4.3,4.9,4.3h11.2
C897.8,494,867.3,463.5,829.9,461.4z"/>
<path id="path154" d="M825.6,612.2c-1.2,0-2.3,0-3.4-0.1c-0.5,0-0.9-0.5-0.9-1v-12.3c0-2.5-1.8-4.6-4.3-4.9
c-25.6-3.9-45.7-24-49.6-49.6c-0.3-2.5-2.4-4.3-4.9-4.3h-12.3c-0.5,0-1-0.4-1-0.9c-0.1-1.1-0.1-2.2-0.1-3.4s0-2.3,0.1-3.4
c0-0.5,0.5-0.9,1-0.9h12.3c2.5,0,4.6-1.8,4.9-4.3c3.9-25.6,24-45.7,49.6-49.6c2.5-0.3,4.3-2.4,4.3-4.9v-12.3c0-0.5,0.4-1,0.9-1
c2.3-0.1,4.5-0.1,6.8,0c0.5,0,0.9,0.5,0.9,1v12.3c0,2.5,1.8,4.6,4.3,4.9c25.6,3.9,45.7,24,49.6,49.6c0.3,2.5,2.4,4.3,4.9,4.3H901
c0.5,0,1,0.4,1,0.9c0.1,1.1,0.1,2.2,0.1,3.4s0,2.3-0.1,3.4c0,0.5-0.5,0.9-1,0.9h-12.3c-2.5,0-4.6,1.8-4.9,4.3
c-3.9,25.6-24,45.7-49.6,49.6c-2.5,0.3-4.3,2.4-4.3,4.9v12.3c0,0.5-0.4,1-0.9,1C827.9,612.1,826.8,612.2,825.6,612.2L825.6,612.2
L825.6,612.2z M823.3,610.1c1.5,0.1,3.1,0.1,4.7,0v-11.3c0-3.5,2.6-6.4,6-6.9c24.7-3.7,44.2-23.1,47.9-47.9c0.5-3.4,3.4-6,6.9-6
h11.3v-4.6h-11.4c-3.5,0-6.4-2.6-6.9-6c-3.7-24.7-23.1-44.2-47.9-47.9c-3.4-0.5-6-3.4-6-6.9v-11.3c-1.5-0.1-3.1-0.1-4.7,0v11.3
c0,3.5-2.6,6.4-6,6.9c-24.7,3.7-44.2,23.1-47.9,47.9c-0.5,3.4-3.4,6-6.9,6h-11.3v4.6h11.3c3.5,0,6.4,2.6,6.9,6
c3.7,24.7,23.1,44.2,47.9,47.9c3.4,0.5,6,3.4,6,6.9L823.3,610.1L823.3,610.1L823.3,610.1z"/>
<path id="path1462" class="st4" d="M743,295.5c-6.3-1.7-11.3-3.9-16.5-9.1c-7.7-7.7-11.3-17-10.8-27.7c0.6-13.5,8-25,20-31
c10.5-5.2,22.6-5.3,32.7-0.3c7.9,3.9,12.8,10,16.6,18c5.2,10.8,4.2,24.4-2.4,34.6c-5,7.6-13.5,13.7-22,15.7
C755.7,296.8,747.8,296.7,743,295.5L743,295.5L743,295.5z"/>
<path id="path1464" class="st4" d="M819.4,222.9c0-0.3-3.7-0.9-5.7-1.6c-3-1.1-5.9-2.6-8-4c-6.9-4.6-11.7-10.9-14.5-18.9
c-1.3-3.8-2.1-5.2-2.1-11.4s0.7-7.2,2.1-11c3.9-11.1,11.8-19.1,22.8-23c4.2-1.5,5.2-2.3,11.6-2.3c6.4,0,7.3,0.8,11.6,2.3
c14.5,5.2,24,17.6,24.7,32.9c0.5,10.6-2.7,19.5-10.2,26.9c-7.6,7.5-14.4,10.7-24.6,11C823.6,223.7,820.2,223,819.4,222.9
L819.4,222.9L819.4,222.9z"/>
<path id="path1466" class="st4" d="M891.7,296.5c-6.7-1-15.6-5.8-20.8-12.9c-3-4.1-6.2-10.7-7.3-15.7
c-2.8-13.2,2.6-27.9,13.3-35.9c20-15.1,48.1-6.7,56.4,17c1.9,5.3,3,13.3,1.7,19.2c-1.5,6.8-4.9,12.9-10.2,18.2
c-5.3,5.2-9.9,8-16.4,9.6C903.5,297.2,896.3,297.2,891.7,296.5L891.7,296.5L891.7,296.5z"/>
<path id="path1468" class="st4" d="M820.4,369.9c-8.1-1.3-14.2-4-20.6-10.3c-3.8-3.7-5.4-6.2-7.2-10c-7.6-15.8-3-33.1,10.8-44.1
c6.4-5.1,13.6-7.6,22.1-7.6c29.9,0,46.7,33.8,28.7,58c-2.8,3.8-6.3,7.5-10.5,9.8C836.7,369.8,828.1,371.1,820.4,369.9L820.4,369.9
L820.4,369.9z"/>
<path id="path1500" class="st6" d="M823.6,602.7c-0.3-8.9-0.8-9.7-10.1-11.9c-21.1-4.8-37.2-20.1-42.6-41.3
c-0.6-2.2-1.1-5.2-1.1-5.9s-1-2.2-2.2-3.5l-2.3-2l-6.9-0.3h-7v-4h5.9c6.6,0,9.9-1,11.2-3.4c0.4-0.8,1.5-3.9,2-7
c2.7-16.3,14.9-30.7,29.5-38c4.7-2.4,11.4-4.6,15.5-5.4c6.6-1.1,8.2-3.6,8.2-12.8v-5.9h4v6.9c0,6.6,0.1,7,1.8,8.8
c1.5,1.6,3.9,1.7,9.5,3.1c20.4,5,35.3,20.5,40.7,40.1c0.8,2.9,1.8,6.4,2.2,7.8c1.2,4.3,3.8,5.6,11.7,5.6h6.5v4h-6.6
c-7,0-9.9,0.8-10.8,3.1c-0.3,0.7-1.3,4.6-2.1,8c-5.4,21.4-21.3,36.7-42.1,41.5c-9.9,2.3-10.7,3.2-10.7,12.8v6.3h-3.8L823.6,602.7
L823.6,602.7z"/>
<path id="path1504" class="st7" d="M752.2,526.5c1.8-15.3,8.9-31.2,20.4-43c11.9-12.2,27.5-19.2,45.7-21.6l2.8-0.4v6.2
c0,7.9-0.1,8.9-6.8,10.2c-22.3,4.6-41.8,22.1-46.1,44.2c-1.8,9.1-2.4,9.1-11.1,9.1h-5.4L752.2,526.5L752.2,526.5z"/>
<path id="path1506" class="st7" d="M811.2,608.2c-32.2-6.9-55.7-32.9-59.4-65.6l-0.2-2.1h6.4c6.9,0,6.8,0,7.8,1
c1.3,1.4,1.5,3.8,2.4,7.9c4.8,21.2,24.2,39.1,46.6,44c6.9,1.5,6.4,2.7,6.4,10.3v5.9l-2.1,0C817.8,609.5,814.3,608.9,811.2,608.2
L811.2,608.2L811.2,608.2z"/>
<path id="path1508" class="st7" d="M830,603c-0.3-7.3,1-8.2,5.3-9c4.8-0.8,11.1-2.9,16.3-5.5c15.8-7.7,27.1-22,31.6-39.9
c1-4,1.6-7.2,2.2-7.7c0.7-0.5,3.9-0.5,7.8-0.5h6.2l-0.4,3.8c-3.8,33.2-31.8,61.3-64.8,65l-4,0.5L830,603L830,603z"/>
<path id="path1510" class="st7" d="M885.4,529.8c-1.2-1.1-1.3-3-2.1-6.7c-4.4-20.5-19-36.6-39.3-43.5c-2.4-0.8-5.8-1.7-7.5-2
c-1.9-0.3-3.9-0.6-4.8-1.5c-1.4-1.4-1.3-2.6-1.3-8.2v-6.2l2.5,0.3c19,2.5,32.7,9.2,45.4,22.1c10.4,10.5,17.4,23.8,20.2,38.1
c0.6,2.9,1,6.1,1,7.1v1.9h-5.9C888.3,531.3,886.8,531.1,885.4,529.8L885.4,529.8L885.4,529.8z"/>
<path id="path1512" class="st4" d="M720.3,131c-0.5-0.6-0.6-1.7-0.6-7.6v-6.9l-1.1-1.2l-1.1-1.2l-7.1-0.1
c-4.1-0.1-6.5,0.5-7.6-0.2c-1.3-0.8-0.9-3.1-0.9-7.6s0.1-6.3,0.5-6.8c0.5-0.7,1.1-0.7,7.3-0.8c3.7-0.1,7.1-0.2,7.6-0.3
c0.5-0.1,1.2-0.7,1.6-1.4c0.7-1.1,0.7-1.9,0.7-7.8c0-3.8,0.2-6.8,0.4-7.3c0.4-0.7,0.9-0.7,7.3-0.7s7,0.1,7.3,0.7
c0.2,0.4,0.4,3.5,0.4,7.3c0,5.8,0.1,6.7,0.7,7.8c0.4,0.7,1.1,1.3,1.6,1.4s3.9,0.2,7.6,0.3c6.2,0.1,6.8,0.2,7.3,0.8
c0.4,0.6,0.5,2.2,0.5,6.8s0.7,7.2-0.9,7.9c-1.2,0.5-3.8-0.1-7.6-0.1l-7.1,0.1l-1.1,1.2l-1.1,1.2v6.9c0,6.2-0.1,7-0.7,7.6
c-0.6,0.5-1.7,0.6-7.1,0.6C721.7,131.7,720.9,131.6,720.3,131L720.3,131L720.3,131z"/>
<path id="path1514" class="st7" d="M766.6,726.4v-5.3h10.5v10.5h-10.5V726.4L766.6,726.4z"/>
<path id="path1518" class="st7" d="M766,761.6c-14.8-2.3-27.2-12.3-32.4-26.2c-7.5-20,2.3-43,22-51.4c6.1-2.6,8-3.5,15.7-3.5
c6.5,0,7.9,0.6,11.3,1.6c14.4,4.4,24.6,14.9,28.6,29.4c0.8,2.9,1.1,5.2,1.1,10.7c0,5.5-0.3,6.2-1.1,9c-4.8,17.7-20,30-38.1,30.5
C770.5,761.9,767.4,761.8,766,761.6L766,761.6L766,761.6z M778.1,749.7c11.9-2.6,21.3-13,22.5-25.1c2.2-21.5-18.1-37.7-38.5-30.7
c-4.2,1.4-7,3.3-10.8,7.1c-2.8,2.8-3.8,4.1-5.3,7.2c-2.6,5.1-3.3,8.7-3,14.6c0.3,6.8,2.2,11.7,6.3,16.9
C755.9,748,767.4,752,778.1,749.7L778.1,749.7L778.1,749.7z"/>
<path id="path1520" class="st9" d="M648.2,714.3c-1.3-0.8-2.3-0.9-2.5-5.3c-0.2-4.3,0.1-13.5,0.1-32.6c0-32.8-0.5-35.2,0.6-36.3
c0.1-0.1,0.1-0.2,0.2-0.2c1.1-1.5,2.7-2.1,4.9-2.1h1.9V715h-2C650.1,715,648.9,714.8,648.2,714.3L648.2,714.3L648.2,714.3z"/>
<path id="path1522" class="st9" d="M649.6,312.1c-0.5-0.1-1.3-0.4-1.8-0.8c-0.8-0.5-1.5-0.8-1.8-2.7c-0.5-3.2-0.2-11.4-0.2-35.1
c0-19.3-0.5-28.4-0.4-32.5c0.1-2.7,0.8-3.2,1-3.6c0.9-1.6,2.4-2.3,4.8-2.3h2.1v77.3l-1.4,0C651.2,312.3,650.1,312.2,649.6,312.1
L649.6,312.1L649.6,312.1z"/>
<path id="path1524" class="st8" d="M978,126.4c-10.1-20.6-21.9-37.2-38.2-53.9c-34.8-35.7-78.7-57-129.6-63.1
c-5.1-0.6-13.1-0.8-40.9-1l-34.6-0.2V6.9c0-1,0-3,1.4-3.7l2.1-1.1l30.4,0.2c16.9,0.1,33.6,0.2,37,0.5
c50.8,3.9,97.1,24.6,133.3,59.5c17.6,16.9,31.5,35.7,42.8,57.9c4.3,8.4,4.9,10.1,4.2,11.8c-0.4,1.1-2.7,4.2-3.2,4.1
C982.6,136.1,980.5,131.5,978,126.4L978,126.4L978,126.4z"/>
<path id="path1530" class="st4" d="M646.8,903.8c-0.6-0.5-1.7-1.2-1.6-5c0.3-6.7-0.2-27.2-0.1-79.1l0.3-82.6l2.6-6.5l7.9-15
l0.1-39.4l-0.4-39.6l-7.1-13.2l-2.9-7.7l-0.4-7.3l0.1-48l-0.1-22.1l0.1-34.1l0.1-10.5V483l-0.1-8.6l0-25.6v-19.4l-0.2-27.5
l0.1-21.6l0-11l0.1-19.6l0-10.2l0.5-5.7l1.9-5.8l3-5.5l5.3-9.8v-39.5l-0.4-39.6l-5.3-9.8l-2.7-5.3l-1.8-5.1l-0.7-7.6l0.2-13.3
l-0.1-12.2v-11.1l-0.1-10.9l0-9.9V142l0-2.9l0.1-17.4l0-21.5V89.4l-0.1-8.4l0-5.1l-0.2-5.9l2.2-3.1l3.2-1.1l3.2,0.1
c1.4,0,2.7,0.1,3.9,0.2c0.9,0.1,1.8,0.1,2.6,0.4c1,0.4,1.7,1,2,1.3c0.2,0.2,1.2,1.7,1.3,4.8c0.1,2.1,0,4.7,0,8.3
c0,4.1,0.1,9.7,0,16.4c-0.1,10,0,23.1-0.1,40.4c0,13.2,0.2,28.7,0.1,47c-0.2,62.8-0.2,158-0.2,301l0,416.2l-1.3,1.8
C660.4,906.2,649.5,906.5,646.8,903.8L646.8,903.8L646.8,903.8z"/>
<path id="path1532" class="st4" d="M665.8,465.1V72.7h8.6v784.7h-8.6V465.1L665.8,465.1z"/>
<ellipse id="path3879" class="st14" cx="771.6" cy="721.1" rx="40.3" ry="40.3"/>
<ellipse id="path3881" class="st15" cx="771.7" cy="721.4" rx="34.1" ry="34.5"/>
<path id="path96" d="M771.7,694.3l-26.8,26.4h7.7v22.5h38.5v-22.5h7.2L771.7,694.3L771.7,694.3z M779.5,735.2h-15.2v-14.5h15.2
V735.2z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 32 KiB

View File

@@ -0,0 +1,185 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 27.9.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 1000.4 356.1" style="enable-background:new 0 0 1000.4 356.1;" xml:space="preserve">
<style type="text/css">
.st0{opacity:0.1;}
.st1{fill:#FF5F55;}
.st2{fill:#FF5F53;}
.st3{fill:#FFFFFF;}
.st4{fill:#1A1A1A;stroke:#000000;}
.st5{fill:#1A1A1A;stroke:#1A1A1A;}
.st6{fill:#1A1A1A;stroke:#4D4D4D;}
.st7{stroke:#000000;}
.st8{stroke:#4D4D4D;}
.st9{fill:#999595;stroke:#000000;stroke-width:2.39;stroke-linecap:round;stroke-linejoin:round;}
.st10{fill:#3A3D40;stroke:#000000;stroke-width:2.73;stroke-linecap:round;stroke-linejoin:round;}
</style>
<g id="g315">
<g id="g64" class="st0">
<path id="path36" class="st1" d="M766.5,11.2H687V7c0-3.3,2.7-6,6-6h67.6c3.3,0,6,2.7,6,6L766.5,11.2L766.5,11.2z"/>
<path id="path38" class="st1" d="M363.8,11.2h-79.5V7c0-3.3,2.7-6,6-6h67.6c3.3,0,6,2.7,6,6L363.8,11.2L363.8,11.2z"/>
<path id="path40" class="st1" d="M865.5,342.3l-3.4-3.4c78-33.2,128.7-109.8,128.7-194.7V90h2.6c3.3,0,6,2.7,6,6v51.2
c0,84.8-49.8,161.7-127.2,196.4C869.9,344.6,867.3,344.1,865.5,342.3L865.5,342.3L865.5,342.3z"/>
<path id="path42" class="st1" d="M905.9,92.8h11.7c1.6,0,3-1.3,3-3V78.1c0-1.6-1.3-3-3-3l0,0h-11.7c-1.6,0-3-1.3-3-3V60.4
c0-1.6-1.3-3-3-3l0,0h-11.7c-1.6,0-3,1.3-3,3v11.7c0,1.6-1.3,3-3,3l0,0h-11.7c-1.6,0-3,1.3-3,3v11.7c0,1.6,1.3,3,3,3l0,0h11.7
c1.6,0,3,1.3,3,3v11.7c0,1.6,1.3,3,3,3l0,0h11.7c1.6,0,3-1.3,3-3V95.8C902.9,94.1,904.3,92.8,905.9,92.8L905.9,92.8z"/>
<circle id="circle44" class="st1" cx="666.5" cy="182.2" r="37.5"/>
<circle id="circle46" class="st1" cx="813.3" cy="182.2" r="37.5"/>
<circle id="circle48" class="st1" cx="739.9" cy="255.6" r="37.5"/>
<circle id="circle50" class="st1" cx="739.9" cy="108.8" r="37.5"/>
<circle id="circle52" class="st1" cx="279.1" cy="128.3" r="27.9"/>
<path id="path54" class="st1" d="M540.1,178.9h-12.3c-3,0-5.5-2.2-5.9-5.2c-3.7-25.2-23.5-45-48.7-48.7c-2.9-0.4-5.1-2.9-5.2-5.9
v-12.3C507.1,108.5,538.4,139.8,540.1,178.9L540.1,178.9L540.1,178.9z"/>
<path id="path56" class="st1" d="M401.6,178.9h-12.3c1.7-39.1,33-70.3,72-72.1v12.3c0,3-2.2,5.5-5.2,5.9
c-25.2,3.7-45,23.5-48.7,48.7C407.1,176.7,404.6,178.9,401.6,178.9L401.6,178.9L401.6,178.9z"/>
<path id="path58" class="st1" d="M461.4,257.6c-39.1-1.7-70.3-33-72-72.1h12.3c3,0,5.5,2.2,5.9,5.2c3.7,25.2,23.5,45,48.7,48.7
c2.9,0.4,5.1,2.9,5.2,5.9L461.4,257.6L461.4,257.6z"/>
<path id="path60" class="st1" d="M468,257.6v-12.3c0-3,2.2-5.5,5.2-5.9c25.2-3.7,45-23.5,48.7-48.7c0.4-2.9,2.9-5.1,5.9-5.2h12.3
C538.4,224.6,507.1,255.9,468,257.6L468,257.6L468,257.6z"/>
<path id="path62" class="st1" d="M464.7,257.7c-1.1,0-2.2,0-3.3-0.1v-12.3c0-3-2.2-5.5-5.2-5.9c-25.2-3.7-45-23.5-48.7-48.7
c-0.4-2.9-2.9-5.1-5.9-5.2h-12.3c-0.1-1.1-0.1-2.2-0.1-3.3s0-2.2,0.1-3.3h12.3c3,0,5.5-2.2,5.9-5.2c3.7-25.2,23.5-45,48.7-48.7
c2.9-0.4,5.1-2.9,5.2-5.9v-12.3c1.1-0.1,2.2-0.1,3.3-0.1s2.2,0,3.3,0.1v12.3c0,3,2.2,5.5,5.2,5.9c25.2,3.7,45,23.5,48.7,48.7
c0.4,2.9,2.9,5.1,5.9,5.2h12.3c0.1,1.1,0.1,2.2,0.1,3.3s0,2.2-0.1,3.3h-12.3c-3,0-5.5,2.2-5.9,5.2c-3.7,25.2-23.5,45-48.7,48.7
c-2.9,0.4-5.1,2.9-5.2,5.9v12.3C466.9,257.6,465.8,257.7,464.7,257.7L464.7,257.7z"/>
</g>
<path id="path72" d="M93.8,14.9V7c0-3.6,2.9-6.4,6.5-6.5h157.2c4.9,0,9.6,1.2,13.9,3.4l13,6.7h79.2l13-6.7c4.3-2.2,9.1-3.4,14-3.4
h269.7c4.9,0,9.6,1.2,13.9,3.4l13,6.7h79.2l13-6.7c4.3-2.2,9.1-3.4,14-3.4h135.8c3.6,0,6.4,2.9,6.5,6.5v7.9c0,3.6-2.9,6.4-6.5,6.5
c-276.3,1.1-552.6,1.2-828.9,0C96.7,21.3,93.8,18.4,93.8,14.9L93.8,14.9L93.8,14.9z M934.7,7c0-3-2.4-5.5-5.5-5.5H793.4
c-4.7,0-9.3,1.1-13.5,3.3l-13.1,6.8c-0.1,0-0.1,0.1-0.2,0.1h-79.5c-0.1,0-0.2,0-0.2-0.1l-13.1-6.8c-4.2-2.2-8.8-3.3-13.5-3.3H390.6
c-4.7,0-9.3,1.1-13.5,3.3L364,11.6c-0.1,0-0.1,0.1-0.2,0.1h-79.5c-0.1,0-0.2,0-0.2-0.1L271,4.8c-4.2-2.2-8.8-3.3-13.5-3.3H100.3
c-3,0-5.5,2.4-5.5,5.5v7.9c0,3,2.4,5.5,5.5,5.5h828.9c3,0,5.5-2.4,5.5-5.5L934.7,7L934.7,7z"/>
<path id="path74" d="M141.5,32.1V20.8c0-0.3,0.2-0.5,0.5-0.5l0,0l786.4,0.8c0.3,0,0.5,0.2,0.5,0.5v11.3c0,0.3-0.2-0.3-0.5-0.3H142
C141.7,32.6,141.5,32.4,141.5,32.1z M142.5,22.2v9.4L928,32.4V22.1l-8.8,1c-256.1-0.6-511.8,4.5-768.3-1.1
C148.1,21.9,145.3,22.2,142.5,22.2L142.5,22.2z"/>
<path id="path76" d="M0.2,144V37.8c0-3.6,2.9-6.5,6.5-6.5h978c3.6,0,6.5,2.9,6.5,6.5V144c0,116.8-95.1,211.9-211.9,211.9H212.1
C95.1,355.9,0.2,261.1,0.2,144L0.2,144L0.2,144z M990.1,37.8c0-3-2.4-5.5-5.5-5.5H6.6c-3,0-5.5,2.4-5.5,5.5V144
c0,116.3,94.6,210.9,210.9,210.9h567.3c116.3,0,210.9-94.6,210.9-210.9L990.1,37.8L990.1,37.8z"/>
<path id="path128" d="M686.1,11.2V7c0-3.8,3.1-6.9,7-7h67.6c3.8,0,6.9,3.1,7,7v4.2c0,0.6-0.4,1-1,1l0,0h-79.5
C686.5,12.2,686.1,11.7,686.1,11.2L686.1,11.2z M765.6,7c0-2.7-2.2-5-5-5H693c-2.7,0-5,2.2-5,5v3.2h77.5L765.6,7L765.6,7z"/>
<path id="path130" d="M283.3,11.2V7c0-3.8,3.1-6.9,7-7h67.6c3.8,0,6.9,3.1,7,7v4.2c0,0.6-0.4,1-1,1l0,0h-79.5
C283.8,12.2,283.3,11.7,283.3,11.2L283.3,11.2z M362.8,7c0-2.7-2.2-5-5-5h-67.6c-2.7,0-5,2.2-5,5v3.2h77.5L362.8,7L362.8,7z"/>
<path id="path1240" class="st2" d="M1.8,162.1c2.4,26.6,9.2,50.5,21.8,76c2.5,5.1,5.2,10,8,14.9c11.3,19.1,27.1,36.3,41.7,49.6
c28,25.5,70.1,45.5,116.5,51.4c0.7,0.1,4.3,0.4,6.8,0.4c6.9-0.1,20,0.7,38.3,0.8c77.5,0.7,244,1.1,376.4,1.1
c101.2,0,182.5-0.3,188.9-1c26.1-2.7,49.1-9.3,72.1-20.8c63.9-31.8,107.4-93,116.7-164c1.5-11.4,1.6-60.7,1.3-96.6
c-0.1-7.7,0.1-14.1-0.1-20.1c-0.2-7.5,0.1-12.9-0.2-16.2c-0.2-1.7-0.8-3-1-3.2c-0.1-0.1-1.4-1.4-3.3-1.5c-5.2-0.3-17.1-0.3-38-0.4
c-4.2,0-8.8-0.1-13.8-0.1c-4.9,0-10.1,0.1-15.8,0.1c-8.8,0-18.6,0.1-29.2,0.1c-9.8,0-20.5-0.2-32,0c-9,0.2-18.4,0.2-28.4,0.2
c-15.1-0.1-31.1,0.6-49,0.3c-15-0.2-30,0.1-46.9,0c-11.1-0.1-22.6,0.1-34.6,0c-7.2-0.1-14.5,0-22.1-0.1c-52.1-0.5-112.2,0-180.3,0
c-254.3,0-376.1-0.3-435.2,0c-7.7,0-14.5-0.2-20.2-0.1c-2.2,0-4.3,0-6.2,0c-3.3,0-6.6,0-9.3,0c-2.9,0-5.8-0.1-8,0
c-5,0.1-8.1,0.1-10,0.3c-2.8,0.3-3.6,1.3-3.8,1.6c-0.2,0.2-0.9,1.3-1.1,3.1c-0.4,4.5-0.3,14.6-0.2,27.1c0.1,9.9,0.2,21.3,0.2,32.8
C1.7,129.3,1.5,162.4,1.8,162.1L1.8,162.1L1.8,162.1z"/>
<path id="path78" d="M237.2,128.3c0-23.1,18.7-41.9,41.9-41.9s41.9,18.7,41.9,41.9s-18.7,41.9-41.9,41.9l0,0
C256,170.1,237.3,151.4,237.2,128.3z M320,128.3c0-22.6-18.3-40.9-40.9-40.9s-40.9,18.3-40.9,40.9s18.3,40.9,40.9,40.9l0,0
C301.7,169.1,320,150.9,320,128.3z"/>
<path id="path92" class="st3" d="M723.9,269.2v-4.7l7-2.2l-0.3-12.4l-6.7-3.2v-4.5l35.2,9.9l-3.2,6.7L723.9,269.2z M735.2,260.5
l14.6-4.9L735,251L735.2,260.5L735.2,260.5z"/>
<path id="path132" d="M861.1,338.9v-0.2c0.1-0.3,0.3-0.6,0.6-0.7c77.7-33,128.2-109.3,128.1-193.8V90c0-0.6,0.4-1,1-1l0,0h2.6
c3.8,0,6.9,3.1,7,7v51.2c-0.1,85.2-50.1,162.4-127.8,197.3c-2.6,1.2-5.7,0.6-7.8-1.5l-3.4-3.4C861.2,339.4,861.1,339.1,861.1,338.9
L861.1,338.9z M991.8,91v53.2c0.1,84.7-50.2,161.3-128,195l2.4,2.4l0,0c1.4,1.5,3.6,1.9,5.5,1.1c77-34.6,126.6-111.1,126.7-195.5
V96c0-2.7-2.2-5-5-5H991.8z"/>
<path id="path134" d="M866.7,89.8V78.1c0-2.2,1.8-4,4-4h11.7c1.1,0,2-0.9,2-2V60.4c0-2.2,1.8-4,4-4h11.7c2.2,0,4,1.8,4,4v11.7
c0,1.1,0.9,2,2,2h11.5c2.2,0,4,1.8,4,4v11.7c0,2.2-1.8,4-4,4h-11.7c-1.1,0-2,0.9-2,2v11.7c0,2.2-1.8,4-4,4h-11.7c-2.2,0-4-1.8-4-4
V95.8c0-1.1-0.9-2-2-2h-11.7C868.5,93.8,866.7,92,866.7,89.8z M901.9,60.5c0-1.1-0.9-2-2-2h-11.7c-1.1,0-2,0.9-2,2v11.7
c0,2.2-1.8,4-4,4h-11.7c-1.1,0-2,0.9-2,2v11.7c0,1.1,0.9,2,2,2h11.7c2.2,0,4,1.8,4,4v11.7c0,1.1,0.9,2,2,2h11.7c1.1,0,2-0.9,2-2
V95.9c0-2.2,1.8-4,4-4h11.7c1.1,0,2-0.9,2-2V78.2c0-1.1-0.9-2-2-2h-11.7c-2.2,0-4-1.8-4-4V60.5z"/>
<path id="path136" d="M628,182.2c0-21.2,17.2-38.5,38.5-38.5s38.5,17.2,38.5,38.5s-17.2,38.5-38.5,38.5
C645.2,220.6,628,203.4,628,182.2z M702.9,182.2c0-20.1-16.3-36.5-36.5-36.5s-36.5,16.3-36.5,36.5s16.3,36.5,36.5,36.5
C686.6,218.6,702.9,202.3,702.9,182.2z"/>
<path id="path138" d="M774.9,182.2c0-21.2,17.2-38.5,38.5-38.5s38.5,17.2,38.5,38.5s-17.2,38.5-38.5,38.5
C792.1,220.6,774.9,203.4,774.9,182.2z M849.8,182.2c0-20.1-16.3-36.5-36.5-36.5c-20.2,0-36.5,16.3-36.5,36.5s16.3,36.5,36.5,36.5
C833.5,218.6,849.8,202.3,849.8,182.2z"/>
<path id="path140" d="M701.4,255.6c0-21.2,17.2-38.5,38.5-38.5s38.5,17.2,38.5,38.5s-17.2,38.5-38.5,38.5S701.5,276.9,701.4,255.6z
M776.4,255.6c0-20.1-16.3-36.5-36.5-36.5s-36.5,16.3-36.5,36.5s16.3,36.5,36.5,36.5S776.4,275.8,776.4,255.6z"/>
<path id="path142" d="M701.4,108.8c0-21.2,17.2-38.5,38.5-38.5s38.5,17.2,38.5,38.5s-17.2,38.5-38.5,38.5
C718.7,147.2,701.5,130,701.4,108.8z M776.4,108.8c0-20.1-16.3-36.5-36.5-36.5s-36.2,16.2-36.2,36.4s16,36.6,36.2,36.6
C760,145.2,776.4,128.9,776.4,108.8z"/>
<path id="path144" d="M250.2,128.3c0-16,13-28.9,28.9-28.9s28.9,13,28.9,28.9s-13,28.9-28.9,28.9l0,0
C263.1,157.2,250.2,144.3,250.2,128.3z M306.1,128.3c0-14.9-12.1-26.9-26.9-26.9s-26.9,12.1-26.9,26.9s12.1,26.9,26.9,26.9
S306,143.2,306.1,128.3z"/>
<path id="path146" d="M467,119.1v-12.3c0-0.6,0.4-1,1-1l0,0c39.6,1.7,71.3,33.4,73,73c0,0.3-0.1,0.5-0.3,0.7s-0.4,0.3-0.7,0.3
h-12.3c-3.5,0-6.4-2.6-6.9-6c-3.7-24.7-23.1-44.2-47.9-47.9C469.6,125.5,467,122.6,467,119.1z M469,107.9v11.2
c0,2.5,1.8,4.6,4.3,4.9c25.6,3.9,45.7,24,49.6,49.6c0.3,2.5,2.4,4.3,4.9,4.3H539C536.9,140.5,506.4,110,469,107.9z"/>
<path id="path148" d="M388.3,178.9C388.3,178.9,388.3,178.8,388.3,178.9c1.7-39.6,33.4-71.3,73-73c0.6,0,1,0.4,1,1v12.3
c0,3.5-2.6,6.4-6,6.9c-24.7,3.7-44.2,23.1-47.9,47.9c-0.5,3.4-3.4,6-6.9,6h-12.3C388.8,179.9,388.3,179.4,388.3,178.9L388.3,178.9
L388.3,178.9z M460.4,107.9c-37.4,2.2-67.8,32.6-70,70h11.2c2.5,0,4.6-1.8,4.9-4.3c3.9-25.6,24-45.7,49.6-49.6
c2.5-0.3,4.3-2.4,4.3-4.9V107.9z"/>
<path id="path150" d="M388.3,185.5c0-0.6,0.4-1,1-1l0,0h12.3c3.5,0,6.4,2.6,6.9,6c3.7,24.7,23.1,44.2,47.9,47.9
c3.4,0.5,6,3.4,6,6.9v12.3c0,0.6-0.4,1-1,1l0,0C421.7,256.8,390.1,225.2,388.3,185.5C388.3,185.6,388.3,185.6,388.3,185.5
L388.3,185.5L388.3,185.5z M460.4,245.3c0-2.5-1.8-4.6-4.3-4.9c-25.6-3.9-45.7-24-49.6-49.6c-0.3-2.5-2.4-4.3-4.9-4.3h-11.2
c2.2,37.4,32.6,67.8,70,70V245.3z"/>
<path id="path152" d="M467,257.6v-12.3c0-3.5,2.6-6.4,6-6.9c24.7-3.7,44.2-23.1,47.9-47.9c0.5-3.4,3.4-6,6.9-6h12.3
c0.3,0,0.5,0.1,0.7,0.3s0.3,0.5,0.3,0.7c-1.7,39.6-33.4,71.3-73,73C467.5,258.6,467.1,258.2,467,257.6L467,257.6L467,257.6z
M539,186.5h-11.2c-2.5,0-4.6,1.8-4.9,4.3c-3.9,25.6-24,45.7-49.6,49.6c-2.5,0.3-4.3,2.4-4.3,4.9v11.2
C506.4,254.4,536.9,223.9,539,186.5z"/>
<path id="path154" d="M388.2,182.2c0-1.2,0-2.3,0.1-3.4c0-0.5,0.5-0.9,1-0.9h12.3c2.5,0,4.6-1.8,4.9-4.3
c3.9-25.6,24-45.7,49.6-49.6c2.5-0.3,4.3-2.4,4.3-4.9v-12.3c0-0.5,0.4-1,0.9-1c1.1-0.1,2.2-0.1,3.4-0.1s2.3,0,3.4,0.1
c0.5,0,0.9,0.5,0.9,1v12.3c0,2.5,1.8,4.6,4.3,4.9c25.6,3.9,45.7,24,49.6,49.6c0.3,2.5,2.4,4.3,4.9,4.3h12.3c0.5,0,1,0.4,1,0.9
c0.1,2.3,0.1,4.5,0,6.8c0,0.5-0.5,0.9-1,0.9h-12.3c-2.5,0-4.6,1.8-4.9,4.3c-3.9,25.6-24,45.7-49.6,49.6c-2.5,0.3-4.3,2.4-4.3,4.9
v12.3c0,0.5-0.4,1-0.9,1c-1.1,0.1-2.2,0.1-3.4,0.1s-2.3,0-3.4-0.1c-0.5,0-0.9-0.5-0.9-1v-12.3c0-2.5-1.8-4.6-4.3-4.9
c-25.6-3.9-45.7-24-49.6-49.6c-0.3-2.5-2.4-4.3-4.9-4.3h-12.3c-0.5,0-1-0.4-1-0.9C388.3,184.5,388.2,183.4,388.2,182.2L388.2,182.2
L388.2,182.2z M390.3,179.9c-0.1,1.5-0.1,3.1,0,4.7h11.3c3.5,0,6.4,2.6,6.9,6c3.7,24.7,23.1,44.2,47.9,47.9c3.4,0.5,6,3.4,6,6.9
v11.3h4.6v-11.4c0-3.5,2.6-6.4,6-6.9c24.7-3.7,44.2-23.1,47.9-47.9c0.5-3.4,3.4-6,6.9-6h11.3c0.1-1.5,0.1-3.1,0-4.7h-11.3
c-3.5,0-6.4-2.6-6.9-6c-3.7-24.7-23.1-44.2-47.9-47.9c-3.4-0.5-6-3.4-6-6.9v-11.3h-4.6V119c0,3.5-2.6,6.4-6,6.9
c-24.7,3.7-44.2,23.1-47.9,47.9c-0.5,3.4-3.4,6-6.9,6h-11.3V179.9L390.3,179.9z"/>
<path id="path1462" class="st4" d="M705,99.6c1.7-6.3,3.9-11.3,9.1-16.5c7.7-7.7,17-11.3,27.7-10.8c13.5,0.6,25,8,31,20
c5.2,10.5,5.3,22.5,0.3,32.7c-4,7.9-10,12.8-18,16.6c-10.8,5.2-24.4,4.2-34.6-2.4c-7.7-5-13.7-13.5-15.7-22
C703.6,112.3,703.7,104.4,705,99.6L705,99.6L705,99.6z"/>
<path id="path1464" class="st4" d="M777.6,176c0.3,0,0.9-3.7,1.6-5.7c1.1-3,2.6-5.9,4-8c4.6-6.9,10.9-11.7,18.9-14.5
c3.8-1.3,5.2-2.1,11.5-2.1s7.2,0.7,11,2.1c11.1,3.9,19.1,11.8,23,22.8c1.5,4.2,2.3,5.2,2.3,11.6s-0.8,7.3-2.3,11.6
c-5.2,14.5-17.6,24-32.9,24.7c-10.6,0.5-19.5-2.7-26.9-10.2c-7.5-7.6-10.7-14.4-11-24.6C776.7,180.2,777.4,176.7,777.6,176
L777.6,176L777.6,176z"/>
<path id="path1466" class="st4" d="M704,248.2c1-6.7,5.8-15.6,12.9-20.8c4.1-3,10.7-6.2,15.7-7.3c13.2-2.8,27.9,2.6,35.9,13.3
c15.2,20,6.7,48.1-17,56.4c-5.3,1.9-13.3,3-19.2,1.7c-6.8-1.5-12.9-4.9-18.2-10.2c-5.2-5.3-8-9.9-9.6-16.4
C703.3,260.1,703.3,252.9,704,248.2L704,248.2L704,248.2z"/>
<path id="path1468" class="st4" d="M630.6,177c1.3-8.1,4-14.2,10.3-20.6c3.7-3.8,6.2-5.4,10-7.2c15.8-7.6,33.1-3,44.1,10.8
c5.1,6.4,7.6,13.6,7.6,22.1c0,29.9-33.8,46.7-58,28.7c-3.8-2.8-7.5-6.3-9.8-10.5C630.7,193.3,629.4,184.7,630.6,177L630.6,177
L630.6,177z"/>
<path id="path1500" class="st5" d="M397.8,180.2c8.9-0.3,9.7-0.8,11.9-10.1c4.8-21.1,20.1-37.2,41.3-42.6c2.2-0.5,5.2-1.1,5.9-1.1
s2.2-1,3.5-2.2l2-2.3l0.3-6.9v-7h4v5.9c0,6.6,1,9.9,3.4,11.2c0.8,0.4,3.9,1.5,7,2c16.3,2.7,30.7,14.9,38,29.5
c2.4,4.7,4.6,11.4,5.4,15.5c1.1,6.6,3.6,8.2,12.8,8.2h5.9v4H532c-6.6,0-7,0.1-8.8,1.8c-1.6,1.5-1.7,3.9-3.1,9.5
c-5,20.4-20.5,35.3-40.2,40.7c-2.9,0.8-6.4,1.8-7.8,2.2c-4.3,1.2-5.6,3.8-5.6,11.7v6.5h-4V250c0-7-0.8-9.9-3.1-10.8
c-0.7-0.3-4.6-1.3-8-2.1c-21.4-5.4-36.7-21.3-41.5-42.1c-2.3-10-3.2-10.7-12.8-10.7h-6.3v-3.8L397.8,180.2L397.8,180.2z"/>
<path id="path1504" class="st6" d="M473.9,108.8c15.3,1.8,31.2,8.9,43,20.4c12.2,12,19.2,27.5,21.6,45.7l0.4,2.8h-6.2
c-7.9,0-8.9-0.1-10.2-6.8c-4.6-22.3-22.1-41.8-44.2-46.1c-9.1-1.8-9.1-2.4-9.1-11.1v-5.4L473.9,108.8L473.9,108.8z"/>
<path id="path1506" class="st6" d="M392.2,167.8c6.9-32.2,32.9-55.7,65.6-59.4l2.1-0.2v6.4c0,6.9,0,6.8-1,7.8
c-1.4,1.3-3.8,1.5-7.9,2.4c-21.2,4.8-39.1,24.2-44,46.6c-1.5,6.9-2.7,6.4-10.3,6.4h-5.9l0-2.1C391,174.4,391.5,170.9,392.2,167.8
L392.2,167.8L392.2,167.8z"/>
<path id="path1508" class="st6" d="M397.4,186.6c7.3-0.3,8.2,1,9,5.3c0.8,4.8,2.9,11.1,5.5,16.3c7.7,15.8,22,27.1,39.9,31.6
c4,1,7.2,1.6,7.7,2.2c0.5,0.7,0.5,3.9,0.5,7.8v6.2l-3.8-0.4c-33.2-3.8-61.3-31.8-65-64.8l-0.5-4L397.4,186.6L397.4,186.6z"/>
<path id="path1510" class="st6" d="M470.6,242c1.1-1.2,3-1.3,6.7-2.1c20.5-4.4,36.6-19,43.5-39.3c0.8-2.4,1.7-5.8,2-7.5
c0.3-1.9,0.6-3.9,1.5-4.8c1.4-1.4,2.6-1.3,8.2-1.3h6.2l-0.3,2.5c-2.5,19-9.2,32.7-22.1,45.4c-10.6,10.4-23.8,17.4-38.2,20.2
c-2.9,0.5-6.1,1-7.1,1h-1.9v-5.9C469.2,244.9,469.3,243.4,470.6,242L470.6,242L470.6,242z"/>
<path id="path1512" class="st4" d="M869.4,76.9c0.6-0.5,1.7-0.6,7.5-0.6h6.9l1.2-1.1l1.2-1.1l0.1-7.1c0.1-4.1-0.5-6.5,0.2-7.6
c0.8-1.3,3.1-0.9,7.6-0.9s6.3,0.1,6.8,0.5c0.7,0.5,0.7,1.1,0.8,7.3c0,3.7,0.2,7.1,0.3,7.6c0.1,0.5,0.7,1.2,1.4,1.6
c1.1,0.7,1.9,0.7,7.8,0.7c3.8,0,6.9,0.2,7.3,0.4c0.7,0.4,0.7,0.9,0.7,7.3s0,7-0.7,7.3c-0.4,0.2-3.5,0.4-7.3,0.4
c-5.8,0-6.7,0.1-7.8,0.7c-0.7,0.4-1.3,1.1-1.4,1.6s-0.2,3.9-0.3,7.6c-0.1,6.2-0.2,6.8-0.8,7.3c-0.6,0.4-2.2,0.5-6.8,0.5
c-4.7,0-7.2,0.7-7.9-0.9c-0.5-1.2,0.1-3.8,0.1-7.5l-0.1-7.1l-1.2-1.1l-1.2-1.1h-6.9c-6.2,0-7-0.1-7.5-0.7c-0.5-0.6-0.6-1.7-0.6-7.1
C868.7,78.2,868.8,77.5,869.4,76.9L869.4,76.9L869.4,76.9z"/>
<path id="path1514" class="st6" d="M274,123.2h5.3v10.5h-10.5v-10.5H274L274,123.2z"/>
<path id="path1518" class="st6" d="M238.8,122.6c2.3-14.8,12.3-27.2,26.2-32.4c20-7.5,43,2.3,51.4,22c2.6,6.1,3.5,8,3.5,15.7
c0,6.5-0.6,7.9-1.6,11.3c-4.4,14.4-14.9,24.6-29.4,28.6c-2.9,0.8-5.2,1.1-10.7,1.1c-5.5,0-6.2-0.3-9-1.1
c-17.7-4.8-29.9-20-30.5-38.1C238.5,127.1,238.6,124,238.8,122.6L238.8,122.6L238.8,122.6z M250.8,134.7
c2.6,11.9,13,21.3,25.1,22.5c21.5,2.2,37.7-18.1,30.7-38.5c-1.4-4.2-3.3-7-7.1-10.8c-2.8-2.8-4.1-3.8-7.2-5.3
c-5.1-2.6-8.7-3.3-14.6-3c-6.8,0.3-11.7,2.2-16.9,6.3C252.4,112.5,248.4,124,250.8,134.7L250.8,134.7L250.8,134.7z"/>
<path id="path1520" class="st7" d="M286.1,4.8c0.8-1.3,0.9-2.3,5.3-2.5c4.3-0.2,13.5,0.1,32.6,0.1c32.8,0,35.2-0.5,36.3,0.6
c0.1,0.1,0.2,0.1,0.2,0.2c1.5,1.1,2.1,2.7,2.1,4.9v1.9h-77.3v-2C285.4,6.6,285.7,5.5,286.1,4.8L286.1,4.8L286.1,4.8z"/>
<path id="path1522" class="st7" d="M688.3,6.2c0.1-0.5,0.4-1.3,0.8-1.8c0.5-0.8,0.8-1.5,2.7-1.8c3.2-0.5,11.4-0.2,35.1-0.2
c19.3,0,28.3-0.5,32.5-0.4c2.7,0.1,3.2,0.8,3.6,1c1.6,0.9,2.3,2.4,2.3,4.8v2.1h-77.3l0-1.4C688.1,7.7,688.2,6.7,688.3,6.2
L688.3,6.2L688.3,6.2z"/>
<path id="path1524" class="st8" d="M874,334.6c20.6-10.1,37.2-21.9,53.9-38.2c35.7-34.8,57-78.7,63.1-129.7
c0.6-5.1,0.8-13.1,1-40.9l0.2-34.6h1.4c1,0,3,0,3.7,1.4l1.1,2.1l-0.2,30.4c-0.1,16.9-0.2,33.5-0.5,37
c-3.9,50.8-24.6,97.1-59.5,133.3c-16.9,17.6-35.7,31.5-57.9,42.8c-8.4,4.3-10.1,4.9-11.8,4.2c-1.1-0.4-4.2-2.7-4.1-3.2
C864.4,339.2,869,337.1,874,334.6L874,334.6L874,334.6z"/>
<path id="path1530" class="st4" d="M96.7,3.4c0.5-0.6,1.2-1.7,5-1.6c6.7,0.3,27.2-0.2,79.1-0.1L263.4,2l6.5,2.6l15,7.9l39.4,0.1
l39.6-0.4l13.2-7.1l7.7-2.9l7.3-0.4l48,0.1l22.1-0.1l34.1,0.1l10.5,0.1h10.5l8.6-0.1l25.6,0h19.4l27.5-0.2L620,1.8l11,0l19.6,0.1
l10.2,0l5.7,0.5l5.8,1.9l5.5,3l9.8,5.3h39.5l39.6-0.4l9.8-5.3l5.3-2.7l5.1-1.8l7.6-0.7l13.3,0.2l12.2-0.1h11.1l10.9,0l9.9,0h6.5
l2.9,0l17.4,0.1l21.5,0h10.8l8.4-0.1l5.1,0l5.9-0.2l3.1,2.2l1.1,3.2l-0.1,3.2c0,1.4-0.1,2.7-0.2,3.9c-0.1,0.9-0.1,1.8-0.4,2.6
c-0.4,1-1,1.7-1.3,2c-0.2,0.2-1.7,1.2-4.8,1.3c-2.1,0.1-4.7,0-8.3,0c-4.1,0-9.7,0.1-16.4,0c-10-0.1-23.1,0-40.4-0.1
c-13.2,0-28.7,0.2-47,0.1c-62.8-0.2-158-0.2-301-0.2L98.7,20l-1.8-1.3C94.3,16.9,94,6.1,96.7,3.4L96.7,3.4L96.7,3.4z"/>
<path id="path1532" class="st4" d="M535.4,22.4h392.4V31H143v-8.6H535.4L535.4,22.4z"/>
<ellipse id="path3879" class="st9" cx="279.4" cy="128.2" rx="40.3" ry="40.3"/>
<ellipse id="path3881" class="st10" cx="279.1" cy="128.2" rx="34.5" ry="34.1"/>
<path id="path96" d="M306.1,128.3l-26.4-26.8v7.7h-22.5v38.5h22.5v7.2L306.1,128.3L306.1,128.3z M265.2,136.1v-15.2h14.5v15.2
H265.2z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -0,0 +1,84 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 996.25 690.92">
<defs>
<style>
.cls-1 {
stroke: #fff;
stroke-miterlimit: 10;
stroke-width: 2px;
}
.cls-1, .cls-2 {
fill: #444542;
}
.cls-3 {
fill: #3b3b3b;
}
.cls-3, .cls-4, .cls-2, .cls-5, .cls-6, .cls-7 {
stroke-width: 0px;
}
.cls-4 {
fill: #3b3c3a;
}
.cls-5 {
fill: #454644;
}
.cls-6 {
fill: #20221f;
}
.cls-7 {
fill: #121212;
}
</style>
</defs>
<g id="Front">
<path id="Right_Grip" data-name="Right Grip" class="cls-6" d="m739.17,492.09c34,28.2,27.6,35.9,68.5,108.5,36.7,74.7,64.4,104.4,125.1,84.1h0c95.3-57.9,59.3-145.3,43.6-275.2-10-60.6-35.6-190.3-35.6-190.3l-201.6,272.9Z"/>
<path id="Left_Grip" data-name="Left Grip" class="cls-6" d="m55.47,219.19s-25.6,129.7-35.6,190.3c-15.7,129.9-51.7,217.2,43.6,275.1h0c60.8,20.3,88.4-9.4,125.1-84.1,40.9-72.7,34.5-80.3,68.5-108.5L55.47,219.19Z"/>
<path id="Right_Bumper" data-name="Right Bumper" class="cls-3" d="m649.47,19.99c10.1-4.3,39.7-22.5,58.7-19.7,59.5.9,166.7,17.7,172.6,81.2"/>
<path id="Left_Bumper" data-name="Left Bumper" class="cls-3" d="m115.57,81.49C121.47,18.09,228.57,1.29,288.17.29c19-2.8,48.6,15.4,58.7,19.7"/>
<path id="Background" class="cls-7" d="m739.17,492.09c35.5-30.8,68.5-74.7,96-113.5,26.9-36.3,94.7-136.7,105.6-159.3,0-2.4-6.3-30.1-12.8-56.2C892.27,3.49,675.37,19.69,498.17,19.69S104.07,3.49,68.27,162.99c-6.5,26-12.8,53.8-12.8,56.2,10.9,22.6,78.8,123,105.6,159.3,27.5,38.8,60.5,82.8,96,113.5"/>
<g id="Directional_Pad" data-name="Directional Pad">
<path id="Background-2" data-name="Background" class="cls-2" d="m439.37,325.09h-40c-2.8,0-5-2.2-5-5v-40c0-2.8-2.2-5-5-5h-30c-2.8,0-5,2.2-5,5v40c0,2.8-2.2,5-5,5h-40c-2.8,0-5,2.2-5,5v30c0,2.8,2.2,5,5,5h40c2.8,0,5,2.2,5,5v40c0,2.8,2.2,5,5,5h30c2.8,0,5-2.2,5-5v-40c0-2.8,2.2-5,5-5h40c2.8,0,5-2.2,5-5v-30c0-2.7-2.2-5-5-5Z"/>
</g>
<g id="R_Thumbstick" data-name="R Thumbstick">
<circle id="Background-3" data-name="Background" class="cls-6" cx="623.77" cy="345.09" r="55"/>
<circle id="Stick" class="cls-1" cx="623.77" cy="345.09" r="45"/>
</g>
<g id="L_Thumbstick" data-name="L Thumbstick">
<circle id="Background-4" data-name="Background" class="cls-6" cx="213.37" cy="206.39" r="55"/>
<circle id="Stick-2" data-name="Stick" class="cls-1" cx="213.37" cy="206.39" r="45" transform="translate(-24.53 383.95) rotate(-80.78)"/>
</g>
<g id="Minus_Button" data-name="Minus Button">
<circle id="_Background" data-name=" Background" class="cls-5" cx="374.17" cy="130.89" r="22.5"/>
</g>
<g id="Plus_Button" data-name="Plus Button">
<circle id="_Background-2" data-name=" Background" class="cls-5" cx="623.57" cy="131.19" r="22.5"/>
</g>
<g id="Home_Button" data-name="Home Button">
<circle id="_Background-3" data-name=" Background" class="cls-5" cx="578.57" cy="206.39" r="22.5"/>
</g>
<g id="Capture_Button" data-name="Capture Button">
<path class="cls-5" d="m441.77,228.09h-30c-2.8,0-5-2.2-5-5v-29.5c0-2.8,2.2-5,5-5h30c2.8,0,5,2.2,5,5v29.5c0,2.7-2.2,5-5,5Z"/>
</g>
<g id="Buttons">
<g id="A_Button" data-name="A Button">
<circle id="Background-5" data-name="Background" class="cls-4" cx="837.07" cy="206.39" r="35"/>
</g>
<g id="X_Button" data-name="X Button">
<circle id="Background-6" data-name="Background" class="cls-4" cx="767.07" cy="136.39" r="35"/>
</g>
<g id="Y_Button" data-name="Y Button">
<circle id="Background-7" data-name="Background" class="cls-4" cx="697.07" cy="206.39" r="35"/>
</g>
<g id="B_Button" data-name="B Button">
<circle id="Background-8" data-name="Background" class="cls-4" cx="767.07" cy="276.39" r="35"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@@ -549,9 +549,10 @@
"SoftwareKeyboardModeNumeric": "Must be 0-9 or '.' only",
"SoftwareKeyboardModeAlphabet": "Must be non CJK-characters only",
"SoftwareKeyboardModeASCII": "Must be ASCII text only",
"DialogControllerAppletMessagePlayerRange": "Application requests {0} player(s) with:\n\nTYPES: {1}\n\nPLAYERS: {2}\n\n{3}Please open Settings and reconfigure Input now or press Close.",
"DialogControllerAppletMessage": "Application requests exactly {0} player(s) with:\n\nTYPES: {1}\n\nPLAYERS: {2}\n\n{3}Please open Settings and reconfigure Input now or press Close.",
"DialogControllerAppletDockModeSet": "Docked mode set. Handheld is also invalid.\n\n",
"ControllerAppletControllers": "Supported Controllers:",
"ControllerAppletPlayers": "Players:",
"ControllerAppletDescription": "Your current configuration is invalid. Open settings and reconfigure your inputs.",
"ControllerAppletDocked": "Docked mode set. Handheld control should be disabled.",
"UpdaterRenaming": "Renaming Old Files...",
"UpdaterRenameFailed": "Updater was unable to rename file: {0}",
"UpdaterAddingFiles": "Adding New Files...",

View File

@@ -16,8 +16,10 @@ namespace Ryujinx.Ava.Common.Locale
private readonly Dictionary<LocaleKeys, string> _localeStrings;
private Dictionary<LocaleKeys, string> _localeDefaultStrings;
private readonly ConcurrentDictionary<LocaleKeys, object[]> _dynamicValues;
private string _localeLanguageCode;
public static LocaleManager Instance { get; } = new();
public event Action LocaleChanged;
public LocaleManager()
{
@@ -104,6 +106,15 @@ namespace Ryujinx.Ava.Common.Locale
}
}
public bool IsRTL()
{
return _localeLanguageCode switch
{
"he_IL" => true,
_ => false
};
}
public string UpdateAndGetDynamicValue(LocaleKeys key, params object[] values)
{
_dynamicValues[key] = values;
@@ -124,6 +135,9 @@ namespace Ryujinx.Ava.Common.Locale
{
this[item.Key] = item.Value;
}
_localeLanguageCode = languageCode;
LocaleChanged?.Invoke();
}
private static Dictionary<LocaleKeys, string> LoadJsonLanguage(string languageCode = DefaultLanguageCode)

View File

@@ -119,6 +119,7 @@
<None Remove="Assets\Locales\en_US.json" />
<None Remove="Assets\Locales\es_ES.json" />
<None Remove="Assets\Locales\fr_FR.json" />
<None Remove="Assets\Locales\he_IL.json" />
<None Remove="Assets\Locales\de_DE.json" />
<None Remove="Assets\Locales\it_IT.json" />
<None Remove="Assets\Locales\ja_JP.json" />
@@ -132,6 +133,10 @@
<None Remove="Assets\Locales\zh_TW.json" />
<None Remove="Assets\Styles\Styles.xaml" />
<None Remove="Assets\Styles\Themes.xaml" />
<None Remove="Assets\Icons\Controller_JoyConLeft.svg" />
<None Remove="Assets\Icons\Controller_JoyConPair.svg" />
<None Remove="Assets\Icons\Controller_JoyConRight.svg" />
<None Remove="Assets\Icons\Controller_ProCon.svg" />
</ItemGroup>
<ItemGroup>
@@ -139,6 +144,7 @@
<EmbeddedResource Include="Assets\Locales\en_US.json" />
<EmbeddedResource Include="Assets\Locales\es_ES.json" />
<EmbeddedResource Include="Assets\Locales\fr_FR.json" />
<EmbeddedResource Include="Assets\Locales\he_IL.json" />
<EmbeddedResource Include="Assets\Locales\de_DE.json" />
<EmbeddedResource Include="Assets\Locales\it_IT.json" />
<EmbeddedResource Include="Assets\Locales\ja_JP.json" />
@@ -151,6 +157,10 @@
<EmbeddedResource Include="Assets\Locales\zh_CN.json" />
<EmbeddedResource Include="Assets\Locales\zh_TW.json" />
<EmbeddedResource Include="Assets\Styles\Styles.xaml" />
<EmbeddedResource Include="Assets\Icons\Controller_JoyConLeft.svg" />
<EmbeddedResource Include="Assets\Icons\Controller_JoyConPair.svg" />
<EmbeddedResource Include="Assets\Icons\Controller_JoyConRight.svg" />
<EmbeddedResource Include="Assets\Icons\Controller_ProCon.svg" />
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="Assets\Locales\en_US.json" />

View File

@@ -29,14 +29,24 @@ namespace Ryujinx.Ava.UI.Applet
public bool DisplayMessageDialog(ControllerAppletUiArgs args)
{
string message = LocaleManager.Instance.UpdateAndGetDynamicValue(
args.PlayerCountMin == args.PlayerCountMax ? LocaleKeys.DialogControllerAppletMessage : LocaleKeys.DialogControllerAppletMessagePlayerRange,
args.PlayerCountMin == args.PlayerCountMax ? args.PlayerCountMin.ToString() : $"{args.PlayerCountMin}-{args.PlayerCountMax}",
args.SupportedStyles,
string.Join(", ", args.SupportedPlayers),
args.IsDocked ? LocaleManager.Instance[LocaleKeys.DialogControllerAppletDockModeSet] : "");
ManualResetEvent dialogCloseEvent = new(false);
return DisplayMessageDialog(LocaleManager.Instance[LocaleKeys.DialogControllerAppletTitle], message);
bool okPressed = false;
Dispatcher.UIThread.InvokeAsync(async () =>
{
var response = await ControllerAppletDialog.ShowControllerAppletDialog(_parent, args);
if (response == UserResult.Ok)
{
okPressed = true;
}
dialogCloseEvent.Set();
});
dialogCloseEvent.WaitOne();
return okPressed;
}
public bool DisplayMessageDialog(string title, string message)
@@ -75,6 +85,8 @@ namespace Ryujinx.Ava.UI.Applet
await _parent.SettingsWindow.ShowDialog(window);
_parent.SettingsWindow = null;
opened = false;
});

View File

@@ -0,0 +1,145 @@
<UserControl
x:Class="Ryujinx.Ava.UI.Applet.ControllerAppletDialog"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:applet="using:Ryujinx.Ava.UI.Applet"
mc:Ignorable="d"
Width="400"
Focusable="True"
x:DataType="applet:ControllerAppletDialog">
<Grid
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Border
Grid.Column="0"
Grid.Row="0"
Grid.ColumnSpan="2"
Margin="0 0 0 10"
BorderBrush="{DynamicResource ThemeControlBorderColor}"
BorderThickness="1"
CornerRadius="5">
<StackPanel
Spacing="10"
Margin="10">
<TextBlock
Text="{locale:Locale ControllerAppletDescription}" />
<TextBlock
IsVisible="{Binding IsDocked}"
FontWeight="Bold"
Text="{locale:Locale ControllerAppletDocked}" />
</StackPanel>
</Border>
<Border
Grid.Column="0"
Grid.Row="1"
BorderBrush="{DynamicResource ThemeControlBorderColor}"
BorderThickness="1"
CornerRadius="5"
Margin="0 0 10 0">
<StackPanel
Margin="10"
Spacing="10"
Orientation="Vertical">
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
TextAlignment="Center"
FontWeight="Bold"
Text="{locale:Locale ControllerAppletControllers}" />
<StackPanel
Spacing="10"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal">
<Image
Height="50"
Width="50"
Stretch="Uniform"
Source="{Binding ProControllerImage}"
IsVisible="{Binding SupportsProController}" />
<Image
Height="50"
Width="50"
Stretch="Uniform"
Source="{Binding JoyconPairImage}"
IsVisible="{Binding SupportsJoyconPair}" />
<Image
Height="50"
Width="50"
Stretch="Uniform"
Source="{Binding JoyconLeftImage}"
IsVisible="{Binding SupportsLeftJoycon}" />
<Image
Height="50"
Width="50"
Stretch="Uniform"
Source="{Binding JoyconRightImage}"
IsVisible="{Binding SupportsRightJoycon}" />
</StackPanel>
</StackPanel>
</Border>
<Border
Grid.Column="1"
Grid.Row="1"
BorderBrush="{DynamicResource ThemeControlBorderColor}"
BorderThickness="1"
CornerRadius="5">
<StackPanel
Margin="10"
Spacing="10"
Orientation="Vertical">
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
TextAlignment="Center"
FontWeight="Bold"
Text="{locale:Locale ControllerAppletPlayers}" />
<Border Height="50">
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
TextAlignment="Center"
FontSize="40"
FontWeight="Thin"
Text="{Binding PlayerCount}" />
</Border>
</StackPanel>
</Border>
<Panel
Margin="0 24 0 0"
Grid.Column="0"
Grid.Row="2"
Grid.ColumnSpan="2">
<StackPanel
Orientation="Horizontal"
Spacing="10"
HorizontalAlignment="Right">
<Button
Name="SaveButton"
MinWidth="90"
Command="{Binding OpenSettingsWindow}">
<TextBlock Text="{locale:Locale DialogOpenSettingsWindowLabel}" />
</Button>
<Button
Name="CancelButton"
MinWidth="90"
Command="{Binding Close}">
<TextBlock Text="{locale:Locale SettingsButtonClose}" />
</Button>
</StackPanel>
</Panel>
</Grid>
</UserControl>

View File

@@ -0,0 +1,139 @@
using Avalonia.Controls;
using Avalonia.Styling;
using Avalonia.Svg.Skia;
using Avalonia.Threading;
using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.Common;
using Ryujinx.HLE.HOS.Applets;
using Ryujinx.HLE.HOS.Services.Hid;
using System.Linq;
using System.Threading.Tasks;
namespace Ryujinx.Ava.UI.Applet
{
internal partial class ControllerAppletDialog : UserControl
{
private const string ProControllerResource = "Ryujinx.Ava/Assets/Icons/Controller_ProCon.svg";
private const string JoyConPairResource = "Ryujinx.Ava/Assets/Icons/Controller_JoyConPair.svg";
private const string JoyConLeftResource = "Ryujinx.Ava/Assets/Icons/Controller_JoyConLeft.svg";
private const string JoyConRightResource = "Ryujinx.Ava/Assets/Icons/Controller_JoyConRight.svg";
public static SvgImage ProControllerImage => GetResource(ProControllerResource);
public static SvgImage JoyconPairImage => GetResource(JoyConPairResource);
public static SvgImage JoyconLeftImage => GetResource(JoyConLeftResource);
public static SvgImage JoyconRightImage => GetResource(JoyConRightResource);
public string PlayerCount { get; set; } = "";
public bool SupportsProController { get; set; }
public bool SupportsLeftJoycon { get; set; }
public bool SupportsRightJoycon { get; set; }
public bool SupportsJoyconPair { get; set; }
public bool IsDocked { get; set; }
private readonly MainWindow _mainWindow;
public ControllerAppletDialog(MainWindow mainWindow, ControllerAppletUiArgs args)
{
if (args.PlayerCountMin == args.PlayerCountMax)
{
PlayerCount = args.PlayerCountMin.ToString();
}
else
{
PlayerCount = $"{args.PlayerCountMin} - {args.PlayerCountMax}";
}
SupportsProController = (args.SupportedStyles & ControllerType.ProController) != 0;
SupportsLeftJoycon = (args.SupportedStyles & ControllerType.JoyconLeft) != 0;
SupportsRightJoycon = (args.SupportedStyles & ControllerType.JoyconRight) != 0;
SupportsJoyconPair = (args.SupportedStyles & ControllerType.JoyconPair) != 0;
IsDocked = args.IsDocked;
_mainWindow = mainWindow;
DataContext = this;
InitializeComponent();
}
public ControllerAppletDialog(MainWindow mainWindow)
{
_mainWindow = mainWindow;
DataContext = this;
InitializeComponent();
}
public static async Task<UserResult> ShowControllerAppletDialog(MainWindow window, ControllerAppletUiArgs args)
{
ContentDialog contentDialog = new();
UserResult result = UserResult.Cancel;
ControllerAppletDialog content = new(window, args);
contentDialog.Title = LocaleManager.Instance[LocaleKeys.DialogControllerAppletTitle];
contentDialog.Content = content;
void Handler(ContentDialog sender, ContentDialogClosedEventArgs eventArgs)
{
if (eventArgs.Result == ContentDialogResult.Primary)
{
result = UserResult.Ok;
}
}
contentDialog.Closed += Handler;
Style bottomBorder = new(x => x.OfType<Grid>().Name("DialogSpace").Child().OfType<Border>());
bottomBorder.Setters.Add(new Setter(IsVisibleProperty, false));
contentDialog.Styles.Add(bottomBorder);
await ContentDialogHelper.ShowAsync(contentDialog);
return result;
}
private static SvgImage GetResource(string path)
{
SvgImage image = new();
if (!string.IsNullOrWhiteSpace(path))
{
SvgSource source = new();
source.Load(EmbeddedResources.GetStream(path));
image.Source = source;
}
return image;
}
public void OpenSettingsWindow()
{
if (_mainWindow.SettingsWindow == null)
{
Dispatcher.UIThread.InvokeAsync(async () =>
{
_mainWindow.SettingsWindow = new SettingsWindow(_mainWindow.VirtualFileSystem, _mainWindow.ContentManager);
_mainWindow.SettingsWindow.NavPanel.Content = _mainWindow.SettingsWindow.InputPage;
_mainWindow.SettingsWindow.NavPanel.SelectedItem = _mainWindow.SettingsWindow.NavPanel.MenuItems.ElementAt(1);
await ContentDialogHelper.ShowWindowAsync(_mainWindow.SettingsWindow, _mainWindow);
_mainWindow.SettingsWindow = null;
this.Close();
});
}
}
public void Close()
{
((ContentDialog)Parent)?.Hide();
}
}
}

View File

@@ -86,17 +86,17 @@
HorizontalAlignment="Stretch"
FontWeight="Bold"
Text="{Binding TitleName}"
TextAlignment="Left"
TextAlignment="Start"
TextWrapping="Wrap" />
<TextBlock
HorizontalAlignment="Stretch"
Text="{Binding Developer}"
TextAlignment="Left"
TextAlignment="Start"
TextWrapping="Wrap" />
<TextBlock
HorizontalAlignment="Stretch"
Text="{Binding Version}"
TextAlignment="Left"
TextAlignment="Start"
TextWrapping="Wrap" />
</StackPanel>
</Border>
@@ -110,12 +110,12 @@
<TextBlock
HorizontalAlignment="Stretch"
Text="{Binding TitleId}"
TextAlignment="Left"
TextAlignment="Start"
TextWrapping="Wrap" />
<TextBlock
HorizontalAlignment="Stretch"
Text="{Binding FileExtension}"
TextAlignment="Left"
TextAlignment="Start"
TextWrapping="Wrap" />
</StackPanel>
<StackPanel
@@ -127,17 +127,17 @@
<TextBlock
HorizontalAlignment="Stretch"
Text="{Binding TimePlayedString}"
TextAlignment="Right"
TextAlignment="End"
TextWrapping="Wrap" />
<TextBlock
HorizontalAlignment="Stretch"
Text="{Binding LastPlayedString, Converter={helpers:LocalizedNeverConverter}}"
TextAlignment="Right"
TextAlignment="End"
TextWrapping="Wrap" />
<TextBlock
HorizontalAlignment="Stretch"
Text="{Binding FileSizeString}"
TextAlignment="Right"
TextAlignment="End"
TextWrapping="Wrap" />
</StackPanel>
<ui:SymbolIcon

View File

@@ -18,6 +18,7 @@ namespace Ryujinx.Ava.UI.Helpers
public static class ContentDialogHelper
{
private static bool _isChoiceDialogOpen;
private static ContentDialogOverlayWindow _contentDialogOverlayWindow;
private async static Task<UserResult> ShowContentDialog(
string title,
@@ -310,16 +311,20 @@ namespace Ryujinx.Ava.UI.Helpers
public static async Task<ContentDialogResult> ShowAsync(ContentDialog contentDialog)
{
ContentDialogResult result;
ContentDialogOverlayWindow contentDialogOverlayWindow = null;
bool isTopDialog = true;
Window parent = GetMainWindow();
if (_contentDialogOverlayWindow != null)
{
isTopDialog = false;
}
if (parent is MainWindow window)
{
parent.Activate();
contentDialogOverlayWindow = new()
_contentDialogOverlayWindow = new ContentDialogOverlayWindow
{
Height = parent.Bounds.Height,
Width = parent.Bounds.Width,
@@ -331,14 +336,14 @@ namespace Ryujinx.Ava.UI.Helpers
void OverlayOnPositionChanged(object sender, PixelPointEventArgs e)
{
contentDialogOverlayWindow.Position = parent.PointToScreen(new Point());
_contentDialogOverlayWindow.Position = parent.PointToScreen(new Point());
}
contentDialogOverlayWindow.ContentDialog = contentDialog;
_contentDialogOverlayWindow.ContentDialog = contentDialog;
bool opened = false;
contentDialogOverlayWindow.Opened += OverlayOnActivated;
_contentDialogOverlayWindow.Opened += OverlayOnActivated;
async void OverlayOnActivated(object sender, EventArgs e)
{
@@ -349,12 +354,12 @@ namespace Ryujinx.Ava.UI.Helpers
opened = true;
contentDialogOverlayWindow.Position = parent.PointToScreen(new Point());
_contentDialogOverlayWindow.Position = parent.PointToScreen(new Point());
result = await ShowDialog();
}
result = await contentDialogOverlayWindow.ShowDialog<ContentDialogResult>(parent);
result = await _contentDialogOverlayWindow.ShowDialog<ContentDialogResult>(parent);
}
else
{
@@ -363,11 +368,11 @@ namespace Ryujinx.Ava.UI.Helpers
async Task<ContentDialogResult> ShowDialog()
{
if (contentDialogOverlayWindow is not null)
if (_contentDialogOverlayWindow is not null)
{
result = await contentDialog.ShowAsync(contentDialogOverlayWindow);
result = await contentDialog.ShowAsync(_contentDialogOverlayWindow);
contentDialogOverlayWindow!.Close();
_contentDialogOverlayWindow!.Close();
}
else
{
@@ -379,15 +384,22 @@ namespace Ryujinx.Ava.UI.Helpers
return result;
}
if (contentDialogOverlayWindow is not null)
if (isTopDialog && _contentDialogOverlayWindow is not null)
{
contentDialogOverlayWindow.Content = null;
contentDialogOverlayWindow.Close();
_contentDialogOverlayWindow.Content = null;
_contentDialogOverlayWindow.Close();
}
return result;
}
public static Task ShowWindowAsync(Window dialogWindow, Window mainWindow = null)
{
mainWindow ??= GetMainWindow();
return dialogWindow.ShowDialog(_contentDialogOverlayWindow ?? mainWindow);
}
private static Window GetMainWindow()
{
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime al)

View File

@@ -1,40 +0,0 @@
using Ryujinx.Ava.UI.ViewModels;
using System;
namespace Ryujinx.Ava.UI.Models
{
public class CheatModel : BaseModel
{
private bool _isEnabled;
public event EventHandler<bool> EnableToggled;
public CheatModel(string name, string buildId, bool isEnabled)
{
Name = name;
BuildId = buildId;
IsEnabled = isEnabled;
}
public bool IsEnabled
{
get => _isEnabled;
set
{
_isEnabled = value;
EnableToggled?.Invoke(this, _isEnabled);
OnPropertyChanged();
}
}
public string BuildId { get; }
public string BuildIdKey => $"{BuildId}-{Name}";
public string Name { get; }
public string CleanName => Name[1..^7];
}
}

View File

@@ -0,0 +1,57 @@
using Ryujinx.Ava.UI.ViewModels;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
namespace Ryujinx.Ava.UI.Models
{
public class CheatNode : BaseModel
{
private bool _isEnabled = false;
public ObservableCollection<CheatNode> SubNodes { get; } = new();
public string CleanName => Name[1..^7];
public string BuildIdKey => $"{BuildId}-{Name}";
public bool IsRootNode { get; }
public string Name { get; }
public string BuildId { get; }
public string Path { get; }
public bool IsEnabled
{
get
{
if (SubNodes.Count > 0)
{
return SubNodes.ToList().TrueForAll(x => x.IsEnabled);
}
return _isEnabled;
}
set
{
foreach (var cheat in SubNodes)
{
cheat.IsEnabled = value;
cheat.OnPropertyChanged();
}
_isEnabled = value;
}
}
public CheatNode(string name, string buildId, string path, bool isRootNode, bool isEnabled = false)
{
Name = name;
BuildId = buildId;
Path = path;
IsEnabled = isEnabled;
IsRootNode = isRootNode;
SubNodes.CollectionChanged += CheatsList_CollectionChanged;
}
private void CheatsList_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
OnPropertyChanged(nameof(IsEnabled));
}
}
}

View File

@@ -1,51 +0,0 @@
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
namespace Ryujinx.Ava.UI.Models
{
public class CheatsList : ObservableCollection<CheatModel>
{
public CheatsList(string buildId, string path)
{
BuildId = buildId;
Path = path;
CollectionChanged += CheatsList_CollectionChanged;
}
public string BuildId { get; }
public string Path { get; }
public bool IsEnabled
{
get
{
return this.ToList().TrueForAll(x => x.IsEnabled);
}
set
{
foreach (var cheat in this)
{
cheat.IsEnabled = value;
}
OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsEnabled)));
}
}
private void CheatsList_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
(e.NewItems[0] as CheatModel).EnableToggled += Item_EnableToggled;
}
}
private void Item_EnableToggled(object sender, bool e)
{
OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsEnabled)));
}
}
}

View File

@@ -1,11 +1,12 @@
<UserControl
<UserControl
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignWidth="800"
mc:Ignorable="d"
d:DesignWidth="800"
d:DesignHeight="450"
x:Class="Ryujinx.Ava.UI.Renderer.RendererHost"
FlowDirection="LeftToRight"
Focusable="True">
</UserControl>
</UserControl>

View File

@@ -17,6 +17,7 @@ using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
namespace Ryujinx.Ava.UI.ViewModels
@@ -188,35 +189,83 @@ namespace Ryujinx.Ava.UI.ViewModels
_httpClient.Dispose();
}
private async Task LoadContentAsync()
private static bool TryGetAmiiboJson(string json, out AmiiboJson amiiboJson)
{
string amiiboJsonString = DefaultJson;
if (File.Exists(_amiiboJsonPath))
if (string.IsNullOrEmpty(json))
{
amiiboJsonString = await File.ReadAllTextAsync(_amiiboJsonPath);
amiiboJson = JsonHelper.Deserialize(DefaultJson, _serializerContext.AmiiboJson);
if (await NeedsUpdate(JsonHelper.Deserialize(amiiboJsonString, _serializerContext.AmiiboJson).LastUpdated))
{
amiiboJsonString = await DownloadAmiiboJson();
}
return false;
}
else
try
{
amiiboJson = JsonHelper.Deserialize(json, _serializerContext.AmiiboJson);
return true;
}
catch (JsonException exception)
{
Logger.Error?.Print(LogClass.Application, $"Unable to deserialize amiibo data: {exception}");
amiiboJson = JsonHelper.Deserialize(DefaultJson, _serializerContext.AmiiboJson);
return false;
}
}
private async Task<AmiiboJson> GetMostRecentAmiiboListOrDefaultJson()
{
bool localIsValid = false;
bool remoteIsValid = false;
AmiiboJson amiiboJson = new();
try
{
try
{
amiiboJsonString = await DownloadAmiiboJson();
if (File.Exists(_amiiboJsonPath))
{
localIsValid = TryGetAmiiboJson(await File.ReadAllTextAsync(_amiiboJsonPath), out amiiboJson);
}
}
catch (Exception ex)
catch (Exception exception)
{
Logger.Error?.Print(LogClass.Application, $"Failed to download amiibo data: {ex}");
Logger.Warning?.Print(LogClass.Application, $"Unable to read data from '{_amiiboJsonPath}': {exception}");
}
if (!localIsValid || await NeedsUpdate(amiiboJson.LastUpdated))
{
remoteIsValid = TryGetAmiiboJson(await DownloadAmiiboJson(), out amiiboJson);
}
}
catch (Exception exception)
{
if (!(localIsValid || remoteIsValid))
{
Logger.Error?.Print(LogClass.Application, $"Couldn't get valid amiibo data: {exception}");
// Neither local or remote files are valid JSON, close window.
ShowInfoDialog();
Close();
}
else if (!remoteIsValid)
{
Logger.Warning?.Print(LogClass.Application, $"Couldn't update amiibo data: {exception}");
// Only the local file is valid, the local one should be used
// but the user should be warned.
ShowInfoDialog();
}
}
_amiiboList = JsonHelper.Deserialize(amiiboJsonString, _serializerContext.AmiiboJson).Amiibo;
_amiiboList = _amiiboList.OrderBy(amiibo => amiibo.AmiiboSeries).ToList();
return amiiboJson;
}
private async Task LoadContentAsync()
{
AmiiboJson amiiboJson = await GetMostRecentAmiiboListOrDefaultJson();
_amiiboList = amiiboJson.Amiibo.OrderBy(amiibo => amiibo.AmiiboSeries).ToList();
ParseAmiiboData();
}
@@ -364,43 +413,50 @@ namespace Ryujinx.Ava.UI.ViewModels
{
try
{
HttpResponseMessage response =
await _httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Head, "https://amiibo.ryujinx.org/"));
HttpResponseMessage response = await _httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Head, "https://amiibo.ryujinx.org/"));
if (response.IsSuccessStatusCode)
{
return response.Content.Headers.LastModified != new DateTimeOffset(oldLastModified.Ticks - (oldLastModified.Ticks % TimeSpan.TicksPerSecond), TimeSpan.Zero);
return response.Content.Headers.LastModified != oldLastModified;
}
return false;
}
catch (Exception ex)
catch (HttpRequestException exception)
{
Logger.Error?.Print(LogClass.Application, $"Failed to check for amiibo updates: {ex}");
ShowInfoDialog();
return false;
Logger.Error?.Print(LogClass.Application, $"Unable to check for amiibo data updates: {exception}");
}
return false;
}
private async Task<string> DownloadAmiiboJson()
{
HttpResponseMessage response = await _httpClient.GetAsync("https://amiibo.ryujinx.org/");
if (response.IsSuccessStatusCode)
try
{
string amiiboJsonString = await response.Content.ReadAsStringAsync();
HttpResponseMessage response = await _httpClient.GetAsync("https://amiibo.ryujinx.org/");
using (FileStream amiiboJsonStream = File.Create(_amiiboJsonPath, 4096, FileOptions.WriteThrough))
if (response.IsSuccessStatusCode)
{
amiiboJsonStream.Write(Encoding.UTF8.GetBytes(amiiboJsonString));
string amiiboJsonString = await response.Content.ReadAsStringAsync();
try
{
using FileStream dlcJsonStream = File.Create(_amiiboJsonPath, 4096, FileOptions.WriteThrough);
dlcJsonStream.Write(Encoding.UTF8.GetBytes(amiiboJsonString));
}
catch (Exception exception)
{
Logger.Warning?.Print(LogClass.Application, $"Couldn't write amiibo data to file '{_amiiboJsonPath}: {exception}'");
}
return amiiboJsonString;
}
return amiiboJsonString;
Logger.Error?.Print(LogClass.Application, $"Failed to download amiibo data. Response status code: {response.StatusCode}");
}
catch (HttpRequestException exception)
{
Logger.Error?.Print(LogClass.Application, $"Failed to request amiibo data: {exception}");
}
Logger.Error?.Print(LogClass.Application, $"Failed to download amiibo data. Response status code: {response.StatusCode}");
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogAmiiboApiTitle],
LocaleManager.Instance[LocaleKeys.DialogAmiiboApiFailFetchMessage],
@@ -408,9 +464,7 @@ namespace Ryujinx.Ava.UI.ViewModels
"",
LocaleManager.Instance[LocaleKeys.RyujinxInfo]);
Close();
return DefaultJson;
return null;
}
private void Close()

View File

@@ -90,6 +90,7 @@ namespace Ryujinx.Ava.UI.ViewModels
private string _pauseKey = "F5";
private string _screenshotKey = "F8";
private float _volume;
private float _volumeBeforeMute;
private string _backendText;
private bool _canUpdate = true;
@@ -554,6 +555,17 @@ namespace Ryujinx.Ava.UI.ViewModels
}
}
public float VolumeBeforeMute
{
get => _volumeBeforeMute;
set
{
_volumeBeforeMute = value;
OnPropertyChanged();
}
}
public bool ShowStatusSeparator
{
get => _showStatusSeparator;

View File

@@ -101,7 +101,7 @@
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
SelectedIndex="0"
ItemsSource="{Binding ProfilesList}"
ItemsSource="{Binding ProfilesList}"
Text="{Binding ProfileName, Mode=TwoWay}" />
<Button
Grid.Column="2"
@@ -218,6 +218,7 @@
<Grid
Name="SettingButtons"
MinHeight="450"
FlowDirection="LeftToRight"
IsVisible="{Binding ShowSettings}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />

View File

@@ -122,6 +122,8 @@ namespace Ryujinx.Ava.UI.Views.Main
await Window.SettingsWindow.ShowDialog(Window);
Window.SettingsWindow = null;
ViewModel.LoadConfigurableHotKeys();
}

View File

@@ -88,7 +88,7 @@
IsVisible="{Binding !ShowLoadProgress}"
PointerReleased="VsyncStatus_PointerReleased"
Text="VSync"
TextAlignment="Left" />
TextAlignment="Start" />
<Border
Width="2"
Height="12"
@@ -105,7 +105,7 @@
IsVisible="{Binding !ShowLoadProgress}"
PointerReleased="DockedStatus_PointerReleased"
Text="{Binding DockedStatusText}"
TextAlignment="Left" />
TextAlignment="Start" />
<Border
Width="2"
Height="12"
@@ -225,7 +225,7 @@
VerticalAlignment="Center"
IsVisible="{Binding !ShowLoadProgress}"
Text="{Binding GameStatusText}"
TextAlignment="Left" />
TextAlignment="Start" />
<Border
Width="2"
Height="12"
@@ -240,7 +240,7 @@
VerticalAlignment="Center"
IsVisible="{Binding !ShowLoadProgress}"
Text="{Binding FifoStatusText}"
TextAlignment="Left" />
TextAlignment="Start" />
<Border
Width="2"
Height="12"
@@ -255,7 +255,7 @@
VerticalAlignment="Center"
IsVisible="{Binding !ShowLoadProgress}"
Text="{Binding BackendText}"
TextAlignment="Left" />
TextAlignment="Start" />
<Border
Width="2"
Height="12"
@@ -270,7 +270,7 @@
VerticalAlignment="Center"
IsVisible="{Binding !ShowLoadProgress}"
Text="{Binding GpuNameText}"
TextAlignment="Left" />
TextAlignment="Start" />
</StackPanel>
<StackPanel
Grid.Column="3"

View File

@@ -27,7 +27,7 @@
Grid.Row="0"
TextWrapping="Wrap"
HorizontalAlignment="Left"
TextAlignment="Left"
TextAlignment="Start"
Text="{locale:Locale ProfileImageSelectionNote}" />
<StackPanel
Grid.Row="2"
@@ -59,4 +59,4 @@
</Button>
</StackPanel>
</Grid>
</UserControl>
</UserControl>

View File

@@ -49,7 +49,7 @@
<TextBlock
HorizontalAlignment="Stretch"
Text="{Binding UserId}"
TextAlignment="Left"
TextAlignment="Start"
TextWrapping="Wrap" />
<Button Grid.Column="1"
HorizontalAlignment="Right"
@@ -79,4 +79,4 @@
</Button>
</StackPanel>
</Grid>
</UserControl>
</UserControl>

View File

@@ -238,7 +238,7 @@
<TextBlock
FontSize="10"
Text="{locale:Locale AboutRyujinxContributorsButtonHeader}"
TextAlignment="Right"
TextAlignment="End"
ToolTip.Tip="{locale:Locale AboutRyujinxMaintainersContentTooltipMessage}" />
</Button>
</StackPanel>

View File

@@ -72,4 +72,4 @@
Click="CancelButton_Click" />
</Grid>
</Grid>
</window:StyleableWindow>
</window:StyleableWindow>

View File

@@ -86,28 +86,16 @@
</Style>
</Styles>
</TreeView.Styles>
<TreeView.DataTemplates>
<TreeDataTemplate DataType="model:CheatsList" ItemsSource="{Binding}">
<TreeView.ItemTemplate>
<TreeDataTemplate ItemsSource="{Binding SubNodes}">
<StackPanel HorizontalAlignment="Left" Orientation="Horizontal">
<CheckBox MinWidth="20" IsChecked="{Binding IsEnabled}" />
<TextBlock Width="150" Text="{Binding BuildId}" />
<TextBlock Text="{Binding Path}" />
<TextBlock Width="150" Text="{Binding CleanName}" IsVisible="{Binding !IsRootNode}" />
<TextBlock Width="150" Text="{Binding BuildId}" IsVisible="{Binding IsRootNode}" />
<TextBlock Text="{Binding Path}" IsVisible="{Binding IsRootNode}" />
</StackPanel>
</TreeDataTemplate>
<DataTemplate x:DataType="model:CheatModel">
<StackPanel
Margin="0"
HorizontalAlignment="Left"
Orientation="Horizontal">
<CheckBox
MinWidth="20"
Margin="5,0"
Padding="0"
IsChecked="{Binding IsEnabled}" />
<TextBlock VerticalAlignment="Center" Text="{Binding CleanName}" />
</StackPanel>
</DataTemplate>
</TreeView.DataTemplates>
</TreeView.ItemTemplate>
</TreeView>
</Border>
<DockPanel

View File

@@ -17,7 +17,7 @@ namespace Ryujinx.Ava.UI.Windows
private readonly string _enabledCheatsPath;
public bool NoCheatsFound { get; }
public AvaloniaList<CheatsList> LoadedCheats { get; }
public AvaloniaList<CheatNode> LoadedCheats { get; }
public string Heading { get; }
public string BuildId { get; }
@@ -33,7 +33,7 @@ namespace Ryujinx.Ava.UI.Windows
public CheatWindow(VirtualFileSystem virtualFileSystem, string titleId, string titleName, string titlePath)
{
LoadedCheats = new AvaloniaList<CheatsList>();
LoadedCheats = new AvaloniaList<CheatNode>();
Heading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.CheatWindowHeading, titleName, titleId.ToUpper());
BuildId = ApplicationData.GetApplicationBuildId(virtualFileSystem, titlePath);
@@ -62,7 +62,7 @@ namespace Ryujinx.Ava.UI.Windows
string currentCheatFile = string.Empty;
string buildId = string.Empty;
CheatsList currentGroup = null;
CheatNode currentGroup = null;
foreach (var cheat in mods.Cheats)
{
@@ -72,13 +72,13 @@ namespace Ryujinx.Ava.UI.Windows
string parentPath = currentCheatFile.Replace(titleModsPath, "");
buildId = Path.GetFileNameWithoutExtension(currentCheatFile).ToUpper();
currentGroup = new CheatsList(buildId, parentPath);
currentGroup = new CheatNode("", buildId, parentPath, true);
LoadedCheats.Add(currentGroup);
}
var model = new CheatModel(cheat.Name, buildId, enabled.Contains($"{buildId}-{cheat.Name}"));
currentGroup?.Add(model);
var model = new CheatNode(cheat.Name, buildId, "", false, enabled.Contains($"{buildId}-{cheat.Name}"));
currentGroup?.SubNodes.Add(model);
cheatAdded++;
}
@@ -104,7 +104,7 @@ namespace Ryujinx.Ava.UI.Windows
foreach (var cheats in LoadedCheats)
{
foreach (var cheat in cheats)
foreach (var cheat in cheats.SubNodes)
{
if (cheat.IsEnabled)
{

View File

@@ -158,7 +158,7 @@
FontWeight="Bold"
IsVisible="{Binding ShowLoadProgress}"
Text="{Binding LoadHeading}"
TextAlignment="Left"
TextAlignment="Start"
TextWrapping="Wrap"
MaxWidth="500" />
<Border
@@ -192,7 +192,7 @@
FontSize="18"
IsVisible="{Binding ShowLoadProgress}"
Text="{Binding CacheLoadStatus}"
TextAlignment="Left"
TextAlignment="Start"
MaxWidth="500" />
</Grid>
</Grid>
@@ -202,4 +202,4 @@
Grid.Row="2" />
</Grid>
</Grid>
</window:StyleableWindow>
</window:StyleableWindow>

View File

@@ -436,10 +436,11 @@ namespace Ryujinx.Ava.UI.Windows
{
if (!volumeSplitButton.IsChecked)
{
ViewModel.AppHost.Device.SetVolume(ConfigurationState.Instance.System.AudioVolume);
ViewModel.AppHost.Device.SetVolume(ViewModel.VolumeBeforeMute);
}
else
{
ViewModel.VolumeBeforeMute = ViewModel.AppHost.Device.GetVolume();
ViewModel.AppHost.Device.SetVolume(0);
}

View File

@@ -101,6 +101,9 @@
<Style Selector="Grid#PlaceholderGrid">
<Setter Property="Height" Value="40" />
</Style>
<Style Selector="ui|NavigationViewItem ui|SymbolIcon">
<Setter Property="FlowDirection" Value="LeftToRight" />
</Style>
</ui:NavigationView.Styles>
</ui:NavigationView>
<ReversibleStackPanel

View File

@@ -1,7 +1,9 @@
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Media;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ui.Common.Configuration;
using System.IO;
using System.Reflection;
@@ -22,6 +24,14 @@ namespace Ryujinx.Ava.UI.Windows
Icon = new WindowIcon(stream);
stream.Position = 0;
IconImage = new Bitmap(stream);
LocaleManager.Instance.LocaleChanged += LocaleChanged;
LocaleChanged();
}
private void LocaleChanged()
{
FlowDirection = LocaleManager.Instance.IsRTL() ? FlowDirection.RightToLeft : FlowDirection.LeftToRight;
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)

View File

@@ -130,4 +130,4 @@
</StackPanel>
</Panel>
</Grid>
</UserControl>
</UserControl>

View File

@@ -1,62 +0,0 @@
using System;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
namespace Ryujinx.Cpu.AppleHv
{
static class HvCodePatcher
{
private const uint XMask = 0x3f808000u;
private const uint XValue = 0x8000000u;
private const uint ZrIndex = 31u;
public static void RewriteUnorderedExclusiveInstructions(Span<byte> code)
{
Span<uint> codeUint = MemoryMarshal.Cast<byte, uint>(code);
Span<Vector128<uint>> codeVector = MemoryMarshal.Cast<byte, Vector128<uint>>(code);
Vector128<uint> mask = Vector128.Create(XMask);
Vector128<uint> value = Vector128.Create(XValue);
for (int index = 0; index < codeVector.Length; index++)
{
Vector128<uint> v = codeVector[index];
if (Vector128.EqualsAny(Vector128.BitwiseAnd(v, mask), value))
{
int baseIndex = index * 4;
for (int instIndex = baseIndex; instIndex < baseIndex + 4; instIndex++)
{
ref uint inst = ref codeUint[instIndex];
if ((inst & XMask) != XValue)
{
continue;
}
bool isPair = (inst & (1u << 21)) != 0;
bool isLoad = (inst & (1u << 22)) != 0;
uint rt2 = (inst >> 10) & 0x1fu;
uint rs = (inst >> 16) & 0x1fu;
if (isLoad && rs != ZrIndex)
{
continue;
}
if (!isPair && rt2 != ZrIndex)
{
continue;
}
// Set the ordered flag.
inst |= 1u << 15;
}
}
}
}
}
}

View File

@@ -724,18 +724,6 @@ namespace Ryujinx.Cpu.AppleHv
/// <inheritdoc/>
public void Reprotect(ulong va, ulong size, MemoryPermission protection)
{
if (protection.HasFlag(MemoryPermission.Execute))
{
// Some applications use unordered exclusive memory access instructions
// where it is not valid to do so, leading to memory re-ordering that
// makes the code behave incorrectly on some CPUs.
// To work around this, we force all such accesses to be ordered.
using WritableRegion writableRegion = GetWritableRegion(va, (int)size);
HvCodePatcher.RewriteUnorderedExclusiveInstructions(writableRegion.Memory.Span);
}
// TODO
}

View File

@@ -1,9 +1,13 @@
using System;
namespace Ryujinx.Graphics.GAL
{
[Flags]
public enum BufferAccess
{
Default,
FlushPersistent,
Stream
Default = 0,
FlushPersistent = 1 << 0,
Stream = 1 << 1,
SparseCompatible = 1 << 2,
}
}

View File

@@ -23,6 +23,7 @@ namespace Ryujinx.Graphics.GAL
public readonly bool SupportsR4G4B4A4Format;
public readonly bool SupportsScaledVertexFormats;
public readonly bool SupportsSnormBufferTextureFormat;
public readonly bool SupportsSparseBuffer;
public readonly bool Supports5BitComponentFormat;
public readonly bool SupportsBlendEquationAdvanced;
public readonly bool SupportsFragmentShaderInterlock;
@@ -79,6 +80,7 @@ namespace Ryujinx.Graphics.GAL
bool supportsScaledVertexFormats,
bool supportsSnormBufferTextureFormat,
bool supports5BitComponentFormat,
bool supportsSparseBuffer,
bool supportsBlendEquationAdvanced,
bool supportsFragmentShaderInterlock,
bool supportsFragmentShaderOrderingIntel,
@@ -130,6 +132,7 @@ namespace Ryujinx.Graphics.GAL
SupportsScaledVertexFormats = supportsScaledVertexFormats;
SupportsSnormBufferTextureFormat = supportsSnormBufferTextureFormat;
Supports5BitComponentFormat = supports5BitComponentFormat;
SupportsSparseBuffer = supportsSparseBuffer;
SupportsBlendEquationAdvanced = supportsBlendEquationAdvanced;
SupportsFragmentShaderInterlock = supportsFragmentShaderInterlock;
SupportsFragmentShaderOrderingIntel = supportsFragmentShaderOrderingIntel;

View File

@@ -16,13 +16,10 @@ namespace Ryujinx.Graphics.GAL
void BackgroundContextAction(Action action, bool alwaysBackground = false);
BufferHandle CreateBuffer(int size, BufferHandle storageHint);
BufferHandle CreateBuffer(int size)
{
return CreateBuffer(size, BufferHandle.Null);
}
BufferHandle CreateBuffer(int size, BufferAccess access = BufferAccess.Default);
BufferHandle CreateBuffer(int size, BufferAccess access, BufferHandle storageHint);
BufferHandle CreateBuffer(nint pointer, int size);
BufferHandle CreateBuffer(int size, BufferAccess access);
BufferHandle CreateBufferSparse(ReadOnlySpan<BufferRange> storageBuffers);
IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info);

View File

@@ -44,6 +44,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
Register<ActionCommand>(CommandType.Action);
Register<CreateBufferCommand>(CommandType.CreateBuffer);
Register<CreateBufferAccessCommand>(CommandType.CreateBufferAccess);
Register<CreateBufferSparseCommand>(CommandType.CreateBufferSparse);
Register<CreateHostBufferCommand>(CommandType.CreateHostBuffer);
Register<CreateProgramCommand>(CommandType.CreateProgram);
Register<CreateSamplerCommand>(CommandType.CreateSampler);

View File

@@ -5,6 +5,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
Action,
CreateBuffer,
CreateBufferAccess,
CreateBufferSparse,
CreateHostBuffer,
CreateProgram,
CreateSampler,

View File

@@ -5,12 +5,14 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer
public readonly CommandType CommandType => CommandType.CreateBuffer;
private BufferHandle _threadedHandle;
private int _size;
private BufferAccess _access;
private BufferHandle _storageHint;
public void Set(BufferHandle threadedHandle, int size, BufferHandle storageHint)
public void Set(BufferHandle threadedHandle, int size, BufferAccess access, BufferHandle storageHint)
{
_threadedHandle = threadedHandle;
_size = size;
_access = access;
_storageHint = storageHint;
}
@@ -23,7 +25,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer
hint = threaded.Buffers.MapBuffer(command._storageHint);
}
threaded.Buffers.AssignBuffer(command._threadedHandle, renderer.CreateBuffer(command._size, hint));
threaded.Buffers.AssignBuffer(command._threadedHandle, renderer.CreateBuffer(command._size, command._access, hint));
}
}
}

View File

@@ -0,0 +1,25 @@
using Ryujinx.Graphics.GAL.Multithreading.Model;
using System;
namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer
{
struct CreateBufferSparseCommand : IGALCommand, IGALCommand<CreateBufferSparseCommand>
{
public readonly CommandType CommandType => CommandType.CreateBufferSparse;
private BufferHandle _threadedHandle;
private SpanRef<BufferRange> _buffers;
public void Set(BufferHandle threadedHandle, SpanRef<BufferRange> buffers)
{
_threadedHandle = threadedHandle;
_buffers = buffers;
}
public static void Run(ref CreateBufferSparseCommand command, ThreadedRenderer threaded, IRenderer renderer)
{
Span<BufferRange> buffers = command._buffers.Get(threaded);
threaded.Buffers.AssignBuffer(command._threadedHandle, renderer.CreateBufferSparse(threaded.Buffers.MapBufferRanges(buffers)));
command._buffers.Dispose(threaded);
}
}
}

View File

@@ -263,10 +263,19 @@ namespace Ryujinx.Graphics.GAL.Multithreading
}
}
public BufferHandle CreateBuffer(int size, BufferHandle storageHint)
public BufferHandle CreateBuffer(int size, BufferAccess access)
{
BufferHandle handle = Buffers.CreateBufferHandle();
New<CreateBufferCommand>().Set(handle, size, storageHint);
New<CreateBufferAccessCommand>().Set(handle, size, access);
QueueCommand();
return handle;
}
public BufferHandle CreateBuffer(int size, BufferAccess access, BufferHandle storageHint)
{
BufferHandle handle = Buffers.CreateBufferHandle();
New<CreateBufferCommand>().Set(handle, size, access, storageHint);
QueueCommand();
return handle;
@@ -281,10 +290,10 @@ namespace Ryujinx.Graphics.GAL.Multithreading
return handle;
}
public BufferHandle CreateBuffer(int size, BufferAccess access)
public BufferHandle CreateBufferSparse(ReadOnlySpan<BufferRange> storageBuffers)
{
BufferHandle handle = Buffers.CreateBufferHandle();
New<CreateBufferAccessCommand>().Set(handle, size, access);
New<CreateBufferSparseCommand>().Set(handle, CopySpan(storageBuffers));
QueueCommand();
return handle;

View File

@@ -5,6 +5,7 @@ using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Engine.GPFifo;
using Ryujinx.Graphics.Gpu.Engine.Threed;
using Ryujinx.Graphics.Gpu.Engine.Types;
using Ryujinx.Memory.Range;
using System;
using System.Collections.Generic;
@@ -392,12 +393,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
_processor.ThreedClass.DrawIndirect(
topology,
indirectBufferAddress,
0,
new MultiRange(indirectBufferAddress, IndirectIndexedDataEntrySize),
default,
1,
IndirectIndexedDataEntrySize,
indexCount,
Threed.IndirectDrawType.DrawIndexedIndirect);
IndirectDrawType.DrawIndexedIndirect);
}
else
{
@@ -494,13 +495,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
ulong indirectBufferSize = (ulong)maxDrawCount * (ulong)stride;
ulong indirectBufferAddress = bufferCache.TranslateAndCreateBuffer(_processor.MemoryManager, indirectBufferGpuVa, indirectBufferSize);
ulong parameterBufferAddress = bufferCache.TranslateAndCreateBuffer(_processor.MemoryManager, parameterBufferGpuVa, 4);
MultiRange indirectBufferRange = bufferCache.TranslateAndCreateMultiBuffers(_processor.MemoryManager, indirectBufferGpuVa, indirectBufferSize);
MultiRange parameterBufferRange = bufferCache.TranslateAndCreateMultiBuffers(_processor.MemoryManager, parameterBufferGpuVa, 4);
_processor.ThreedClass.DrawIndirect(
topology,
indirectBufferAddress,
parameterBufferAddress,
indirectBufferRange,
parameterBufferRange,
maxDrawCount,
stride,
indexCount,

View File

@@ -370,8 +370,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
{
var memoryManager = _channel.MemoryManager;
address = memoryManager.Translate(address);
BufferRange range = memoryManager.Physical.BufferCache.GetBufferRange(address, size);
BufferRange range = memoryManager.Physical.BufferCache.GetBufferRange(memoryManager.GetPhysicalRegions(address, size));
ITexture bufferTexture = _vacContext.EnsureBufferTexture(index + 2, format);
bufferTexture.SetStorage(range);
@@ -412,9 +411,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
var memoryManager = _channel.MemoryManager;
address = memoryManager.Translate(address + indexOffset);
ulong misalign = address & ((ulong)_context.Capabilities.TextureBufferOffsetAlignment - 1);
BufferRange range = memoryManager.Physical.BufferCache.GetBufferRange(address - misalign, size + misalign);
BufferRange range = memoryManager.Physical.BufferCache.GetBufferRange(memoryManager.GetPhysicalRegions(address + indexOffset - misalign, size + misalign));
misalignedOffset = (int)misalign >> shift;
SetIndexBufferTexture(reservations, range, format);

View File

@@ -3,6 +3,7 @@ using Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw;
using Ryujinx.Graphics.Gpu.Engine.Types;
using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Memory.Range;
using System;
namespace Ryujinx.Graphics.Gpu.Engine.Threed
@@ -102,6 +103,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// <param name="argument">Method call argument</param>
public void DrawEnd(ThreedClass engine, int argument)
{
_drawState.DrawUsesEngineState = true;
DrawEnd(
engine,
_state.State.IndexBufferState.First,
@@ -204,10 +207,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
}
else
{
#pragma warning disable IDE0059 // Remove unnecessary value assignment
var drawState = _state.State.VertexBufferDrawState;
#pragma warning restore IDE0059
DrawImpl(engine, drawVertexCount, 1, 0, drawFirstVertex, firstInstance, indexed: false);
}
@@ -378,6 +377,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
bool oldDrawIndexed = _drawState.DrawIndexed;
_drawState.DrawIndexed = true;
_drawState.DrawUsesEngineState = false;
engine.ForceStateDirty(IndexBufferCountMethodOffset * 4);
DrawEnd(engine, firstIndex, indexCount, 0, 0);
@@ -423,6 +423,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
bool oldDrawIndexed = _drawState.DrawIndexed;
_drawState.DrawIndexed = false;
_drawState.DrawUsesEngineState = false;
engine.ForceStateDirty(VertexBufferFirstMethodOffset * 4);
DrawEnd(engine, 0, 0, firstVertex, vertexCount);
@@ -543,6 +544,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_state.State.FirstInstance = (uint)firstInstance;
_drawState.DrawIndexed = indexed;
_drawState.DrawUsesEngineState = true;
_currentSpecState.SetHasConstantBufferDrawParameters(true);
engine.UpdateState();
@@ -630,8 +632,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// </summary>
/// <param name="engine">3D engine where this method is being called</param>
/// <param name="topology">Primitive topology</param>
/// <param name="indirectBufferAddress">Address of the buffer with the draw parameters, such as count, first index, etc</param>
/// <param name="parameterBufferAddress">Address of the buffer with the draw count</param>
/// <param name="indirectBufferRange">Memory range of the buffer with the draw parameters, such as count, first index, etc</param>
/// <param name="parameterBufferRange">Memory range of the buffer with the draw count</param>
/// <param name="maxDrawCount">Maximum number of draws that can be made</param>
/// <param name="stride">Distance in bytes between each entry on the data pointed to by <paramref name="indirectBufferAddress"/></param>
/// <param name="indexCount">Maximum number of indices that the draw can consume</param>
@@ -639,8 +641,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
public void DrawIndirect(
ThreedClass engine,
PrimitiveTopology topology,
ulong indirectBufferAddress,
ulong parameterBufferAddress,
MultiRange indirectBufferRange,
MultiRange parameterBufferRange,
int maxDrawCount,
int stride,
int indexCount,
@@ -675,14 +677,15 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_drawState.DrawIndexed = indexed;
_drawState.DrawIndirect = true;
_drawState.DrawUsesEngineState = true;
_currentSpecState.SetHasConstantBufferDrawParameters(true);
engine.UpdateState();
if (hasCount)
{
var indirectBuffer = memory.BufferCache.GetBufferRange(indirectBufferAddress, (ulong)maxDrawCount * (ulong)stride);
var parameterBuffer = memory.BufferCache.GetBufferRange(parameterBufferAddress, 4);
var indirectBuffer = memory.BufferCache.GetBufferRange(indirectBufferRange);
var parameterBuffer = memory.BufferCache.GetBufferRange(parameterBufferRange);
if (indexed)
{
@@ -695,7 +698,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
}
else
{
var indirectBuffer = memory.BufferCache.GetBufferRange(indirectBufferAddress, (ulong)stride);
var indirectBuffer = memory.BufferCache.GetBufferRange(indirectBufferRange);
if (indexed)
{

View File

@@ -38,6 +38,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// </summary>
public bool DrawIndirect;
/// <summary>
/// Indicates that the draw is using the draw parameters on the 3D engine state, rather than inline parameters submitted with the draw command.
/// </summary>
public bool DrawUsesEngineState;
/// <summary>
/// Indicates if any of the currently used vertex shaders reads the instance ID.
/// </summary>
@@ -48,11 +53,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// </summary>
public bool IsAnyVbInstanced;
/// <summary>
/// Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0.
/// </summary>
public bool HasConstantBufferDrawParameters;
/// <summary>
/// Primitive topology for the next draw.
/// </summary>

View File

@@ -47,7 +47,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
private uint _vbEnableMask;
private bool _prevDrawIndexed;
private readonly bool _prevDrawIndirect;
private bool _prevDrawIndirect;
private bool _prevDrawUsesEngineState;
private IndexType _prevIndexType;
private uint _prevFirstVertex;
private bool _prevTfEnable;
@@ -236,7 +237,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
// method when doing indexed draws, so we need to make sure
// to update the vertex buffers if we are doing a regular
// draw after a indexed one and vice-versa.
if (_drawState.DrawIndexed != _prevDrawIndexed)
// Some draws also do not update the engine state, so it is possible for it
// to not be dirty even if the vertex counts or other state changed. We need to force it to be dirty in this case.
if (_drawState.DrawIndexed != _prevDrawIndexed || _drawState.DrawUsesEngineState != _prevDrawUsesEngineState)
{
_updateTracker.ForceDirty(VertexBufferStateIndex);
@@ -251,6 +254,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
}
_prevDrawIndexed = _drawState.DrawIndexed;
_prevDrawUsesEngineState = _drawState.DrawUsesEngineState;
}
// Some draw parameters are used to restrict the vertex buffer size,
@@ -260,6 +264,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
if (_drawState.DrawIndirect != _prevDrawIndirect)
{
_updateTracker.ForceDirty(VertexBufferStateIndex);
_prevDrawIndirect = _drawState.DrawIndirect;
}
// In some cases, the index type is also used to guess the

View File

@@ -6,6 +6,7 @@ using Ryujinx.Graphics.Gpu.Engine.InlineToMemory;
using Ryujinx.Graphics.Gpu.Engine.Threed.Blender;
using Ryujinx.Graphics.Gpu.Engine.Types;
using Ryujinx.Graphics.Gpu.Synchronization;
using Ryujinx.Memory.Range;
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
@@ -803,22 +804,22 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// Performs a indirect draw, with parameters from a GPU buffer.
/// </summary>
/// <param name="topology">Primitive topology</param>
/// <param name="indirectBufferAddress">Address of the buffer with the draw parameters, such as count, first index, etc</param>
/// <param name="parameterBufferAddress">Address of the buffer with the draw count</param>
/// <param name="indirectBufferRange">Memory range of the buffer with the draw parameters, such as count, first index, etc</param>
/// <param name="parameterBufferRange">Memory range of the buffer with the draw count</param>
/// <param name="maxDrawCount">Maximum number of draws that can be made</param>
/// <param name="stride">Distance in bytes between each entry on the data pointed to by <paramref name="indirectBufferAddress"/></param>
/// <param name="stride">Distance in bytes between each entry on the data pointed to by <paramref name="indirectBufferRange"/></param>
/// <param name="indexCount">Maximum number of indices that the draw can consume</param>
/// <param name="drawType">Type of the indirect draw, which can be indexed or non-indexed, with or without a draw count</param>
public void DrawIndirect(
PrimitiveTopology topology,
ulong indirectBufferAddress,
ulong parameterBufferAddress,
MultiRange indirectBufferRange,
MultiRange parameterBufferRange,
int maxDrawCount,
int stride,
int indexCount,
IndirectDrawType drawType)
{
_drawManager.DrawIndirect(this, topology, indirectBufferAddress, parameterBufferAddress, maxDrawCount, stride, indexCount, drawType);
_drawManager.DrawIndirect(this, topology, indirectBufferRange, parameterBufferRange, maxDrawCount, stride, indexCount, drawType);
}
/// <summary>

View File

@@ -382,7 +382,7 @@ namespace Ryujinx.Graphics.Gpu.Image
{
ref BufferBounds bounds = ref _channel.BufferManager.GetUniformBufferBounds(_isCompute, stageIndex, textureBufferIndex);
cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(_channel.MemoryManager.Physical.GetSpan(bounds.Address, (int)bounds.Size));
cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(_channel.MemoryManager.Physical.GetSpan(bounds.Range));
cachedTextureBufferIndex = textureBufferIndex;
if (samplerBufferIndex == textureBufferIndex)
@@ -396,7 +396,7 @@ namespace Ryujinx.Graphics.Gpu.Image
{
ref BufferBounds bounds = ref _channel.BufferManager.GetUniformBufferBounds(_isCompute, stageIndex, samplerBufferIndex);
cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(_channel.MemoryManager.Physical.GetSpan(bounds.Address, (int)bounds.Size));
cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(_channel.MemoryManager.Physical.GetSpan(bounds.Range));
cachedSamplerBufferIndex = samplerBufferIndex;
}
}
@@ -524,7 +524,7 @@ namespace Ryujinx.Graphics.Gpu.Image
// Ensure that the buffer texture is using the correct buffer as storage.
// Buffers are frequently re-created to accommodate larger data, so we need to re-bind
// to ensure we're not using a old buffer that was already deleted.
_channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, texture.Range.GetSubRange(0).Address, texture.Size, bindingInfo, bindingInfo.Format, false);
_channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, texture.Range, bindingInfo, bindingInfo.Format, false);
// Cache is not used for buffer texture, it must always rebind.
state.CachedTexture = null;
@@ -661,7 +661,7 @@ namespace Ryujinx.Graphics.Gpu.Image
format = texture.Format;
}
_channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, texture.Range.GetSubRange(0).Address, texture.Size, bindingInfo, format, true);
_channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, texture.Range, bindingInfo, format, true);
// Cache is not used for buffer texture, it must always rebind.
state.CachedTexture = null;

View File

@@ -43,6 +43,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary>
public int UnmappedSequence { get; private set; }
/// <summary>
/// Indicates if the buffer can be used in a sparse buffer mapping.
/// </summary>
public bool SparseCompatible { get; }
/// <summary>
/// Ranges of the buffer that have been modified on the GPU.
/// Ranges defined here cannot be updated from CPU until a CPU waiting sync point is reached.
@@ -77,15 +82,25 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="physicalMemory">Physical memory where the buffer is mapped</param>
/// <param name="address">Start address of the buffer</param>
/// <param name="size">Size of the buffer in bytes</param>
/// <param name="sparseCompatible">Indicates if the buffer can be used in a sparse buffer mapping</param>
/// <param name="baseBuffers">Buffers which this buffer contains, and will inherit tracking handles from</param>
public Buffer(GpuContext context, PhysicalMemory physicalMemory, ulong address, ulong size, IEnumerable<Buffer> baseBuffers = null)
public Buffer(
GpuContext context,
PhysicalMemory physicalMemory,
ulong address,
ulong size,
bool sparseCompatible,
IEnumerable<Buffer> baseBuffers = null)
{
_context = context;
_physicalMemory = physicalMemory;
Address = address;
Size = size;
SparseCompatible = sparseCompatible;
Handle = context.Renderer.CreateBuffer((int)size, baseBuffers?.MaxBy(x => x.Size).Handle ?? BufferHandle.Null);
BufferAccess access = sparseCompatible ? BufferAccess.SparseCompatible : BufferAccess.Default;
Handle = context.Renderer.CreateBuffer((int)size, access, baseBuffers?.MaxBy(x => x.Size).Handle ?? BufferHandle.Null);
_useGranular = size > GranularBufferThreshold;

View File

@@ -1,4 +1,5 @@
using Ryujinx.Graphics.Shader;
using Ryujinx.Memory.Range;
namespace Ryujinx.Graphics.Gpu.Memory
{
@@ -8,30 +9,28 @@ namespace Ryujinx.Graphics.Gpu.Memory
readonly struct BufferBounds
{
/// <summary>
/// Region virtual address.
/// Physical memory ranges where the buffer is mapped.
/// </summary>
public ulong Address { get; }
/// <summary>
/// Region size in bytes.
/// </summary>
public ulong Size { get; }
public MultiRange Range { get; }
/// <summary>
/// Buffer usage flags.
/// </summary>
public BufferUsageFlags Flags { get; }
/// <summary>
/// Indicates that the backing memory for the buffer does not exist.
/// </summary>
public bool IsUnmapped => Range.IsUnmapped;
/// <summary>
/// Creates a new buffer region.
/// </summary>
/// <param name="address">Region address</param>
/// <param name="size">Region size</param>
/// <param name="range">Physical memory ranges where the buffer is mapped</param>
/// <param name="flags">Buffer usage flags</param>
public BufferBounds(ulong address, ulong size, BufferUsageFlags flags = BufferUsageFlags.None)
public BufferBounds(MultiRange range, BufferUsageFlags flags = BufferUsageFlags.None)
{
Address = address;
Size = size;
Range = range;
Flags = flags;
}
}

View File

@@ -11,12 +11,24 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary>
class BufferCache : IDisposable
{
private const int OverlapsBufferInitialCapacity = 10;
private const int OverlapsBufferMaxCapacity = 10000;
/// <summary>
/// Initial size for the array holding overlaps.
/// </summary>
public const int OverlapsBufferInitialCapacity = 10;
/// <summary>
/// Maximum size that an array holding overlaps may have after trimming.
/// </summary>
public const int OverlapsBufferMaxCapacity = 10000;
private const ulong BufferAlignmentSize = 0x1000;
private const ulong BufferAlignmentMask = BufferAlignmentSize - 1;
/// <summary>
/// Alignment required for sparse buffer mappings.
/// </summary>
public const ulong SparseBufferAlignmentSize = 0x10000;
private const ulong MaxDynamicGrowthSize = 0x100000;
private readonly GpuContext _context;
@@ -27,6 +39,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// Must lock for any access from other threads.
/// </remarks>
private readonly RangeList<Buffer> _buffers;
private readonly MultiRangeList<MultiRangeBuffer> _multiRangeBuffers;
private Buffer[] _bufferOverlaps;
@@ -47,6 +60,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
_physicalMemory = physicalMemory;
_buffers = new RangeList<Buffer>();
_multiRangeBuffers = new MultiRangeList<MultiRangeBuffer>();
_bufferOverlaps = new Buffer[OverlapsBufferInitialCapacity];
@@ -66,45 +80,100 @@ namespace Ryujinx.Graphics.Gpu.Memory
Buffer[] overlaps = new Buffer[10];
int overlapCount;
ulong address = ((MemoryManager)sender).Translate(e.Address);
ulong size = e.Size;
MultiRange range = ((MemoryManager)sender).GetPhysicalRegions(e.Address, e.Size);
lock (_buffers)
for (int index = 0; index < range.Count; index++)
{
overlapCount = _buffers.FindOverlaps(address, size, ref overlaps);
}
MemoryRange subRange = range.GetSubRange(index);
for (int i = 0; i < overlapCount; i++)
{
overlaps[i].Unmapped(address, size);
lock (_buffers)
{
overlapCount = _buffers.FindOverlaps(subRange.Address, subRange.Size, ref overlaps);
}
for (int i = 0; i < overlapCount; i++)
{
overlaps[i].Unmapped(subRange.Address, subRange.Size);
}
}
}
/// <summary>
/// Performs address translation of the GPU virtual address, and creates a
/// new buffer, if needed, for the specified range.
/// new buffer, if needed, for the specified contiguous range.
/// </summary>
/// <param name="memoryManager">GPU memory manager where the buffer is mapped</param>
/// <param name="gpuVa">Start GPU virtual address of the buffer</param>
/// <param name="size">Size in bytes of the buffer</param>
/// <returns>CPU virtual address of the buffer, after address translation</returns>
public ulong TranslateAndCreateBuffer(MemoryManager memoryManager, ulong gpuVa, ulong size)
/// <returns>Contiguous physical range of the buffer, after address translation</returns>
public MultiRange TranslateAndCreateBuffer(MemoryManager memoryManager, ulong gpuVa, ulong size)
{
if (gpuVa == 0)
{
return 0;
return new MultiRange(MemoryManager.PteUnmapped, size);
}
ulong address = memoryManager.Translate(gpuVa);
if (address == MemoryManager.PteUnmapped)
if (address != MemoryManager.PteUnmapped)
{
return 0;
CreateBuffer(address, size);
}
CreateBuffer(address, size);
return new MultiRange(address, size);
}
return address;
/// <summary>
/// Performs address translation of the GPU virtual address, and creates
/// new buffers, if needed, for the specified range.
/// </summary>
/// <param name="memoryManager">GPU memory manager where the buffer is mapped</param>
/// <param name="gpuVa">Start GPU virtual address of the buffer</param>
/// <param name="size">Size in bytes of the buffer</param>
/// <returns>Physical ranges of the buffer, after address translation</returns>
public MultiRange TranslateAndCreateMultiBuffers(MemoryManager memoryManager, ulong gpuVa, ulong size)
{
if (gpuVa == 0)
{
return new MultiRange(MemoryManager.PteUnmapped, size);
}
bool supportsSparse = _context.Capabilities.SupportsSparseBuffer;
// Fast path not taken for non-contiguous ranges,
// since multi-range buffers are not coalesced, so a buffer that covers
// the entire cached range might not actually exist.
if (memoryManager.VirtualBufferCache.TryGetOrAddRange(gpuVa, size, supportsSparse, out MultiRange range) &&
range.Count == 1)
{
return range;
}
CreateBuffer(range);
return range;
}
/// <summary>
/// Creates a new buffer for the specified range, if it does not yet exist.
/// This can be used to ensure the existance of a buffer.
/// </summary>
/// <param name="range">Physical ranges of memory where the buffer data is located</param>
public void CreateBuffer(MultiRange range)
{
if (range.Count > 1)
{
CreateMultiRangeBuffer(range);
}
else
{
MemoryRange subRange = range.GetSubRange(0);
if (subRange.Address != MemoryManager.PteUnmapped)
{
CreateBuffer(subRange.Address, subRange.Size);
}
}
}
/// <summary>
@@ -118,7 +187,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
ulong endAddress = address + size;
ulong alignedAddress = address & ~BufferAlignmentMask;
ulong alignedEndAddress = (endAddress + BufferAlignmentMask) & ~BufferAlignmentMask;
// The buffer must have the size of at least one page.
@@ -130,6 +198,108 @@ namespace Ryujinx.Graphics.Gpu.Memory
CreateBufferAligned(alignedAddress, alignedEndAddress - alignedAddress);
}
/// <summary>
/// Creates a new buffer for the specified range, if it does not yet exist.
/// This can be used to ensure the existance of a buffer.
/// </summary>
/// <param name="address">Address of the buffer in memory</param>
/// <param name="size">Size of the buffer in bytes</param>
/// <param name="alignment">Alignment of the start address of the buffer in bytes</param>
public void CreateBuffer(ulong address, ulong size, ulong alignment)
{
ulong alignmentMask = alignment - 1;
ulong pageAlignmentMask = BufferAlignmentMask;
ulong endAddress = address + size;
ulong alignedAddress = address & ~alignmentMask;
ulong alignedEndAddress = (endAddress + pageAlignmentMask) & ~pageAlignmentMask;
// The buffer must have the size of at least one page.
if (alignedEndAddress == alignedAddress)
{
alignedEndAddress += pageAlignmentMask;
}
CreateBufferAligned(alignedAddress, alignedEndAddress - alignedAddress, alignment);
}
/// <summary>
/// Creates a buffer for a memory region composed of multiple physical ranges,
/// if it does not exist yet.
/// </summary>
/// <param name="range">Physical ranges of memory</param>
private void CreateMultiRangeBuffer(MultiRange range)
{
// Ensure all non-contiguous buffer we might use are sparse aligned.
for (int i = 0; i < range.Count; i++)
{
MemoryRange subRange = range.GetSubRange(i);
if (subRange.Address != MemoryManager.PteUnmapped)
{
CreateBuffer(subRange.Address, subRange.Size, SparseBufferAlignmentSize);
}
}
// Create sparse buffer.
MultiRangeBuffer[] overlaps = new MultiRangeBuffer[10];
int overlapCount = _multiRangeBuffers.FindOverlaps(range, ref overlaps);
for (int index = 0; index < overlapCount; index++)
{
if (overlaps[index].Range.Contains(range))
{
return;
}
}
for (int index = 0; index < overlapCount; index++)
{
if (range.Contains(overlaps[index].Range))
{
_multiRangeBuffers.Remove(overlaps[index]);
overlaps[index].Dispose();
}
}
BufferRange[] storages = new BufferRange[range.Count];
MemoryRange[] alignedSubRanges = new MemoryRange[range.Count];
ulong alignmentMask = SparseBufferAlignmentSize - 1;
for (int i = 0; i < range.Count; i++)
{
MemoryRange subRange = range.GetSubRange(i);
if (subRange.Address != MemoryManager.PteUnmapped)
{
ulong endAddress = subRange.Address + subRange.Size;
ulong alignedAddress = subRange.Address & ~alignmentMask;
ulong alignedEndAddress = (endAddress + alignmentMask) & ~alignmentMask;
ulong alignedSize = alignedEndAddress - alignedAddress;
Buffer buffer = _buffers.FindFirstOverlap(alignedAddress, alignedSize);
BufferRange bufferRange = buffer.GetRange(alignedAddress, alignedSize, false);
storages[i] = bufferRange;
alignedSubRanges[i] = new MemoryRange(alignedAddress, alignedSize);
}
else
{
ulong alignedSize = (subRange.Size + alignmentMask) & ~alignmentMask;
storages[i] = new BufferRange(BufferHandle.Null, 0, (int)alignedSize);
alignedSubRanges[i] = new MemoryRange(MemoryManager.PteUnmapped, alignedSize);
}
}
MultiRangeBuffer multiRangeBuffer = new(_context, new MultiRange(alignedSubRanges), storages);
_multiRangeBuffers.Add(multiRangeBuffer);
}
/// <summary>
/// Performs address translation of the GPU virtual address, and attempts to force
/// the buffer in the region as dirty.
@@ -150,7 +320,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
result.EndGpuAddress < gpuVa + size ||
result.UnmappedSequence != result.Buffer.UnmappedSequence)
{
ulong address = TranslateAndCreateBuffer(memoryManager, gpuVa, size);
MultiRange range = TranslateAndCreateBuffer(memoryManager, gpuVa, size);
ulong address = range.GetSubRange(0).Address;
result = new BufferCacheEntry(address, gpuVa, GetBuffer(address, size));
_dirtyCache[gpuVa] = result;
@@ -184,7 +355,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
result.EndGpuAddress < alignedEndGpuVa ||
result.UnmappedSequence != result.Buffer.UnmappedSequence)
{
ulong address = TranslateAndCreateBuffer(memoryManager, alignedGpuVa, size);
MultiRange range = TranslateAndCreateBuffer(memoryManager, alignedGpuVa, size);
ulong address = range.GetSubRange(0).Address;
result = new BufferCacheEntry(address, alignedGpuVa, GetBuffer(address, size));
_modifiedCache[alignedGpuVa] = result;
@@ -204,7 +376,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="size">Size in bytes of the buffer</param>
private void CreateBufferAligned(ulong address, ulong size)
{
int overlapsCount = _buffers.FindOverlapsNonOverlapping(address, size, ref _bufferOverlaps);
Buffer[] overlaps = _bufferOverlaps;
int overlapsCount = _buffers.FindOverlapsNonOverlapping(address, size, ref overlaps);
if (overlapsCount != 0)
{
@@ -215,9 +388,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
// old buffer(s) to the new buffer.
ulong endAddress = address + size;
Buffer overlap0 = overlaps[0];
if (_bufferOverlaps[0].Address > address || _bufferOverlaps[0].EndAddress < endAddress)
if (overlap0.Address > address || overlap0.EndAddress < endAddress)
{
bool anySparseCompatible = false;
// Check if the following conditions are met:
// - We have a single overlap.
// - The overlap starts at or before the requested range. That is, the overlap happens at the end.
@@ -228,23 +404,25 @@ namespace Ryujinx.Graphics.Gpu.Memory
// Allowing for 2 pages (rather than just one) is necessary to catch cases where the
// range crosses a page, and after alignment, ends having a size of 2 pages.
if (overlapsCount == 1 &&
address >= _bufferOverlaps[0].Address &&
endAddress - _bufferOverlaps[0].EndAddress <= BufferAlignmentSize * 2)
address >= overlap0.Address &&
endAddress - overlap0.EndAddress <= BufferAlignmentSize * 2)
{
// Try to grow the buffer by 1.5x of its current size.
// This improves performance in the cases where the buffer is resized often by small amounts.
ulong existingSize = _bufferOverlaps[0].Size;
ulong existingSize = overlap0.Size;
ulong growthSize = (existingSize + Math.Min(existingSize >> 1, MaxDynamicGrowthSize)) & ~BufferAlignmentMask;
size = Math.Max(size, growthSize);
endAddress = address + size;
overlapsCount = _buffers.FindOverlapsNonOverlapping(address, size, ref _bufferOverlaps);
overlapsCount = _buffers.FindOverlapsNonOverlapping(address, size, ref overlaps);
}
for (int index = 0; index < overlapsCount; index++)
{
Buffer buffer = _bufferOverlaps[index];
Buffer buffer = overlaps[index];
anySparseCompatible |= buffer.SparseCompatible;
address = Math.Min(address, buffer.Address);
endAddress = Math.Max(endAddress, buffer.EndAddress);
@@ -257,35 +435,13 @@ namespace Ryujinx.Graphics.Gpu.Memory
ulong newSize = endAddress - address;
Buffer newBuffer = new(_context, _physicalMemory, address, newSize, _bufferOverlaps.Take(overlapsCount));
lock (_buffers)
{
_buffers.Add(newBuffer);
}
for (int index = 0; index < overlapsCount; index++)
{
Buffer buffer = _bufferOverlaps[index];
int dstOffset = (int)(buffer.Address - newBuffer.Address);
buffer.CopyTo(newBuffer, dstOffset);
newBuffer.InheritModifiedRanges(buffer);
buffer.DecrementReferenceCount();
}
newBuffer.SynchronizeMemory(address, newSize);
// Existing buffers were modified, we need to rebind everything.
NotifyBuffersModified?.Invoke();
CreateBufferAligned(address, newSize, anySparseCompatible, overlaps, overlapsCount);
}
}
else
{
// No overlap, just create a new buffer.
Buffer buffer = new(_context, _physicalMemory, address, size);
Buffer buffer = new(_context, _physicalMemory, address, size, sparseCompatible: false);
lock (_buffers)
{
@@ -296,6 +452,151 @@ namespace Ryujinx.Graphics.Gpu.Memory
ShrinkOverlapsBufferIfNeeded();
}
/// <summary>
/// Creates a new buffer for the specified range, if needed.
/// If a buffer where this range can be fully contained already exists,
/// then the creation of a new buffer is not necessary.
/// </summary>
/// <param name="address">Address of the buffer in guest memory</param>
/// <param name="size">Size in bytes of the buffer</param>
/// <param name="alignment">Alignment of the start address of the buffer</param>
private void CreateBufferAligned(ulong address, ulong size, ulong alignment)
{
Buffer[] overlaps = _bufferOverlaps;
int overlapsCount = _buffers.FindOverlapsNonOverlapping(address, size, ref overlaps);
bool sparseAligned = alignment >= SparseBufferAlignmentSize;
if (overlapsCount != 0)
{
// If the buffer already exists, make sure if covers the entire range,
// and make sure it is properly aligned, otherwise sparse mapping may fail.
ulong endAddress = address + size;
Buffer overlap0 = overlaps[0];
if (overlap0.Address > address ||
overlap0.EndAddress < endAddress ||
(overlap0.Address & (alignment - 1)) != 0 ||
(!overlap0.SparseCompatible && sparseAligned))
{
// We need to make sure the new buffer is properly aligned.
// However, after the range is aligned, it is possible that it
// overlaps more buffers, so try again after each extension
// and ensure we cover all overlaps.
int oldOverlapsCount;
do
{
for (int index = 0; index < overlapsCount; index++)
{
Buffer buffer = overlaps[index];
address = Math.Min(address, buffer.Address);
endAddress = Math.Max(endAddress, buffer.EndAddress);
}
address &= ~(alignment - 1);
oldOverlapsCount = overlapsCount;
overlapsCount = _buffers.FindOverlapsNonOverlapping(address, endAddress - address, ref overlaps);
}
while (oldOverlapsCount != overlapsCount);
lock (_buffers)
{
for (int index = 0; index < overlapsCount; index++)
{
_buffers.Remove(overlaps[index]);
}
}
ulong newSize = endAddress - address;
CreateBufferAligned(address, newSize, sparseAligned, overlaps, overlapsCount);
}
}
else
{
// No overlap, just create a new buffer.
Buffer buffer = new(_context, _physicalMemory, address, size, sparseAligned);
lock (_buffers)
{
_buffers.Add(buffer);
}
}
ShrinkOverlapsBufferIfNeeded();
}
/// <summary>
/// Creates a new buffer for the specified range, if needed.
/// If a buffer where this range can be fully contained already exists,
/// then the creation of a new buffer is not necessary.
/// </summary>
/// <param name="address">Address of the buffer in guest memory</param>
/// <param name="size">Size in bytes of the buffer</param>
/// <param name="sparseCompatible">Indicates if the buffer can be used in a sparse buffer mapping</param>
/// <param name="overlaps">Buffers overlapping the range</param>
/// <param name="overlapsCount">Total of overlaps</param>
private void CreateBufferAligned(ulong address, ulong size, bool sparseCompatible, Buffer[] overlaps, int overlapsCount)
{
Buffer newBuffer = new Buffer(_context, _physicalMemory, address, size, sparseCompatible, overlaps.Take(overlapsCount));
lock (_buffers)
{
_buffers.Add(newBuffer);
}
for (int index = 0; index < overlapsCount; index++)
{
Buffer buffer = overlaps[index];
int dstOffset = (int)(buffer.Address - newBuffer.Address);
buffer.CopyTo(newBuffer, dstOffset);
newBuffer.InheritModifiedRanges(buffer);
buffer.DecrementReferenceCount();
}
newBuffer.SynchronizeMemory(address, size);
// Existing buffers were modified, we need to rebind everything.
NotifyBuffersModified?.Invoke();
RecreateMultiRangeBuffers(address, size);
}
/// <summary>
/// Recreates all the multi-range buffers that overlaps a given physical memory range.
/// </summary>
/// <param name="address">Start address of the range</param>
/// <param name="size">Size of the range in bytes</param>
private void RecreateMultiRangeBuffers(ulong address, ulong size)
{
if ((address & (SparseBufferAlignmentSize - 1)) != 0 || (size & (SparseBufferAlignmentSize - 1)) != 0)
{
return;
}
MultiRangeBuffer[] overlaps = new MultiRangeBuffer[10];
int overlapCount = _multiRangeBuffers.FindOverlaps(address, size, ref overlaps);
for (int index = 0; index < overlapCount; index++)
{
_multiRangeBuffers.Remove(overlaps[index]);
overlaps[index].Dispose();
}
for (int index = 0; index < overlapCount; index++)
{
CreateMultiRangeBuffer(overlaps[index].Range);
}
}
/// <summary>
/// Resizes the temporary buffer used for range list intersection results, if it has grown too much.
/// </summary>
@@ -319,9 +620,63 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="size">Size in bytes of the copy</param>
public void CopyBuffer(MemoryManager memoryManager, ulong srcVa, ulong dstVa, ulong size)
{
ulong srcAddress = TranslateAndCreateBuffer(memoryManager, srcVa, size);
ulong dstAddress = TranslateAndCreateBuffer(memoryManager, dstVa, size);
MultiRange srcRange = TranslateAndCreateMultiBuffers(memoryManager, srcVa, size);
MultiRange dstRange = TranslateAndCreateMultiBuffers(memoryManager, dstVa, size);
if (srcRange.Count == 1 && dstRange.Count == 1)
{
CopyBufferSingleRange(memoryManager, srcRange.GetSubRange(0).Address, dstRange.GetSubRange(0).Address, size);
}
else
{
ulong copiedSize = 0;
ulong srcOffset = 0;
ulong dstOffset = 0;
int srcRangeIndex = 0;
int dstRangeIndex = 0;
while (copiedSize < size)
{
if (srcRange.GetSubRange(srcRangeIndex).Size == srcOffset)
{
srcRangeIndex++;
srcOffset = 0;
}
if (dstRange.GetSubRange(dstRangeIndex).Size == dstOffset)
{
dstRangeIndex++;
dstOffset = 0;
}
MemoryRange srcSubRange = srcRange.GetSubRange(srcRangeIndex);
MemoryRange dstSubRange = dstRange.GetSubRange(dstRangeIndex);
ulong srcSize = srcSubRange.Size - srcOffset;
ulong dstSize = dstSubRange.Size - dstOffset;
ulong copySize = Math.Min(srcSize, dstSize);
CopyBufferSingleRange(memoryManager, srcSubRange.Address + srcOffset, dstSubRange.Address + dstOffset, copySize);
srcOffset += copySize;
dstOffset += copySize;
copiedSize += copySize;
}
}
}
/// <summary>
/// Copy a buffer data from a given address to another.
/// </summary>
/// <remarks>
/// This does a GPU side copy.
/// </remarks>
/// <param name="memoryManager">GPU memory manager where the buffer is mapped</param>
/// <param name="srcAddress">Physical address of the copy source</param>
/// <param name="dstAddress">Physical address of the copy destination</param>
/// <param name="size">Size in bytes of the copy</param>
private void CopyBufferSingleRange(MemoryManager memoryManager, ulong srcAddress, ulong dstAddress, ulong size)
{
Buffer srcBuffer = GetBuffer(srcAddress, size);
Buffer dstBuffer = GetBuffer(dstAddress, size);
@@ -360,39 +715,98 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="value">Value to be written into the buffer</param>
public void ClearBuffer(MemoryManager memoryManager, ulong gpuVa, ulong size, uint value)
{
ulong address = TranslateAndCreateBuffer(memoryManager, gpuVa, size);
MultiRange range = TranslateAndCreateMultiBuffers(memoryManager, gpuVa, size);
Buffer buffer = GetBuffer(address, size);
for (int index = 0; index < range.Count; index++)
{
MemoryRange subRange = range.GetSubRange(index);
Buffer buffer = GetBuffer(subRange.Address, subRange.Size);
int offset = (int)(address - buffer.Address);
int offset = (int)(subRange.Address - buffer.Address);
_context.Renderer.Pipeline.ClearBuffer(buffer.Handle, offset, (int)size, value);
_context.Renderer.Pipeline.ClearBuffer(buffer.Handle, offset, (int)subRange.Size, value);
memoryManager.Physical.FillTrackedResource(address, size, value, ResourceKind.Buffer);
memoryManager.Physical.FillTrackedResource(subRange.Address, subRange.Size, value, ResourceKind.Buffer);
}
}
/// <summary>
/// Gets a buffer sub-range from a start address til a page boundary after the given size.
/// Gets a buffer sub-range starting at a given memory address, aligned to the next page boundary.
/// </summary>
/// <param name="address">Start address of the memory range</param>
/// <param name="size">Size in bytes of the memory range</param>
/// <param name="range">Physical regions of memory where the buffer is mapped</param>
/// <param name="write">Whether the buffer will be written to by this use</param>
/// <returns>The buffer sub-range starting at the given memory address</returns>
public BufferRange GetBufferRangeAligned(ulong address, ulong size, bool write = false)
public BufferRange GetBufferRangeAligned(MultiRange range, bool write = false)
{
return GetBuffer(address, size, write).GetRangeAligned(address, size, write);
if (range.Count > 1)
{
return GetBuffer(range, write).GetRange(range);
}
else
{
MemoryRange subRange = range.GetSubRange(0);
return GetBuffer(subRange.Address, subRange.Size, write).GetRangeAligned(subRange.Address, subRange.Size, write);
}
}
/// <summary>
/// Gets a buffer sub-range for a given memory range.
/// </summary>
/// <param name="address">Start address of the memory range</param>
/// <param name="size">Size in bytes of the memory range</param>
/// <param name="range">Physical regions of memory where the buffer is mapped</param>
/// <param name="write">Whether the buffer will be written to by this use</param>
/// <returns>The buffer sub-range for the given range</returns>
public BufferRange GetBufferRange(ulong address, ulong size, bool write = false)
public BufferRange GetBufferRange(MultiRange range, bool write = false)
{
return GetBuffer(address, size, write).GetRange(address, size, write);
if (range.Count > 1)
{
return GetBuffer(range, write).GetRange(range);
}
else
{
MemoryRange subRange = range.GetSubRange(0);
return GetBuffer(subRange.Address, subRange.Size, write).GetRange(subRange.Address, subRange.Size, write);
}
}
/// <summary>
/// Gets a buffer for a given memory range.
/// A buffer overlapping with the specified range is assumed to already exist on the cache.
/// </summary>
/// <param name="range">Physical regions of memory where the buffer is mapped</param>
/// <param name="write">Whether the buffer will be written to by this use</param>
/// <returns>The buffer where the range is fully contained</returns>
private MultiRangeBuffer GetBuffer(MultiRange range, bool write = false)
{
for (int i = 0; i < range.Count; i++)
{
MemoryRange subRange = range.GetSubRange(i);
Buffer subBuffer = _buffers.FindFirstOverlap(subRange.Address, subRange.Size);
subBuffer.SynchronizeMemory(subRange.Address, subRange.Size);
if (write)
{
subBuffer.SignalModified(subRange.Address, subRange.Size);
}
}
MultiRangeBuffer[] overlaps = new MultiRangeBuffer[10];
int overlapCount = _multiRangeBuffers.FindOverlaps(range, ref overlaps);
MultiRangeBuffer buffer = null;
for (int i = 0; i < overlapCount; i++)
{
if (overlaps[i].Range.Contains(range))
{
buffer = overlaps[i];
break;
}
}
return buffer;
}
/// <summary>
@@ -426,12 +840,33 @@ namespace Ryujinx.Graphics.Gpu.Memory
return buffer;
}
/// <summary>
/// Performs guest to host memory synchronization of a given memory range.
/// </summary>
/// <param name="range">Physical regions of memory where the buffer is mapped</param>
public void SynchronizeBufferRange(MultiRange range)
{
if (range.Count == 1)
{
MemoryRange subRange = range.GetSubRange(0);
SynchronizeBufferRange(subRange.Address, subRange.Size);
}
else
{
for (int index = 0; index < range.Count; index++)
{
MemoryRange subRange = range.GetSubRange(index);
SynchronizeBufferRange(subRange.Address, subRange.Size);
}
}
}
/// <summary>
/// Performs guest to host memory synchronization of a given memory range.
/// </summary>
/// <param name="address">Start address of the memory range</param>
/// <param name="size">Size in bytes of the memory range</param>
public void SynchronizeBufferRange(ulong address, ulong size)
private void SynchronizeBufferRange(ulong address, ulong size)
{
if (size != 0)
{
@@ -491,7 +926,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <summary>
/// Disposes all buffers in the cache.
/// It's an error to use the buffer manager after disposal.
/// It's an error to use the buffer cache after disposal.
/// </summary>
public void Dispose()
{

View File

@@ -3,6 +3,7 @@ using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Gpu.Shader;
using Ryujinx.Graphics.Shader;
using Ryujinx.Memory.Range;
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
@@ -62,18 +63,19 @@ namespace Ryujinx.Graphics.Gpu.Memory
Bindings = new BufferDescriptor[count];
Buffers = new BufferBounds[count];
Unaligned = new bool[count];
Buffers.AsSpan().Fill(new BufferBounds(new MultiRange(MemoryManager.PteUnmapped, 0UL)));
}
/// <summary>
/// Sets the region of a buffer at a given slot.
/// </summary>
/// <param name="index">Buffer slot</param>
/// <param name="address">Region virtual address</param>
/// <param name="size">Region size in bytes</param>
/// <param name="range">Physical memory regions where the buffer is mapped</param>
/// <param name="flags">Buffer usage flags</param>
public void SetBounds(int index, ulong address, ulong size, BufferUsageFlags flags = BufferUsageFlags.None)
public void SetBounds(int index, MultiRange range, BufferUsageFlags flags = BufferUsageFlags.None)
{
Buffers[index] = new BufferBounds(address, size, flags);
Buffers[index] = new BufferBounds(range, flags);
}
/// <summary>
@@ -120,6 +122,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
_context = context;
_channel = channel;
_indexBuffer.Range = new MultiRange(MemoryManager.PteUnmapped, 0UL);
_vertexBuffers = new VertexBuffer[Constants.TotalVertexBuffers];
_transformFeedbackBuffers = new BufferBounds[Constants.TotalTransformFeedbackBuffers];
@@ -150,10 +153,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="type">Type of each index buffer element</param>
public void SetIndexBuffer(ulong gpuVa, ulong size, IndexType type)
{
ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
_indexBuffer.Address = address;
_indexBuffer.Size = size;
_indexBuffer.Range = range;
_indexBuffer.Type = type;
_indexBufferDirty = true;
@@ -181,16 +183,15 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="divisor">Vertex divisor of the buffer, for instanced draws</param>
public void SetVertexBuffer(int index, ulong gpuVa, ulong size, int stride, int divisor)
{
ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
_vertexBuffers[index].Address = address;
_vertexBuffers[index].Size = size;
_vertexBuffers[index].Range = range;
_vertexBuffers[index].Stride = stride;
_vertexBuffers[index].Divisor = divisor;
_vertexBuffersDirty = true;
if (address != 0)
if (!range.IsUnmapped)
{
_vertexBuffersEnableMask |= 1u << index;
}
@@ -209,9 +210,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="size">Size in bytes of the transform feedback buffer</param>
public void SetTransformFeedbackBuffer(int index, ulong gpuVa, ulong size)
{
ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size);
_transformFeedbackBuffers[index] = new BufferBounds(address, size);
_transformFeedbackBuffers[index] = new BufferBounds(range);
_transformFeedbackBuffersDirty = true;
}
@@ -256,9 +257,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
gpuVa = BitUtils.AlignDown<ulong>(gpuVa, (ulong)_context.Capabilities.StorageBufferOffsetAlignment);
ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size);
_cpStorageBuffers.SetBounds(index, address, size, flags);
_cpStorageBuffers.SetBounds(index, range, flags);
}
/// <summary>
@@ -280,15 +281,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
gpuVa = BitUtils.AlignDown<ulong>(gpuVa, (ulong)_context.Capabilities.StorageBufferOffsetAlignment);
ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size);
if (buffers.Buffers[index].Address != address ||
buffers.Buffers[index].Size != size)
if (!buffers.Buffers[index].Range.Equals(range))
{
_gpStorageBuffersDirty = true;
}
buffers.SetBounds(index, address, size, flags);
buffers.SetBounds(index, range, flags);
}
/// <summary>
@@ -300,9 +300,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="size">Size in bytes of the storage buffer</param>
public void SetComputeUniformBuffer(int index, ulong gpuVa, ulong size)
{
ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
_cpUniformBuffers.SetBounds(index, address, size);
_cpUniformBuffers.SetBounds(index, range);
}
/// <summary>
@@ -315,9 +315,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="size">Size in bytes of the storage buffer</param>
public void SetGraphicsUniformBuffer(int stage, int index, ulong gpuVa, ulong size)
{
ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
_gpUniformBuffers[stage].SetBounds(index, address, size);
_gpUniformBuffers[stage].SetBounds(index, range);
_gpUniformBuffersDirty = true;
}
@@ -379,7 +379,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
for (int i = 0; i < _cpUniformBuffers.Buffers.Length; i++)
{
if (_cpUniformBuffers.Buffers[i].Address != 0)
if (!_cpUniformBuffers.Buffers[i].IsUnmapped)
{
mask |= 1u << i;
}
@@ -399,7 +399,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
for (int i = 0; i < _gpUniformBuffers[stage].Buffers.Length; i++)
{
if (_gpUniformBuffers[stage].Buffers[i].Address != 0)
if (!_gpUniformBuffers[stage].Buffers[i].IsUnmapped)
{
mask |= 1u << i;
}
@@ -415,7 +415,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <returns>The uniform buffer address, or an undefined value if the buffer is not currently bound</returns>
public ulong GetComputeUniformBufferAddress(int index)
{
return _cpUniformBuffers.Buffers[index].Address;
return _cpUniformBuffers.Buffers[index].Range.GetSubRange(0).Address;
}
/// <summary>
@@ -426,7 +426,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <returns>The uniform buffer address, or an undefined value if the buffer is not currently bound</returns>
public ulong GetGraphicsUniformBufferAddress(int stage, int index)
{
return _gpUniformBuffers[stage].Buffers[index].Address;
return _gpUniformBuffers[stage].Buffers[index].Range.GetSubRange(0).Address;
}
/// <summary>
@@ -477,7 +477,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
foreach (var binding in _bufferTextures)
{
var isStore = binding.BindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore);
var range = _channel.MemoryManager.Physical.BufferCache.GetBufferRange(binding.Address, binding.Size, isStore);
var range = _channel.MemoryManager.Physical.BufferCache.GetBufferRange(binding.Range, isStore);
binding.Texture.SetStorage(range);
// The texture must be rebound to use the new storage if it was updated.
@@ -511,16 +511,16 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
_indexBufferDirty = false;
if (_indexBuffer.Address != 0)
if (!_indexBuffer.Range.IsUnmapped)
{
BufferRange buffer = bufferCache.GetBufferRange(_indexBuffer.Address, _indexBuffer.Size);
BufferRange buffer = bufferCache.GetBufferRange(_indexBuffer.Range);
_context.Renderer.Pipeline.SetIndexBuffer(buffer, _indexBuffer.Type);
}
}
else if (_indexBuffer.Address != 0)
else if (!_indexBuffer.Range.IsUnmapped)
{
bufferCache.SynchronizeBufferRange(_indexBuffer.Address, _indexBuffer.Size);
bufferCache.SynchronizeBufferRange(_indexBuffer.Range);
}
}
else if (_rebind)
@@ -540,12 +540,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
VertexBuffer vb = _vertexBuffers[index];
if (vb.Address == 0)
if (vb.Range.IsUnmapped)
{
continue;
}
BufferRange buffer = bufferCache.GetBufferRange(vb.Address, vb.Size);
BufferRange buffer = bufferCache.GetBufferRange(vb.Range);
vertexBuffers[index] = new VertexBufferDescriptor(buffer, vb.Stride, vb.Divisor);
}
@@ -558,12 +558,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
VertexBuffer vb = _vertexBuffers[index];
if (vb.Address == 0)
if (vb.Range.IsUnmapped)
{
continue;
}
bufferCache.SynchronizeBufferRange(vb.Address, vb.Size);
bufferCache.SynchronizeBufferRange(vb.Range);
}
}
@@ -579,13 +579,13 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
BufferBounds tfb = _transformFeedbackBuffers[index];
if (tfb.Address == 0)
if (tfb.IsUnmapped)
{
tfbs[index] = BufferRange.Empty;
continue;
}
tfbs[index] = bufferCache.GetBufferRange(tfb.Address, tfb.Size, write: true);
tfbs[index] = bufferCache.GetBufferRange(tfb.Range, write: true);
}
_context.Renderer.Pipeline.SetTransformFeedbackBuffers(tfbs);
@@ -600,21 +600,39 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
BufferBounds tfb = _transformFeedbackBuffers[index];
if (tfb.Address == 0)
if (tfb.IsUnmapped)
{
buffers[index] = new BufferAssignment(index, BufferRange.Empty);
}
else
{
ulong endAddress = tfb.Address + tfb.Size;
ulong address = BitUtils.AlignDown(tfb.Address, (ulong)alignment);
ulong size = endAddress - address;
MultiRange range = tfb.Range;
ulong address0 = range.GetSubRange(0).Address;
ulong address = BitUtils.AlignDown(address0, (ulong)alignment);
int tfeOffset = ((int)tfb.Address & (alignment - 1)) / 4;
if (range.Count == 1)
{
range = new MultiRange(address, range.GetSubRange(0).Size + (address0 - address));
}
else
{
MemoryRange[] subRanges = new MemoryRange[range.Count];
subRanges[0] = new MemoryRange(address, range.GetSubRange(0).Size + (address0 - address));
for (int i = 1; i < range.Count; i++)
{
subRanges[i] = range.GetSubRange(i);
}
range = new MultiRange(subRanges);
}
int tfeOffset = ((int)address0 & (alignment - 1)) / 4;
_context.SupportBufferUpdater.SetTfeOffset(index, tfeOffset);
buffers[index] = new BufferAssignment(index, bufferCache.GetBufferRange(address, size, write: true));
buffers[index] = new BufferAssignment(index, bufferCache.GetBufferRange(range, write: true));
}
}
@@ -627,12 +645,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
BufferBounds tfb = _transformFeedbackBuffers[index];
if (tfb.Address == 0)
if (tfb.IsUnmapped)
{
continue;
}
bufferCache.SynchronizeBufferRange(tfb.Address, tfb.Size);
bufferCache.SynchronizeBufferRange(tfb.Range);
}
}
@@ -688,12 +706,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
BufferBounds bounds = buffers.Buffers[bindingInfo.Slot];
if (bounds.Address != 0)
if (!bounds.IsUnmapped)
{
var isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write);
var range = isStorage
? bufferCache.GetBufferRangeAligned(bounds.Address, bounds.Size, isWrite)
: bufferCache.GetBufferRange(bounds.Address, bounds.Size);
? bufferCache.GetBufferRangeAligned(bounds.Range, isWrite)
: bufferCache.GetBufferRange(bounds.Range);
ranges[rangesCount++] = new BufferAssignment(bindingInfo.Binding, range);
}
@@ -725,12 +743,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
BufferBounds bounds = buffers.Buffers[bindingInfo.Slot];
if (bounds.Address != 0)
if (!bounds.IsUnmapped)
{
var isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write);
var range = isStorage
? bufferCache.GetBufferRangeAligned(bounds.Address, bounds.Size, isWrite)
: bufferCache.GetBufferRange(bounds.Address, bounds.Size);
? bufferCache.GetBufferRangeAligned(bounds.Range, isWrite)
: bufferCache.GetBufferRange(bounds.Range);
ranges[rangesCount++] = new BufferAssignment(bindingInfo.Binding, range);
}
@@ -778,12 +796,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
BufferBounds bounds = buffers.Buffers[binding.Slot];
if (bounds.Address == 0)
if (bounds.IsUnmapped)
{
continue;
}
_channel.MemoryManager.Physical.BufferCache.SynchronizeBufferRange(bounds.Address, bounds.Size);
_channel.MemoryManager.Physical.BufferCache.SynchronizeBufferRange(bounds.Range);
}
}
}
@@ -793,23 +811,21 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary>
/// <param name="stage">Shader stage accessing the texture</param>
/// <param name="texture">Buffer texture</param>
/// <param name="address">Address of the buffer in memory</param>
/// <param name="size">Size of the buffer in bytes</param>
/// <param name="range">Physical ranges of memory where the buffer texture data is located</param>
/// <param name="bindingInfo">Binding info for the buffer texture</param>
/// <param name="format">Format of the buffer texture</param>
/// <param name="isImage">Whether the binding is for an image or a sampler</param>
public void SetBufferTextureStorage(
ShaderStage stage,
ITexture texture,
ulong address,
ulong size,
MultiRange range,
TextureBindingInfo bindingInfo,
Format format,
bool isImage)
{
_channel.MemoryManager.Physical.BufferCache.CreateBuffer(address, size);
_channel.MemoryManager.Physical.BufferCache.CreateBuffer(range);
_bufferTextures.Add(new BufferTextureBinding(stage, texture, address, size, bindingInfo, format, isImage));
_bufferTextures.Add(new BufferTextureBinding(stage, texture, range, bindingInfo, format, isImage));
}
/// <summary>

View File

@@ -1,6 +1,7 @@
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Shader;
using Ryujinx.Memory.Range;
namespace Ryujinx.Graphics.Gpu.Memory
{
@@ -20,14 +21,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
public ITexture Texture { get; }
/// <summary>
/// The base address of the buffer binding.
/// Physical ranges of memory where the buffer texture data is located.
/// </summary>
public ulong Address { get; }
/// <summary>
/// The size of the buffer binding in bytes.
/// </summary>
public ulong Size { get; }
public MultiRange Range { get; }
/// <summary>
/// The image or sampler binding info for the buffer texture.
@@ -49,24 +45,21 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary>
/// <param name="stage">Shader stage accessing the texture</param>
/// <param name="texture">Buffer texture</param>
/// <param name="address">Base address</param>
/// <param name="size">Size in bytes</param>
/// <param name="range">Physical ranges of memory where the buffer texture data is located</param>
/// <param name="bindingInfo">Binding info</param>
/// <param name="format">Binding format</param>
/// <param name="isImage">Whether the binding is for an image or a sampler</param>
public BufferTextureBinding(
ShaderStage stage,
ITexture texture,
ulong address,
ulong size,
MultiRange range,
TextureBindingInfo bindingInfo,
Format format,
bool isImage)
{
Stage = stage;
Texture = texture;
Address = address;
Size = size;
Range = range;
BindingInfo = bindingInfo;
Format = format;
IsImage = isImage;

View File

@@ -1,4 +1,5 @@
using Ryujinx.Graphics.GAL;
using Ryujinx.Memory.Range;
namespace Ryujinx.Graphics.Gpu.Memory
{
@@ -7,9 +8,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary>
struct IndexBuffer
{
public ulong Address;
public ulong Size;
public MultiRange Range;
public IndexType Type;
}
}

View File

@@ -39,6 +39,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary>
internal PhysicalMemory Physical { get; }
/// <summary>
/// Virtual buffer cache.
/// </summary>
internal VirtualBufferCache VirtualBufferCache { get; }
/// <summary>
/// Cache of GPU counters.
/// </summary>
@@ -51,10 +56,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
internal MemoryManager(PhysicalMemory physicalMemory)
{
Physical = physicalMemory;
VirtualBufferCache = new VirtualBufferCache(this);
CounterCache = new CounterCache();
_pageTable = new ulong[PtLvl0Size][];
MemoryUnmapped += Physical.TextureCache.MemoryUnmappedHandler;
MemoryUnmapped += Physical.BufferCache.MemoryUnmappedHandler;
MemoryUnmapped += VirtualBufferCache.MemoryUnmappedHandler;
MemoryUnmapped += CounterCache.MemoryUnmappedHandler;
}
@@ -508,6 +515,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
regionSize += Math.Min(endVa - va, PageSize);
}
if (regions.Count == 0)
{
return new MultiRange(regionStart, regionSize);
}
regions.Add(new MemoryRange(regionStart, regionSize));
return new MultiRange(regions.ToArray());

View File

@@ -0,0 +1,60 @@
using Ryujinx.Graphics.GAL;
using Ryujinx.Memory.Range;
using System;
namespace Ryujinx.Graphics.Gpu.Memory
{
/// <summary>
/// Buffer, used to store vertex and index data, uniform and storage buffers, and others.
/// </summary>
class MultiRangeBuffer : IMultiRangeItem, IDisposable
{
private readonly GpuContext _context;
/// <summary>
/// Host buffer handle.
/// </summary>
public BufferHandle Handle { get; }
/// <summary>
/// Range of memory where the data is located.
/// </summary>
public MultiRange Range { get; }
/// <summary>
/// Creates a new instance of the buffer.
/// </summary>
/// <param name="context">GPU context that the buffer belongs to</param>
/// <param name="range">Range of memory where the data is mapped</param>
/// <param name="storages">Backing memory for the buffers</param>
public MultiRangeBuffer(GpuContext context, MultiRange range, ReadOnlySpan<BufferRange> storages)
{
_context = context;
Range = range;
Handle = context.Renderer.CreateBufferSparse(storages);
}
/// <summary>
/// Gets a sub-range from the buffer.
/// </summary>
/// <remarks>
/// This can be used to bind and use sub-ranges of the buffer on the host API.
/// </remarks>
/// <param name="range">Range of memory where the data is mapped</param>
/// <returns>The buffer sub-range</returns>
public BufferRange GetRange(MultiRange range)
{
int offset = Range.FindOffset(range);
return new BufferRange(Handle, offset, (int)range.GetSize());
}
/// <summary>
/// Disposes the host buffer.
/// </summary>
public void Dispose()
{
_context.Renderer.DeleteBuffer(Handle);
}
}
}

View File

@@ -1,3 +1,5 @@
using Ryujinx.Memory.Range;
namespace Ryujinx.Graphics.Gpu.Memory
{
/// <summary>
@@ -5,8 +7,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary>
struct VertexBuffer
{
public ulong Address;
public ulong Size;
public MultiRange Range;
public int Stride;
public int Divisor;
}

View File

@@ -0,0 +1,238 @@
using Ryujinx.Memory.Range;
using System;
using System.Collections.Concurrent;
using System.Threading;
namespace Ryujinx.Graphics.Gpu.Memory
{
/// <summary>
/// Virtual buffer cache.
/// </summary>
class VirtualBufferCache
{
private readonly MemoryManager _memoryManager;
/// <summary>
/// Represents a GPU virtual memory range.
/// </summary>
private readonly struct VirtualRange : IRange
{
/// <summary>
/// GPU virtual address where the range starts.
/// </summary>
public ulong Address { get; }
/// <summary>
/// Size of the range in bytes.
/// </summary>
public ulong Size { get; }
/// <summary>
/// GPU virtual address where the range ends.
/// </summary>
public ulong EndAddress => Address + Size;
/// <summary>
/// Physical regions where the GPU virtual region is mapped.
/// </summary>
public MultiRange Range { get; }
/// <summary>
/// Creates a new virtual memory range.
/// </summary>
/// <param name="address">GPU virtual address where the range starts</param>
/// <param name="size">Size of the range in bytes</param>
/// <param name="range">Physical regions where the GPU virtual region is mapped</param>
public VirtualRange(ulong address, ulong size, MultiRange range)
{
Address = address;
Size = size;
Range = range;
}
/// <summary>
/// Checks if a given range overlaps with the buffer.
/// </summary>
/// <param name="address">Start address of the range</param>
/// <param name="size">Size in bytes of the range</param>
/// <returns>True if the range overlaps, false otherwise</returns>
public bool OverlapsWith(ulong address, ulong size)
{
return Address < address + size && address < EndAddress;
}
}
private readonly RangeList<VirtualRange> _virtualRanges;
private VirtualRange[] _virtualRangeOverlaps;
private readonly ConcurrentQueue<VirtualRange> _deferredUnmaps;
private int _hasDeferredUnmaps;
/// <summary>
/// Creates a new instance of the virtual buffer cache.
/// </summary>
/// <param name="memoryManager">Memory manager that the virtual buffer cache belongs to</param>
public VirtualBufferCache(MemoryManager memoryManager)
{
_memoryManager = memoryManager;
_virtualRanges = new RangeList<VirtualRange>();
_virtualRangeOverlaps = new VirtualRange[BufferCache.OverlapsBufferInitialCapacity];
_deferredUnmaps = new ConcurrentQueue<VirtualRange>();
}
/// <summary>
/// Handles removal of buffers written to a memory region being unmapped.
/// </summary>
/// <param name="sender">Sender object</param>
/// <param name="e">Event arguments</param>
public void MemoryUnmappedHandler(object sender, UnmapEventArgs e)
{
void EnqueueUnmap()
{
_deferredUnmaps.Enqueue(new VirtualRange(e.Address, e.Size, default));
Interlocked.Exchange(ref _hasDeferredUnmaps, 1);
}
e.AddRemapAction(EnqueueUnmap);
}
/// <summary>
/// Tries to get a existing, cached physical range for the specified virtual region.
/// If no cached range is found, a new one is created and added.
/// </summary>
/// <param name="gpuVa">GPU virtual address to get the physical range from</param>
/// <param name="size">Size in bytes of the region</param>
/// <param name="supportsSparse">Indicates host support for sparse buffer mapping of non-contiguous ranges</param>
/// <param name="range">Physical range for the specified GPU virtual region</param>
/// <returns>True if the range already existed, false if a new one was created and added</returns>
public bool TryGetOrAddRange(ulong gpuVa, ulong size, bool supportsSparse, out MultiRange range)
{
VirtualRange[] overlaps = _virtualRangeOverlaps;
int overlapsCount;
if (Interlocked.Exchange(ref _hasDeferredUnmaps, 0) != 0)
{
while (_deferredUnmaps.TryDequeue(out VirtualRange unmappedRange))
{
overlapsCount = _virtualRanges.FindOverlapsNonOverlapping(unmappedRange.Address, unmappedRange.Size, ref overlaps);
for (int index = 0; index < overlapsCount; index++)
{
_virtualRanges.Remove(overlaps[index]);
}
}
}
bool found = false;
ulong originalVa = gpuVa;
overlapsCount = _virtualRanges.FindOverlapsNonOverlapping(gpuVa, size, ref overlaps);
if (overlapsCount != 0)
{
// The virtual range already exists. We just need to check if our range fits inside
// the existing one, and if not, we must extend the existing one.
ulong endAddress = gpuVa + size;
VirtualRange overlap0 = overlaps[0];
if (overlap0.Address > gpuVa || overlap0.EndAddress < endAddress)
{
for (int index = 0; index < overlapsCount; index++)
{
VirtualRange virtualRange = overlaps[index];
gpuVa = Math.Min(gpuVa, virtualRange.Address);
endAddress = Math.Max(endAddress, virtualRange.EndAddress);
_virtualRanges.Remove(virtualRange);
}
ulong newSize = endAddress - gpuVa;
MultiRange newRange = _memoryManager.GetPhysicalRegions(gpuVa, newSize);
_virtualRanges.Add(new(gpuVa, newSize, newRange));
range = newRange.Slice(originalVa - gpuVa, size);
}
else
{
found = true;
range = overlap0.Range.Slice(gpuVa - overlap0.Address, size);
}
}
else
{
// No overlap, just create a new virtual range.
range = _memoryManager.GetPhysicalRegions(gpuVa, size);
VirtualRange virtualRange = new(gpuVa, size, range);
_virtualRanges.Add(virtualRange);
}
ShrinkOverlapsBufferIfNeeded();
// If the the range is not properly aligned for sparse mapping,
// or if the host does not support sparse mapping, let's just
// force it to a single range.
// This might cause issues in some applications that uses sparse
// mappings.
if (!IsSparseAligned(range) || !supportsSparse)
{
range = new MultiRange(range.GetSubRange(0).Address, size);
}
return found;
}
/// <summary>
/// Checks if the physical memory ranges are valid for sparse mapping,
/// which requires all sub-ranges to be 64KB aligned.
/// </summary>
/// <param name="range">Range to check</param>
/// <returns>True if the range is valid for sparse mapping, false otherwise</returns>
private static bool IsSparseAligned(MultiRange range)
{
if (range.Count == 1)
{
return (range.GetSubRange(0).Address & (BufferCache.SparseBufferAlignmentSize - 1)) == 0;
}
for (int i = 0; i < range.Count; i++)
{
MemoryRange subRange = range.GetSubRange(i);
// Check if address is aligned. The address of the first sub-range can
// be misaligned as it is at the start.
if (i > 0 &&
subRange.Address != MemoryManager.PteUnmapped &&
(subRange.Address & (BufferCache.SparseBufferAlignmentSize - 1)) != 0)
{
return false;
}
// Check if the size is aligned. The size of the last sub-range can
// be misaligned as it is at the end.
if (i < range.Count - 1 && (subRange.Size & (BufferCache.SparseBufferAlignmentSize - 1)) != 0)
{
return false;
}
}
return true;
}
/// <summary>
/// Resizes the temporary buffer used for range list intersection results, if it has grown too much.
/// </summary>
private void ShrinkOverlapsBufferIfNeeded()
{
if (_virtualRangeOverlaps.Length > BufferCache.OverlapsBufferMaxCapacity)
{
Array.Resize(ref _virtualRangeOverlaps, BufferCache.OverlapsBufferMaxCapacity);
}
}
}
}

View File

@@ -730,8 +730,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
codeA ??= memoryManager.GetSpan(vertexA.Address, vertexA.Size).ToArray();
codeB ??= memoryManager.GetSpan(currentStage.Address, currentStage.Size).ToArray();
byte[] cb1DataA = memoryManager.Physical.GetSpan(cb1DataAddress, vertexA.Cb1DataSize).ToArray();
byte[] cb1DataB = memoryManager.Physical.GetSpan(cb1DataAddress, currentStage.Cb1DataSize).ToArray();
byte[] cb1DataA = ReadArray(memoryManager, cb1DataAddress, vertexA.Cb1DataSize);
byte[] cb1DataB = ReadArray(memoryManager, cb1DataAddress, currentStage.Cb1DataSize);
ShaderDumpPaths pathsA = default;
ShaderDumpPaths pathsB = default;
@@ -770,7 +770,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
? channel.BufferManager.GetComputeUniformBufferAddress(1)
: channel.BufferManager.GetGraphicsUniformBufferAddress(StageToStageIndex(context.Stage), 1);
byte[] cb1Data = memoryManager.Physical.GetSpan(cb1DataAddress, context.Cb1DataSize).ToArray();
byte[] cb1Data = ReadArray(memoryManager, cb1DataAddress, context.Cb1DataSize);
code ??= memoryManager.GetSpan(context.Address, context.Size).ToArray();
ShaderDumpPaths paths = dumper?.Dump(code, context.Stage == ShaderStage.Compute) ?? default;
@@ -781,6 +781,23 @@ namespace Ryujinx.Graphics.Gpu.Shader
return new TranslatedShader(new CachedShaderStage(program.Info, code, cb1Data), program);
}
/// <summary>
/// Reads data from physical memory, returns an empty array if the memory is unmapped or size is 0.
/// </summary>
/// <param name="memoryManager">Memory manager with the physical memory to read from</param>
/// <param name="address">Physical address of the region to read</param>
/// <param name="size">Size in bytes of the data</param>
/// <returns>An array with the data at the specified memory location</returns>
private static byte[] ReadArray(MemoryManager memoryManager, ulong address, int size)
{
if (address == MemoryManager.PteUnmapped || size == 0)
{
return Array.Empty<byte>();
}
return memoryManager.Physical.GetSpan(address, size).ToArray();
}
/// <summary>
/// Gets the index of a stage from a <see cref="ShaderStage"/>.
/// </summary>

View File

@@ -611,7 +611,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
{
ref BufferBounds bounds = ref channel.BufferManager.GetUniformBufferBounds(isCompute, stageIndex, textureBufferIndex);
cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(channel.MemoryManager.Physical.GetSpan(bounds.Address, (int)bounds.Size));
cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(channel.MemoryManager.Physical.GetSpan(bounds.Range));
cachedTextureBufferIndex = textureBufferIndex;
if (samplerBufferIndex == textureBufferIndex)
@@ -625,7 +625,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
{
ref BufferBounds bounds = ref channel.BufferManager.GetUniformBufferBounds(isCompute, stageIndex, samplerBufferIndex);
cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(channel.MemoryManager.Physical.GetSpan(bounds.Address, (int)bounds.Size));
cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(channel.MemoryManager.Physical.GetSpan(bounds.Range));
cachedSamplerBufferIndex = samplerBufferIndex;
}

View File

@@ -57,16 +57,11 @@ namespace Ryujinx.Graphics.OpenGL
ResourcePool = new ResourcePool();
}
public BufferHandle CreateBuffer(int size, BufferHandle storageHint)
{
return CreateBuffer(size, GAL.BufferAccess.Default);
}
public BufferHandle CreateBuffer(int size, GAL.BufferAccess access)
{
BufferCount++;
if (access == GAL.BufferAccess.FlushPersistent)
if (access.HasFlag(GAL.BufferAccess.FlushPersistent))
{
BufferHandle handle = Buffer.CreatePersistent(size);
@@ -80,11 +75,21 @@ namespace Ryujinx.Graphics.OpenGL
}
}
public BufferHandle CreateBuffer(int size, GAL.BufferAccess access, BufferHandle storageHint)
{
return CreateBuffer(size, access);
}
public BufferHandle CreateBuffer(nint pointer, int size)
{
throw new NotSupportedException();
}
public BufferHandle CreateBufferSparse(ReadOnlySpan<BufferRange> storageBuffers)
{
throw new NotSupportedException();
}
public IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info)
{
return new Program(shaders, info.FragmentOutputMap);
@@ -148,6 +153,7 @@ namespace Ryujinx.Graphics.OpenGL
supportsR4G4B4A4Format: true,
supportsSnormBufferTextureFormat: false,
supports5BitComponentFormat: true,
supportsSparseBuffer: false,
supportsBlendEquationAdvanced: HwCapabilities.SupportsBlendEquationAdvanced,
supportsFragmentShaderInterlock: HwCapabilities.SupportsFragmentShaderInterlock,
supportsFragmentShaderOrderingIntel: HwCapabilities.SupportsFragmentShaderOrdering,

View File

@@ -8,5 +8,6 @@ namespace Ryujinx.Graphics.Vulkan
HostMapped,
DeviceLocal,
DeviceLocalMapped,
Sparse,
}
}

View File

@@ -46,7 +46,7 @@ namespace Ryujinx.Graphics.Vulkan
private bool _lastAccessIsWrite;
private readonly BufferAllocationType _baseType;
private BufferAllocationType _baseType;
private BufferAllocationType _currentType;
private bool _swapQueued;
@@ -109,6 +109,22 @@ namespace Ryujinx.Graphics.Vulkan
_flushLock = new ReaderWriterLockSlim();
}
public BufferHolder(VulkanRenderer gd, Device device, VkBuffer buffer, int size, Auto<MemoryAllocation>[] storageAllocations)
{
_gd = gd;
_device = device;
_waitable = new MultiFenceHolder(size);
_buffer = new Auto<DisposableBuffer>(new DisposableBuffer(gd.Api, device, buffer), _waitable, storageAllocations);
_bufferHandle = buffer.Handle;
Size = size;
_baseType = BufferAllocationType.Sparse;
_currentType = BufferAllocationType.Sparse;
DesiredType = BufferAllocationType.Sparse;
_flushLock = new ReaderWriterLockSlim();
}
public bool TryBackingSwap(ref CommandBufferScoped? cbs)
{
if (_swapQueued && DesiredType != _currentType)
@@ -122,7 +138,7 @@ namespace Ryujinx.Graphics.Vulkan
var currentBuffer = _buffer;
IntPtr currentMap = _map;
(VkBuffer buffer, MemoryAllocation allocation, BufferAllocationType resultType) = _gd.BufferManager.CreateBacking(_gd, Size, DesiredType, false, _currentType);
(VkBuffer buffer, MemoryAllocation allocation, BufferAllocationType resultType) = _gd.BufferManager.CreateBacking(_gd, Size, DesiredType, false, false, _currentType);
if (buffer.Handle != 0)
{
@@ -253,6 +269,14 @@ namespace Ryujinx.Graphics.Vulkan
}
}
public void Pin()
{
if (_baseType == BufferAllocationType.Auto)
{
_baseType = _currentType;
}
}
public unsafe Auto<DisposableBufferView> CreateView(VkFormat format, int offset, int size, Action invalidateView)
{
var bufferViewCreateInfo = new BufferViewCreateInfo
@@ -496,7 +520,7 @@ namespace Ryujinx.Graphics.Vulkan
if (_gd.PipelineInternal.CurrentCommandBuffer.CommandBuffer.Handle == cbs.CommandBuffer.Handle)
{
SetData(rangeOffset, _pendingData.AsSpan(rangeOffset, rangeSize), cbs, _gd.PipelineInternal.EndRenderPass, false);
SetData(rangeOffset, _pendingData.AsSpan(rangeOffset, rangeSize), cbs, _gd.PipelineInternal.EndRenderPassDelegate, false);
}
else
{
@@ -506,6 +530,16 @@ namespace Ryujinx.Graphics.Vulkan
}
}
public Auto<MemoryAllocation> GetAllocation()
{
return _allocationAuto;
}
public (DeviceMemory, ulong) GetDeviceMemoryAndOffset()
{
return (_allocation.Memory, _allocation.Offset);
}
public void SignalWrite(int offset, int size)
{
ConsiderBackingSwap();
@@ -1072,7 +1106,7 @@ namespace Ryujinx.Graphics.Vulkan
}
else
{
_allocationAuto.Dispose();
_allocationAuto?.Dispose();
}
_flushLock.EnterWriteLock();

View File

@@ -96,25 +96,131 @@ namespace Ryujinx.Graphics.Vulkan
return Unsafe.As<ulong, BufferHandle>(ref handle64);
}
public unsafe BufferHandle CreateSparse(VulkanRenderer gd, ReadOnlySpan<BufferRange> storageBuffers)
{
var usage = DefaultBufferUsageFlags;
if (gd.Capabilities.SupportsIndirectParameters)
{
usage |= BufferUsageFlags.IndirectBufferBit;
}
ulong size = 0;
foreach (BufferRange range in storageBuffers)
{
size += (ulong)range.Size;
}
var bufferCreateInfo = new BufferCreateInfo()
{
SType = StructureType.BufferCreateInfo,
Size = size,
Usage = usage,
SharingMode = SharingMode.Exclusive,
Flags = BufferCreateFlags.SparseBindingBit | BufferCreateFlags.SparseAliasedBit
};
gd.Api.CreateBuffer(_device, in bufferCreateInfo, null, out var buffer).ThrowOnError();
var memoryBinds = new SparseMemoryBind[storageBuffers.Length];
var storageAllocations = new Auto<MemoryAllocation>[storageBuffers.Length];
int storageAllocationsCount = 0;
ulong dstOffset = 0;
for (int index = 0; index < storageBuffers.Length; index++)
{
BufferRange range = storageBuffers[index];
if (TryGetBuffer(range.Handle, out var existingHolder))
{
// Since this buffer now also owns the memory from the referenced buffer,
// we pin it to ensure the memory location will not change.
existingHolder.Pin();
(var memory, var offset) = existingHolder.GetDeviceMemoryAndOffset();
memoryBinds[index] = new SparseMemoryBind()
{
ResourceOffset = dstOffset,
Size = (ulong)range.Size,
Memory = memory,
MemoryOffset = offset + (ulong)range.Offset,
Flags = SparseMemoryBindFlags.None
};
storageAllocations[storageAllocationsCount++] = existingHolder.GetAllocation();
}
else
{
memoryBinds[index] = new SparseMemoryBind()
{
ResourceOffset = dstOffset,
Size = (ulong)range.Size,
Memory = default,
MemoryOffset = 0UL,
Flags = SparseMemoryBindFlags.None
};
}
dstOffset += (ulong)range.Size;
}
if (storageAllocations.Length != storageAllocationsCount)
{
Array.Resize(ref storageAllocations, storageAllocationsCount);
}
fixed (SparseMemoryBind* pMemoryBinds = memoryBinds)
{
SparseBufferMemoryBindInfo bufferBind = new SparseBufferMemoryBindInfo()
{
Buffer = buffer,
BindCount = (uint)memoryBinds.Length,
PBinds = pMemoryBinds
};
BindSparseInfo bindSparseInfo = new BindSparseInfo()
{
SType = StructureType.BindSparseInfo,
BufferBindCount = 1,
PBufferBinds = &bufferBind
};
gd.Api.QueueBindSparse(gd.Queue, 1, bindSparseInfo, default).ThrowOnError();
}
var holder = new BufferHolder(gd, _device, buffer, (int)size, storageAllocations);
BufferCount++;
ulong handle64 = (uint)_buffers.Add(holder);
return Unsafe.As<ulong, BufferHandle>(ref handle64);
}
public BufferHandle CreateWithHandle(
VulkanRenderer gd,
int size,
bool sparseCompatible = false,
BufferAllocationType baseType = BufferAllocationType.HostMapped,
BufferHandle storageHint = default,
bool forceMirrors = false)
{
return CreateWithHandle(gd, size, out _, baseType, storageHint, forceMirrors);
return CreateWithHandle(gd, size, out _, sparseCompatible, baseType, storageHint, forceMirrors);
}
public BufferHandle CreateWithHandle(
VulkanRenderer gd,
int size,
out BufferHolder holder,
bool sparseCompatible = false,
BufferAllocationType baseType = BufferAllocationType.HostMapped,
BufferHandle storageHint = default,
bool forceMirrors = false)
{
holder = Create(gd, size, baseType: baseType, storageHint: storageHint);
holder = Create(gd, size, forConditionalRendering: false, sparseCompatible, baseType, storageHint);
if (holder == null)
{
return BufferHandle.Null;
@@ -163,6 +269,7 @@ namespace Ryujinx.Graphics.Vulkan
int size,
BufferAllocationType type,
bool forConditionalRendering = false,
bool sparseCompatible = false,
BufferAllocationType fallbackType = BufferAllocationType.Auto)
{
var usage = DefaultBufferUsageFlags;
@@ -187,6 +294,11 @@ namespace Ryujinx.Graphics.Vulkan
gd.Api.CreateBuffer(_device, in bufferCreateInfo, null, out var buffer).ThrowOnError();
gd.Api.GetBufferMemoryRequirements(_device, buffer, out var requirements);
if (sparseCompatible)
{
requirements.Alignment = Math.Max(requirements.Alignment, Constants.SparseBufferAlignment);
}
MemoryAllocation allocation;
do
@@ -227,6 +339,7 @@ namespace Ryujinx.Graphics.Vulkan
VulkanRenderer gd,
int size,
bool forConditionalRendering = false,
bool sparseCompatible = false,
BufferAllocationType baseType = BufferAllocationType.HostMapped,
BufferHandle storageHint = default)
{
@@ -255,7 +368,7 @@ namespace Ryujinx.Graphics.Vulkan
}
(VkBuffer buffer, MemoryAllocation allocation, BufferAllocationType resultType) =
CreateBacking(gd, size, type, forConditionalRendering);
CreateBacking(gd, size, type, forConditionalRendering, sparseCompatible);
if (buffer.Handle != 0)
{

View File

@@ -16,5 +16,7 @@ namespace Ryujinx.Graphics.Vulkan
public const int MaxStorageBufferBindings = MaxStorageBuffersPerStage * MaxShaderStages;
public const int MaxTextureBindings = MaxTexturesPerStage * MaxShaderStages;
public const int MaxImageBindings = MaxImagesPerStage * MaxShaderStages;
public const ulong SparseBufferAlignment = 0x10000;
}
}

View File

@@ -424,12 +424,12 @@ namespace Ryujinx.Graphics.Vulkan
public static BufferAllocationType Convert(this BufferAccess access)
{
return access switch
if (access.HasFlag(BufferAccess.FlushPersistent) || access.HasFlag(BufferAccess.Stream))
{
BufferAccess.FlushPersistent => BufferAllocationType.HostMapped,
BufferAccess.Stream => BufferAllocationType.HostMapped,
_ => BufferAllocationType.Auto,
};
return BufferAllocationType.HostMapped;
}
return BufferAllocationType.Auto;
}
private static T2 LogInvalidAndReturn<T1, T2>(T1 value, string name, T2 defaultValue = default)

View File

@@ -30,6 +30,7 @@ namespace Ryujinx.Graphics.Vulkan
public readonly PipelineCache PipelineCache;
public readonly AutoFlushCounter AutoFlush;
public readonly Action EndRenderPassDelegate;
protected PipelineDynamicState DynamicState;
private PipelineState _newState;
@@ -92,6 +93,7 @@ namespace Ryujinx.Graphics.Vulkan
Device = device;
AutoFlush = new AutoFlushCounter(gd);
EndRenderPassDelegate = EndRenderPass;
var pipelineCacheCreateInfo = new PipelineCacheCreateInfo
{

View File

@@ -392,6 +392,8 @@ namespace Ryujinx.Graphics.Vulkan
LoadFeatures(maxQueueCount, queueFamilyIndex);
QueueFamilyIndex = queueFamilyIndex;
_window = new Window(this, _surface, _physicalDevice.PhysicalDevice, _device);
_initialized = true;
@@ -399,12 +401,12 @@ namespace Ryujinx.Graphics.Vulkan
public BufferHandle CreateBuffer(int size, BufferAccess access)
{
return BufferManager.CreateWithHandle(this, size, access.Convert(), default, access == BufferAccess.Stream);
return BufferManager.CreateWithHandle(this, size, access.HasFlag(BufferAccess.SparseCompatible), access.Convert(), default, access == BufferAccess.Stream);
}
public BufferHandle CreateBuffer(int size, BufferHandle storageHint)
public BufferHandle CreateBuffer(int size, BufferAccess access, BufferHandle storageHint)
{
return BufferManager.CreateWithHandle(this, size, BufferAllocationType.Auto, storageHint);
return BufferManager.CreateWithHandle(this, size, access.HasFlag(BufferAccess.SparseCompatible), access.Convert(), storageHint);
}
public BufferHandle CreateBuffer(nint pointer, int size)
@@ -412,6 +414,11 @@ namespace Ryujinx.Graphics.Vulkan
return BufferManager.CreateHostImported(this, pointer, size);
}
public BufferHandle CreateBufferSparse(ReadOnlySpan<BufferRange> storageBuffers)
{
return BufferManager.CreateSparse(this, storageBuffers);
}
public IProgram CreateProgram(ShaderSource[] sources, ShaderInfo info)
{
bool isCompute = sources.Length == 1 && sources[0].Stage == ShaderStage.Compute;
@@ -571,6 +578,7 @@ namespace Ryujinx.Graphics.Vulkan
Api.GetPhysicalDeviceFeatures2(_physicalDevice.PhysicalDevice, &features2);
var limits = _physicalDevice.PhysicalDeviceProperties.Limits;
var mainQueueProperties = _physicalDevice.QueueFamilyProperties[QueueFamilyIndex];
return new Capabilities(
api: TargetApi.Vulkan,
@@ -590,6 +598,7 @@ namespace Ryujinx.Graphics.Vulkan
supportsR4G4B4A4Format: supportsR4G4B4A4Format,
supportsSnormBufferTextureFormat: true,
supports5BitComponentFormat: supports5BitComponentFormat,
supportsSparseBuffer: features2.Features.SparseBinding && mainQueueProperties.QueueFlags.HasFlag(QueueFlags.SparseBindingBit),
supportsBlendEquationAdvanced: Capabilities.SupportsBlendEquationAdvanced,
supportsFragmentShaderInterlock: Capabilities.SupportsFragmentShaderInterlock,
supportsFragmentShaderOrderingIntel: false,
@@ -775,7 +784,7 @@ namespace Ryujinx.Graphics.Vulkan
public void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan<byte> data)
{
BufferManager.SetData(buffer, offset, data, _pipeline.CurrentCommandBuffer, _pipeline.EndRenderPass);
BufferManager.SetData(buffer, offset, data, _pipeline.CurrentCommandBuffer, _pipeline.EndRenderPassDelegate);
}
public void UpdateCounters()

View File

@@ -675,7 +675,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
KMemoryPermission.None,
MemoryAttribute.Mask,
MemoryAttribute.None,
MemoryAttribute.IpcAndDeviceMapped,
MemoryAttribute.IpcAndDeviceMapped | MemoryAttribute.PermissionLocked,
out MemoryState state,
out _,
out _);
@@ -687,7 +687,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
state,
KMemoryPermission.None,
KMemoryPermission.None,
MemoryAttribute.Mask,
MemoryAttribute.Mask & ~MemoryAttribute.PermissionLocked,
MemoryAttribute.None);
if (success)
@@ -913,19 +913,27 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
return Result.Success;
}
public Result SetMemoryAttribute(
ulong address,
ulong size,
MemoryAttribute attributeMask,
MemoryAttribute attributeValue)
public Result SetMemoryAttribute(ulong address, ulong size, MemoryAttribute attributeMask, MemoryAttribute attributeValue)
{
lock (_blockManager)
{
MemoryState stateCheckMask = 0;
if (attributeMask.HasFlag(MemoryAttribute.Uncached))
{
stateCheckMask = MemoryState.AttributeChangeAllowed;
}
if (attributeMask.HasFlag(MemoryAttribute.PermissionLocked))
{
stateCheckMask |= MemoryState.PermissionLockAllowed;
}
if (CheckRange(
address,
size,
MemoryState.AttributeChangeAllowed,
MemoryState.AttributeChangeAllowed,
stateCheckMask,
stateCheckMask,
KMemoryPermission.None,
KMemoryPermission.None,
MemoryAttribute.BorrowedAndIpcMapped,

View File

@@ -12,11 +12,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
IpcMapped = 1 << 1,
DeviceMapped = 1 << 2,
Uncached = 1 << 3,
PermissionLocked = 1 << 4,
IpcAndDeviceMapped = IpcMapped | DeviceMapped,
BorrowedAndIpcMapped = Borrowed | IpcMapped,
DeviceMappedAndUncached = DeviceMapped | Uncached,
}
}

View File

@@ -5,35 +5,155 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
[Flags]
enum MemoryState : uint
{
Unmapped = 0x00000000,
Io = 0x00002001,
Normal = 0x00042002,
CodeStatic = 0x00DC7E03,
CodeMutable = 0x03FEBD04,
Heap = 0x037EBD05,
SharedMemory = 0x00402006,
ModCodeStatic = 0x00DD7E08,
ModCodeMutable = 0x03FFBD09,
IpcBuffer0 = 0x005C3C0A,
Stack = 0x005C3C0B,
ThreadLocal = 0x0040200C,
TransferMemoryIsolated = 0x015C3C0D,
TransferMemory = 0x005C380E,
ProcessMemory = 0x0040380F,
Reserved = 0x00000010,
IpcBuffer1 = 0x005C3811,
IpcBuffer3 = 0x004C2812,
KernelStack = 0x00002013,
CodeReadOnly = 0x00402214,
CodeWritable = 0x00402015,
UserMask = 0xff,
Mask = 0xffffffff,
Unmapped = 0x0,
Io = Mapped | 0x1,
Normal = Mapped | QueryPhysicalAddressAllowed | 0x2,
CodeStatic = ForceReadWritableByDebugSyscalls |
IpcSendAllowedType0 |
IpcSendAllowedType3 |
IpcSendAllowedType1 |
Mapped |
ProcessPermissionChangeAllowed |
QueryPhysicalAddressAllowed |
MapDeviceAllowed |
MapDeviceAlignedAllowed |
IsPoolAllocated |
MapProcessAllowed |
LinearMapped |
0x3,
CodeMutable = PermissionChangeAllowed |
IpcSendAllowedType0 |
IpcSendAllowedType3 |
IpcSendAllowedType1 |
Mapped |
MapAllowed |
TransferMemoryAllowed |
QueryPhysicalAddressAllowed |
MapDeviceAllowed |
MapDeviceAlignedAllowed |
IpcBufferAllowed |
IsPoolAllocated |
MapProcessAllowed |
AttributeChangeAllowed |
CodeMemoryAllowed |
LinearMapped |
PermissionLockAllowed |
0x4,
Heap = PermissionChangeAllowed |
IpcSendAllowedType0 |
IpcSendAllowedType3 |
IpcSendAllowedType1 |
Mapped |
MapAllowed |
TransferMemoryAllowed |
QueryPhysicalAddressAllowed |
MapDeviceAllowed |
MapDeviceAlignedAllowed |
IpcBufferAllowed |
IsPoolAllocated |
AttributeChangeAllowed |
CodeMemoryAllowed |
LinearMapped |
0x5,
SharedMemory = Mapped | IsPoolAllocated | LinearMapped | 0x6,
ModCodeStatic = ForceReadWritableByDebugSyscalls |
IpcSendAllowedType0 |
IpcSendAllowedType3 |
IpcSendAllowedType1 |
Mapped |
ProcessPermissionChangeAllowed |
UnmapProcessCodeMemoryAllowed |
QueryPhysicalAddressAllowed |
MapDeviceAllowed |
MapDeviceAlignedAllowed |
IsPoolAllocated |
MapProcessAllowed |
LinearMapped |
0x8,
ModCodeMutable = PermissionChangeAllowed |
IpcSendAllowedType0 |
IpcSendAllowedType3 |
IpcSendAllowedType1 |
Mapped |
MapAllowed |
UnmapProcessCodeMemoryAllowed |
TransferMemoryAllowed |
QueryPhysicalAddressAllowed |
MapDeviceAllowed |
MapDeviceAlignedAllowed |
IpcBufferAllowed |
IsPoolAllocated |
MapProcessAllowed |
AttributeChangeAllowed |
CodeMemoryAllowed |
LinearMapped |
PermissionLockAllowed |
0x9,
IpcBuffer0 = IpcSendAllowedType0 |
IpcSendAllowedType3 |
IpcSendAllowedType1 |
Mapped |
QueryPhysicalAddressAllowed |
MapDeviceAllowed |
MapDeviceAlignedAllowed |
IsPoolAllocated |
LinearMapped |
0xA,
Stack = IpcSendAllowedType0 |
IpcSendAllowedType3 |
IpcSendAllowedType1 |
Mapped |
QueryPhysicalAddressAllowed |
MapDeviceAllowed |
MapDeviceAlignedAllowed |
IsPoolAllocated |
LinearMapped |
0xB,
ThreadLocal = Mapped | IsPoolAllocated | LinearMapped | 0xC,
TransferMemoryIsolated = IpcSendAllowedType0 |
IpcSendAllowedType3 |
IpcSendAllowedType1 |
Mapped |
QueryPhysicalAddressAllowed |
MapDeviceAllowed |
MapDeviceAlignedAllowed |
IsPoolAllocated |
AttributeChangeAllowed |
LinearMapped |
0xD,
TransferMemory = IpcSendAllowedType3 |
IpcSendAllowedType1 |
Mapped |
QueryPhysicalAddressAllowed |
MapDeviceAllowed |
MapDeviceAlignedAllowed |
IsPoolAllocated |
LinearMapped |
0xE,
ProcessMemory = IpcSendAllowedType3 | IpcSendAllowedType1 | Mapped | IsPoolAllocated | LinearMapped | 0xF,
Reserved = 0x10,
IpcBuffer1 = IpcSendAllowedType3 |
IpcSendAllowedType1 |
Mapped |
QueryPhysicalAddressAllowed |
MapDeviceAllowed |
MapDeviceAlignedAllowed |
IsPoolAllocated |
LinearMapped |
0x11,
IpcBuffer3 = IpcSendAllowedType3 | Mapped | QueryPhysicalAddressAllowed | MapDeviceAllowed | IsPoolAllocated | LinearMapped | 0x12,
KernelStack = Mapped | 0x13,
CodeReadOnly = ForceReadWritableByDebugSyscalls | Mapped | IsPoolAllocated | LinearMapped | 0x14,
CodeWritable = Mapped | IsPoolAllocated | LinearMapped | 0x15,
UserMask = 0xFF,
Mask = 0xFFFFFFFF,
PermissionChangeAllowed = 1 << 8,
ForceReadWritableByDebugSyscalls = 1 << 9,
IpcSendAllowedType0 = 1 << 10,
IpcSendAllowedType3 = 1 << 11,
IpcSendAllowedType1 = 1 << 12,
Mapped = 1 << 13,
ProcessPermissionChangeAllowed = 1 << 14,
MapAllowed = 1 << 15,
UnmapProcessCodeMemoryAllowed = 1 << 16,
@@ -46,5 +166,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
MapProcessAllowed = 1 << 23,
AttributeChangeAllowed = 1 << 24,
CodeMemoryAllowed = 1 << 25,
LinearMapped = 1 << 26,
PermissionLockAllowed = 1 << 27,
}
}

View File

@@ -949,8 +949,16 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
MemoryAttribute attributes = attributeMask | attributeValue;
const MemoryAttribute SupportedAttributes = MemoryAttribute.Uncached | MemoryAttribute.PermissionLocked;
if (attributes != attributeMask ||
(attributes | MemoryAttribute.Uncached) != MemoryAttribute.Uncached)
(attributes | SupportedAttributes) != SupportedAttributes)
{
return KernelResult.InvalidCombination;
}
// The permission locked attribute can't be unset.
if ((attributeMask & MemoryAttribute.PermissionLocked) != (attributeValue & MemoryAttribute.PermissionLocked))
{
return KernelResult.InvalidCombination;
}

View File

@@ -1,10 +1,12 @@
using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.HLE.HOS.Services.Account.Acc.AsyncContext;
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Security.Principal;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@@ -37,11 +39,6 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService
credentials.Key.KeyId = parameters.ToString();
var header = new JwtHeader(credentials)
{
{ "jku", "https://e0d67c509fb203858ebcb2fe3f88c2aa.baas.nintendo.com/1.0.0/certificates" },
};
byte[] rawUserId = new byte[0x10];
RandomNumberGenerator.Fill(rawUserId);
@@ -51,23 +48,25 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService
byte[] deviceAccountId = new byte[0x10];
RandomNumberGenerator.Fill(deviceId);
var payload = new JwtPayload
var descriptor = new SecurityTokenDescriptor
{
{ "sub", Convert.ToHexString(rawUserId).ToLower() },
{ "aud", "ed9e2f05d286f7b8" },
{ "di", Convert.ToHexString(deviceId).ToLower() },
{ "sn", "XAW10000000000" },
{ "bs:did", Convert.ToHexString(deviceAccountId).ToLower() },
{ "iss", "https://e0d67c509fb203858ebcb2fe3f88c2aa.baas.nintendo.com" },
{ "typ", "id_token" },
{ "iat", DateTimeOffset.UtcNow.ToUnixTimeSeconds() },
{ "jti", Guid.NewGuid().ToString() },
{ "exp", (DateTimeOffset.UtcNow + TimeSpan.FromHours(3)).ToUnixTimeSeconds() },
Subject = new GenericIdentity(Convert.ToHexString(rawUserId).ToLower()),
SigningCredentials = credentials,
Audience = "ed9e2f05d286f7b8",
Issuer = "https://e0d67c509fb203858ebcb2fe3f88c2aa.baas.nintendo.com",
TokenType = "id_token",
IssuedAt = DateTime.UtcNow,
Expires = DateTime.UtcNow + TimeSpan.FromHours(3),
Claims = new Dictionary<string, object>
{
{ "jku", "https://e0d67c509fb203858ebcb2fe3f88c2aa.baas.nintendo.com/1.0.0/certificates" },
{ "di", Convert.ToHexString(deviceId).ToLower() },
{ "sn", "XAW10000000000" },
{ "bs:did", Convert.ToHexString(deviceAccountId).ToLower() }
}
};
JwtSecurityToken securityToken = new(header, payload);
return new JwtSecurityTokenHandler().WriteToken(securityToken);
return new JsonWebTokenHandler().CreateToken(descriptor);
}
public ResultCode CheckAvailability(ServiceCtx context)

View File

@@ -356,11 +356,22 @@ namespace Ryujinx.HLE.Loaders.Processes
return ProcessResult.Failed;
}
string displayVersion;
if (metaLoader.GetProgramId() > 0x0100000000007FFF)
{
displayVersion = applicationControlProperties.Value.DisplayVersionString.ToString();
}
else
{
displayVersion = device.System.ContentManager.GetCurrentFirmwareVersion()?.VersionString ?? string.Empty;
}
var processContextFactory = new ArmProcessContextFactory(
context.Device.System.TickSource,
context.Device.Gpu,
$"{programId:x16}",
applicationControlProperties.Value.DisplayVersionString.ToString(),
displayVersion,
diskCacheEnabled,
codeStart,
codeSize);

View File

@@ -23,10 +23,10 @@
<ItemGroup>
<PackageReference Include="Concentus" />
<PackageReference Include="LibHac" />
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" />
<PackageReference Include="MsgPack.Cli" />
<PackageReference Include="SixLabors.ImageSharp" />
<PackageReference Include="SixLabors.ImageSharp.Drawing" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" />
<PackageReference Include="NetCoreServer" />
</ItemGroup>

View File

@@ -15,6 +15,11 @@ namespace Ryujinx.Memory.Range
private bool HasSingleRange => _ranges == null;
/// <summary>
/// Indicates that the range is fully unmapped.
/// </summary>
public bool IsUnmapped => HasSingleRange && _singleRange.Address == InvalidAddress;
/// <summary>
/// Total of physical sub-ranges on the virtual memory region.
/// </summary>
@@ -38,8 +43,18 @@ namespace Ryujinx.Memory.Range
/// <exception cref="ArgumentNullException"><paramref name="ranges"/> is null</exception>
public MultiRange(MemoryRange[] ranges)
{
_singleRange = MemoryRange.Empty;
_ranges = ranges ?? throw new ArgumentNullException(nameof(ranges));
ArgumentNullException.ThrowIfNull(ranges);
if (ranges.Length == 1)
{
_singleRange = ranges[0];
_ranges = null;
}
else
{
_singleRange = MemoryRange.Empty;
_ranges = ranges;
}
}
/// <summary>
@@ -91,7 +106,7 @@ namespace Ryujinx.Memory.Range
offset -= range.Size;
}
return new MultiRange(ranges.ToArray());
return ranges.Count == 1 ? new MultiRange(ranges[0].Address, ranges[0].Size) : new MultiRange(ranges.ToArray());
}
}

View File

@@ -1,3 +1,4 @@
using Gdk;
using Gtk;
using Ryujinx.Common;
using Ryujinx.Common.Configuration;
@@ -13,7 +14,9 @@ using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using Window = Gtk.Window;
namespace Ryujinx.Ui.Windows
{
@@ -49,11 +52,11 @@ namespace Ryujinx.Ui.Windows
public AmiiboWindow() : base($"Ryujinx {Program.Version} - Amiibo")
{
Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png");
Icon = new Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png");
InitializeComponent();
_httpClient = new HttpClient()
_httpClient = new HttpClient
{
Timeout = TimeSpan.FromSeconds(30),
};
@@ -64,7 +67,7 @@ namespace Ryujinx.Ui.Windows
_amiiboList = new List<AmiiboApi>();
_amiiboLogoBytes = EmbeddedResources.Read("Ryujinx.Ui.Common/Resources/Logo_Amiibo.png");
_amiiboImage.Pixbuf = new Gdk.Pixbuf(_amiiboLogoBytes);
_amiiboImage.Pixbuf = new Pixbuf(_amiiboLogoBytes);
_scanButton.Sensitive = false;
_randomUuidCheckBox.Sensitive = false;
@@ -72,37 +75,83 @@ namespace Ryujinx.Ui.Windows
_ = LoadContentAsync();
}
private async Task LoadContentAsync()
private static bool TryGetAmiiboJson(string json, out AmiiboJson amiiboJson)
{
string amiiboJsonString = DefaultJson;
if (File.Exists(_amiiboJsonPath))
if (string.IsNullOrEmpty(json))
{
amiiboJsonString = await File.ReadAllTextAsync(_amiiboJsonPath);
amiiboJson = JsonHelper.Deserialize(DefaultJson, _serializerContext.AmiiboJson);
if (await NeedsUpdate(JsonHelper.Deserialize(amiiboJsonString, _serializerContext.AmiiboJson).LastUpdated))
{
amiiboJsonString = await DownloadAmiiboJson();
}
return false;
}
else
try
{
amiiboJson = JsonHelper.Deserialize(json, _serializerContext.AmiiboJson);
return true;
}
catch (JsonException exception)
{
Logger.Error?.Print(LogClass.Application, $"Unable to deserialize amiibo data: {exception}");
amiiboJson = JsonHelper.Deserialize(DefaultJson, _serializerContext.AmiiboJson);
return false;
}
}
private async Task<AmiiboJson> GetMostRecentAmiiboListOrDefaultJson()
{
bool localIsValid = false;
bool remoteIsValid = false;
AmiiboJson amiiboJson = new();
try
{
try
{
amiiboJsonString = await DownloadAmiiboJson();
if (File.Exists(_amiiboJsonPath))
{
localIsValid = TryGetAmiiboJson(await File.ReadAllTextAsync(_amiiboJsonPath), out amiiboJson);
}
}
catch (Exception ex)
catch (Exception exception)
{
Logger.Error?.Print(LogClass.Application, $"Failed to download amiibo data: {ex}");
Logger.Warning?.Print(LogClass.Application, $"Unable to read data from '{_amiiboJsonPath}': {exception}");
}
if (!localIsValid || await NeedsUpdate(amiiboJson.LastUpdated))
{
remoteIsValid = TryGetAmiiboJson(await DownloadAmiiboJson(), out amiiboJson);
}
}
catch (Exception exception)
{
if (!(localIsValid || remoteIsValid))
{
Logger.Error?.Print(LogClass.Application, $"Couldn't get valid amiibo data: {exception}");
// Neither local or remote files are valid JSON, close window.
ShowInfoDialog();
Close();
}
else if (!remoteIsValid)
{
Logger.Warning?.Print(LogClass.Application, $"Couldn't update amiibo data: {exception}");
// Only the local file is valid, the local one should be used
// but the user should be warned.
ShowInfoDialog();
}
}
_amiiboList = JsonHelper.Deserialize(amiiboJsonString, _serializerContext.AmiiboJson).Amiibo;
_amiiboList = _amiiboList.OrderBy(amiibo => amiibo.AmiiboSeries).ToList();
return amiiboJson;
}
private async Task LoadContentAsync()
{
AmiiboJson amiiboJson = await GetMostRecentAmiiboListOrDefaultJson();
_amiiboList = amiiboJson.Amiibo.OrderBy(amiibo => amiibo.AmiiboSeries).ToList();
if (LastScannedAmiiboShowAll)
{
@@ -178,46 +227,50 @@ namespace Ryujinx.Ui.Windows
if (response.IsSuccessStatusCode)
{
return response.Content.Headers.LastModified != new DateTimeOffset(oldLastModified.Ticks - (oldLastModified.Ticks % TimeSpan.TicksPerSecond), TimeSpan.Zero);
return response.Content.Headers.LastModified != oldLastModified;
}
return false;
}
catch (Exception ex)
catch (HttpRequestException exception)
{
Logger.Error?.Print(LogClass.Application, $"Failed to check for amiibo updates: {ex}");
ShowInfoDialog();
return false;
Logger.Error?.Print(LogClass.Application, $"Unable to check for amiibo data updates: {exception}");
}
return false;
}
private async Task<string> DownloadAmiiboJson()
{
HttpResponseMessage response = await _httpClient.GetAsync("https://amiibo.ryujinx.org/");
if (response.IsSuccessStatusCode)
try
{
string amiiboJsonString = await response.Content.ReadAsStringAsync();
HttpResponseMessage response = await _httpClient.GetAsync("https://amiibo.ryujinx.org/");
using (FileStream dlcJsonStream = File.Create(_amiiboJsonPath, 4096, FileOptions.WriteThrough))
if (response.IsSuccessStatusCode)
{
dlcJsonStream.Write(Encoding.UTF8.GetBytes(amiiboJsonString));
string amiiboJsonString = await response.Content.ReadAsStringAsync();
try
{
using FileStream dlcJsonStream = File.Create(_amiiboJsonPath, 4096, FileOptions.WriteThrough);
dlcJsonStream.Write(Encoding.UTF8.GetBytes(amiiboJsonString));
}
catch (Exception exception)
{
Logger.Warning?.Print(LogClass.Application, $"Couldn't write amiibo data to file '{_amiiboJsonPath}: {exception}'");
}
return amiiboJsonString;
}
return amiiboJsonString;
}
else
{
Logger.Error?.Print(LogClass.Application, $"Failed to download amiibo data. Response status code: {response.StatusCode}");
GtkDialog.CreateInfoDialog($"Amiibo API", "An error occured while fetching information from the API.");
Close();
}
catch (HttpRequestException exception)
{
Logger.Error?.Print(LogClass.Application, $"Failed to request amiibo data: {exception}");
}
return DefaultJson;
GtkDialog.CreateInfoDialog("Amiibo API", "An error occured while fetching information from the API.");
return null;
}
private async Task UpdateAmiiboPreview(string imageUrl)
@@ -227,7 +280,7 @@ namespace Ryujinx.Ui.Windows
if (response.IsSuccessStatusCode)
{
byte[] amiiboPreviewBytes = await response.Content.ReadAsByteArrayAsync();
Gdk.Pixbuf amiiboPreview = new(amiiboPreviewBytes);
Pixbuf amiiboPreview = new(amiiboPreviewBytes);
float ratio = Math.Min((float)_amiiboImage.AllocatedWidth / amiiboPreview.Width,
(float)_amiiboImage.AllocatedHeight / amiiboPreview.Height);
@@ -235,7 +288,7 @@ namespace Ryujinx.Ui.Windows
int resizeHeight = (int)(amiiboPreview.Height * ratio);
int resizeWidth = (int)(amiiboPreview.Width * ratio);
_amiiboImage.Pixbuf = amiiboPreview.ScaleSimple(resizeWidth, resizeHeight, Gdk.InterpType.Bilinear);
_amiiboImage.Pixbuf = amiiboPreview.ScaleSimple(resizeWidth, resizeHeight, InterpType.Bilinear);
}
else
{
@@ -245,7 +298,7 @@ namespace Ryujinx.Ui.Windows
private static void ShowInfoDialog()
{
GtkDialog.CreateInfoDialog($"Amiibo API", "Unable to connect to Amiibo API server. The service may be down or you may need to verify your internet connection is online.");
GtkDialog.CreateInfoDialog("Amiibo API", "Unable to connect to Amiibo API server. The service may be down or you may need to verify your internet connection is online.");
}
//
@@ -301,7 +354,7 @@ namespace Ryujinx.Ui.Windows
{
AmiiboId = _amiiboCharsComboBox.ActiveId;
_amiiboImage.Pixbuf = new Gdk.Pixbuf(_amiiboLogoBytes);
_amiiboImage.Pixbuf = new Pixbuf(_amiiboLogoBytes);
string imageUrl = _amiiboList.Find(amiibo => amiibo.Head + amiibo.Tail == _amiiboCharsComboBox.ActiveId).Image;
@@ -341,7 +394,7 @@ namespace Ryujinx.Ui.Windows
private void ShowAllCheckBox_Clicked(object sender, EventArgs e)
{
_amiiboImage.Pixbuf = new Gdk.Pixbuf(_amiiboLogoBytes);
_amiiboImage.Pixbuf = new Pixbuf(_amiiboLogoBytes);
_amiiboSeriesComboBox.Changed -= SeriesComboBox_Changed;
_amiiboCharsComboBox.Changed -= CharacterComboBox_Changed;
@@ -352,7 +405,7 @@ namespace Ryujinx.Ui.Windows
_scanButton.Sensitive = false;
_randomUuidCheckBox.Sensitive = false;
new Task(() => ParseAmiiboData()).Start();
new Task(ParseAmiiboData).Start();
}
private void ScanButton_Pressed(object sender, EventArgs args)