summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md4
m---------externals/dynarmic0
-rw-r--r--license.txt937
-rw-r--r--src/common/logging/filter.cpp1
-rw-r--r--src/common/logging/types.h1
-rw-r--r--src/common/math_util.h50
-rw-r--r--src/core/CMakeLists.txt29
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp6
-rw-r--r--src/core/hle/kernel/k_code_memory.cpp3
-rw-r--r--src/core/hle/kernel/k_page_linked_list.h4
-rw-r--r--src/core/hle/kernel/k_page_table.cpp242
-rw-r--r--src/core/hle/kernel/k_page_table.h33
-rw-r--r--src/core/hle/kernel/svc.cpp13
-rw-r--r--src/core/hle/service/am/applets/applet_software_keyboard.cpp474
-rw-r--r--src/core/hle/service/am/applets/applet_software_keyboard.h35
-rw-r--r--src/core/hle/service/am/applets/applet_software_keyboard_types.h72
-rw-r--r--src/core/hle/service/hid/hid.cpp4
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp10
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.h7
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp7
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.h10
-rw-r--r--src/core/hle/service/nvdrv/nvdata.h10
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.h3
-rw-r--r--src/core/hle/service/nvflinger/binder.h42
-rw-r--r--src/core/hle/service/nvflinger/buffer_item.h46
-rw-r--r--src/core/hle/service/nvflinger/buffer_item_consumer.cpp59
-rw-r--r--src/core/hle/service/nvflinger/buffer_item_consumer.h28
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.cpp206
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.h154
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_consumer.cpp203
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_consumer.h37
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_core.cpp118
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_core.h81
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_defs.h21
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_producer.cpp942
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_producer.h83
-rw-r--r--src/core/hle/service/nvflinger/buffer_slot.h39
-rw-r--r--src/core/hle/service/nvflinger/buffer_transform_flags.h25
-rw-r--r--src/core/hle/service/nvflinger/consumer_base.cpp130
-rw-r--r--src/core/hle/service/nvflinger/consumer_base.h61
-rw-r--r--src/core/hle/service/nvflinger/consumer_listener.h26
-rw-r--r--src/core/hle/service/nvflinger/graphic_buffer_producer.cpp20
-rw-r--r--src/core/hle/service/nvflinger/graphic_buffer_producer.h76
-rw-r--r--src/core/hle/service/nvflinger/hos_binder_driver_server.cpp36
-rw-r--r--src/core/hle/service/nvflinger/hos_binder_driver_server.h37
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp81
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.h26
-rw-r--r--src/core/hle/service/nvflinger/parcel.h172
-rw-r--r--src/core/hle/service/nvflinger/pixel_format.h21
-rw-r--r--src/core/hle/service/nvflinger/producer_listener.h16
-rw-r--r--src/core/hle/service/nvflinger/status.h28
-rw-r--r--src/core/hle/service/nvflinger/ui/fence.h32
-rw-r--r--src/core/hle/service/nvflinger/ui/graphic_buffer.h100
-rw-r--r--src/core/hle/service/nvflinger/window.h53
-rw-r--r--src/core/hle/service/service.cpp6
-rw-r--r--src/core/hle/service/service.h4
-rw-r--r--src/core/hle/service/vi/display/vi_display.cpp50
-rw-r--r--src/core/hle/service/vi/display/vi_display.h29
-rw-r--r--src/core/hle/service/vi/layer/vi_layer.cpp6
-rw-r--r--src/core/hle/service/vi/layer/vi_layer.h57
-rw-r--r--src/core/hle/service/vi/vi.cpp690
-rw-r--r--src/core/hle/service/vi/vi.h10
-rw-r--r--src/core/hle/service/vi/vi_m.cpp9
-rw-r--r--src/core/hle/service/vi/vi_m.h7
-rw-r--r--src/core/hle/service/vi/vi_s.cpp9
-rw-r--r--src/core/hle/service/vi/vi_s.h7
-rw-r--r--src/core/hle/service/vi/vi_u.cpp9
-rw-r--r--src/core/hle/service/vi/vi_u.h7
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp2
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.cpp1
-rw-r--r--src/shader_recompiler/frontend/ir/microinstruction.cpp5
-rw-r--r--src/shader_recompiler/frontend/ir/value.h4
-rw-r--r--src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp98
-rw-r--r--src/video_core/buffer_cache/buffer_base.h3
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h34
-rw-r--r--src/video_core/framebuffer_config.h29
-rw-r--r--src/video_core/gpu.h1
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.cpp14
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.h12
-rw-r--r--src/video_core/renderer_opengl/gl_device.cpp10
-rw-r--r--src/video_core/renderer_opengl/gl_device.h7
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp6
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.cpp18
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.h13
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp8
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h4
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.cpp10
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp12
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.h6
-rw-r--r--src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp9
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.h4
-rw-r--r--src/video_core/surface.cpp8
-rw-r--r--src/video_core/surface.h2
-rw-r--r--src/video_core/texture_cache/image_base.h7
-rw-r--r--src/video_core/texture_cache/texture_cache.h66
-rw-r--r--src/video_core/texture_cache/texture_cache_base.h7
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp51
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h11
-rw-r--r--src/video_core/vulkan_common/vulkan_memory_allocator.cpp2
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.cpp13
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.h4
-rw-r--r--src/yuzu/configuration/config.cpp2
-rw-r--r--src/yuzu/configuration/configure_debug.cpp8
-rw-r--r--src/yuzu/configuration/configure_debug.ui91
-rw-r--r--src/yuzu/main.cpp23
-rw-r--r--src/yuzu/main.h3
-rw-r--r--src/yuzu/uisettings.h1
-rw-r--r--src/yuzu_cmd/yuzu.cpp1
110 files changed, 4664 insertions, 1803 deletions
diff --git a/README.md b/README.md
index c09d49ca4..139eb8599 100644
--- a/README.md
+++ b/README.md
@@ -77,6 +77,6 @@ If you wish to support us a different way, please join our [Discord](https://dis
77 77
78## License 78## License
79 79
80yuzu is licensed under the GPLv2 (or any later version). Refer to the [license.txt](https://github.com/yuzu-emu/yuzu/blob/master/license.txt) file. 80yuzu is licensed under the GPLv3 (or any later version). Refer to the [license.txt](https://github.com/yuzu-emu/yuzu/blob/master/license.txt) file.
81 81
82The [Skyline-Emulator Team](https://github.com/skyline-emu/skyline) is exempt from GPLv2 for the contributions from all these contributors [FernandoS27](https://github.com/FernandoS27), [lioncash](https://github.com/lioncash), [bunnei](https://github.com/bunnei), [ReinUsesLisp](https://github.com/ReinUsesLisp), [Morph1984](https://github.com/Morph1984), [ogniK5377](https://github.com/ogniK5377), [german77](https://github.com/german77), [ameerj](https://github.com/ameerj), [Kelebek1](https://github.com/Kelebek1) and [lat9nq](https://github.com/lat9nq). They may only use the code from these contributors under Mozilla Public License, version 2.0. 82The [Skyline-Emulator Team](https://github.com/skyline-emu/skyline) is exempt from GPLv3 for the contributions from all these contributors [FernandoS27](https://github.com/FernandoS27), [lioncash](https://github.com/lioncash), [bunnei](https://github.com/bunnei), [ReinUsesLisp](https://github.com/ReinUsesLisp), [Morph1984](https://github.com/Morph1984), [ogniK5377](https://github.com/ogniK5377), [german77](https://github.com/german77), [ameerj](https://github.com/ameerj), [Kelebek1](https://github.com/Kelebek1) and [lat9nq](https://github.com/lat9nq). They may only use the code from these contributors under Mozilla Public License, version 2.0.
diff --git a/externals/dynarmic b/externals/dynarmic
Subproject e1a266b9299be81cc0318c7e25b00388c342704 Subproject af2d50288fc537201014c4230bb55ab9018a743
diff --git a/license.txt b/license.txt
index 495f3e676..e72bfddab 100644
--- a/license.txt
+++ b/license.txt
@@ -1,285 +1,626 @@
1 GNU GENERAL PUBLIC LICENSE 1 GNU GENERAL PUBLIC LICENSE
2 Version 2, June 1991 2 Version 3, 29 June 2007
3 3
4 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 4 Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
5 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 Everyone is permitted to copy and distribute verbatim copies 5 Everyone is permitted to copy and distribute verbatim copies
7 of this license document, but changing it is not allowed. 6 of this license document, but changing it is not allowed.
8 7
9 Preamble 8 Preamble
10 9
11 The licenses for most software are designed to take away your 10 The GNU General Public License is a free, copyleft license for
12freedom to share and change it. By contrast, the GNU General Public 11software and other kinds of works.
13License is intended to guarantee your freedom to share and change free 12
14software--to make sure the software is free for all its users. This 13 The licenses for most software and other practical works are designed
15General Public License applies to most of the Free Software 14to take away your freedom to share and change the works. By contrast,
16Foundation's software and to any other program whose authors commit to 15the GNU General Public License is intended to guarantee your freedom to
17using it. (Some other Free Software Foundation software is covered by 16share and change all versions of a program--to make sure it remains free
18the GNU Lesser General Public License instead.) You can apply it to 17software for all its users. We, the Free Software Foundation, use the
18GNU General Public License for most of our software; it applies also to
19any other work released this way by its authors. You can apply it to
19your programs, too. 20your programs, too.
20 21
21 When we speak of free software, we are referring to freedom, not 22 When we speak of free software, we are referring to freedom, not
22price. Our General Public Licenses are designed to make sure that you 23price. Our General Public Licenses are designed to make sure that you
23have the freedom to distribute copies of free software (and charge for 24have the freedom to distribute copies of free software (and charge for
24this service if you wish), that you receive source code or can get it 25them if you wish), that you receive source code or can get it if you
25if you want it, that you can change the software or use pieces of it 26want it, that you can change the software or use pieces of it in new
26in new free programs; and that you know you can do these things. 27free programs, and that you know you can do these things.
27 28
28 To protect your rights, we need to make restrictions that forbid 29 To protect your rights, we need to prevent others from denying you
29anyone to deny you these rights or to ask you to surrender the rights. 30these rights or asking you to surrender the rights. Therefore, you have
30These restrictions translate to certain responsibilities for you if you 31certain responsibilities if you distribute copies of the software, or if
31distribute copies of the software, or if you modify it. 32you modify it: responsibilities to respect the freedom of others.
32 33
33 For example, if you distribute copies of such a program, whether 34 For example, if you distribute copies of such a program, whether
34gratis or for a fee, you must give the recipients all the rights that 35gratis or for a fee, you must pass on to the recipients the same
35you have. You must make sure that they, too, receive or can get the 36freedoms that you received. You must make sure that they, too, receive
36source code. And you must show them these terms so they know their 37or can get the source code. And you must show them these terms so they
37rights. 38know their rights.
38 39
39 We protect your rights with two steps: (1) copyright the software, and 40 Developers that use the GNU GPL protect your rights with two steps:
40(2) offer you this license which gives you legal permission to copy, 41(1) assert copyright on the software, and (2) offer you this License
41distribute and/or modify the software. 42giving you legal permission to copy, distribute and/or modify it.
42 43
43 Also, for each author's protection and ours, we want to make certain 44 For the developers' and authors' protection, the GPL clearly explains
44that everyone understands that there is no warranty for this free 45that there is no warranty for this free software. For both users' and
45software. If the software is modified by someone else and passed on, we 46authors' sake, the GPL requires that modified versions be marked as
46want its recipients to know that what they have is not the original, so 47changed, so that their problems will not be attributed erroneously to
47that any problems introduced by others will not reflect on the original 48authors of previous versions.
48authors' reputations. 49
49 50 Some devices are designed to deny users access to install or run
50 Finally, any free program is threatened constantly by software 51modified versions of the software inside them, although the manufacturer
51patents. We wish to avoid the danger that redistributors of a free 52can do so. This is fundamentally incompatible with the aim of
52program will individually obtain patent licenses, in effect making the 53protecting users' freedom to change the software. The systematic
53program proprietary. To prevent this, we have made it clear that any 54pattern of such abuse occurs in the area of products for individuals to
54patent must be licensed for everyone's free use or not licensed at all. 55use, which is precisely where it is most unacceptable. Therefore, we
56have designed this version of the GPL to prohibit the practice for those
57products. If such problems arise substantially in other domains, we
58stand ready to extend this provision to those domains in future versions
59of the GPL, as needed to protect the freedom of users.
60
61 Finally, every program is threatened constantly by software patents.
62States should not allow patents to restrict development and use of
63software on general-purpose computers, but in those that do, we wish to
64avoid the special danger that patents applied to a free program could
65make it effectively proprietary. To prevent this, the GPL assures that
66patents cannot be used to render the program non-free.
55 67
56 The precise terms and conditions for copying, distribution and 68 The precise terms and conditions for copying, distribution and
57modification follow. 69modification follow.
58 70
59 GNU GENERAL PUBLIC LICENSE 71 TERMS AND CONDITIONS
60 TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 72
61 73 0. Definitions.
62 0. This License applies to any program or other work which contains 74
63a notice placed by the copyright holder saying it may be distributed 75 "This License" refers to version 3 of the GNU General Public License.
64under the terms of this General Public License. The "Program", below, 76
65refers to any such program or work, and a "work based on the Program" 77 "Copyright" also means copyright-like laws that apply to other kinds of
66means either the Program or any derivative work under copyright law: 78works, such as semiconductor masks.
67that is to say, a work containing the Program or a portion of it, 79
68either verbatim or with modifications and/or translated into another 80 "The Program" refers to any copyrightable work licensed under this
69language. (Hereinafter, translation is included without limitation in 81License. Each licensee is addressed as "you". "Licensees" and
70the term "modification".) Each licensee is addressed as "you". 82"recipients" may be individuals or organizations.
71 83
72Activities other than copying, distribution and modification are not 84 To "modify" a work means to copy from or adapt all or part of the work
73covered by this License; they are outside its scope. The act of 85in a fashion requiring copyright permission, other than the making of an
74running the Program is not restricted, and the output from the Program 86exact copy. The resulting work is called a "modified version" of the
75is covered only if its contents constitute a work based on the 87earlier work or a work "based on" the earlier work.
76Program (independent of having been made by running the Program). 88
77Whether that is true depends on what the Program does. 89 A "covered work" means either the unmodified Program or a work based
78 90on the Program.
79 1. You may copy and distribute verbatim copies of the Program's 91
80source code as you receive it, in any medium, provided that you 92 To "propagate" a work means to do anything with it that, without
81conspicuously and appropriately publish on each copy an appropriate 93permission, would make you directly or secondarily liable for
82copyright notice and disclaimer of warranty; keep intact all the 94infringement under applicable copyright law, except executing it on a
83notices that refer to this License and to the absence of any warranty; 95computer or modifying a private copy. Propagation includes copying,
84and give any other recipients of the Program a copy of this License 96distribution (with or without modification), making available to the
85along with the Program. 97public, and in some countries other activities as well.
86 98
87You may charge a fee for the physical act of transferring a copy, and 99 To "convey" a work means any kind of propagation that enables other
88you may at your option offer warranty protection in exchange for a fee. 100parties to make or receive copies. Mere interaction with a user through
89 101a computer network, with no transfer of a copy, is not conveying.
90 2. You may modify your copy or copies of the Program or any portion 102
91of it, thus forming a work based on the Program, and copy and 103 An interactive user interface displays "Appropriate Legal Notices"
92distribute such modifications or work under the terms of Section 1 104to the extent that it includes a convenient and prominently visible
93above, provided that you also meet all of these conditions: 105feature that (1) displays an appropriate copyright notice, and (2)
94 106tells the user that there is no warranty for the work (except to the
95 a) You must cause the modified files to carry prominent notices 107extent that warranties are provided), that licensees may convey the
96 stating that you changed the files and the date of any change. 108work under this License, and how to view a copy of this License. If
97 109the interface presents a list of user commands or options, such as a
98 b) You must cause any work that you distribute or publish, that in 110menu, a prominent item in the list meets this criterion.
99 whole or in part contains or is derived from the Program or any 111
100 part thereof, to be licensed as a whole at no charge to all third 112 1. Source Code.
101 parties under the terms of this License. 113
102 114 The "source code" for a work means the preferred form of the work
103 c) If the modified program normally reads commands interactively 115for making modifications to it. "Object code" means any non-source
104 when run, you must cause it, when started running for such 116form of a work.
105 interactive use in the most ordinary way, to print or display an 117
106 announcement including an appropriate copyright notice and a 118 A "Standard Interface" means an interface that either is an official
107 notice that there is no warranty (or else, saying that you provide 119standard defined by a recognized standards body, or, in the case of
108 a warranty) and that users may redistribute the program under 120interfaces specified for a particular programming language, one that
109 these conditions, and telling the user how to view a copy of this 121is widely used among developers working in that language.
110 License. (Exception: if the Program itself is interactive but 122
111 does not normally print such an announcement, your work based on 123 The "System Libraries" of an executable work include anything, other
112 the Program is not required to print an announcement.) 124than the work as a whole, that (a) is included in the normal form of
113 125packaging a Major Component, but which is not part of that Major
114These requirements apply to the modified work as a whole. If 126Component, and (b) serves only to enable use of the work with that
115identifiable sections of that work are not derived from the Program, 127Major Component, or to implement a Standard Interface for which an
116and can be reasonably considered independent and separate works in 128implementation is available to the public in source code form. A
117themselves, then this License, and its terms, do not apply to those 129"Major Component", in this context, means a major essential component
118sections when you distribute them as separate works. But when you 130(kernel, window system, and so on) of the specific operating system
119distribute the same sections as part of a whole which is a work based 131(if any) on which the executable work runs, or a compiler used to
120on the Program, the distribution of the whole must be on the terms of 132produce the work, or an object code interpreter used to run it.
121this License, whose permissions for other licensees extend to the 133
122entire whole, and thus to each and every part regardless of who wrote it. 134 The "Corresponding Source" for a work in object code form means all
123 135the source code needed to generate, install, and (for an executable
124Thus, it is not the intent of this section to claim rights or contest 136work) run the object code and to modify the work, including scripts to
125your rights to work written entirely by you; rather, the intent is to 137control those activities. However, it does not include the work's
126exercise the right to control the distribution of derivative or 138System Libraries, or general-purpose tools or generally available free
127collective works based on the Program. 139programs which are used unmodified in performing those activities but
128 140which are not part of the work. For example, Corresponding Source
129In addition, mere aggregation of another work not based on the Program 141includes interface definition files associated with source files for
130with the Program (or with a work based on the Program) on a volume of 142the work, and the source code for shared libraries and dynamically
131a storage or distribution medium does not bring the other work under 143linked subprograms that the work is specifically designed to require,
132the scope of this License. 144such as by intimate data communication or control flow between those
133 145subprograms and other parts of the work.
134 3. You may copy and distribute the Program (or a work based on it, 146
135under Section 2) in object code or executable form under the terms of 147 The Corresponding Source need not include anything that users
136Sections 1 and 2 above provided that you also do one of the following: 148can regenerate automatically from other parts of the Corresponding
137 149Source.
138 a) Accompany it with the complete corresponding machine-readable 150
139 source code, which must be distributed under the terms of Sections 151 The Corresponding Source for a work in source code form is that
140 1 and 2 above on a medium customarily used for software interchange; or, 152same work.
141 153
142 b) Accompany it with a written offer, valid for at least three 154 2. Basic Permissions.
143 years, to give any third party, for a charge no more than your 155
144 cost of physically performing source distribution, a complete 156 All rights granted under this License are granted for the term of
145 machine-readable copy of the corresponding source code, to be 157copyright on the Program, and are irrevocable provided the stated
146 distributed under the terms of Sections 1 and 2 above on a medium 158conditions are met. This License explicitly affirms your unlimited
147 customarily used for software interchange; or, 159permission to run the unmodified Program. The output from running a
148 160covered work is covered by this License only if the output, given its
149 c) Accompany it with the information you received as to the offer 161content, constitutes a covered work. This License acknowledges your
150 to distribute corresponding source code. (This alternative is 162rights of fair use or other equivalent, as provided by copyright law.
151 allowed only for noncommercial distribution and only if you 163
152 received the program in object code or executable form with such 164 You may make, run and propagate covered works that you do not
153 an offer, in accord with Subsection b above.) 165convey, without conditions so long as your license otherwise remains
154 166in force. You may convey covered works to others for the sole purpose
155The source code for a work means the preferred form of the work for 167of having them make modifications exclusively for you, or provide you
156making modifications to it. For an executable work, complete source 168with facilities for running those works, provided that you comply with
157code means all the source code for all modules it contains, plus any 169the terms of this License in conveying all material for which you do
158associated interface definition files, plus the scripts used to 170not control copyright. Those thus making or running the covered works
159control compilation and installation of the executable. However, as a 171for you must do so exclusively on your behalf, under your direction
160special exception, the source code distributed need not include 172and control, on terms that prohibit them from making any copies of
161anything that is normally distributed (in either source or binary 173your copyrighted material outside their relationship with you.
162form) with the major components (compiler, kernel, and so on) of the 174
163operating system on which the executable runs, unless that component 175 Conveying under any other circumstances is permitted solely under
164itself accompanies the executable. 176the conditions stated below. Sublicensing is not allowed; section 10
165 177makes it unnecessary.
166If distribution of executable or object code is made by offering 178
167access to copy from a designated place, then offering equivalent 179 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
168access to copy the source code from the same place counts as 180
169distribution of the source code, even though third parties are not 181 No covered work shall be deemed part of an effective technological
170compelled to copy the source along with the object code. 182measure under any applicable law fulfilling obligations under article
171 18311 of the WIPO copyright treaty adopted on 20 December 1996, or
172 4. You may not copy, modify, sublicense, or distribute the Program 184similar laws prohibiting or restricting circumvention of such
173except as expressly provided under this License. Any attempt 185measures.
174otherwise to copy, modify, sublicense or distribute the Program is 186
175void, and will automatically terminate your rights under this License. 187 When you convey a covered work, you waive any legal power to forbid
176However, parties who have received copies, or rights, from you under 188circumvention of technological measures to the extent such circumvention
177this License will not have their licenses terminated so long as such 189is effected by exercising rights under this License with respect to
178parties remain in full compliance. 190the covered work, and you disclaim any intention to limit operation or
179 191modification of the work as a means of enforcing, against the work's
180 5. You are not required to accept this License, since you have not 192users, your or third parties' legal rights to forbid circumvention of
181signed it. However, nothing else grants you permission to modify or 193technological measures.
182distribute the Program or its derivative works. These actions are 194
183prohibited by law if you do not accept this License. Therefore, by 195 4. Conveying Verbatim Copies.
184modifying or distributing the Program (or any work based on the 196
185Program), you indicate your acceptance of this License to do so, and 197 You may convey verbatim copies of the Program's source code as you
186all its terms and conditions for copying, distributing or modifying 198receive it, in any medium, provided that you conspicuously and
187the Program or works based on it. 199appropriately publish on each copy an appropriate copyright notice;
188 200keep intact all notices stating that this License and any
189 6. Each time you redistribute the Program (or any work based on the 201non-permissive terms added in accord with section 7 apply to the code;
190Program), the recipient automatically receives a license from the 202keep intact all notices of the absence of any warranty; and give all
191original licensor to copy, distribute or modify the Program subject to 203recipients a copy of this License along with the Program.
192these terms and conditions. You may not impose any further 204
193restrictions on the recipients' exercise of the rights granted herein. 205 You may charge any price or no price for each copy that you convey,
194You are not responsible for enforcing compliance by third parties to 206and you may offer support or warranty protection for a fee.
207
208 5. Conveying Modified Source Versions.
209
210 You may convey a work based on the Program, or the modifications to
211produce it from the Program, in the form of source code under the
212terms of section 4, provided that you also meet all of these conditions:
213
214 a) The work must carry prominent notices stating that you modified
215 it, and giving a relevant date.
216
217 b) The work must carry prominent notices stating that it is
218 released under this License and any conditions added under section
219 7. This requirement modifies the requirement in section 4 to
220 "keep intact all notices".
221
222 c) You must license the entire work, as a whole, under this
223 License to anyone who comes into possession of a copy. This
224 License will therefore apply, along with any applicable section 7
225 additional terms, to the whole of the work, and all its parts,
226 regardless of how they are packaged. This License gives no
227 permission to license the work in any other way, but it does not
228 invalidate such permission if you have separately received it.
229
230 d) If the work has interactive user interfaces, each must display
231 Appropriate Legal Notices; however, if the Program has interactive
232 interfaces that do not display Appropriate Legal Notices, your
233 work need not make them do so.
234
235 A compilation of a covered work with other separate and independent
236works, which are not by their nature extensions of the covered work,
237and which are not combined with it such as to form a larger program,
238in or on a volume of a storage or distribution medium, is called an
239"aggregate" if the compilation and its resulting copyright are not
240used to limit the access or legal rights of the compilation's users
241beyond what the individual works permit. Inclusion of a covered work
242in an aggregate does not cause this License to apply to the other
243parts of the aggregate.
244
245 6. Conveying Non-Source Forms.
246
247 You may convey a covered work in object code form under the terms
248of sections 4 and 5, provided that you also convey the
249machine-readable Corresponding Source under the terms of this License,
250in one of these ways:
251
252 a) Convey the object code in, or embodied in, a physical product
253 (including a physical distribution medium), accompanied by the
254 Corresponding Source fixed on a durable physical medium
255 customarily used for software interchange.
256
257 b) Convey the object code in, or embodied in, a physical product
258 (including a physical distribution medium), accompanied by a
259 written offer, valid for at least three years and valid for as
260 long as you offer spare parts or customer support for that product
261 model, to give anyone who possesses the object code either (1) a
262 copy of the Corresponding Source for all the software in the
263 product that is covered by this License, on a durable physical
264 medium customarily used for software interchange, for a price no
265 more than your reasonable cost of physically performing this
266 conveying of source, or (2) access to copy the
267 Corresponding Source from a network server at no charge.
268
269 c) Convey individual copies of the object code with a copy of the
270 written offer to provide the Corresponding Source. This
271 alternative is allowed only occasionally and noncommercially, and
272 only if you received the object code with such an offer, in accord
273 with subsection 6b.
274
275 d) Convey the object code by offering access from a designated
276 place (gratis or for a charge), and offer equivalent access to the
277 Corresponding Source in the same way through the same place at no
278 further charge. You need not require recipients to copy the
279 Corresponding Source along with the object code. If the place to
280 copy the object code is a network server, the Corresponding Source
281 may be on a different server (operated by you or a third party)
282 that supports equivalent copying facilities, provided you maintain
283 clear directions next to the object code saying where to find the
284 Corresponding Source. Regardless of what server hosts the
285 Corresponding Source, you remain obligated to ensure that it is
286 available for as long as needed to satisfy these requirements.
287
288 e) Convey the object code using peer-to-peer transmission, provided
289 you inform other peers where the object code and Corresponding
290 Source of the work are being offered to the general public at no
291 charge under subsection 6d.
292
293 A separable portion of the object code, whose source code is excluded
294from the Corresponding Source as a System Library, need not be
295included in conveying the object code work.
296
297 A "User Product" is either (1) a "consumer product", which means any
298tangible personal property which is normally used for personal, family,
299or household purposes, or (2) anything designed or sold for incorporation
300into a dwelling. In determining whether a product is a consumer product,
301doubtful cases shall be resolved in favor of coverage. For a particular
302product received by a particular user, "normally used" refers to a
303typical or common use of that class of product, regardless of the status
304of the particular user or of the way in which the particular user
305actually uses, or expects or is expected to use, the product. A product
306is a consumer product regardless of whether the product has substantial
307commercial, industrial or non-consumer uses, unless such uses represent
308the only significant mode of use of the product.
309
310 "Installation Information" for a User Product means any methods,
311procedures, authorization keys, or other information required to install
312and execute modified versions of a covered work in that User Product from
313a modified version of its Corresponding Source. The information must
314suffice to ensure that the continued functioning of the modified object
315code is in no case prevented or interfered with solely because
316modification has been made.
317
318 If you convey an object code work under this section in, or with, or
319specifically for use in, a User Product, and the conveying occurs as
320part of a transaction in which the right of possession and use of the
321User Product is transferred to the recipient in perpetuity or for a
322fixed term (regardless of how the transaction is characterized), the
323Corresponding Source conveyed under this section must be accompanied
324by the Installation Information. But this requirement does not apply
325if neither you nor any third party retains the ability to install
326modified object code on the User Product (for example, the work has
327been installed in ROM).
328
329 The requirement to provide Installation Information does not include a
330requirement to continue to provide support service, warranty, or updates
331for a work that has been modified or installed by the recipient, or for
332the User Product in which it has been modified or installed. Access to a
333network may be denied when the modification itself materially and
334adversely affects the operation of the network or violates the rules and
335protocols for communication across the network.
336
337 Corresponding Source conveyed, and Installation Information provided,
338in accord with this section must be in a format that is publicly
339documented (and with an implementation available to the public in
340source code form), and must require no special password or key for
341unpacking, reading or copying.
342
343 7. Additional Terms.
344
345 "Additional permissions" are terms that supplement the terms of this
346License by making exceptions from one or more of its conditions.
347Additional permissions that are applicable to the entire Program shall
348be treated as though they were included in this License, to the extent
349that they are valid under applicable law. If additional permissions
350apply only to part of the Program, that part may be used separately
351under those permissions, but the entire Program remains governed by
352this License without regard to the additional permissions.
353
354 When you convey a copy of a covered work, you may at your option
355remove any additional permissions from that copy, or from any part of
356it. (Additional permissions may be written to require their own
357removal in certain cases when you modify the work.) You may place
358additional permissions on material, added by you to a covered work,
359for which you have or can give appropriate copyright permission.
360
361 Notwithstanding any other provision of this License, for material you
362add to a covered work, you may (if authorized by the copyright holders of
363that material) supplement the terms of this License with terms:
364
365 a) Disclaiming warranty or limiting liability differently from the
366 terms of sections 15 and 16 of this License; or
367
368 b) Requiring preservation of specified reasonable legal notices or
369 author attributions in that material or in the Appropriate Legal
370 Notices displayed by works containing it; or
371
372 c) Prohibiting misrepresentation of the origin of that material, or
373 requiring that modified versions of such material be marked in
374 reasonable ways as different from the original version; or
375
376 d) Limiting the use for publicity purposes of names of licensors or
377 authors of the material; or
378
379 e) Declining to grant rights under trademark law for use of some
380 trade names, trademarks, or service marks; or
381
382 f) Requiring indemnification of licensors and authors of that
383 material by anyone who conveys the material (or modified versions of
384 it) with contractual assumptions of liability to the recipient, for
385 any liability that these contractual assumptions directly impose on
386 those licensors and authors.
387
388 All other non-permissive additional terms are considered "further
389restrictions" within the meaning of section 10. If the Program as you
390received it, or any part of it, contains a notice stating that it is
391governed by this License along with a term that is a further
392restriction, you may remove that term. If a license document contains
393a further restriction but permits relicensing or conveying under this
394License, you may add to a covered work material governed by the terms
395of that license document, provided that the further restriction does
396not survive such relicensing or conveying.
397
398 If you add terms to a covered work in accord with this section, you
399must place, in the relevant source files, a statement of the
400additional terms that apply to those files, or a notice indicating
401where to find the applicable terms.
402
403 Additional terms, permissive or non-permissive, may be stated in the
404form of a separately written license, or stated as exceptions;
405the above requirements apply either way.
406
407 8. Termination.
408
409 You may not propagate or modify a covered work except as expressly
410provided under this License. Any attempt otherwise to propagate or
411modify it is void, and will automatically terminate your rights under
412this License (including any patent licenses granted under the third
413paragraph of section 11).
414
415 However, if you cease all violation of this License, then your
416license from a particular copyright holder is reinstated (a)
417provisionally, unless and until the copyright holder explicitly and
418finally terminates your license, and (b) permanently, if the copyright
419holder fails to notify you of the violation by some reasonable means
420prior to 60 days after the cessation.
421
422 Moreover, your license from a particular copyright holder is
423reinstated permanently if the copyright holder notifies you of the
424violation by some reasonable means, this is the first time you have
425received notice of violation of this License (for any work) from that
426copyright holder, and you cure the violation prior to 30 days after
427your receipt of the notice.
428
429 Termination of your rights under this section does not terminate the
430licenses of parties who have received copies or rights from you under
431this License. If your rights have been terminated and not permanently
432reinstated, you do not qualify to receive new licenses for the same
433material under section 10.
434
435 9. Acceptance Not Required for Having Copies.
436
437 You are not required to accept this License in order to receive or
438run a copy of the Program. Ancillary propagation of a covered work
439occurring solely as a consequence of using peer-to-peer transmission
440to receive a copy likewise does not require acceptance. However,
441nothing other than this License grants you permission to propagate or
442modify any covered work. These actions infringe copyright if you do
443not accept this License. Therefore, by modifying or propagating a
444covered work, you indicate your acceptance of this License to do so.
445
446 10. Automatic Licensing of Downstream Recipients.
447
448 Each time you convey a covered work, the recipient automatically
449receives a license from the original licensors, to run, modify and
450propagate that work, subject to this License. You are not responsible
451for enforcing compliance by third parties with this License.
452
453 An "entity transaction" is a transaction transferring control of an
454organization, or substantially all assets of one, or subdividing an
455organization, or merging organizations. If propagation of a covered
456work results from an entity transaction, each party to that
457transaction who receives a copy of the work also receives whatever
458licenses to the work the party's predecessor in interest had or could
459give under the previous paragraph, plus a right to possession of the
460Corresponding Source of the work from the predecessor in interest, if
461the predecessor has it or can get it with reasonable efforts.
462
463 You may not impose any further restrictions on the exercise of the
464rights granted or affirmed under this License. For example, you may
465not impose a license fee, royalty, or other charge for exercise of
466rights granted under this License, and you may not initiate litigation
467(including a cross-claim or counterclaim in a lawsuit) alleging that
468any patent claim is infringed by making, using, selling, offering for
469sale, or importing the Program or any portion of it.
470
471 11. Patents.
472
473 A "contributor" is a copyright holder who authorizes use under this
474License of the Program or a work on which the Program is based. The
475work thus licensed is called the contributor's "contributor version".
476
477 A contributor's "essential patent claims" are all patent claims
478owned or controlled by the contributor, whether already acquired or
479hereafter acquired, that would be infringed by some manner, permitted
480by this License, of making, using, or selling its contributor version,
481but do not include claims that would be infringed only as a
482consequence of further modification of the contributor version. For
483purposes of this definition, "control" includes the right to grant
484patent sublicenses in a manner consistent with the requirements of
195this License. 485this License.
196 486
197 7. If, as a consequence of a court judgment or allegation of patent 487 Each contributor grants you a non-exclusive, worldwide, royalty-free
198infringement or for any other reason (not limited to patent issues), 488patent license under the contributor's essential patent claims, to
199conditions are imposed on you (whether by court order, agreement or 489make, use, sell, offer for sale, import and otherwise run, modify and
490propagate the contents of its contributor version.
491
492 In the following three paragraphs, a "patent license" is any express
493agreement or commitment, however denominated, not to enforce a patent
494(such as an express permission to practice a patent or covenant not to
495sue for patent infringement). To "grant" such a patent license to a
496party means to make such an agreement or commitment not to enforce a
497patent against the party.
498
499 If you convey a covered work, knowingly relying on a patent license,
500and the Corresponding Source of the work is not available for anyone
501to copy, free of charge and under the terms of this License, through a
502publicly available network server or other readily accessible means,
503then you must either (1) cause the Corresponding Source to be so
504available, or (2) arrange to deprive yourself of the benefit of the
505patent license for this particular work, or (3) arrange, in a manner
506consistent with the requirements of this License, to extend the patent
507license to downstream recipients. "Knowingly relying" means you have
508actual knowledge that, but for the patent license, your conveying the
509covered work in a country, or your recipient's use of the covered work
510in a country, would infringe one or more identifiable patents in that
511country that you have reason to believe are valid.
512
513 If, pursuant to or in connection with a single transaction or
514arrangement, you convey, or propagate by procuring conveyance of, a
515covered work, and grant a patent license to some of the parties
516receiving the covered work authorizing them to use, propagate, modify
517or convey a specific copy of the covered work, then the patent license
518you grant is automatically extended to all recipients of the covered
519work and works based on it.
520
521 A patent license is "discriminatory" if it does not include within
522the scope of its coverage, prohibits the exercise of, or is
523conditioned on the non-exercise of one or more of the rights that are
524specifically granted under this License. You may not convey a covered
525work if you are a party to an arrangement with a third party that is
526in the business of distributing software, under which you make payment
527to the third party based on the extent of your activity of conveying
528the work, and under which the third party grants, to any of the
529parties who would receive the covered work from you, a discriminatory
530patent license (a) in connection with copies of the covered work
531conveyed by you (or copies made from those copies), or (b) primarily
532for and in connection with specific products or compilations that
533contain the covered work, unless you entered into that arrangement,
534or that patent license was granted, prior to 28 March 2007.
535
536 Nothing in this License shall be construed as excluding or limiting
537any implied license or other defenses to infringement that may
538otherwise be available to you under applicable patent law.
539
540 12. No Surrender of Others' Freedom.
541
542 If conditions are imposed on you (whether by court order, agreement or
200otherwise) that contradict the conditions of this License, they do not 543otherwise) that contradict the conditions of this License, they do not
201excuse you from the conditions of this License. If you cannot 544excuse you from the conditions of this License. If you cannot convey a
202distribute so as to satisfy simultaneously your obligations under this 545covered work so as to satisfy simultaneously your obligations under this
203License and any other pertinent obligations, then as a consequence you 546License and any other pertinent obligations, then as a consequence you may
204may not distribute the Program at all. For example, if a patent 547not convey it at all. For example, if you agree to terms that obligate you
205license would not permit royalty-free redistribution of the Program by 548to collect a royalty for further conveying from those to whom you convey
206all those who receive copies directly or indirectly through you, then 549the Program, the only way you could satisfy both those terms and this
207the only way you could satisfy both it and this License would be to 550License would be to refrain entirely from conveying the Program.
208refrain entirely from distribution of the Program. 551
209 552 13. Use with the GNU Affero General Public License.
210If any portion of this section is held invalid or unenforceable under 553
211any particular circumstance, the balance of the section is intended to 554 Notwithstanding any other provision of this License, you have
212apply and the section as a whole is intended to apply in other 555permission to link or combine any covered work with a work licensed
213circumstances. 556under version 3 of the GNU Affero General Public License into a single
214 557combined work, and to convey the resulting work. The terms of this
215It is not the purpose of this section to induce you to infringe any 558License will continue to apply to the part which is the covered work,
216patents or other property right claims or to contest validity of any 559but the special requirements of the GNU Affero General Public License,
217such claims; this section has the sole purpose of protecting the 560section 13, concerning interaction through a network will apply to the
218integrity of the free software distribution system, which is 561combination as such.
219implemented by public license practices. Many people have made 562
220generous contributions to the wide range of software distributed 563 14. Revised Versions of this License.
221through that system in reliance on consistent application of that 564
222system; it is up to the author/donor to decide if he or she is willing 565 The Free Software Foundation may publish revised and/or new versions of
223to distribute software through any other system and a licensee cannot 566the GNU General Public License from time to time. Such new versions will
224impose that choice.
225
226This section is intended to make thoroughly clear what is believed to
227be a consequence of the rest of this License.
228
229 8. If the distribution and/or use of the Program is restricted in
230certain countries either by patents or by copyrighted interfaces, the
231original copyright holder who places the Program under this License
232may add an explicit geographical distribution limitation excluding
233those countries, so that distribution is permitted only in or among
234countries not thus excluded. In such case, this License incorporates
235the limitation as if written in the body of this License.
236
237 9. The Free Software Foundation may publish revised and/or new versions
238of the General Public License from time to time. Such new versions will
239be similar in spirit to the present version, but may differ in detail to 567be similar in spirit to the present version, but may differ in detail to
240address new problems or concerns. 568address new problems or concerns.
241 569
242Each version is given a distinguishing version number. If the Program 570 Each version is given a distinguishing version number. If the
243specifies a version number of this License which applies to it and "any 571Program specifies that a certain numbered version of the GNU General
244later version", you have the option of following the terms and conditions 572Public License "or any later version" applies to it, you have the
245either of that version or of any later version published by the Free 573option of following the terms and conditions either of that numbered
246Software Foundation. If the Program does not specify a version number of 574version or of any later version published by the Free Software
247this License, you may choose any version ever published by the Free Software 575Foundation. If the Program does not specify a version number of the
248Foundation. 576GNU General Public License, you may choose any version ever published
249 577by the Free Software Foundation.
250 10. If you wish to incorporate parts of the Program into other free 578
251programs whose distribution conditions are different, write to the author 579 If the Program specifies that a proxy can decide which future
252to ask for permission. For software which is copyrighted by the Free 580versions of the GNU General Public License can be used, that proxy's
253Software Foundation, write to the Free Software Foundation; we sometimes 581public statement of acceptance of a version permanently authorizes you
254make exceptions for this. Our decision will be guided by the two goals 582to choose that version for the Program.
255of preserving the free status of all derivatives of our free software and 583
256of promoting the sharing and reuse of software generally. 584 Later license versions may give you additional or different
257 585permissions. However, no additional obligations are imposed on any
258 NO WARRANTY 586author or copyright holder as a result of your choosing to follow a
259 587later version.
260 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 588
261FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 589 15. Disclaimer of Warranty.
262OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 590
263PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 591 THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
264OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 592APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
265MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 593HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
266TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 594OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
267PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 595THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
268REPAIR OR CORRECTION. 596PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
269 597IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
270 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 598ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
271WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 599
272REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 600 16. Limitation of Liability.
273INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 601
274OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 602 IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
275TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 603WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
276YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 604THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
277PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 605GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
278POSSIBILITY OF SUCH DAMAGES. 606USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
279 607DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
280 END OF TERMS AND CONDITIONS 608PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
281 609EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
282 How to Apply These Terms to Your New Programs 610SUCH DAMAGES.
611
612 17. Interpretation of Sections 15 and 16.
613
614 If the disclaimer of warranty and limitation of liability provided
615above cannot be given local legal effect according to their terms,
616reviewing courts shall apply local law that most closely approximates
617an absolute waiver of all civil liability in connection with the
618Program, unless a warranty or assumption of liability accompanies a
619copy of the Program in return for a fee.
620
621 END OF TERMS AND CONDITIONS
622
623 How to Apply These Terms to Your New Programs
283 624
284 If you develop a new program, and you want it to be of the greatest 625 If you develop a new program, and you want it to be of the greatest
285possible use to the public, the best way to achieve this is to make it 626possible use to the public, the best way to achieve this is to make it
@@ -287,15 +628,15 @@ free software which everyone can redistribute and change under these terms.
287 628
288 To do so, attach the following notices to the program. It is safest 629 To do so, attach the following notices to the program. It is safest
289to attach them to the start of each source file to most effectively 630to attach them to the start of each source file to most effectively
290convey the exclusion of warranty; and each file should have at least 631state the exclusion of warranty; and each file should have at least
291the "copyright" line and a pointer to where the full notice is found. 632the "copyright" line and a pointer to where the full notice is found.
292 633
293 <one line to give the program's name and a brief idea of what it does.> 634 <one line to give the program's name and a brief idea of what it does.>
294 Copyright (C) <year> <name of author> 635 Copyright (C) <year> <name of author>
295 636
296 This program is free software; you can redistribute it and/or modify 637 This program is free software: you can redistribute it and/or modify
297 it under the terms of the GNU General Public License as published by 638 it under the terms of the GNU General Public License as published by
298 the Free Software Foundation; either version 2 of the License, or 639 the Free Software Foundation, either version 3 of the License, or
299 (at your option) any later version. 640 (at your option) any later version.
300 641
301 This program is distributed in the hope that it will be useful, 642 This program is distributed in the hope that it will be useful,
@@ -303,63 +644,31 @@ the "copyright" line and a pointer to where the full notice is found.
303 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 644 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 GNU General Public License for more details. 645 GNU General Public License for more details.
305 646
306 You should have received a copy of the GNU General Public License along 647 You should have received a copy of the GNU General Public License
307 with this program; if not, write to the Free Software Foundation, Inc., 648 along with this program. If not, see <https://www.gnu.org/licenses/>.
308 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309 649
310Also add information on how to contact you by electronic and paper mail. 650Also add information on how to contact you by electronic and paper mail.
311 651
312If the program is interactive, make it output a short notice like this 652 If the program does terminal interaction, make it output a short
313when it starts in an interactive mode: 653notice like this when it starts in an interactive mode:
314 654
315 Gnomovision version 69, Copyright (C) year name of author 655 <program> Copyright (C) <year> <name of author>
316 Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 656 This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317 This is free software, and you are welcome to redistribute it 657 This is free software, and you are welcome to redistribute it
318 under certain conditions; type `show c' for details. 658 under certain conditions; type `show c' for details.
319 659
320The hypothetical commands `show w' and `show c' should show the appropriate 660The hypothetical commands `show w' and `show c' should show the appropriate
321parts of the General Public License. Of course, the commands you use may 661parts of the General Public License. Of course, your program's commands
322be called something other than `show w' and `show c'; they could even be 662might be different; for a GUI interface, you would use an "about box".
323mouse-clicks or menu items--whatever suits your program. 663
324 664 You should also get your employer (if you work as a programmer) or school,
325You should also get your employer (if you work as a programmer) or your 665if any, to sign a "copyright disclaimer" for the program, if necessary.
326school, if any, to sign a "copyright disclaimer" for the program, if 666For more information on this, and how to apply and follow the GNU GPL, see
327necessary. Here is a sample; alter the names: 667<https://www.gnu.org/licenses/>.
328 668
329 Yoyodyne, Inc., hereby disclaims all copyright interest in the program 669 The GNU General Public License does not permit incorporating your program
330 `Gnomovision' (which makes passes at compilers) written by James Hacker. 670into proprietary programs. If your program is a subroutine library, you
331 671may consider it more useful to permit linking proprietary applications with
332 <signature of Ty Coon>, 1 April 1989 672the library. If this is what you want to do, use the GNU Lesser General
333 Ty Coon, President of Vice 673Public License instead of this License. But first, please read
334 674<https://www.gnu.org/licenses/why-not-lgpl.html>. \ No newline at end of file
335This General Public License does not permit incorporating your program into
336proprietary programs. If your program is a subroutine library, you may
337consider it more useful to permit linking proprietary applications with the
338library. If this is what you want to do, use the GNU Lesser General
339Public License instead of this License.
340
341
342The icons used in this project have the following licenses:
343
344Icon Name | License | Origin/Author
345--- | --- | ---
346checked.png | CC BY-ND 3.0 | https://icons8.com
347failed.png | CC BY-ND 3.0 | https://icons8.com
348lock.png | CC BY-ND 3.0 | https://icons8.com
349plus_folder.png (Default, Dark) | CC BY-ND 3.0 | https://icons8.com
350bad_folder.png (Default, Dark) | CC BY-ND 3.0 | https://icons8.com
351chip.png (Default, Dark) | CC BY-ND 3.0 | https://icons8.com
352folder.png (Default, Dark) | CC BY-ND 3.0 | https://icons8.com
353plus.png (Default, Dark) | CC0 1.0 | Designed by BreadFish64 from the Citra team
354sd_card.png (Default, Dark) | CC BY-ND 3.0 | https://icons8.com
355plus_folder.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
356bad_folder.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
357chip.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
358folder.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
359plus.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
360sd_card.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
361star.png | CC BY-ND 3.0 | https://icons8.com
362
363Note:
364Some icons are different in different themes, and they are separately listed
365only when they have different licenses/origins.
diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp
index 4afc1369a..9120cc178 100644
--- a/src/common/logging/filter.cpp
+++ b/src/common/logging/filter.cpp
@@ -119,6 +119,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
119 SUB(Service, NPNS) \ 119 SUB(Service, NPNS) \
120 SUB(Service, NS) \ 120 SUB(Service, NS) \
121 SUB(Service, NVDRV) \ 121 SUB(Service, NVDRV) \
122 SUB(Service, NVFlinger) \
122 SUB(Service, OLSC) \ 123 SUB(Service, OLSC) \
123 SUB(Service, PCIE) \ 124 SUB(Service, PCIE) \
124 SUB(Service, PCTL) \ 125 SUB(Service, PCTL) \
diff --git a/src/common/logging/types.h b/src/common/logging/types.h
index 2b6e4daa7..f803ab796 100644
--- a/src/common/logging/types.h
+++ b/src/common/logging/types.h
@@ -87,6 +87,7 @@ enum class Class : u8 {
87 Service_NPNS, ///< The NPNS service 87 Service_NPNS, ///< The NPNS service
88 Service_NS, ///< The NS services 88 Service_NS, ///< The NS services
89 Service_NVDRV, ///< The NVDRV (Nvidia driver) service 89 Service_NVDRV, ///< The NVDRV (Nvidia driver) service
90 Service_NVFlinger, ///< The NVFlinger service
90 Service_OLSC, ///< The OLSC service 91 Service_OLSC, ///< The OLSC service
91 Service_PCIE, ///< The PCIe service 92 Service_PCIE, ///< The PCIe service
92 Service_PCTL, ///< The PCTL (Parental control) service 93 Service_PCTL, ///< The PCTL (Parental control) service
diff --git a/src/common/math_util.h b/src/common/math_util.h
index 510c4e56d..54485bf53 100644
--- a/src/common/math_util.h
+++ b/src/common/math_util.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <algorithm>
7#include <cstdlib> 8#include <cstdlib>
8#include <type_traits> 9#include <type_traits>
9 10
@@ -20,10 +21,32 @@ struct Rectangle {
20 21
21 constexpr Rectangle() = default; 22 constexpr Rectangle() = default;
22 23
24 constexpr Rectangle(T width, T height) : right(width), bottom(height) {}
25
23 constexpr Rectangle(T left_, T top_, T right_, T bottom_) 26 constexpr Rectangle(T left_, T top_, T right_, T bottom_)
24 : left(left_), top(top_), right(right_), bottom(bottom_) {} 27 : left(left_), top(top_), right(right_), bottom(bottom_) {}
25 28
26 [[nodiscard]] T GetWidth() const { 29 [[nodiscard]] constexpr T Left() const {
30 return left;
31 }
32
33 [[nodiscard]] constexpr T Top() const {
34 return top;
35 }
36
37 [[nodiscard]] constexpr T Right() const {
38 return right;
39 }
40
41 [[nodiscard]] constexpr T Bottom() const {
42 return bottom;
43 }
44
45 [[nodiscard]] constexpr bool IsEmpty() const {
46 return (GetWidth() <= 0) || (GetHeight() <= 0);
47 }
48
49 [[nodiscard]] constexpr T GetWidth() const {
27 if constexpr (std::is_floating_point_v<T>) { 50 if constexpr (std::is_floating_point_v<T>) {
28 return std::abs(right - left); 51 return std::abs(right - left);
29 } else { 52 } else {
@@ -31,7 +54,7 @@ struct Rectangle {
31 } 54 }
32 } 55 }
33 56
34 [[nodiscard]] T GetHeight() const { 57 [[nodiscard]] constexpr T GetHeight() const {
35 if constexpr (std::is_floating_point_v<T>) { 58 if constexpr (std::is_floating_point_v<T>) {
36 return std::abs(bottom - top); 59 return std::abs(bottom - top);
37 } else { 60 } else {
@@ -39,18 +62,35 @@ struct Rectangle {
39 } 62 }
40 } 63 }
41 64
42 [[nodiscard]] Rectangle<T> TranslateX(const T x) const { 65 [[nodiscard]] constexpr Rectangle<T> TranslateX(const T x) const {
43 return Rectangle{left + x, top, right + x, bottom}; 66 return Rectangle{left + x, top, right + x, bottom};
44 } 67 }
45 68
46 [[nodiscard]] Rectangle<T> TranslateY(const T y) const { 69 [[nodiscard]] constexpr Rectangle<T> TranslateY(const T y) const {
47 return Rectangle{left, top + y, right, bottom + y}; 70 return Rectangle{left, top + y, right, bottom + y};
48 } 71 }
49 72
50 [[nodiscard]] Rectangle<T> Scale(const float s) const { 73 [[nodiscard]] constexpr Rectangle<T> Scale(const float s) const {
51 return Rectangle{left, top, static_cast<T>(static_cast<float>(left + GetWidth()) * s), 74 return Rectangle{left, top, static_cast<T>(static_cast<float>(left + GetWidth()) * s),
52 static_cast<T>(static_cast<float>(top + GetHeight()) * s)}; 75 static_cast<T>(static_cast<float>(top + GetHeight()) * s)};
53 } 76 }
77
78 [[nodiscard]] constexpr bool operator==(const Rectangle<T>& rhs) const {
79 return (left == rhs.left) && (top == rhs.top) && (right == rhs.right) &&
80 (bottom == rhs.bottom);
81 }
82
83 [[nodiscard]] constexpr bool operator!=(const Rectangle<T>& rhs) const {
84 return !operator==(rhs);
85 }
86
87 [[nodiscard]] constexpr bool Intersect(const Rectangle<T>& with, Rectangle<T>* result) const {
88 result->left = std::max(left, with.left);
89 result->top = std::max(top, with.top);
90 result->right = std::min(right, with.right);
91 result->bottom = std::min(bottom, with.bottom);
92 return !result->IsEmpty();
93 }
54}; 94};
55 95
56template <typename T> 96template <typename T>
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index a6f442316..6536d0544 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -535,10 +535,35 @@ add_library(core STATIC
535 hle/service/nvdrv/nvmemp.h 535 hle/service/nvdrv/nvmemp.h
536 hle/service/nvdrv/syncpoint_manager.cpp 536 hle/service/nvdrv/syncpoint_manager.cpp
537 hle/service/nvdrv/syncpoint_manager.h 537 hle/service/nvdrv/syncpoint_manager.h
538 hle/service/nvflinger/buffer_queue.cpp 538 hle/service/nvflinger/binder.h
539 hle/service/nvflinger/buffer_queue.h 539 hle/service/nvflinger/buffer_item.h
540 hle/service/nvflinger/buffer_item_consumer.cpp
541 hle/service/nvflinger/buffer_item_consumer.h
542 hle/service/nvflinger/buffer_queue_consumer.cpp
543 hle/service/nvflinger/buffer_queue_consumer.h
544 hle/service/nvflinger/buffer_queue_core.cpp
545 hle/service/nvflinger/buffer_queue_core.h
546 hle/service/nvflinger/buffer_queue_defs.h
547 hle/service/nvflinger/buffer_queue_producer.cpp
548 hle/service/nvflinger/buffer_queue_producer.h
549 hle/service/nvflinger/buffer_slot.h
550 hle/service/nvflinger/buffer_transform_flags.h
551 hle/service/nvflinger/consumer_base.cpp
552 hle/service/nvflinger/consumer_base.h
553 hle/service/nvflinger/consumer_listener.h
554 hle/service/nvflinger/graphic_buffer_producer.cpp
555 hle/service/nvflinger/graphic_buffer_producer.h
556 hle/service/nvflinger/hos_binder_driver_server.cpp
557 hle/service/nvflinger/hos_binder_driver_server.h
540 hle/service/nvflinger/nvflinger.cpp 558 hle/service/nvflinger/nvflinger.cpp
541 hle/service/nvflinger/nvflinger.h 559 hle/service/nvflinger/nvflinger.h
560 hle/service/nvflinger/parcel.h
561 hle/service/nvflinger/pixel_format.h
562 hle/service/nvflinger/producer_listener.h
563 hle/service/nvflinger/status.h
564 hle/service/nvflinger/ui/fence.h
565 hle/service/nvflinger/ui/graphic_buffer.h
566 hle/service/nvflinger/window.h
542 hle/service/olsc/olsc.cpp 567 hle/service/olsc/olsc.cpp
543 hle/service/olsc/olsc.h 568 hle/service/olsc/olsc.h
544 hle/service/pcie/pcie.cpp 569 hle/service/pcie/pcie.cpp
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index d96226c41..24107f9f6 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -93,17 +93,19 @@ public:
93 static constexpr u64 ICACHE_LINE_SIZE = 64; 93 static constexpr u64 ICACHE_LINE_SIZE = 64;
94 94
95 const u64 cache_line_start = value & ~(ICACHE_LINE_SIZE - 1); 95 const u64 cache_line_start = value & ~(ICACHE_LINE_SIZE - 1);
96 parent.InvalidateCacheRange(cache_line_start, ICACHE_LINE_SIZE); 96 parent.system.InvalidateCpuInstructionCacheRange(cache_line_start, ICACHE_LINE_SIZE);
97 break; 97 break;
98 } 98 }
99 case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoU: 99 case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoU:
100 parent.ClearInstructionCache(); 100 parent.system.InvalidateCpuInstructionCaches();
101 break; 101 break;
102 case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoUInnerSharable: 102 case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoUInnerSharable:
103 default: 103 default:
104 LOG_DEBUG(Core_ARM, "Unprocesseed instruction cache operation: {}", op); 104 LOG_DEBUG(Core_ARM, "Unprocesseed instruction cache operation: {}", op);
105 break; 105 break;
106 } 106 }
107
108 parent.jit->HaltExecution();
107 } 109 }
108 110
109 void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override { 111 void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override {
diff --git a/src/core/hle/kernel/k_code_memory.cpp b/src/core/hle/kernel/k_code_memory.cpp
index b365ce7b7..63bbe02e9 100644
--- a/src/core/hle/kernel/k_code_memory.cpp
+++ b/src/core/hle/kernel/k_code_memory.cpp
@@ -28,7 +28,8 @@ ResultCode KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr
28 auto& page_table = m_owner->PageTable(); 28 auto& page_table = m_owner->PageTable();
29 29
30 // Construct the page group. 30 // Construct the page group.
31 m_page_group = KPageLinkedList(addr, Common::DivideUp(size, PageSize)); 31 m_page_group =
32 KPageLinkedList(page_table.GetPhysicalAddr(addr), Common::DivideUp(size, PageSize));
32 33
33 // Lock the memory. 34 // Lock the memory.
34 R_TRY(page_table.LockForCodeMemory(addr, size)) 35 R_TRY(page_table.LockForCodeMemory(addr, size))
diff --git a/src/core/hle/kernel/k_page_linked_list.h b/src/core/hle/kernel/k_page_linked_list.h
index 0e2ae582a..869228322 100644
--- a/src/core/hle/kernel/k_page_linked_list.h
+++ b/src/core/hle/kernel/k_page_linked_list.h
@@ -89,6 +89,10 @@ public:
89 return ResultSuccess; 89 return ResultSuccess;
90 } 90 }
91 91
92 bool Empty() const {
93 return nodes.empty();
94 }
95
92private: 96private:
93 std::list<Node> nodes; 97 std::list<Node> nodes;
94}; 98};
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp
index 02d93b12e..599013cf6 100644
--- a/src/core/hle/kernel/k_page_table.cpp
+++ b/src/core/hle/kernel/k_page_table.cpp
@@ -486,6 +486,58 @@ VAddr KPageTable::FindFreeArea(VAddr region_start, std::size_t region_num_pages,
486 return address; 486 return address;
487} 487}
488 488
489ResultCode KPageTable::MakePageGroup(KPageLinkedList& pg, VAddr addr, size_t num_pages) {
490 ASSERT(this->IsLockedByCurrentThread());
491
492 const size_t size = num_pages * PageSize;
493
494 // We're making a new group, not adding to an existing one.
495 R_UNLESS(pg.Empty(), ResultInvalidCurrentMemory);
496
497 // Begin traversal.
498 Common::PageTable::TraversalContext context;
499 Common::PageTable::TraversalEntry next_entry;
500 R_UNLESS(page_table_impl.BeginTraversal(next_entry, context, addr), ResultInvalidCurrentMemory);
501
502 // Prepare tracking variables.
503 PAddr cur_addr = next_entry.phys_addr;
504 size_t cur_size = next_entry.block_size - (cur_addr & (next_entry.block_size - 1));
505 size_t tot_size = cur_size;
506
507 // Iterate, adding to group as we go.
508 const auto& memory_layout = system.Kernel().MemoryLayout();
509 while (tot_size < size) {
510 R_UNLESS(page_table_impl.ContinueTraversal(next_entry, context),
511 ResultInvalidCurrentMemory);
512
513 if (next_entry.phys_addr != (cur_addr + cur_size)) {
514 const size_t cur_pages = cur_size / PageSize;
515
516 R_UNLESS(IsHeapPhysicalAddress(memory_layout, cur_addr), ResultInvalidCurrentMemory);
517 R_TRY(pg.AddBlock(cur_addr, cur_pages));
518
519 cur_addr = next_entry.phys_addr;
520 cur_size = next_entry.block_size;
521 } else {
522 cur_size += next_entry.block_size;
523 }
524
525 tot_size += next_entry.block_size;
526 }
527
528 // Ensure we add the right amount for the last block.
529 if (tot_size > size) {
530 cur_size -= (tot_size - size);
531 }
532
533 // Add the last block.
534 const size_t cur_pages = cur_size / PageSize;
535 R_UNLESS(IsHeapPhysicalAddress(memory_layout, cur_addr), ResultInvalidCurrentMemory);
536 R_TRY(pg.AddBlock(cur_addr, cur_pages));
537
538 return ResultSuccess;
539}
540
489ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size, 541ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size,
490 KPageTable& src_page_table, VAddr src_addr) { 542 KPageTable& src_page_table, VAddr src_addr) {
491 KScopedLightLock lk(general_lock); 543 KScopedLightLock lk(general_lock);
@@ -1223,6 +1275,31 @@ ResultCode KPageTable::UnmapPages(VAddr address, std::size_t num_pages, KMemoryS
1223 return ResultSuccess; 1275 return ResultSuccess;
1224} 1276}
1225 1277
1278ResultCode KPageTable::MakeAndOpenPageGroup(KPageLinkedList* out, VAddr address, size_t num_pages,
1279 KMemoryState state_mask, KMemoryState state,
1280 KMemoryPermission perm_mask, KMemoryPermission perm,
1281 KMemoryAttribute attr_mask, KMemoryAttribute attr) {
1282 // Ensure that the page group isn't null.
1283 ASSERT(out != nullptr);
1284
1285 // Make sure that the region we're mapping is valid for the table.
1286 const size_t size = num_pages * PageSize;
1287 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
1288
1289 // Lock the table.
1290 KScopedLightLock lk(general_lock);
1291
1292 // Check if state allows us to create the group.
1293 R_TRY(this->CheckMemoryState(address, size, state_mask | KMemoryState::FlagReferenceCounted,
1294 state | KMemoryState::FlagReferenceCounted, perm_mask, perm,
1295 attr_mask, attr));
1296
1297 // Create a new page group for the region.
1298 R_TRY(this->MakePageGroup(*out, address, num_pages));
1299
1300 return ResultSuccess;
1301}
1302
1226ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size, 1303ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size,
1227 Svc::MemoryPermission svc_perm) { 1304 Svc::MemoryPermission svc_perm) {
1228 const size_t num_pages = size / PageSize; 1305 const size_t num_pages = size / PageSize;
@@ -1605,57 +1682,21 @@ ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size)
1605} 1682}
1606 1683
1607ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) { 1684ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) {
1608 KScopedLightLock lk(general_lock); 1685 return this->LockMemoryAndOpen(
1609 1686 nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
1610 KMemoryPermission new_perm = KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite; 1687 KMemoryState::FlagCanCodeMemory, KMemoryPermission::All, KMemoryPermission::UserReadWrite,
1611 1688 KMemoryAttribute::All, KMemoryAttribute::None,
1612 KMemoryPermission old_perm{}; 1689 static_cast<KMemoryPermission>(KMemoryPermission::NotMapped |
1613 1690 KMemoryPermission::KernelReadWrite),
1614 if (const ResultCode result{CheckMemoryState( 1691 KMemoryAttribute::Locked);
1615 nullptr, &old_perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
1616 KMemoryState::FlagCanCodeMemory, KMemoryPermission::All,
1617 KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None)};
1618 result.IsError()) {
1619 return result;
1620 }
1621
1622 new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
1623
1624 block_manager->UpdateLock(
1625 addr, size / PageSize,
1626 [](KMemoryBlockManager::iterator block, KMemoryPermission permission) {
1627 block->ShareToDevice(permission);
1628 },
1629 new_perm);
1630
1631 return ResultSuccess;
1632} 1692}
1633 1693
1634ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) { 1694ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) {
1635 KScopedLightLock lk(general_lock); 1695 return this->UnlockMemory(addr, size, KMemoryState::FlagCanCodeMemory,
1636 1696 KMemoryState::FlagCanCodeMemory, KMemoryPermission::None,
1637 KMemoryPermission new_perm = KMemoryPermission::UserReadWrite; 1697 KMemoryPermission::None, KMemoryAttribute::All,
1638 1698 KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite,
1639 KMemoryPermission old_perm{}; 1699 KMemoryAttribute::Locked, nullptr);
1640
1641 if (const ResultCode result{CheckMemoryState(
1642 nullptr, &old_perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
1643 KMemoryState::FlagCanCodeMemory, KMemoryPermission::None, KMemoryPermission::None,
1644 KMemoryAttribute::All, KMemoryAttribute::Locked)};
1645 result.IsError()) {
1646 return result;
1647 }
1648
1649 new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
1650
1651 block_manager->UpdateLock(
1652 addr, size / PageSize,
1653 [](KMemoryBlockManager::iterator block, KMemoryPermission permission) {
1654 block->UnshareToDevice(permission);
1655 },
1656 new_perm);
1657
1658 return ResultSuccess;
1659} 1700}
1660 1701
1661ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) { 1702ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) {
@@ -1991,4 +2032,109 @@ ResultCode KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermissi
1991 return ResultSuccess; 2032 return ResultSuccess;
1992} 2033}
1993 2034
2035ResultCode KPageTable::LockMemoryAndOpen(KPageLinkedList* out_pg, PAddr* out_paddr, VAddr addr,
2036 size_t size, KMemoryState state_mask, KMemoryState state,
2037 KMemoryPermission perm_mask, KMemoryPermission perm,
2038 KMemoryAttribute attr_mask, KMemoryAttribute attr,
2039 KMemoryPermission new_perm, KMemoryAttribute lock_attr) {
2040 // Validate basic preconditions.
2041 ASSERT((lock_attr & attr) == KMemoryAttribute::None);
2042 ASSERT((lock_attr & (KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared)) ==
2043 KMemoryAttribute::None);
2044
2045 // Validate the lock request.
2046 const size_t num_pages = size / PageSize;
2047 R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory);
2048
2049 // Lock the table.
2050 KScopedLightLock lk(general_lock);
2051
2052 // Check that the output page group is empty, if it exists.
2053 if (out_pg) {
2054 ASSERT(out_pg->GetNumPages() == 0);
2055 }
2056
2057 // Check the state.
2058 KMemoryState old_state{};
2059 KMemoryPermission old_perm{};
2060 KMemoryAttribute old_attr{};
2061 size_t num_allocator_blocks{};
2062 R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm),
2063 std::addressof(old_attr), std::addressof(num_allocator_blocks),
2064 addr, size, state_mask | KMemoryState::FlagReferenceCounted,
2065 state | KMemoryState::FlagReferenceCounted, perm_mask, perm,
2066 attr_mask, attr));
2067
2068 // Get the physical address, if we're supposed to.
2069 if (out_paddr != nullptr) {
2070 ASSERT(this->GetPhysicalAddressLocked(out_paddr, addr));
2071 }
2072
2073 // Make the page group, if we're supposed to.
2074 if (out_pg != nullptr) {
2075 R_TRY(this->MakePageGroup(*out_pg, addr, num_pages));
2076 }
2077
2078 // Decide on new perm and attr.
2079 new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
2080 KMemoryAttribute new_attr = static_cast<KMemoryAttribute>(old_attr | lock_attr);
2081
2082 // Update permission, if we need to.
2083 if (new_perm != old_perm) {
2084 R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions));
2085 }
2086
2087 // Apply the memory block updates.
2088 block_manager->Update(addr, num_pages, old_state, new_perm, new_attr);
2089
2090 return ResultSuccess;
2091}
2092
2093ResultCode KPageTable::UnlockMemory(VAddr addr, size_t size, KMemoryState state_mask,
2094 KMemoryState state, KMemoryPermission perm_mask,
2095 KMemoryPermission perm, KMemoryAttribute attr_mask,
2096 KMemoryAttribute attr, KMemoryPermission new_perm,
2097 KMemoryAttribute lock_attr, const KPageLinkedList* pg) {
2098 // Validate basic preconditions.
2099 ASSERT((attr_mask & lock_attr) == lock_attr);
2100 ASSERT((attr & lock_attr) == lock_attr);
2101
2102 // Validate the unlock request.
2103 const size_t num_pages = size / PageSize;
2104 R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory);
2105
2106 // Lock the table.
2107 KScopedLightLock lk(general_lock);
2108
2109 // Check the state.
2110 KMemoryState old_state{};
2111 KMemoryPermission old_perm{};
2112 KMemoryAttribute old_attr{};
2113 size_t num_allocator_blocks{};
2114 R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm),
2115 std::addressof(old_attr), std::addressof(num_allocator_blocks),
2116 addr, size, state_mask | KMemoryState::FlagReferenceCounted,
2117 state | KMemoryState::FlagReferenceCounted, perm_mask, perm,
2118 attr_mask, attr));
2119
2120 // Check the page group.
2121 if (pg != nullptr) {
2122 UNIMPLEMENTED_MSG("PageGroup support is unimplemented!");
2123 }
2124
2125 // Decide on new perm and attr.
2126 new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
2127 KMemoryAttribute new_attr = static_cast<KMemoryAttribute>(old_attr & ~lock_attr);
2128
2129 // Update permission, if we need to.
2130 if (new_perm != old_perm) {
2131 R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions));
2132 }
2133
2134 // Apply the memory block updates.
2135 block_manager->Update(addr, num_pages, old_state, new_perm, new_attr);
2136
2137 return ResultSuccess;
2138}
2139
1994} // namespace Kernel 2140} // namespace Kernel
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h
index 54c6adf8d..bfabdf38c 100644
--- a/src/core/hle/kernel/k_page_table.h
+++ b/src/core/hle/kernel/k_page_table.h
@@ -12,6 +12,7 @@
12#include "core/file_sys/program_metadata.h" 12#include "core/file_sys/program_metadata.h"
13#include "core/hle/kernel/k_light_lock.h" 13#include "core/hle/kernel/k_light_lock.h"
14#include "core/hle/kernel/k_memory_block.h" 14#include "core/hle/kernel/k_memory_block.h"
15#include "core/hle/kernel/k_memory_layout.h"
15#include "core/hle/kernel/k_memory_manager.h" 16#include "core/hle/kernel/k_memory_manager.h"
16#include "core/hle/result.h" 17#include "core/hle/result.h"
17 18
@@ -71,6 +72,10 @@ public:
71 ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size); 72 ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size);
72 ResultCode LockForCodeMemory(VAddr addr, std::size_t size); 73 ResultCode LockForCodeMemory(VAddr addr, std::size_t size);
73 ResultCode UnlockForCodeMemory(VAddr addr, std::size_t size); 74 ResultCode UnlockForCodeMemory(VAddr addr, std::size_t size);
75 ResultCode MakeAndOpenPageGroup(KPageLinkedList* out, VAddr address, size_t num_pages,
76 KMemoryState state_mask, KMemoryState state,
77 KMemoryPermission perm_mask, KMemoryPermission perm,
78 KMemoryAttribute attr_mask, KMemoryAttribute attr);
74 79
75 Common::PageTable& PageTableImpl() { 80 Common::PageTable& PageTableImpl() {
76 return page_table_impl; 81 return page_table_impl;
@@ -159,10 +164,37 @@ private:
159 attr_mask, attr, ignore_attr); 164 attr_mask, attr, ignore_attr);
160 } 165 }
161 166
167 ResultCode LockMemoryAndOpen(KPageLinkedList* out_pg, PAddr* out_paddr, VAddr addr, size_t size,
168 KMemoryState state_mask, KMemoryState state,
169 KMemoryPermission perm_mask, KMemoryPermission perm,
170 KMemoryAttribute attr_mask, KMemoryAttribute attr,
171 KMemoryPermission new_perm, KMemoryAttribute lock_attr);
172 ResultCode UnlockMemory(VAddr addr, size_t size, KMemoryState state_mask, KMemoryState state,
173 KMemoryPermission perm_mask, KMemoryPermission perm,
174 KMemoryAttribute attr_mask, KMemoryAttribute attr,
175 KMemoryPermission new_perm, KMemoryAttribute lock_attr,
176 const KPageLinkedList* pg);
177
178 ResultCode MakePageGroup(KPageLinkedList& pg, VAddr addr, size_t num_pages);
179
162 bool IsLockedByCurrentThread() const { 180 bool IsLockedByCurrentThread() const {
163 return general_lock.IsLockedByCurrentThread(); 181 return general_lock.IsLockedByCurrentThread();
164 } 182 }
165 183
184 bool IsHeapPhysicalAddress(const KMemoryLayout& layout, PAddr phys_addr) {
185 ASSERT(this->IsLockedByCurrentThread());
186
187 return layout.IsHeapPhysicalAddress(cached_physical_heap_region, phys_addr);
188 }
189
190 bool GetPhysicalAddressLocked(PAddr* out, VAddr virt_addr) const {
191 ASSERT(this->IsLockedByCurrentThread());
192
193 *out = GetPhysicalAddr(virt_addr);
194
195 return *out != 0;
196 }
197
166 mutable KLightLock general_lock; 198 mutable KLightLock general_lock;
167 mutable KLightLock map_physical_memory_lock; 199 mutable KLightLock map_physical_memory_lock;
168 200
@@ -322,6 +354,7 @@ private:
322 bool is_aslr_enabled{}; 354 bool is_aslr_enabled{};
323 355
324 u32 heap_fill_value{}; 356 u32 heap_fill_value{};
357 const KMemoryRegion* cached_physical_heap_region{};
325 358
326 KMemoryManager::Pool memory_pool{KMemoryManager::Pool::Application}; 359 KMemoryManager::Pool memory_pool{KMemoryManager::Pool::Application};
327 KMemoryManager::Direction allocation_option{KMemoryManager::Direction::FromFront}; 360 KMemoryManager::Direction allocation_option{KMemoryManager::Direction::FromFront};
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 839171e85..976d63234 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -1362,8 +1362,11 @@ static ResultCode MapProcessMemory(Core::System& system, VAddr dst_address, Hand
1362 ResultInvalidMemoryRegion); 1362 ResultInvalidMemoryRegion);
1363 1363
1364 // Create a new page group. 1364 // Create a new page group.
1365 KMemoryInfo kBlockInfo = dst_pt.QueryInfo(dst_address); 1365 KPageLinkedList pg;
1366 KPageLinkedList pg(kBlockInfo.GetAddress(), kBlockInfo.GetNumPages()); 1366 R_TRY(src_pt.MakeAndOpenPageGroup(
1367 std::addressof(pg), src_address, size / PageSize, KMemoryState::FlagCanMapProcess,
1368 KMemoryState::FlagCanMapProcess, KMemoryPermission::None, KMemoryPermission::None,
1369 KMemoryAttribute::All, KMemoryAttribute::None));
1367 1370
1368 // Map the group. 1371 // Map the group.
1369 R_TRY(dst_pt.MapPages(dst_address, pg, KMemoryState::SharedCode, 1372 R_TRY(dst_pt.MapPages(dst_address, pg, KMemoryState::SharedCode,
@@ -1408,8 +1411,8 @@ static ResultCode UnmapProcessMemory(Core::System& system, VAddr dst_address, Ha
1408} 1411}
1409 1412
1410static ResultCode CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) { 1413static ResultCode CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) {
1411 LOG_TRACE(Kernel_SVC, "called, handle_out={}, address=0x{:X}, size=0x{:X}", 1414 LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, size=0x{:X}", address, size);
1412 static_cast<void*>(out), address, size); 1415
1413 // Get kernel instance. 1416 // Get kernel instance.
1414 auto& kernel = system.Kernel(); 1417 auto& kernel = system.Kernel();
1415 1418
@@ -1664,7 +1667,7 @@ static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_ha
1664 return ResultInvalidAddress; 1667 return ResultInvalidAddress;
1665 } 1668 }
1666 1669
1667 if (size == 0 || Common::Is4KBAligned(size)) { 1670 if (size == 0 || !Common::Is4KBAligned(size)) {
1668 LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size); 1671 LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size);
1669 return ResultInvalidSize; 1672 return ResultInvalidSize;
1670 } 1673 }
diff --git a/src/core/hle/service/am/applets/applet_software_keyboard.cpp b/src/core/hle/service/am/applets/applet_software_keyboard.cpp
index f38f53f69..ee669686c 100644
--- a/src/core/hle/service/am/applets/applet_software_keyboard.cpp
+++ b/src/core/hle/service/am/applets/applet_software_keyboard.cpp
@@ -226,7 +226,7 @@ void SoftwareKeyboard::InitializeForeground() {
226 ASSERT(work_buffer_storage != nullptr); 226 ASSERT(work_buffer_storage != nullptr);
227 227
228 if (swkbd_config_common.initial_string_length == 0) { 228 if (swkbd_config_common.initial_string_length == 0) {
229 InitializeFrontendKeyboard(); 229 InitializeFrontendNormalKeyboard();
230 return; 230 return;
231 } 231 }
232 232
@@ -243,7 +243,7 @@ void SoftwareKeyboard::InitializeForeground() {
243 243
244 LOG_DEBUG(Service_AM, "\nInitial Text: {}", Common::UTF16ToUTF8(initial_text)); 244 LOG_DEBUG(Service_AM, "\nInitial Text: {}", Common::UTF16ToUTF8(initial_text));
245 245
246 InitializeFrontendKeyboard(); 246 InitializeFrontendNormalKeyboard();
247} 247}
248 248
249void SoftwareKeyboard::InitializeBackground(LibraryAppletMode library_applet_mode) { 249void SoftwareKeyboard::InitializeBackground(LibraryAppletMode library_applet_mode) {
@@ -480,129 +480,173 @@ void SoftwareKeyboard::ChangeState(SwkbdState state) {
480 ReplyDefault(); 480 ReplyDefault();
481} 481}
482 482
483void SoftwareKeyboard::InitializeFrontendKeyboard() { 483void SoftwareKeyboard::InitializeFrontendNormalKeyboard() {
484 if (is_background) { 484 std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
485 const auto& appear_arg = swkbd_calc_arg.appear_arg; 485 swkbd_config_common.ok_text.data(), swkbd_config_common.ok_text.size());
486 486
487 std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( 487 std::u16string header_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
488 appear_arg.ok_text.data(), appear_arg.ok_text.size()); 488 swkbd_config_common.header_text.data(), swkbd_config_common.header_text.size());
489 489
490 const u32 max_text_length = 490 std::u16string sub_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
491 appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH 491 swkbd_config_common.sub_text.data(), swkbd_config_common.sub_text.size());
492 ? appear_arg.max_text_length 492
493 : DEFAULT_MAX_TEXT_LENGTH; 493 std::u16string guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
494 494 swkbd_config_common.guide_text.data(), swkbd_config_common.guide_text.size());
495 const u32 min_text_length = 495
496 appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0; 496 const u32 max_text_length =
497 497 swkbd_config_common.max_text_length > 0 &&
498 const s32 initial_cursor_position = 498 swkbd_config_common.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
499 current_cursor_position > 0 ? current_cursor_position : 0; 499 ? swkbd_config_common.max_text_length
500 500 : DEFAULT_MAX_TEXT_LENGTH;
501 const auto text_draw_type = 501
502 max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box; 502 const u32 min_text_length = swkbd_config_common.min_text_length <= max_text_length
503 503 ? swkbd_config_common.min_text_length
504 Core::Frontend::KeyboardInitializeParameters initialize_parameters{ 504 : 0;
505 .ok_text{std::move(ok_text)}, 505
506 .header_text{}, 506 const s32 initial_cursor_position = [this] {
507 .sub_text{}, 507 switch (swkbd_config_common.initial_cursor_position) {
508 .guide_text{}, 508 case SwkbdInitialCursorPosition::Start:
509 .initial_text{current_text}, 509 default:
510 .max_text_length{max_text_length}, 510 return 0;
511 .min_text_length{min_text_length}, 511 case SwkbdInitialCursorPosition::End:
512 .initial_cursor_position{initial_cursor_position}, 512 return static_cast<s32>(initial_text.size());
513 .type{appear_arg.type}, 513 }
514 .password_mode{SwkbdPasswordMode::Disabled}, 514 }();
515 .text_draw_type{text_draw_type}, 515
516 .key_disable_flags{appear_arg.key_disable_flags}, 516 const auto text_draw_type = [this, max_text_length] {
517 .use_blur_background{false}, 517 switch (swkbd_config_common.text_draw_type) {
518 .enable_backspace_button{swkbd_calc_arg.enable_backspace_button}, 518 case SwkbdTextDrawType::Line:
519 .enable_return_button{appear_arg.enable_return_button}, 519 default:
520 .disable_cancel_button{appear_arg.disable_cancel_button}, 520 return max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box;
521 }; 521 case SwkbdTextDrawType::Box:
522 522 case SwkbdTextDrawType::DownloadCode:
523 frontend.InitializeKeyboard( 523 return swkbd_config_common.text_draw_type;
524 true, std::move(initialize_parameters), {}, 524 }
525 [this](SwkbdReplyType reply_type, std::u16string submitted_text, s32 cursor_position) { 525 }();
526 SubmitTextInline(reply_type, submitted_text, cursor_position); 526
527 }); 527 const auto enable_return_button =
528 } else { 528 text_draw_type == SwkbdTextDrawType::Box ? swkbd_config_common.enable_return_button : false;
529 std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( 529
530 swkbd_config_common.ok_text.data(), swkbd_config_common.ok_text.size()); 530 const auto disable_cancel_button = swkbd_applet_version >= SwkbdAppletVersion::Version393227
531 531 ? swkbd_config_new.disable_cancel_button
532 std::u16string header_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( 532 : false;
533 swkbd_config_common.header_text.data(), swkbd_config_common.header_text.size()); 533
534 534 Core::Frontend::KeyboardInitializeParameters initialize_parameters{
535 std::u16string sub_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( 535 .ok_text{std::move(ok_text)},
536 swkbd_config_common.sub_text.data(), swkbd_config_common.sub_text.size()); 536 .header_text{std::move(header_text)},
537 537 .sub_text{std::move(sub_text)},
538 std::u16string guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( 538 .guide_text{std::move(guide_text)},
539 swkbd_config_common.guide_text.data(), swkbd_config_common.guide_text.size()); 539 .initial_text{initial_text},
540 540 .max_text_length{max_text_length},
541 const u32 max_text_length = 541 .min_text_length{min_text_length},
542 swkbd_config_common.max_text_length > 0 && 542 .initial_cursor_position{initial_cursor_position},
543 swkbd_config_common.max_text_length <= DEFAULT_MAX_TEXT_LENGTH 543 .type{swkbd_config_common.type},
544 ? swkbd_config_common.max_text_length 544 .password_mode{swkbd_config_common.password_mode},
545 : DEFAULT_MAX_TEXT_LENGTH; 545 .text_draw_type{text_draw_type},
546 546 .key_disable_flags{swkbd_config_common.key_disable_flags},
547 const u32 min_text_length = swkbd_config_common.min_text_length <= max_text_length 547 .use_blur_background{swkbd_config_common.use_blur_background},
548 ? swkbd_config_common.min_text_length 548 .enable_backspace_button{true},
549 : 0; 549 .enable_return_button{enable_return_button},
550 550 .disable_cancel_button{disable_cancel_button},
551 const s32 initial_cursor_position = [this] { 551 };
552 switch (swkbd_config_common.initial_cursor_position) { 552
553 case SwkbdInitialCursorPosition::Start: 553 frontend.InitializeKeyboard(
554 default: 554 false, std::move(initialize_parameters),
555 return 0; 555 [this](SwkbdResult result, std::u16string submitted_text, bool confirmed) {
556 case SwkbdInitialCursorPosition::End: 556 SubmitTextNormal(result, submitted_text, confirmed);
557 return static_cast<s32>(initial_text.size()); 557 },
558 } 558 {});
559 }(); 559}
560 560
561 const auto text_draw_type = [this, max_text_length] { 561void SoftwareKeyboard::InitializeFrontendInlineKeyboard(
562 switch (swkbd_config_common.text_draw_type) { 562 Core::Frontend::KeyboardInitializeParameters initialize_parameters) {
563 case SwkbdTextDrawType::Line: 563 frontend.InitializeKeyboard(
564 default: 564 true, std::move(initialize_parameters), {},
565 return max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box; 565 [this](SwkbdReplyType reply_type, std::u16string submitted_text, s32 cursor_position) {
566 case SwkbdTextDrawType::Box: 566 SubmitTextInline(reply_type, submitted_text, cursor_position);
567 case SwkbdTextDrawType::DownloadCode: 567 });
568 return swkbd_config_common.text_draw_type; 568}
569 } 569
570 }(); 570void SoftwareKeyboard::InitializeFrontendInlineKeyboardOld() {
571 571 const auto& appear_arg = swkbd_calc_arg_old.appear_arg;
572 const auto enable_return_button = text_draw_type == SwkbdTextDrawType::Box 572
573 ? swkbd_config_common.enable_return_button 573 std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
574 : false; 574 appear_arg.ok_text.data(), appear_arg.ok_text.size());
575 575
576 const auto disable_cancel_button = swkbd_applet_version >= SwkbdAppletVersion::Version393227 576 const u32 max_text_length =
577 ? swkbd_config_new.disable_cancel_button 577 appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
578 : false; 578 ? appear_arg.max_text_length
579 579 : DEFAULT_MAX_TEXT_LENGTH;
580 Core::Frontend::KeyboardInitializeParameters initialize_parameters{ 580
581 .ok_text{std::move(ok_text)}, 581 const u32 min_text_length =
582 .header_text{std::move(header_text)}, 582 appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0;
583 .sub_text{std::move(sub_text)}, 583
584 .guide_text{std::move(guide_text)}, 584 const s32 initial_cursor_position = current_cursor_position > 0 ? current_cursor_position : 0;
585 .initial_text{initial_text}, 585
586 .max_text_length{max_text_length}, 586 const auto text_draw_type =
587 .min_text_length{min_text_length}, 587 max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box;
588 .initial_cursor_position{initial_cursor_position}, 588
589 .type{swkbd_config_common.type}, 589 Core::Frontend::KeyboardInitializeParameters initialize_parameters{
590 .password_mode{swkbd_config_common.password_mode}, 590 .ok_text{std::move(ok_text)},
591 .text_draw_type{text_draw_type}, 591 .header_text{},
592 .key_disable_flags{swkbd_config_common.key_disable_flags}, 592 .sub_text{},
593 .use_blur_background{swkbd_config_common.use_blur_background}, 593 .guide_text{},
594 .enable_backspace_button{true}, 594 .initial_text{current_text},
595 .enable_return_button{enable_return_button}, 595 .max_text_length{max_text_length},
596 .disable_cancel_button{disable_cancel_button}, 596 .min_text_length{min_text_length},
597 }; 597 .initial_cursor_position{initial_cursor_position},
598 598 .type{appear_arg.type},
599 frontend.InitializeKeyboard( 599 .password_mode{SwkbdPasswordMode::Disabled},
600 false, std::move(initialize_parameters), 600 .text_draw_type{text_draw_type},
601 [this](SwkbdResult result, std::u16string submitted_text, bool confirmed) { 601 .key_disable_flags{appear_arg.key_disable_flags},
602 SubmitTextNormal(result, submitted_text, confirmed); 602 .use_blur_background{false},
603 }, 603 .enable_backspace_button{swkbd_calc_arg_old.enable_backspace_button},
604 {}); 604 .enable_return_button{appear_arg.enable_return_button},
605 } 605 .disable_cancel_button{appear_arg.disable_cancel_button},
606 };
607
608 InitializeFrontendInlineKeyboard(std::move(initialize_parameters));
609}
610
611void SoftwareKeyboard::InitializeFrontendInlineKeyboardNew() {
612 const auto& appear_arg = swkbd_calc_arg_new.appear_arg;
613
614 std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
615 appear_arg.ok_text.data(), appear_arg.ok_text.size());
616
617 const u32 max_text_length =
618 appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
619 ? appear_arg.max_text_length
620 : DEFAULT_MAX_TEXT_LENGTH;
621
622 const u32 min_text_length =
623 appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0;
624
625 const s32 initial_cursor_position = current_cursor_position > 0 ? current_cursor_position : 0;
626
627 const auto text_draw_type =
628 max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box;
629
630 Core::Frontend::KeyboardInitializeParameters initialize_parameters{
631 .ok_text{std::move(ok_text)},
632 .header_text{},
633 .sub_text{},
634 .guide_text{},
635 .initial_text{current_text},
636 .max_text_length{max_text_length},
637 .min_text_length{min_text_length},
638 .initial_cursor_position{initial_cursor_position},
639 .type{appear_arg.type},
640 .password_mode{SwkbdPasswordMode::Disabled},
641 .text_draw_type{text_draw_type},
642 .key_disable_flags{appear_arg.key_disable_flags},
643 .use_blur_background{false},
644 .enable_backspace_button{swkbd_calc_arg_new.enable_backspace_button},
645 .enable_return_button{appear_arg.enable_return_button},
646 .disable_cancel_button{appear_arg.disable_cancel_button},
647 };
648
649 InitializeFrontendInlineKeyboard(std::move(initialize_parameters));
606} 650}
607 651
608void SoftwareKeyboard::ShowNormalKeyboard() { 652void SoftwareKeyboard::ShowNormalKeyboard() {
@@ -614,14 +658,21 @@ void SoftwareKeyboard::ShowTextCheckDialog(SwkbdTextCheckResult text_check_resul
614 frontend.ShowTextCheckDialog(text_check_result, std::move(text_check_message)); 658 frontend.ShowTextCheckDialog(text_check_result, std::move(text_check_message));
615} 659}
616 660
617void SoftwareKeyboard::ShowInlineKeyboard() { 661void SoftwareKeyboard::ShowInlineKeyboard(
662 Core::Frontend::InlineAppearParameters appear_parameters) {
663 frontend.ShowInlineKeyboard(std::move(appear_parameters));
664
665 ChangeState(SwkbdState::InitializedIsShown);
666}
667
668void SoftwareKeyboard::ShowInlineKeyboardOld() {
618 if (swkbd_state != SwkbdState::InitializedIsHidden) { 669 if (swkbd_state != SwkbdState::InitializedIsHidden) {
619 return; 670 return;
620 } 671 }
621 672
622 ChangeState(SwkbdState::InitializedIsAppearing); 673 ChangeState(SwkbdState::InitializedIsAppearing);
623 674
624 const auto& appear_arg = swkbd_calc_arg.appear_arg; 675 const auto& appear_arg = swkbd_calc_arg_old.appear_arg;
625 676
626 const u32 max_text_length = 677 const u32 max_text_length =
627 appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH 678 appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
@@ -634,21 +685,54 @@ void SoftwareKeyboard::ShowInlineKeyboard() {
634 Core::Frontend::InlineAppearParameters appear_parameters{ 685 Core::Frontend::InlineAppearParameters appear_parameters{
635 .max_text_length{max_text_length}, 686 .max_text_length{max_text_length},
636 .min_text_length{min_text_length}, 687 .min_text_length{min_text_length},
637 .key_top_scale_x{swkbd_calc_arg.key_top_scale_x}, 688 .key_top_scale_x{swkbd_calc_arg_old.key_top_scale_x},
638 .key_top_scale_y{swkbd_calc_arg.key_top_scale_y}, 689 .key_top_scale_y{swkbd_calc_arg_old.key_top_scale_y},
639 .key_top_translate_x{swkbd_calc_arg.key_top_translate_x}, 690 .key_top_translate_x{swkbd_calc_arg_old.key_top_translate_x},
640 .key_top_translate_y{swkbd_calc_arg.key_top_translate_y}, 691 .key_top_translate_y{swkbd_calc_arg_old.key_top_translate_y},
641 .type{appear_arg.type}, 692 .type{appear_arg.type},
642 .key_disable_flags{appear_arg.key_disable_flags}, 693 .key_disable_flags{appear_arg.key_disable_flags},
643 .key_top_as_floating{swkbd_calc_arg.key_top_as_floating}, 694 .key_top_as_floating{swkbd_calc_arg_old.key_top_as_floating},
644 .enable_backspace_button{swkbd_calc_arg.enable_backspace_button}, 695 .enable_backspace_button{swkbd_calc_arg_old.enable_backspace_button},
645 .enable_return_button{appear_arg.enable_return_button}, 696 .enable_return_button{appear_arg.enable_return_button},
646 .disable_cancel_button{appear_arg.disable_cancel_button}, 697 .disable_cancel_button{appear_arg.disable_cancel_button},
647 }; 698 };
648 699
649 frontend.ShowInlineKeyboard(std::move(appear_parameters)); 700 ShowInlineKeyboard(std::move(appear_parameters));
701}
650 702
651 ChangeState(SwkbdState::InitializedIsShown); 703void SoftwareKeyboard::ShowInlineKeyboardNew() {
704 if (swkbd_state != SwkbdState::InitializedIsHidden) {
705 return;
706 }
707
708 ChangeState(SwkbdState::InitializedIsAppearing);
709
710 const auto& appear_arg = swkbd_calc_arg_new.appear_arg;
711
712 const u32 max_text_length =
713 appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
714 ? appear_arg.max_text_length
715 : DEFAULT_MAX_TEXT_LENGTH;
716
717 const u32 min_text_length =
718 appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0;
719
720 Core::Frontend::InlineAppearParameters appear_parameters{
721 .max_text_length{max_text_length},
722 .min_text_length{min_text_length},
723 .key_top_scale_x{swkbd_calc_arg_new.key_top_scale_x},
724 .key_top_scale_y{swkbd_calc_arg_new.key_top_scale_y},
725 .key_top_translate_x{swkbd_calc_arg_new.key_top_translate_x},
726 .key_top_translate_y{swkbd_calc_arg_new.key_top_translate_y},
727 .type{appear_arg.type},
728 .key_disable_flags{appear_arg.key_disable_flags},
729 .key_top_as_floating{swkbd_calc_arg_new.key_top_as_floating},
730 .enable_backspace_button{swkbd_calc_arg_new.enable_backspace_button},
731 .enable_return_button{appear_arg.enable_return_button},
732 .disable_cancel_button{appear_arg.disable_cancel_button},
733 };
734
735 ShowInlineKeyboard(std::move(appear_parameters));
652} 736}
653 737
654void SoftwareKeyboard::HideInlineKeyboard() { 738void SoftwareKeyboard::HideInlineKeyboard() {
@@ -693,6 +777,8 @@ void SoftwareKeyboard::RequestFinalize(const std::vector<u8>& request_data) {
693 777
694void SoftwareKeyboard::RequestSetUserWordInfo(const std::vector<u8>& request_data) { 778void SoftwareKeyboard::RequestSetUserWordInfo(const std::vector<u8>& request_data) {
695 LOG_WARNING(Service_AM, "SetUserWordInfo is not implemented."); 779 LOG_WARNING(Service_AM, "SetUserWordInfo is not implemented.");
780
781 ReplyReleasedUserWordInfo();
696} 782}
697 783
698void SoftwareKeyboard::RequestSetCustomizeDic(const std::vector<u8>& request_data) { 784void SoftwareKeyboard::RequestSetCustomizeDic(const std::vector<u8>& request_data) {
@@ -702,53 +788,135 @@ void SoftwareKeyboard::RequestSetCustomizeDic(const std::vector<u8>& request_dat
702void SoftwareKeyboard::RequestCalc(const std::vector<u8>& request_data) { 788void SoftwareKeyboard::RequestCalc(const std::vector<u8>& request_data) {
703 LOG_DEBUG(Service_AM, "Processing Request: Calc"); 789 LOG_DEBUG(Service_AM, "Processing Request: Calc");
704 790
705 ASSERT(request_data.size() == sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArg)); 791 ASSERT(request_data.size() >= sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon));
706 792
707 std::memcpy(&swkbd_calc_arg, request_data.data() + sizeof(SwkbdRequestCommand), 793 std::memcpy(&swkbd_calc_arg_common, request_data.data() + sizeof(SwkbdRequestCommand),
708 sizeof(SwkbdCalcArg)); 794 sizeof(SwkbdCalcArgCommon));
795
796 switch (swkbd_calc_arg_common.calc_arg_size) {
797 case sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgOld):
798 ASSERT(request_data.size() ==
799 sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgOld));
800 std::memcpy(&swkbd_calc_arg_old,
801 request_data.data() + sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon),
802 sizeof(SwkbdCalcArgOld));
803 RequestCalcOld();
804 break;
805 case sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgNew):
806 ASSERT(request_data.size() ==
807 sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgNew));
808 std::memcpy(&swkbd_calc_arg_new,
809 request_data.data() + sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon),
810 sizeof(SwkbdCalcArgNew));
811 RequestCalcNew();
812 break;
813 default:
814 UNIMPLEMENTED_MSG("Unknown SwkbdCalcArg size={}", swkbd_calc_arg_common.calc_arg_size);
815 ASSERT(request_data.size() >=
816 sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgNew));
817 std::memcpy(&swkbd_calc_arg_new,
818 request_data.data() + sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon),
819 sizeof(SwkbdCalcArgNew));
820 RequestCalcNew();
821 break;
822 }
823}
824
825void SoftwareKeyboard::RequestCalcOld() {
826 if (swkbd_calc_arg_common.flags.set_input_text) {
827 current_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
828 swkbd_calc_arg_old.input_text.data(), swkbd_calc_arg_old.input_text.size());
829 }
830
831 if (swkbd_calc_arg_common.flags.set_cursor_position) {
832 current_cursor_position = swkbd_calc_arg_old.cursor_position;
833 }
834
835 if (swkbd_calc_arg_common.flags.set_utf8_mode) {
836 inline_use_utf8 = swkbd_calc_arg_old.utf8_mode;
837 }
838
839 if (swkbd_state <= SwkbdState::InitializedIsHidden &&
840 swkbd_calc_arg_common.flags.unset_customize_dic) {
841 ReplyUnsetCustomizeDic();
842 }
843
844 if (swkbd_state <= SwkbdState::InitializedIsHidden &&
845 swkbd_calc_arg_common.flags.unset_user_word_info) {
846 ReplyReleasedUserWordInfo();
847 }
848
849 if (swkbd_state == SwkbdState::NotInitialized &&
850 swkbd_calc_arg_common.flags.set_initialize_arg) {
851 InitializeFrontendInlineKeyboardOld();
852
853 ChangeState(SwkbdState::InitializedIsHidden);
854
855 ReplyFinishedInitialize();
856 }
857
858 if (!swkbd_calc_arg_common.flags.set_initialize_arg &&
859 (swkbd_calc_arg_common.flags.set_input_text ||
860 swkbd_calc_arg_common.flags.set_cursor_position)) {
861 InlineTextChanged();
862 }
863
864 if (swkbd_state == SwkbdState::InitializedIsHidden && swkbd_calc_arg_common.flags.appear) {
865 ShowInlineKeyboardOld();
866 return;
867 }
868
869 if (swkbd_state == SwkbdState::InitializedIsShown && swkbd_calc_arg_common.flags.disappear) {
870 HideInlineKeyboard();
871 return;
872 }
873}
709 874
710 if (swkbd_calc_arg.flags.set_input_text) { 875void SoftwareKeyboard::RequestCalcNew() {
876 if (swkbd_calc_arg_common.flags.set_input_text) {
711 current_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( 877 current_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
712 swkbd_calc_arg.input_text.data(), swkbd_calc_arg.input_text.size()); 878 swkbd_calc_arg_new.input_text.data(), swkbd_calc_arg_new.input_text.size());
713 } 879 }
714 880
715 if (swkbd_calc_arg.flags.set_cursor_position) { 881 if (swkbd_calc_arg_common.flags.set_cursor_position) {
716 current_cursor_position = swkbd_calc_arg.cursor_position; 882 current_cursor_position = swkbd_calc_arg_new.cursor_position;
717 } 883 }
718 884
719 if (swkbd_calc_arg.flags.set_utf8_mode) { 885 if (swkbd_calc_arg_common.flags.set_utf8_mode) {
720 inline_use_utf8 = swkbd_calc_arg.utf8_mode; 886 inline_use_utf8 = swkbd_calc_arg_new.utf8_mode;
721 } 887 }
722 888
723 if (swkbd_state <= SwkbdState::InitializedIsHidden && 889 if (swkbd_state <= SwkbdState::InitializedIsHidden &&
724 swkbd_calc_arg.flags.unset_customize_dic) { 890 swkbd_calc_arg_common.flags.unset_customize_dic) {
725 ReplyUnsetCustomizeDic(); 891 ReplyUnsetCustomizeDic();
726 } 892 }
727 893
728 if (swkbd_state <= SwkbdState::InitializedIsHidden && 894 if (swkbd_state <= SwkbdState::InitializedIsHidden &&
729 swkbd_calc_arg.flags.unset_user_word_info) { 895 swkbd_calc_arg_common.flags.unset_user_word_info) {
730 ReplyReleasedUserWordInfo(); 896 ReplyReleasedUserWordInfo();
731 } 897 }
732 898
733 if (swkbd_state == SwkbdState::NotInitialized && swkbd_calc_arg.flags.set_initialize_arg) { 899 if (swkbd_state == SwkbdState::NotInitialized &&
734 InitializeFrontendKeyboard(); 900 swkbd_calc_arg_common.flags.set_initialize_arg) {
901 InitializeFrontendInlineKeyboardNew();
735 902
736 ChangeState(SwkbdState::InitializedIsHidden); 903 ChangeState(SwkbdState::InitializedIsHidden);
737 904
738 ReplyFinishedInitialize(); 905 ReplyFinishedInitialize();
739 } 906 }
740 907
741 if (!swkbd_calc_arg.flags.set_initialize_arg && 908 if (!swkbd_calc_arg_common.flags.set_initialize_arg &&
742 (swkbd_calc_arg.flags.set_input_text || swkbd_calc_arg.flags.set_cursor_position)) { 909 (swkbd_calc_arg_common.flags.set_input_text ||
910 swkbd_calc_arg_common.flags.set_cursor_position)) {
743 InlineTextChanged(); 911 InlineTextChanged();
744 } 912 }
745 913
746 if (swkbd_state == SwkbdState::InitializedIsHidden && swkbd_calc_arg.flags.appear) { 914 if (swkbd_state == SwkbdState::InitializedIsHidden && swkbd_calc_arg_common.flags.appear) {
747 ShowInlineKeyboard(); 915 ShowInlineKeyboardNew();
748 return; 916 return;
749 } 917 }
750 918
751 if (swkbd_state == SwkbdState::InitializedIsShown && swkbd_calc_arg.flags.disappear) { 919 if (swkbd_state == SwkbdState::InitializedIsShown && swkbd_calc_arg_common.flags.disappear) {
752 HideInlineKeyboard(); 920 HideInlineKeyboard();
753 return; 921 return;
754 } 922 }
diff --git a/src/core/hle/service/am/applets/applet_software_keyboard.h b/src/core/hle/service/am/applets/applet_software_keyboard.h
index a0fddd965..7ee4c5eea 100644
--- a/src/core/hle/service/am/applets/applet_software_keyboard.h
+++ b/src/core/hle/service/am/applets/applet_software_keyboard.h
@@ -13,6 +13,11 @@ namespace Core {
13class System; 13class System;
14} 14}
15 15
16namespace Core::Frontend {
17struct KeyboardInitializeParameters;
18struct InlineAppearParameters;
19} // namespace Core::Frontend
20
16namespace Service::AM::Applets { 21namespace Service::AM::Applets {
17 22
18class SoftwareKeyboard final : public Applet { 23class SoftwareKeyboard final : public Applet {
@@ -78,13 +83,22 @@ private:
78 void ChangeState(SwkbdState state); 83 void ChangeState(SwkbdState state);
79 84
80 /** 85 /**
81 * Signals the frontend to initialize the software keyboard with common parameters. 86 * Signals the frontend to initialize the normal software keyboard with common parameters.
82 * This initializes either the normal software keyboard or the inline software keyboard
83 * depending on the state of is_background.
84 * Note that this does not cause the keyboard to appear. 87 * Note that this does not cause the keyboard to appear.
85 * Use the respective Show*Keyboard() functions to cause the respective keyboards to appear. 88 * Use the ShowNormalKeyboard() functions to cause the keyboard to appear.
86 */ 89 */
87 void InitializeFrontendKeyboard(); 90 void InitializeFrontendNormalKeyboard();
91
92 /**
93 * Signals the frontend to initialize the inline software keyboard with common parameters.
94 * Note that this does not cause the keyboard to appear.
95 * Use the ShowInlineKeyboard() to cause the keyboard to appear.
96 */
97 void InitializeFrontendInlineKeyboard(
98 Core::Frontend::KeyboardInitializeParameters initialize_parameters);
99
100 void InitializeFrontendInlineKeyboardOld();
101 void InitializeFrontendInlineKeyboardNew();
88 102
89 /// Signals the frontend to show the normal software keyboard. 103 /// Signals the frontend to show the normal software keyboard.
90 void ShowNormalKeyboard(); 104 void ShowNormalKeyboard();
@@ -94,7 +108,10 @@ private:
94 std::u16string text_check_message); 108 std::u16string text_check_message);
95 109
96 /// Signals the frontend to show the inline software keyboard. 110 /// Signals the frontend to show the inline software keyboard.
97 void ShowInlineKeyboard(); 111 void ShowInlineKeyboard(Core::Frontend::InlineAppearParameters appear_parameters);
112
113 void ShowInlineKeyboardOld();
114 void ShowInlineKeyboardNew();
98 115
99 /// Signals the frontend to hide the inline software keyboard. 116 /// Signals the frontend to hide the inline software keyboard.
100 void HideInlineKeyboard(); 117 void HideInlineKeyboard();
@@ -111,6 +128,8 @@ private:
111 void RequestSetUserWordInfo(const std::vector<u8>& request_data); 128 void RequestSetUserWordInfo(const std::vector<u8>& request_data);
112 void RequestSetCustomizeDic(const std::vector<u8>& request_data); 129 void RequestSetCustomizeDic(const std::vector<u8>& request_data);
113 void RequestCalc(const std::vector<u8>& request_data); 130 void RequestCalc(const std::vector<u8>& request_data);
131 void RequestCalcOld();
132 void RequestCalcNew();
114 void RequestSetCustomizedDictionaries(const std::vector<u8>& request_data); 133 void RequestSetCustomizedDictionaries(const std::vector<u8>& request_data);
115 void RequestUnsetCustomizedDictionaries(const std::vector<u8>& request_data); 134 void RequestUnsetCustomizedDictionaries(const std::vector<u8>& request_data);
116 void RequestSetChangedStringV2Flag(const std::vector<u8>& request_data); 135 void RequestSetChangedStringV2Flag(const std::vector<u8>& request_data);
@@ -149,7 +168,9 @@ private:
149 168
150 SwkbdState swkbd_state{SwkbdState::NotInitialized}; 169 SwkbdState swkbd_state{SwkbdState::NotInitialized};
151 SwkbdInitializeArg swkbd_initialize_arg; 170 SwkbdInitializeArg swkbd_initialize_arg;
152 SwkbdCalcArg swkbd_calc_arg; 171 SwkbdCalcArgCommon swkbd_calc_arg_common;
172 SwkbdCalcArgOld swkbd_calc_arg_old;
173 SwkbdCalcArgNew swkbd_calc_arg_new;
153 bool use_changed_string_v2{false}; 174 bool use_changed_string_v2{false};
154 bool use_moved_cursor_v2{false}; 175 bool use_moved_cursor_v2{false};
155 bool inline_use_utf8{false}; 176 bool inline_use_utf8{false};
diff --git a/src/core/hle/service/am/applets/applet_software_keyboard_types.h b/src/core/hle/service/am/applets/applet_software_keyboard_types.h
index 21aa8e800..de1ca7aa5 100644
--- a/src/core/hle/service/am/applets/applet_software_keyboard_types.h
+++ b/src/core/hle/service/am/applets/applet_software_keyboard_types.h
@@ -10,6 +10,7 @@
10#include "common/common_funcs.h" 10#include "common/common_funcs.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/swap.h" 12#include "common/swap.h"
13#include "common/uuid.h"
13 14
14namespace Service::AM::Applets { 15namespace Service::AM::Applets {
15 16
@@ -216,7 +217,7 @@ struct SwkbdInitializeArg {
216}; 217};
217static_assert(sizeof(SwkbdInitializeArg) == 0x8, "SwkbdInitializeArg has incorrect size."); 218static_assert(sizeof(SwkbdInitializeArg) == 0x8, "SwkbdInitializeArg has incorrect size.");
218 219
219struct SwkbdAppearArg { 220struct SwkbdAppearArgOld {
220 SwkbdType type{}; 221 SwkbdType type{};
221 std::array<char16_t, MAX_OK_TEXT_LENGTH + 1> ok_text{}; 222 std::array<char16_t, MAX_OK_TEXT_LENGTH + 1> ok_text{};
222 char16_t left_optional_symbol_key{}; 223 char16_t left_optional_symbol_key{};
@@ -229,19 +230,76 @@ struct SwkbdAppearArg {
229 bool enable_return_button{}; 230 bool enable_return_button{};
230 INSERT_PADDING_BYTES(3); 231 INSERT_PADDING_BYTES(3);
231 u32 flags{}; 232 u32 flags{};
232 INSERT_PADDING_WORDS(6); 233 bool is_use_save_data{};
234 INSERT_PADDING_BYTES(7);
235 Common::UUID user_id{};
233}; 236};
234static_assert(sizeof(SwkbdAppearArg) == 0x48, "SwkbdAppearArg has incorrect size."); 237static_assert(sizeof(SwkbdAppearArgOld) == 0x48, "SwkbdAppearArg has incorrect size.");
235 238
236struct SwkbdCalcArg { 239struct SwkbdAppearArgNew {
240 SwkbdType type{};
241 std::array<char16_t, MAX_OK_TEXT_LENGTH + 1> ok_text{};
242 char16_t left_optional_symbol_key{};
243 char16_t right_optional_symbol_key{};
244 bool use_prediction{};
245 bool disable_cancel_button{};
246 SwkbdKeyDisableFlags key_disable_flags{};
247 u32 max_text_length{};
248 u32 min_text_length{};
249 bool enable_return_button{};
250 INSERT_PADDING_BYTES(3);
251 u32 flags{};
252 bool is_use_save_data{};
253 INSERT_PADDING_BYTES(7);
254 Common::UUID user_id{};
255 u64 start_sampling_number{};
256 INSERT_PADDING_WORDS(8);
257};
258static_assert(sizeof(SwkbdAppearArgNew) == 0x70, "SwkbdAppearArg has incorrect size.");
259
260struct SwkbdCalcArgCommon {
237 u32 unknown{}; 261 u32 unknown{};
238 u16 calc_arg_size{}; 262 u16 calc_arg_size{};
239 INSERT_PADDING_BYTES(2); 263 INSERT_PADDING_BYTES(2);
240 SwkbdCalcArgFlags flags{}; 264 SwkbdCalcArgFlags flags{};
241 SwkbdInitializeArg initialize_arg{}; 265 SwkbdInitializeArg initialize_arg{};
266};
267static_assert(sizeof(SwkbdCalcArgCommon) == 0x18, "SwkbdCalcArgCommon has incorrect size.");
268
269struct SwkbdCalcArgOld {
270 f32 volume{};
271 s32 cursor_position{};
272 SwkbdAppearArgOld appear_arg{};
273 std::array<char16_t, 0x1FA> input_text{};
274 bool utf8_mode{};
275 INSERT_PADDING_BYTES(1);
276 bool enable_backspace_button{};
277 INSERT_PADDING_BYTES(3);
278 bool key_top_as_floating{};
279 bool footer_scalable{};
280 bool alpha_enabled_in_input_mode{};
281 u8 input_mode_fade_type{};
282 bool disable_touch{};
283 bool disable_hardware_keyboard{};
284 INSERT_PADDING_BYTES(8);
285 f32 key_top_scale_x{};
286 f32 key_top_scale_y{};
287 f32 key_top_translate_x{};
288 f32 key_top_translate_y{};
289 f32 key_top_bg_alpha{};
290 f32 footer_bg_alpha{};
291 f32 balloon_scale{};
292 INSERT_PADDING_WORDS(4);
293 u8 se_group{};
294 INSERT_PADDING_BYTES(3);
295};
296static_assert(sizeof(SwkbdCalcArgOld) == 0x4A0 - sizeof(SwkbdCalcArgCommon),
297 "SwkbdCalcArgOld has incorrect size.");
298
299struct SwkbdCalcArgNew {
300 SwkbdAppearArgNew appear_arg{};
242 f32 volume{}; 301 f32 volume{};
243 s32 cursor_position{}; 302 s32 cursor_position{};
244 SwkbdAppearArg appear_arg{};
245 std::array<char16_t, 0x1FA> input_text{}; 303 std::array<char16_t, 0x1FA> input_text{};
246 bool utf8_mode{}; 304 bool utf8_mode{};
247 INSERT_PADDING_BYTES(1); 305 INSERT_PADDING_BYTES(1);
@@ -264,8 +322,10 @@ struct SwkbdCalcArg {
264 INSERT_PADDING_WORDS(4); 322 INSERT_PADDING_WORDS(4);
265 u8 se_group{}; 323 u8 se_group{};
266 INSERT_PADDING_BYTES(3); 324 INSERT_PADDING_BYTES(3);
325 INSERT_PADDING_WORDS(8);
267}; 326};
268static_assert(sizeof(SwkbdCalcArg) == 0x4A0, "SwkbdCalcArg has incorrect size."); 327static_assert(sizeof(SwkbdCalcArgNew) == 0x4E8 - sizeof(SwkbdCalcArgCommon),
328 "SwkbdCalcArgNew has incorrect size.");
269 329
270struct SwkbdChangedStringArg { 330struct SwkbdChangedStringArg {
271 u32 text_length{}; 331 u32 text_length{};
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index d9202ea6c..b2cec2253 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -878,6 +878,10 @@ void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
878 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}", 878 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}",
879 parameters.npad_id, parameters.applet_resource_user_id, parameters.unknown); 879 parameters.npad_id, parameters.applet_resource_user_id, parameters.unknown);
880 880
881 // Games expect this event to be signaled after calling this function
882 applet_resource->GetController<Controller_NPad>(HidController::NPad)
883 .SignalStyleSetChangedEvent(parameters.npad_id);
884
881 IPC::ResponseBuilder rb{ctx, 2, 1}; 885 IPC::ResponseBuilder rb{ctx, 2, 1};
882 rb.Push(ResultSuccess); 886 rb.Push(ResultSuccess);
883 rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad) 887 rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad)
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
index 68f1e9060..9fad45fe1 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
@@ -38,18 +38,16 @@ NvResult nvdisp_disp0::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>&
38void nvdisp_disp0::OnOpen(DeviceFD fd) {} 38void nvdisp_disp0::OnOpen(DeviceFD fd) {}
39void nvdisp_disp0::OnClose(DeviceFD fd) {} 39void nvdisp_disp0::OnClose(DeviceFD fd) {}
40 40
41void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, 41void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width,
42 u32 stride, NVFlinger::BufferQueue::BufferTransformFlags transform, 42 u32 height, u32 stride, android::BufferTransformFlags transform,
43 const Common::Rectangle<int>& crop_rect) { 43 const Common::Rectangle<int>& crop_rect) {
44 const VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle); 44 const VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle);
45 LOG_TRACE(Service, 45 LOG_TRACE(Service,
46 "Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}", 46 "Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}",
47 addr, offset, width, height, stride, format); 47 addr, offset, width, height, stride, format);
48 48
49 const auto pixel_format = static_cast<Tegra::FramebufferConfig::PixelFormat>(format); 49 const Tegra::FramebufferConfig framebuffer{addr, offset, width, height,
50 const auto transform_flags = static_cast<Tegra::FramebufferConfig::TransformFlags>(transform); 50 stride, format, transform, crop_rect};
51 const Tegra::FramebufferConfig framebuffer{addr, offset, width, height,
52 stride, pixel_format, transform_flags, crop_rect};
53 51
54 system.GetPerfStats().EndSystemFrame(); 52 system.GetPerfStats().EndSystemFrame();
55 system.GPU().SwapBuffers(&framebuffer); 53 system.GPU().SwapBuffers(&framebuffer);
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
index de01e1d5f..30b5da429 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
@@ -9,7 +9,8 @@
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/math_util.h" 10#include "common/math_util.h"
11#include "core/hle/service/nvdrv/devices/nvdevice.h" 11#include "core/hle/service/nvdrv/devices/nvdevice.h"
12#include "core/hle/service/nvflinger/buffer_queue.h" 12#include "core/hle/service/nvflinger/buffer_transform_flags.h"
13#include "core/hle/service/nvflinger/pixel_format.h"
13 14
14namespace Service::Nvidia::Devices { 15namespace Service::Nvidia::Devices {
15 16
@@ -31,8 +32,8 @@ public:
31 void OnClose(DeviceFD fd) override; 32 void OnClose(DeviceFD fd) override;
32 33
33 /// Performs a screen flip, drawing the buffer pointed to by the handle. 34 /// Performs a screen flip, drawing the buffer pointed to by the handle.
34 void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride, 35 void flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width, u32 height,
35 NVFlinger::BufferQueue::BufferTransformFlags transform, 36 u32 stride, android::BufferTransformFlags transform,
36 const Common::Rectangle<int>& crop_rect); 37 const Common::Rectangle<int>& crop_rect);
37 38
38private: 39private:
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index 0a043e386..dde5b1507 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -187,7 +187,7 @@ NvResult nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::ve
187 return NvResult::Success; 187 return NvResult::Success;
188} 188}
189 189
190static std::vector<Tegra::CommandHeader> BuildWaitCommandList(Fence fence) { 190static std::vector<Tegra::CommandHeader> BuildWaitCommandList(NvFence fence) {
191 return { 191 return {
192 Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1, 192 Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1,
193 Tegra::SubmissionMode::Increasing), 193 Tegra::SubmissionMode::Increasing),
@@ -198,7 +198,8 @@ static std::vector<Tegra::CommandHeader> BuildWaitCommandList(Fence fence) {
198 }; 198 };
199} 199}
200 200
201static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(Fence fence, u32 add_increment) { 201static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(NvFence fence,
202 u32 add_increment) {
202 std::vector<Tegra::CommandHeader> result{ 203 std::vector<Tegra::CommandHeader> result{
203 Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1, 204 Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1,
204 Tegra::SubmissionMode::Increasing), 205 Tegra::SubmissionMode::Increasing),
@@ -213,7 +214,7 @@ static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(Fence fence,
213 return result; 214 return result;
214} 215}
215 216
216static std::vector<Tegra::CommandHeader> BuildIncrementWithWfiCommandList(Fence fence, 217static std::vector<Tegra::CommandHeader> BuildIncrementWithWfiCommandList(NvFence fence,
217 u32 add_increment) { 218 u32 add_increment) {
218 std::vector<Tegra::CommandHeader> result{ 219 std::vector<Tegra::CommandHeader> result{
219 Tegra::BuildCommandHeader(Tegra::BufferMethods::WaitForInterrupt, 1, 220 Tegra::BuildCommandHeader(Tegra::BufferMethods::WaitForInterrupt, 1,
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
index 3e4f3b6a7..b2e943e45 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
@@ -109,7 +109,7 @@ private:
109 static_assert(sizeof(IoctlGetErrorNotification) == 16, 109 static_assert(sizeof(IoctlGetErrorNotification) == 16,
110 "IoctlGetErrorNotification is incorrect size"); 110 "IoctlGetErrorNotification is incorrect size");
111 111
112 static_assert(sizeof(Fence) == 8, "Fence is incorrect size"); 112 static_assert(sizeof(NvFence) == 8, "Fence is incorrect size");
113 113
114 struct IoctlAllocGpfifoEx { 114 struct IoctlAllocGpfifoEx {
115 u32_le num_entries{}; 115 u32_le num_entries{};
@@ -127,7 +127,7 @@ private:
127 u32_le num_entries{}; // in 127 u32_le num_entries{}; // in
128 u32_le flags{}; // in 128 u32_le flags{}; // in
129 u32_le unk0{}; // in (1 works) 129 u32_le unk0{}; // in (1 works)
130 Fence fence_out{}; // out 130 NvFence fence_out{}; // out
131 u32_le unk1{}; // in 131 u32_le unk1{}; // in
132 u32_le unk2{}; // in 132 u32_le unk2{}; // in
133 u32_le unk3{}; // in 133 u32_le unk3{}; // in
@@ -153,13 +153,13 @@ private:
153 BitField<4, 1, u32_le> suppress_wfi; // suppress wait for interrupt 153 BitField<4, 1, u32_le> suppress_wfi; // suppress wait for interrupt
154 BitField<8, 1, u32_le> increment; // increment the returned fence 154 BitField<8, 1, u32_le> increment; // increment the returned fence
155 } flags; 155 } flags;
156 Fence fence_out{}; // returned new fence object for others to wait on 156 NvFence fence_out{}; // returned new fence object for others to wait on
157 157
158 u32 AddIncrementValue() const { 158 u32 AddIncrementValue() const {
159 return flags.add_increment.Value() << 1; 159 return flags.add_increment.Value() << 1;
160 } 160 }
161 }; 161 };
162 static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(Fence), 162 static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(NvFence),
163 "IoctlSubmitGpfifo is incorrect size"); 163 "IoctlSubmitGpfifo is incorrect size");
164 164
165 struct IoctlGetWaitbase { 165 struct IoctlGetWaitbase {
@@ -194,7 +194,7 @@ private:
194 194
195 std::shared_ptr<nvmap> nvmap_dev; 195 std::shared_ptr<nvmap> nvmap_dev;
196 SyncpointManager& syncpoint_manager; 196 SyncpointManager& syncpoint_manager;
197 Fence channel_fence; 197 NvFence channel_fence;
198}; 198};
199 199
200} // namespace Service::Nvidia::Devices 200} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/nvdata.h b/src/core/hle/service/nvdrv/nvdata.h
index 5ab221fc1..3069c3c80 100644
--- a/src/core/hle/service/nvdrv/nvdata.h
+++ b/src/core/hle/service/nvdrv/nvdata.h
@@ -16,17 +16,11 @@ using DeviceFD = s32;
16 16
17constexpr DeviceFD INVALID_NVDRV_FD = -1; 17constexpr DeviceFD INVALID_NVDRV_FD = -1;
18 18
19struct Fence { 19struct NvFence {
20 s32 id; 20 s32 id;
21 u32 value; 21 u32 value;
22}; 22};
23 23static_assert(sizeof(NvFence) == 8, "NvFence has wrong size");
24static_assert(sizeof(Fence) == 8, "Fence has wrong size");
25
26struct MultiFence {
27 u32 num_fences;
28 std::array<Fence, 4> fences;
29};
30 24
31enum class NvResult : u32 { 25enum class NvResult : u32 {
32 Success = 0x0, 26 Success = 0x0,
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h
index a5af5b785..11cf6c0d1 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -12,6 +12,7 @@
12#include "core/hle/service/kernel_helpers.h" 12#include "core/hle/service/kernel_helpers.h"
13#include "core/hle/service/nvdrv/nvdata.h" 13#include "core/hle/service/nvdrv/nvdata.h"
14#include "core/hle/service/nvdrv/syncpoint_manager.h" 14#include "core/hle/service/nvdrv/syncpoint_manager.h"
15#include "core/hle/service/nvflinger/ui/fence.h"
15#include "core/hle/service/service.h" 16#include "core/hle/service/service.h"
16 17
17namespace Core { 18namespace Core {
@@ -37,7 +38,7 @@ class nvdevice;
37/// Represents an Nvidia event 38/// Represents an Nvidia event
38struct NvEvent { 39struct NvEvent {
39 Kernel::KEvent* event{}; 40 Kernel::KEvent* event{};
40 Fence fence{}; 41 NvFence fence{};
41}; 42};
42 43
43struct EventInterface { 44struct EventInterface {
diff --git a/src/core/hle/service/nvflinger/binder.h b/src/core/hle/service/nvflinger/binder.h
new file mode 100644
index 000000000..7d0d4d819
--- /dev/null
+++ b/src/core/hle/service/nvflinger/binder.h
@@ -0,0 +1,42 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/binder/IBinder.h
6
7#pragma once
8
9#include "common/common_types.h"
10
11namespace Kernel {
12class HLERequestContext;
13class KReadableEvent;
14} // namespace Kernel
15
16namespace Service::android {
17
18enum class TransactionId {
19 RequestBuffer = 1,
20 SetBufferCount = 2,
21 DequeueBuffer = 3,
22 DetachBuffer = 4,
23 DetachNextBuffer = 5,
24 AttachBuffer = 6,
25 QueueBuffer = 7,
26 CancelBuffer = 8,
27 Query = 9,
28 Connect = 10,
29 Disconnect = 11,
30 AllocateBuffers = 13,
31 SetPreallocatedBuffer = 14,
32 GetBufferHistory = 17,
33};
34
35class IBinder {
36public:
37 virtual void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code,
38 u32 flags) = 0;
39 virtual Kernel::KReadableEvent& GetNativeHandle() = 0;
40};
41
42} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_item.h b/src/core/hle/service/nvflinger/buffer_item.h
new file mode 100644
index 000000000..64b82b851
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_item.h
@@ -0,0 +1,46 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferItem.h
6
7#pragma once
8
9#include <memory>
10
11#include "common/common_types.h"
12#include "common/math_util.h"
13#include "core/hle/service/nvflinger/ui/fence.h"
14#include "core/hle/service/nvflinger/window.h"
15
16namespace Service::android {
17
18class GraphicBuffer;
19
20class BufferItem final {
21public:
22 constexpr BufferItem() = default;
23
24 std::shared_ptr<GraphicBuffer> graphic_buffer;
25 Fence fence;
26 Common::Rectangle<s32> crop;
27 NativeWindowTransform transform{};
28 u32 scaling_mode{};
29 s64 timestamp{};
30 bool is_auto_timestamp{};
31 u64 frame_number{};
32
33 // The default value for buf, used to indicate this doesn't correspond to a slot.
34 static constexpr s32 INVALID_BUFFER_SLOT = -1;
35 union {
36 s32 slot{INVALID_BUFFER_SLOT};
37 s32 buf;
38 };
39
40 bool is_droppable{};
41 bool acquire_called{};
42 bool transform_to_display_inverse{};
43 s32 swap_interval{};
44};
45
46} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_item_consumer.cpp b/src/core/hle/service/nvflinger/buffer_item_consumer.cpp
new file mode 100644
index 000000000..93fa1ec10
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_item_consumer.cpp
@@ -0,0 +1,59 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2012 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferItemConsumer.cpp
6
7#include "common/assert.h"
8#include "common/logging/log.h"
9#include "core/hle/service/nvflinger/buffer_item.h"
10#include "core/hle/service/nvflinger/buffer_item_consumer.h"
11#include "core/hle/service/nvflinger/buffer_queue_consumer.h"
12
13namespace Service::android {
14
15BufferItemConsumer::BufferItemConsumer(std::unique_ptr<BufferQueueConsumer> consumer_)
16 : ConsumerBase{std::move(consumer_)} {}
17
18Status BufferItemConsumer::AcquireBuffer(BufferItem* item, std::chrono::nanoseconds present_when,
19 bool wait_for_fence) {
20 if (!item) {
21 return Status::BadValue;
22 }
23
24 std::scoped_lock lock(mutex);
25
26 if (const auto status = AcquireBufferLocked(item, present_when); status != Status::NoError) {
27 if (status != Status::NoBufferAvailable) {
28 LOG_ERROR(Service_NVFlinger, "Failed to acquire buffer: {}", status);
29 }
30 return status;
31 }
32
33 if (wait_for_fence) {
34 UNIMPLEMENTED();
35 }
36
37 item->graphic_buffer = slots[item->slot].graphic_buffer;
38
39 return Status::NoError;
40}
41
42Status BufferItemConsumer::ReleaseBuffer(const BufferItem& item, Fence& release_fence) {
43 std::scoped_lock lock(mutex);
44
45 if (const auto status = AddReleaseFenceLocked(item.buf, item.graphic_buffer, release_fence);
46 status != Status::NoError) {
47 LOG_ERROR(Service_NVFlinger, "Failed to add fence: {}", status);
48 }
49
50 if (const auto status = ReleaseBufferLocked(item.buf, item.graphic_buffer);
51 status != Status::NoError) {
52 LOG_WARNING(Service_NVFlinger, "Failed to release buffer: {}", status);
53 return status;
54 }
55
56 return Status::NoError;
57}
58
59} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_item_consumer.h b/src/core/hle/service/nvflinger/buffer_item_consumer.h
new file mode 100644
index 000000000..536db81e2
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_item_consumer.h
@@ -0,0 +1,28 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2012 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferItemConsumer.h
6
7#pragma once
8
9#include <chrono>
10#include <memory>
11
12#include "common/common_types.h"
13#include "core/hle/service/nvflinger/consumer_base.h"
14#include "core/hle/service/nvflinger/status.h"
15
16namespace Service::android {
17
18class BufferItem;
19
20class BufferItemConsumer final : public ConsumerBase {
21public:
22 explicit BufferItemConsumer(std::unique_ptr<BufferQueueConsumer> consumer);
23 Status AcquireBuffer(BufferItem* item, std::chrono::nanoseconds present_when,
24 bool wait_for_fence = true);
25 Status ReleaseBuffer(const BufferItem& item, Fence& release_fence);
26};
27
28} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp
deleted file mode 100644
index 5fead6d1b..000000000
--- a/src/core/hle/service/nvflinger/buffer_queue.cpp
+++ /dev/null
@@ -1,206 +0,0 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6
7#include "common/assert.h"
8#include "common/logging/log.h"
9#include "core/core.h"
10#include "core/hle/kernel/k_writable_event.h"
11#include "core/hle/kernel/kernel.h"
12#include "core/hle/service/kernel_helpers.h"
13#include "core/hle/service/nvflinger/buffer_queue.h"
14
15namespace Service::NVFlinger {
16
17BufferQueue::BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_,
18 KernelHelpers::ServiceContext& service_context_)
19 : id(id_), layer_id(layer_id_), service_context{service_context_} {
20 buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent");
21}
22
23BufferQueue::~BufferQueue() {
24 service_context.CloseEvent(buffer_wait_event);
25}
26
27void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) {
28 ASSERT(slot < buffer_slots);
29 LOG_WARNING(Service, "Adding graphics buffer {}", slot);
30
31 {
32 std::unique_lock lock{free_buffers_mutex};
33 free_buffers.push_back(slot);
34 }
35 free_buffers_condition.notify_one();
36
37 buffers[slot] = {
38 .slot = slot,
39 .status = Buffer::Status::Free,
40 .igbp_buffer = igbp_buffer,
41 .transform = {},
42 .crop_rect = {},
43 .swap_interval = 0,
44 .multi_fence = {},
45 };
46
47 buffer_wait_event->GetWritableEvent().Signal();
48}
49
50std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::DequeueBuffer(u32 width,
51 u32 height) {
52 // Wait for first request before trying to dequeue
53 {
54 std::unique_lock lock{free_buffers_mutex};
55 free_buffers_condition.wait(lock, [this] { return !free_buffers.empty() || !is_connect; });
56 }
57
58 if (!is_connect) {
59 // Buffer was disconnected while the thread was blocked, this is most likely due to
60 // emulation being stopped
61 return std::nullopt;
62 }
63
64 std::unique_lock lock{free_buffers_mutex};
65
66 auto f_itr = free_buffers.begin();
67 auto slot = buffers.size();
68
69 while (f_itr != free_buffers.end()) {
70 const Buffer& buffer = buffers[*f_itr];
71 if (buffer.status == Buffer::Status::Free && buffer.igbp_buffer.width == width &&
72 buffer.igbp_buffer.height == height) {
73 slot = *f_itr;
74 free_buffers.erase(f_itr);
75 break;
76 }
77 ++f_itr;
78 }
79 if (slot == buffers.size()) {
80 return std::nullopt;
81 }
82 buffers[slot].status = Buffer::Status::Dequeued;
83 return {{buffers[slot].slot, &buffers[slot].multi_fence}};
84}
85
86const IGBPBuffer& BufferQueue::RequestBuffer(u32 slot) const {
87 ASSERT(slot < buffers.size());
88 ASSERT(buffers[slot].status == Buffer::Status::Dequeued);
89 ASSERT(buffers[slot].slot == slot);
90
91 return buffers[slot].igbp_buffer;
92}
93
94void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform,
95 const Common::Rectangle<int>& crop_rect, u32 swap_interval,
96 Service::Nvidia::MultiFence& multi_fence) {
97 ASSERT(slot < buffers.size());
98 ASSERT(buffers[slot].status == Buffer::Status::Dequeued);
99 ASSERT(buffers[slot].slot == slot);
100
101 buffers[slot].status = Buffer::Status::Queued;
102 buffers[slot].transform = transform;
103 buffers[slot].crop_rect = crop_rect;
104 buffers[slot].swap_interval = swap_interval;
105 buffers[slot].multi_fence = multi_fence;
106 std::unique_lock lock{queue_sequence_mutex};
107 queue_sequence.push_back(slot);
108}
109
110void BufferQueue::CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& multi_fence) {
111 ASSERT(slot < buffers.size());
112 ASSERT(buffers[slot].status != Buffer::Status::Free);
113 ASSERT(buffers[slot].slot == slot);
114
115 buffers[slot].status = Buffer::Status::Free;
116 buffers[slot].multi_fence = multi_fence;
117 buffers[slot].swap_interval = 0;
118
119 {
120 std::unique_lock lock{free_buffers_mutex};
121 free_buffers.push_back(slot);
122 }
123 free_buffers_condition.notify_one();
124
125 buffer_wait_event->GetWritableEvent().Signal();
126}
127
128std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() {
129 std::unique_lock lock{queue_sequence_mutex};
130 std::size_t buffer_slot = buffers.size();
131 // Iterate to find a queued buffer matching the requested slot.
132 while (buffer_slot == buffers.size() && !queue_sequence.empty()) {
133 const auto slot = static_cast<std::size_t>(queue_sequence.front());
134 ASSERT(slot < buffers.size());
135 if (buffers[slot].status == Buffer::Status::Queued) {
136 ASSERT(buffers[slot].slot == slot);
137 buffer_slot = slot;
138 }
139 queue_sequence.pop_front();
140 }
141 if (buffer_slot == buffers.size()) {
142 return std::nullopt;
143 }
144 buffers[buffer_slot].status = Buffer::Status::Acquired;
145 return {{buffers[buffer_slot]}};
146}
147
148void BufferQueue::ReleaseBuffer(u32 slot) {
149 ASSERT(slot < buffers.size());
150 ASSERT(buffers[slot].status == Buffer::Status::Acquired);
151 ASSERT(buffers[slot].slot == slot);
152
153 buffers[slot].status = Buffer::Status::Free;
154 {
155 std::unique_lock lock{free_buffers_mutex};
156 free_buffers.push_back(slot);
157 }
158 free_buffers_condition.notify_one();
159
160 buffer_wait_event->GetWritableEvent().Signal();
161}
162
163void BufferQueue::Connect() {
164 std::unique_lock lock{queue_sequence_mutex};
165 queue_sequence.clear();
166 is_connect = true;
167}
168
169void BufferQueue::Disconnect() {
170 buffers.fill({});
171 {
172 std::unique_lock lock{queue_sequence_mutex};
173 queue_sequence.clear();
174 }
175 buffer_wait_event->GetWritableEvent().Signal();
176 is_connect = false;
177 free_buffers_condition.notify_one();
178}
179
180u32 BufferQueue::Query(QueryType type) {
181 LOG_WARNING(Service, "(STUBBED) called type={}", type);
182
183 switch (type) {
184 case QueryType::NativeWindowFormat:
185 return static_cast<u32>(PixelFormat::RGBA8888);
186 case QueryType::NativeWindowWidth:
187 case QueryType::NativeWindowHeight:
188 break;
189 case QueryType::NativeWindowMinUndequeuedBuffers:
190 return 0;
191 case QueryType::NativeWindowConsumerUsageBits:
192 return 0;
193 }
194 UNIMPLEMENTED_MSG("Unimplemented query type={}", type);
195 return 0;
196}
197
198Kernel::KWritableEvent& BufferQueue::GetWritableBufferWaitEvent() {
199 return buffer_wait_event->GetWritableEvent();
200}
201
202Kernel::KReadableEvent& BufferQueue::GetBufferWaitEvent() {
203 return buffer_wait_event->GetReadableEvent();
204}
205
206} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h
deleted file mode 100644
index f2a579133..000000000
--- a/src/core/hle/service/nvflinger/buffer_queue.h
+++ /dev/null
@@ -1,154 +0,0 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <condition_variable>
8#include <list>
9#include <mutex>
10#include <optional>
11
12#include "common/common_funcs.h"
13#include "common/math_util.h"
14#include "common/swap.h"
15#include "core/hle/kernel/k_event.h"
16#include "core/hle/kernel/k_readable_event.h"
17#include "core/hle/service/nvdrv/nvdata.h"
18
19namespace Kernel {
20class KernelCore;
21class KEvent;
22class KReadableEvent;
23class KWritableEvent;
24} // namespace Kernel
25
26namespace Service::KernelHelpers {
27class ServiceContext;
28} // namespace Service::KernelHelpers
29
30namespace Service::NVFlinger {
31
32constexpr u32 buffer_slots = 0x40;
33struct IGBPBuffer {
34 u32_le magic;
35 u32_le width;
36 u32_le height;
37 u32_le stride;
38 u32_le format;
39 u32_le usage;
40 INSERT_PADDING_WORDS(1);
41 u32_le index;
42 INSERT_PADDING_WORDS(3);
43 u32_le gpu_buffer_id;
44 INSERT_PADDING_WORDS(6);
45 u32_le external_format;
46 INSERT_PADDING_WORDS(10);
47 u32_le nvmap_handle;
48 u32_le offset;
49 INSERT_PADDING_WORDS(60);
50};
51
52static_assert(sizeof(IGBPBuffer) == 0x16C, "IGBPBuffer has wrong size");
53
54class BufferQueue final {
55public:
56 enum class QueryType {
57 NativeWindowWidth = 0,
58 NativeWindowHeight = 1,
59 NativeWindowFormat = 2,
60 /// The minimum number of buffers that must remain un-dequeued after a buffer has been
61 /// queued
62 NativeWindowMinUndequeuedBuffers = 3,
63 /// The consumer gralloc usage bits currently set by the consumer
64 NativeWindowConsumerUsageBits = 10,
65 };
66
67 explicit BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_,
68 KernelHelpers::ServiceContext& service_context_);
69 ~BufferQueue();
70
71 enum class BufferTransformFlags : u32 {
72 /// No transform flags are set
73 Unset = 0x00,
74 /// Flip source image horizontally (around the vertical axis)
75 FlipH = 0x01,
76 /// Flip source image vertically (around the horizontal axis)
77 FlipV = 0x02,
78 /// Rotate source image 90 degrees clockwise
79 Rotate90 = 0x04,
80 /// Rotate source image 180 degrees
81 Rotate180 = 0x03,
82 /// Rotate source image 270 degrees clockwise
83 Rotate270 = 0x07,
84 };
85
86 enum class PixelFormat : u32 {
87 RGBA8888 = 1,
88 RGBX8888 = 2,
89 RGB888 = 3,
90 RGB565 = 4,
91 BGRA8888 = 5,
92 RGBA5551 = 6,
93 RRGBA4444 = 7,
94 };
95
96 struct Buffer {
97 enum class Status { Free = 0, Queued = 1, Dequeued = 2, Acquired = 3 };
98
99 u32 slot;
100 Status status = Status::Free;
101 IGBPBuffer igbp_buffer;
102 BufferTransformFlags transform;
103 Common::Rectangle<int> crop_rect;
104 u32 swap_interval;
105 Service::Nvidia::MultiFence multi_fence;
106 };
107
108 void SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer);
109 std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> DequeueBuffer(u32 width,
110 u32 height);
111 const IGBPBuffer& RequestBuffer(u32 slot) const;
112 void QueueBuffer(u32 slot, BufferTransformFlags transform,
113 const Common::Rectangle<int>& crop_rect, u32 swap_interval,
114 Service::Nvidia::MultiFence& multi_fence);
115 void CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& multi_fence);
116 std::optional<std::reference_wrapper<const Buffer>> AcquireBuffer();
117 void ReleaseBuffer(u32 slot);
118 void Connect();
119 void Disconnect();
120 u32 Query(QueryType type);
121
122 u32 GetId() const {
123 return id;
124 }
125
126 bool IsConnected() const {
127 return is_connect;
128 }
129
130 Kernel::KWritableEvent& GetWritableBufferWaitEvent();
131
132 Kernel::KReadableEvent& GetBufferWaitEvent();
133
134private:
135 BufferQueue(const BufferQueue&) = delete;
136
137 u32 id{};
138 u64 layer_id{};
139 std::atomic_bool is_connect{};
140
141 std::list<u32> free_buffers;
142 std::array<Buffer, buffer_slots> buffers;
143 std::list<u32> queue_sequence;
144 Kernel::KEvent* buffer_wait_event{};
145
146 std::mutex free_buffers_mutex;
147 std::condition_variable free_buffers_condition;
148
149 std::mutex queue_sequence_mutex;
150
151 KernelHelpers::ServiceContext& service_context;
152};
153
154} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp
new file mode 100644
index 000000000..41fbba219
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp
@@ -0,0 +1,203 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueConsumer.cpp
6
7#include "common/logging/log.h"
8#include "core/hle/service/nvflinger/buffer_item.h"
9#include "core/hle/service/nvflinger/buffer_queue_consumer.h"
10#include "core/hle/service/nvflinger/buffer_queue_core.h"
11#include "core/hle/service/nvflinger/producer_listener.h"
12
13namespace Service::android {
14
15BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_)
16 : core{std::move(core_)}, slots{core->slots} {}
17
18BufferQueueConsumer::~BufferQueueConsumer() = default;
19
20Status BufferQueueConsumer::AcquireBuffer(BufferItem* out_buffer,
21 std::chrono::nanoseconds expected_present,
22 u64 max_frame_number) {
23 std::scoped_lock lock(core->mutex);
24
25 // Check that the consumer doesn't currently have the maximum number of buffers acquired.
26 const s32 num_acquired_buffers{
27 static_cast<s32>(std::count_if(slots.begin(), slots.end(), [](const auto& slot) {
28 return slot.buffer_state == BufferState::Acquired;
29 }))};
30
31 if (num_acquired_buffers >= core->max_acquired_buffer_count + 1) {
32 LOG_ERROR(Service_NVFlinger, "max acquired buffer count reached: {} (max {})",
33 num_acquired_buffers, core->max_acquired_buffer_count);
34 return Status::InvalidOperation;
35 }
36
37 // Check if the queue is empty.
38 if (core->queue.empty()) {
39 return Status::NoBufferAvailable;
40 }
41
42 auto front(core->queue.begin());
43
44 // If expected_present is specified, we may not want to return a buffer yet.
45 if (expected_present.count() != 0) {
46 constexpr auto MAX_REASONABLE_NSEC = 1000000000LL; // 1 second
47
48 // The expected_present argument indicates when the buffer is expected to be presented
49 // on-screen.
50 while (core->queue.size() > 1 && !core->queue[0].is_auto_timestamp) {
51 const auto& buffer_item{core->queue[1]};
52
53 // If dropping entry[0] would leave us with a buffer that the consumer is not yet ready
54 // for, don't drop it.
55 if (max_frame_number && buffer_item.frame_number > max_frame_number) {
56 break;
57 }
58
59 // If entry[1] is timely, drop entry[0] (and repeat).
60 const auto desired_present = buffer_item.timestamp;
61 if (desired_present < expected_present.count() - MAX_REASONABLE_NSEC ||
62 desired_present > expected_present.count()) {
63 // This buffer is set to display in the near future, or desired_present is garbage.
64 LOG_DEBUG(Service_NVFlinger, "nodrop desire={} expect={}", desired_present,
65 expected_present.count());
66 break;
67 }
68
69 LOG_DEBUG(Service_NVFlinger, "drop desire={} expect={} size={}", desired_present,
70 expected_present.count(), core->queue.size());
71
72 if (core->StillTracking(*front)) {
73 // Front buffer is still in mSlots, so mark the slot as free
74 slots[front->slot].buffer_state = BufferState::Free;
75 }
76
77 core->queue.erase(front);
78 front = core->queue.begin();
79 }
80
81 // See if the front buffer is ready to be acquired.
82 const auto desired_present = front->timestamp;
83 if (desired_present > expected_present.count() &&
84 desired_present < expected_present.count() + MAX_REASONABLE_NSEC) {
85 LOG_DEBUG(Service_NVFlinger, "defer desire={} expect={}", desired_present,
86 expected_present.count());
87 return Status::PresentLater;
88 }
89
90 LOG_DEBUG(Service_NVFlinger, "accept desire={} expect={}", desired_present,
91 expected_present.count());
92 }
93
94 const auto slot = front->slot;
95 *out_buffer = *front;
96
97 LOG_DEBUG(Service_NVFlinger, "acquiring slot={}", slot);
98
99 // If the front buffer is still being tracked, update its slot state
100 if (core->StillTracking(*front)) {
101 slots[slot].acquire_called = true;
102 slots[slot].needs_cleanup_on_release = false;
103 slots[slot].buffer_state = BufferState::Acquired;
104 slots[slot].fence = Fence::NoFence();
105 }
106
107 // If the buffer has previously been acquired by the consumer, set graphic_buffer to nullptr to
108 // avoid unnecessarily remapping this buffer on the consumer side.
109 if (out_buffer->acquire_called) {
110 out_buffer->graphic_buffer = nullptr;
111 }
112
113 core->queue.erase(front);
114
115 // We might have freed a slot while dropping old buffers, or the producer may be blocked
116 // waiting for the number of buffers in the queue to decrease.
117 core->SignalDequeueCondition();
118
119 return Status::NoError;
120}
121
122Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence) {
123 if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
124 LOG_ERROR(Service_NVFlinger, "slot {} out of range", slot);
125 return Status::BadValue;
126 }
127
128 std::shared_ptr<IProducerListener> listener;
129 {
130 std::scoped_lock lock(core->mutex);
131
132 // If the frame number has changed because the buffer has been reallocated, we can ignore
133 // this ReleaseBuffer for the old buffer.
134 if (frame_number != slots[slot].frame_number) {
135 return Status::StaleBufferSlot;
136 }
137
138 // Make sure this buffer hasn't been queued while acquired by the consumer.
139 auto current(core->queue.begin());
140 while (current != core->queue.end()) {
141 if (current->slot == slot) {
142 LOG_ERROR(Service_NVFlinger, "buffer slot {} pending release is currently queued",
143 slot);
144 return Status::BadValue;
145 }
146 ++current;
147 }
148
149 if (slots[slot].buffer_state == BufferState::Acquired) {
150 slots[slot].fence = release_fence;
151 slots[slot].buffer_state = BufferState::Free;
152
153 listener = core->connected_producer_listener;
154
155 LOG_DEBUG(Service_NVFlinger, "releasing slot {}", slot);
156 } else if (slots[slot].needs_cleanup_on_release) {
157 LOG_DEBUG(Service_NVFlinger, "releasing a stale buffer slot {} (state = {})", slot,
158 slots[slot].buffer_state);
159
160 slots[slot].needs_cleanup_on_release = false;
161
162 return Status::StaleBufferSlot;
163 } else {
164 LOG_ERROR(Service_NVFlinger, "attempted to release buffer slot {} but its state was {}",
165 slot, slots[slot].buffer_state);
166
167 return Status::BadValue;
168 }
169
170 core->SignalDequeueCondition();
171 }
172
173 // Call back without lock held
174 if (listener != nullptr) {
175 listener->OnBufferReleased();
176 }
177
178 return Status::NoError;
179}
180
181Status BufferQueueConsumer::Connect(std::shared_ptr<IConsumerListener> consumer_listener,
182 bool controlled_by_app) {
183 if (consumer_listener == nullptr) {
184 LOG_ERROR(Service_NVFlinger, "consumer_listener may not be nullptr");
185 return Status::BadValue;
186 }
187
188 LOG_DEBUG(Service_NVFlinger, "controlled_by_app={}", controlled_by_app);
189
190 std::scoped_lock lock(core->mutex);
191
192 if (core->is_abandoned) {
193 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
194 return Status::NoInit;
195 }
196
197 core->consumer_listener = consumer_listener;
198 core->consumer_controlled_by_app = controlled_by_app;
199
200 return Status::NoError;
201}
202
203} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_queue_consumer.h b/src/core/hle/service/nvflinger/buffer_queue_consumer.h
new file mode 100644
index 000000000..f22854394
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_queue_consumer.h
@@ -0,0 +1,37 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueConsumer.h
6
7#pragma once
8
9#include <chrono>
10#include <memory>
11
12#include "common/common_types.h"
13#include "core/hle/service/nvflinger/buffer_queue_defs.h"
14#include "core/hle/service/nvflinger/status.h"
15
16namespace Service::android {
17
18class BufferItem;
19class BufferQueueCore;
20class IConsumerListener;
21
22class BufferQueueConsumer final {
23public:
24 explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_);
25 ~BufferQueueConsumer();
26
27 Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present,
28 u64 max_frame_number = 0);
29 Status ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence);
30 Status Connect(std::shared_ptr<IConsumerListener> consumer_listener, bool controlled_by_app);
31
32private:
33 std::shared_ptr<BufferQueueCore> core;
34 BufferQueueDefs::SlotsType& slots;
35};
36
37} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_queue_core.cpp b/src/core/hle/service/nvflinger/buffer_queue_core.cpp
new file mode 100644
index 000000000..6082610e0
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_queue_core.cpp
@@ -0,0 +1,118 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueCore.cpp
6
7#include "common/assert.h"
8
9#include "core/hle/service/nvflinger/buffer_queue_core.h"
10
11namespace Service::android {
12
13BufferQueueCore::BufferQueueCore() = default;
14
15BufferQueueCore::~BufferQueueCore() = default;
16
17void BufferQueueCore::NotifyShutdown() {
18 std::scoped_lock lock(mutex);
19
20 is_shutting_down = true;
21
22 SignalDequeueCondition();
23}
24
25void BufferQueueCore::SignalDequeueCondition() {
26 dequeue_condition.notify_all();
27}
28
29bool BufferQueueCore::WaitForDequeueCondition() {
30 if (is_shutting_down) {
31 return false;
32 }
33
34 dequeue_condition.wait(mutex);
35
36 return true;
37}
38
39s32 BufferQueueCore::GetMinUndequeuedBufferCountLocked(bool async) const {
40 // If DequeueBuffer is allowed to error out, we don't have to add an extra buffer.
41 if (!use_async_buffer) {
42 return max_acquired_buffer_count;
43 }
44
45 if (dequeue_buffer_cannot_block || async) {
46 return max_acquired_buffer_count + 1;
47 }
48
49 return max_acquired_buffer_count;
50}
51
52s32 BufferQueueCore::GetMinMaxBufferCountLocked(bool async) const {
53 return GetMinUndequeuedBufferCountLocked(async) + 1;
54}
55
56s32 BufferQueueCore::GetMaxBufferCountLocked(bool async) const {
57 const auto min_buffer_count = GetMinMaxBufferCountLocked(async);
58 auto max_buffer_count = std::max(default_max_buffer_count, min_buffer_count);
59
60 if (override_max_buffer_count != 0) {
61 ASSERT(override_max_buffer_count >= min_buffer_count);
62 max_buffer_count = override_max_buffer_count;
63 }
64
65 // Any buffers that are dequeued by the producer or sitting in the queue waiting to be consumed
66 // need to have their slots preserved.
67 for (s32 slot = max_buffer_count; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) {
68 const auto state = slots[slot].buffer_state;
69 if (state == BufferState::Queued || state == BufferState::Dequeued) {
70 max_buffer_count = slot + 1;
71 }
72 }
73
74 return max_buffer_count;
75}
76
77s32 BufferQueueCore::GetPreallocatedBufferCountLocked() const {
78 return static_cast<s32>(std::count_if(slots.begin(), slots.end(),
79 [](const auto& slot) { return slot.is_preallocated; }));
80}
81
82void BufferQueueCore::FreeBufferLocked(s32 slot) {
83 LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
84
85 slots[slot].graphic_buffer.reset();
86
87 if (slots[slot].buffer_state == BufferState::Acquired) {
88 slots[slot].needs_cleanup_on_release = true;
89 }
90
91 slots[slot].buffer_state = BufferState::Free;
92 slots[slot].frame_number = UINT32_MAX;
93 slots[slot].acquire_called = false;
94 slots[slot].fence = Fence::NoFence();
95}
96
97void BufferQueueCore::FreeAllBuffersLocked() {
98 queue.clear();
99 buffer_has_been_queued = false;
100
101 for (s32 slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) {
102 FreeBufferLocked(slot);
103 }
104}
105
106bool BufferQueueCore::StillTracking(const BufferItem& item) const {
107 const BufferSlot& slot = slots[item.slot];
108
109 return (slot.graphic_buffer != nullptr) && (item.graphic_buffer == slot.graphic_buffer);
110}
111
112void BufferQueueCore::WaitWhileAllocatingLocked() const {
113 while (is_allocating) {
114 is_allocating_condition.wait(mutex);
115 }
116}
117
118} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_queue_core.h b/src/core/hle/service/nvflinger/buffer_queue_core.h
new file mode 100644
index 000000000..4dfd53387
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_queue_core.h
@@ -0,0 +1,81 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueCore.h
6
7#pragma once
8
9#include <condition_variable>
10#include <list>
11#include <memory>
12#include <mutex>
13#include <set>
14#include <vector>
15
16#include "core/hle/service/nvflinger/buffer_item.h"
17#include "core/hle/service/nvflinger/buffer_queue_defs.h"
18#include "core/hle/service/nvflinger/pixel_format.h"
19#include "core/hle/service/nvflinger/status.h"
20#include "core/hle/service/nvflinger/window.h"
21
22namespace Service::android {
23
24class IConsumerListener;
25class IProducerListener;
26
27class BufferQueueCore final {
28 friend class BufferQueueProducer;
29 friend class BufferQueueConsumer;
30
31public:
32 static constexpr s32 INVALID_BUFFER_SLOT = BufferItem::INVALID_BUFFER_SLOT;
33
34 BufferQueueCore();
35 ~BufferQueueCore();
36
37 void NotifyShutdown();
38
39private:
40 void SignalDequeueCondition();
41 bool WaitForDequeueCondition();
42
43 s32 GetMinUndequeuedBufferCountLocked(bool async) const;
44 s32 GetMinMaxBufferCountLocked(bool async) const;
45 s32 GetMaxBufferCountLocked(bool async) const;
46 s32 GetPreallocatedBufferCountLocked() const;
47 void FreeBufferLocked(s32 slot);
48 void FreeAllBuffersLocked();
49 bool StillTracking(const BufferItem& item) const;
50 void WaitWhileAllocatingLocked() const;
51
52private:
53 mutable std::mutex mutex;
54 bool is_abandoned{};
55 bool consumer_controlled_by_app{};
56 std::shared_ptr<IConsumerListener> consumer_listener;
57 u32 consumer_usage_bit{};
58 NativeWindowApi connected_api{NativeWindowApi::NoConnectedApi};
59 std::shared_ptr<IProducerListener> connected_producer_listener;
60 BufferQueueDefs::SlotsType slots{};
61 std::vector<BufferItem> queue;
62 s32 override_max_buffer_count{};
63 mutable std::condition_variable_any dequeue_condition;
64 const bool use_async_buffer{}; // This is always disabled on HOS
65 bool dequeue_buffer_cannot_block{};
66 PixelFormat default_buffer_format{PixelFormat::Rgba8888};
67 u32 default_width{1};
68 u32 default_height{1};
69 s32 default_max_buffer_count{2};
70 const s32 max_acquired_buffer_count{}; // This is always zero on HOS
71 bool buffer_has_been_queued{};
72 u64 frame_counter{};
73 u32 transform_hint{};
74 bool is_allocating{};
75 mutable std::condition_variable_any is_allocating_condition;
76 bool allow_allocation{true};
77 u64 buffer_age{};
78 bool is_shutting_down{};
79};
80
81} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_queue_defs.h b/src/core/hle/service/nvflinger/buffer_queue_defs.h
new file mode 100644
index 000000000..387d3d36a
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_queue_defs.h
@@ -0,0 +1,21 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueDefs.h
6
7#pragma once
8
9#include <array>
10
11#include "common/common_types.h"
12#include "core/hle/service/nvflinger/buffer_slot.h"
13
14namespace Service::android::BufferQueueDefs {
15
16// BufferQueue will keep track of at most this value of buffers.
17constexpr s32 NUM_BUFFER_SLOTS = 64;
18
19using SlotsType = std::array<BufferSlot, NUM_BUFFER_SLOTS>;
20
21} // namespace Service::android::BufferQueueDefs
diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp
new file mode 100644
index 000000000..0833be57a
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp
@@ -0,0 +1,942 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueProducer.cpp
6
7#include "common/assert.h"
8#include "common/logging/log.h"
9#include "common/settings.h"
10#include "core/core.h"
11#include "core/hle/kernel/hle_ipc.h"
12#include "core/hle/kernel/k_event.h"
13#include "core/hle/kernel/k_readable_event.h"
14#include "core/hle/kernel/k_writable_event.h"
15#include "core/hle/kernel/kernel.h"
16#include "core/hle/service/kernel_helpers.h"
17#include "core/hle/service/nvdrv/nvdrv.h"
18#include "core/hle/service/nvflinger/buffer_queue_core.h"
19#include "core/hle/service/nvflinger/buffer_queue_producer.h"
20#include "core/hle/service/nvflinger/consumer_listener.h"
21#include "core/hle/service/nvflinger/parcel.h"
22#include "core/hle/service/nvflinger/ui/graphic_buffer.h"
23#include "core/hle/service/nvflinger/window.h"
24#include "core/hle/service/vi/vi.h"
25
26namespace Service::android {
27
28BufferQueueProducer::BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_,
29 std::shared_ptr<BufferQueueCore> buffer_queue_core_)
30 : service_context{service_context_}, core{std::move(buffer_queue_core_)}, slots(core->slots) {
31 buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent");
32}
33
34BufferQueueProducer::~BufferQueueProducer() {
35 service_context.CloseEvent(buffer_wait_event);
36}
37
38Status BufferQueueProducer::RequestBuffer(s32 slot, std::shared_ptr<GraphicBuffer>* buf) {
39 LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
40
41 std::scoped_lock lock(core->mutex);
42
43 if (core->is_abandoned) {
44 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
45 return Status::NoInit;
46 }
47 if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
48 LOG_ERROR(Service_NVFlinger, "slot index {} out of range [0, {})", slot,
49 BufferQueueDefs::NUM_BUFFER_SLOTS);
50 return Status::BadValue;
51 } else if (slots[slot].buffer_state != BufferState::Dequeued) {
52 LOG_ERROR(Service_NVFlinger, "slot {} is not owned by the producer (state = {})", slot,
53 slots[slot].buffer_state);
54 return Status::BadValue;
55 }
56
57 slots[slot].request_buffer_called = true;
58 *buf = slots[slot].graphic_buffer;
59
60 return Status::NoError;
61}
62
63Status BufferQueueProducer::SetBufferCount(s32 buffer_count) {
64 LOG_DEBUG(Service_NVFlinger, "count = {}", buffer_count);
65 std::shared_ptr<IConsumerListener> listener;
66
67 {
68 std::scoped_lock lock(core->mutex);
69 core->WaitWhileAllocatingLocked();
70 if (core->is_abandoned) {
71 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
72 return Status::NoInit;
73 }
74
75 if (buffer_count > BufferQueueDefs::NUM_BUFFER_SLOTS) {
76 LOG_ERROR(Service_NVFlinger, "buffer_count {} too large (max {})", buffer_count,
77 BufferQueueDefs::NUM_BUFFER_SLOTS);
78 return Status::BadValue;
79 }
80
81 // There must be no dequeued buffers when changing the buffer count.
82 for (s32 s{}; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
83 if (slots[s].buffer_state == BufferState::Dequeued) {
84 LOG_ERROR(Service_NVFlinger, "buffer owned by producer");
85 return Status::BadValue;
86 }
87 }
88
89 if (buffer_count == 0) {
90 core->override_max_buffer_count = 0;
91 core->SignalDequeueCondition();
92 return Status::NoError;
93 }
94
95 const s32 min_buffer_slots = core->GetMinMaxBufferCountLocked(false);
96 if (buffer_count < min_buffer_slots) {
97 LOG_ERROR(Service_NVFlinger, "requested buffer count {} is less than minimum {}",
98 buffer_count, min_buffer_slots);
99 return Status::BadValue;
100 }
101
102 // Here we are guaranteed that the producer doesn't have any dequeued buffers and will
103 // release all of its buffer references.
104 if (core->GetPreallocatedBufferCountLocked() <= 0) {
105 core->FreeAllBuffersLocked();
106 }
107
108 core->override_max_buffer_count = buffer_count;
109 core->SignalDequeueCondition();
110 buffer_wait_event->GetWritableEvent().Signal();
111 listener = core->consumer_listener;
112 }
113
114 // Call back without lock held
115 if (listener != nullptr) {
116 listener->OnBuffersReleased();
117 }
118
119 return Status::NoError;
120}
121
122Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found,
123 Status* returnFlags) const {
124 bool try_again = true;
125
126 while (try_again) {
127 if (core->is_abandoned) {
128 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
129 return Status::NoInit;
130 }
131
132 const s32 max_buffer_count = core->GetMaxBufferCountLocked(async);
133 if (async && core->override_max_buffer_count) {
134 if (core->override_max_buffer_count < max_buffer_count) {
135 LOG_ERROR(Service_NVFlinger, "async mode is invalid with buffer count override");
136 return Status::BadValue;
137 }
138 }
139
140 // Free up any buffers that are in slots beyond the max buffer count
141 for (s32 s = max_buffer_count; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
142 ASSERT(slots[s].buffer_state == BufferState::Free);
143 if (slots[s].graphic_buffer != nullptr) {
144 core->FreeBufferLocked(s);
145 *returnFlags |= Status::ReleaseAllBuffers;
146 }
147 }
148
149 s32 dequeued_count{};
150 s32 acquired_count{};
151 for (s32 s{}; s < max_buffer_count; ++s) {
152 switch (slots[s].buffer_state) {
153 case BufferState::Dequeued:
154 ++dequeued_count;
155 break;
156 case BufferState::Acquired:
157 ++acquired_count;
158 break;
159 case BufferState::Free:
160 // We return the oldest of the free buffers to avoid stalling the producer if
161 // possible, since the consumer may still have pending reads of in-flight buffers
162 if (*found == BufferQueueCore::INVALID_BUFFER_SLOT ||
163 slots[s].frame_number < slots[*found].frame_number) {
164 *found = s;
165 }
166 break;
167 default:
168 break;
169 }
170 }
171
172 // Producers are not allowed to dequeue more than one buffer if they did not set a buffer
173 // count
174 if (!core->override_max_buffer_count && dequeued_count) {
175 LOG_ERROR(Service_NVFlinger,
176 "can't dequeue multiple buffers without setting the buffer count");
177 return Status::InvalidOperation;
178 }
179
180 // See whether a buffer has been queued since the last SetBufferCount so we know whether to
181 // perform the min undequeued buffers check below
182 if (core->buffer_has_been_queued) {
183 // Make sure the producer is not trying to dequeue more buffers than allowed
184 const s32 new_undequeued_count = max_buffer_count - (dequeued_count + 1);
185 const s32 min_undequeued_count = core->GetMinUndequeuedBufferCountLocked(async);
186 if (new_undequeued_count < min_undequeued_count) {
187 LOG_ERROR(Service_NVFlinger,
188 "min undequeued buffer count({}) exceeded (dequeued={} undequeued={})",
189 min_undequeued_count, dequeued_count, new_undequeued_count);
190 return Status::InvalidOperation;
191 }
192 }
193
194 // If we disconnect and reconnect quickly, we can be in a state where our slots are empty
195 // but we have many buffers in the queue. This can cause us to run out of memory if we
196 // outrun the consumer. Wait here if it looks like we have too many buffers queued up.
197 const bool too_many_buffers = core->queue.size() > static_cast<size_t>(max_buffer_count);
198 if (too_many_buffers) {
199 LOG_ERROR(Service_NVFlinger, "queue size is {}, waiting", core->queue.size());
200 }
201
202 // If no buffer is found, or if the queue has too many buffers outstanding, wait for a
203 // buffer to be acquired or released, or for the max buffer count to change.
204 try_again = (*found == BufferQueueCore::INVALID_BUFFER_SLOT) || too_many_buffers;
205 if (try_again) {
206 // Return an error if we're in non-blocking mode (producer and consumer are controlled
207 // by the application).
208 if (core->dequeue_buffer_cannot_block &&
209 (acquired_count <= core->max_acquired_buffer_count)) {
210 return Status::WouldBlock;
211 }
212
213 if (!core->WaitForDequeueCondition()) {
214 // We are no longer running
215 return Status::NoError;
216 }
217 }
218 }
219
220 return Status::NoError;
221}
222
223Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool async, u32 width,
224 u32 height, PixelFormat format, u32 usage) {
225 LOG_DEBUG(Service_NVFlinger, "async={} w={} h={} format={}, usage={}", async ? "true" : "false",
226 width, height, format, usage);
227
228 if ((width != 0 && height == 0) || (width == 0 && height != 0)) {
229 LOG_ERROR(Service_NVFlinger, "invalid size: w={} h={}", width, height);
230 return Status::BadValue;
231 }
232
233 Status return_flags = Status::NoError;
234 bool attached_by_consumer = false;
235 {
236 std::scoped_lock lock(core->mutex);
237 core->WaitWhileAllocatingLocked();
238 if (format == PixelFormat::NoFormat) {
239 format = core->default_buffer_format;
240 }
241
242 // Enable the usage bits the consumer requested
243 usage |= core->consumer_usage_bit;
244 const bool use_default_size = !width && !height;
245 if (use_default_size) {
246 width = core->default_width;
247 height = core->default_height;
248 }
249
250 s32 found = BufferItem::INVALID_BUFFER_SLOT;
251 while (found == BufferItem::INVALID_BUFFER_SLOT) {
252 Status status = WaitForFreeSlotThenRelock(async, &found, &return_flags);
253 if (status != Status::NoError) {
254 return status;
255 }
256
257 // This should not happen
258 if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
259 LOG_DEBUG(Service_NVFlinger, "no available buffer slots");
260 return Status::Busy;
261 }
262
263 const std::shared_ptr<GraphicBuffer>& buffer(slots[found].graphic_buffer);
264
265 // If we are not allowed to allocate new buffers, WaitForFreeSlotThenRelock must have
266 // returned a slot containing a buffer. If this buffer would require reallocation to
267 // meet the requested attributes, we free it and attempt to get another one.
268 if (!core->allow_allocation) {
269 if (buffer->NeedsReallocation(width, height, format, usage)) {
270 core->FreeBufferLocked(found);
271 found = BufferItem::INVALID_BUFFER_SLOT;
272 continue;
273 }
274 }
275 }
276
277 *out_slot = found;
278 attached_by_consumer = slots[found].attached_by_consumer;
279 slots[found].buffer_state = BufferState::Dequeued;
280
281 const std::shared_ptr<GraphicBuffer>& buffer(slots[found].graphic_buffer);
282
283 if ((buffer == nullptr) || buffer->NeedsReallocation(width, height, format, usage)) {
284 slots[found].acquire_called = false;
285 slots[found].graphic_buffer = nullptr;
286 slots[found].request_buffer_called = false;
287 slots[found].fence = Fence::NoFence();
288 core->buffer_age = 0;
289 return_flags |= Status::BufferNeedsReallocation;
290 } else {
291 // We add 1 because that will be the frame number when this buffer
292 // is queued
293 core->buffer_age = core->frame_counter + 1 - slots[found].frame_number;
294 }
295
296 LOG_DEBUG(Service_NVFlinger, "setting buffer age to {}", core->buffer_age);
297
298 *out_fence = slots[found].fence;
299
300 slots[found].fence = Fence::NoFence();
301 }
302
303 if ((return_flags & Status::BufferNeedsReallocation) != Status::None) {
304 LOG_DEBUG(Service_NVFlinger, "allocating a new buffer for slot {}", *out_slot);
305
306 auto graphic_buffer = std::make_shared<GraphicBuffer>(width, height, format, usage);
307 if (graphic_buffer == nullptr) {
308 LOG_ERROR(Service_NVFlinger, "creating GraphicBuffer failed");
309 return Status::NoMemory;
310 }
311
312 {
313 std::scoped_lock lock(core->mutex);
314 if (core->is_abandoned) {
315 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
316 return Status::NoInit;
317 }
318
319 slots[*out_slot].frame_number = UINT32_MAX;
320 slots[*out_slot].graphic_buffer = graphic_buffer;
321 }
322 }
323
324 if (attached_by_consumer) {
325 return_flags |= Status::BufferNeedsReallocation;
326 }
327
328 LOG_DEBUG(Service_NVFlinger, "returning slot={} frame={}, flags={}", *out_slot,
329 slots[*out_slot].frame_number, return_flags);
330 return return_flags;
331}
332
333Status BufferQueueProducer::DetachBuffer(s32 slot) {
334 LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
335
336 std::scoped_lock lock(core->mutex);
337 if (core->is_abandoned) {
338 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
339 return Status::NoInit;
340 }
341
342 if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
343 LOG_ERROR(Service_NVFlinger, "slot {} out of range [0, {})", slot,
344 BufferQueueDefs::NUM_BUFFER_SLOTS);
345 return Status::BadValue;
346 } else if (slots[slot].buffer_state != BufferState::Dequeued) {
347 LOG_ERROR(Service_NVFlinger, "slot {} is not owned by the producer (state = {})", slot,
348 slots[slot].buffer_state);
349 return Status::BadValue;
350 } else if (!slots[slot].request_buffer_called) {
351 LOG_ERROR(Service_NVFlinger, "buffer in slot {} has not been requested", slot);
352 return Status::BadValue;
353 }
354
355 core->FreeBufferLocked(slot);
356 core->SignalDequeueCondition();
357
358 return Status::NoError;
359}
360
361Status BufferQueueProducer::DetachNextBuffer(std::shared_ptr<GraphicBuffer>* out_buffer,
362 Fence* out_fence) {
363 if (out_buffer == nullptr) {
364 LOG_ERROR(Service_NVFlinger, "out_buffer must not be nullptr");
365 return Status::BadValue;
366 } else if (out_fence == nullptr) {
367 LOG_ERROR(Service_NVFlinger, "out_fence must not be nullptr");
368 return Status::BadValue;
369 }
370
371 std::scoped_lock lock(core->mutex);
372
373 core->WaitWhileAllocatingLocked();
374
375 if (core->is_abandoned) {
376 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
377 return Status::NoInit;
378 }
379
380 // Find the oldest valid slot
381 int found = BufferQueueCore::INVALID_BUFFER_SLOT;
382 for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
383 if (slots[s].buffer_state == BufferState::Free && slots[s].graphic_buffer != nullptr) {
384 if (found == BufferQueueCore::INVALID_BUFFER_SLOT ||
385 slots[s].frame_number < slots[found].frame_number) {
386 found = s;
387 }
388 }
389 }
390
391 if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
392 return Status::NoMemory;
393 }
394
395 LOG_DEBUG(Service_NVFlinger, "Detached slot {}", found);
396
397 *out_buffer = slots[found].graphic_buffer;
398 *out_fence = slots[found].fence;
399
400 core->FreeBufferLocked(found);
401
402 return Status::NoError;
403}
404
405Status BufferQueueProducer::AttachBuffer(s32* out_slot,
406 const std::shared_ptr<GraphicBuffer>& buffer) {
407 if (out_slot == nullptr) {
408 LOG_ERROR(Service_NVFlinger, "out_slot must not be nullptr");
409 return Status::BadValue;
410 } else if (buffer == nullptr) {
411 LOG_ERROR(Service_NVFlinger, "Cannot attach nullptr buffer");
412 return Status::BadValue;
413 }
414
415 std::scoped_lock lock(core->mutex);
416 core->WaitWhileAllocatingLocked();
417
418 Status return_flags = Status::NoError;
419 s32 found{};
420
421 const auto status = WaitForFreeSlotThenRelock(false, &found, &return_flags);
422 if (status != Status::NoError) {
423 return status;
424 }
425
426 if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
427 LOG_ERROR(Service_NVFlinger, "No available buffer slots");
428 return Status::Busy;
429 }
430
431 *out_slot = found;
432
433 LOG_DEBUG(Service_NVFlinger, "Returning slot {} flags={}", *out_slot, return_flags);
434
435 slots[*out_slot].graphic_buffer = buffer;
436 slots[*out_slot].buffer_state = BufferState::Dequeued;
437 slots[*out_slot].fence = Fence::NoFence();
438 slots[*out_slot].request_buffer_called = true;
439
440 return return_flags;
441}
442
443Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
444 QueueBufferOutput* output) {
445 s64 timestamp{};
446 bool is_auto_timestamp{};
447 Common::Rectangle<s32> crop;
448 NativeWindowScalingMode scaling_mode{};
449 NativeWindowTransform transform;
450 u32 sticky_transform_{};
451 bool async{};
452 s32 swap_interval{};
453 Fence fence{};
454
455 input.Deflate(&timestamp, &is_auto_timestamp, &crop, &scaling_mode, &transform,
456 &sticky_transform_, &async, &swap_interval, &fence);
457
458 switch (scaling_mode) {
459 case NativeWindowScalingMode::Freeze:
460 case NativeWindowScalingMode::ScaleToWindow:
461 case NativeWindowScalingMode::ScaleCrop:
462 case NativeWindowScalingMode::NoScaleCrop:
463 break;
464 default:
465 LOG_ERROR(Service_NVFlinger, "unknown scaling mode {}", scaling_mode);
466 return Status::BadValue;
467 }
468
469 std::shared_ptr<IConsumerListener> frameAvailableListener;
470 std::shared_ptr<IConsumerListener> frameReplacedListener;
471 s32 callback_ticket{};
472 BufferItem item;
473
474 {
475 std::scoped_lock lock(core->mutex);
476
477 if (core->is_abandoned) {
478 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
479 return Status::NoInit;
480 }
481
482 const s32 max_buffer_count = core->GetMaxBufferCountLocked(async);
483 if (async && core->override_max_buffer_count) {
484 if (core->override_max_buffer_count < max_buffer_count) {
485 LOG_ERROR(Service_NVFlinger, "async mode is invalid with "
486 "buffer count override");
487 return Status::BadValue;
488 }
489 }
490
491 if (slot < 0 || slot >= max_buffer_count) {
492 LOG_ERROR(Service_NVFlinger, "slot index {} out of range [0, {})", slot,
493 max_buffer_count);
494 return Status::BadValue;
495 } else if (slots[slot].buffer_state != BufferState::Dequeued) {
496 LOG_ERROR(Service_NVFlinger,
497 "slot {} is not owned by the producer "
498 "(state = {})",
499 slot, slots[slot].buffer_state);
500 return Status::BadValue;
501 } else if (!slots[slot].request_buffer_called) {
502 LOG_ERROR(Service_NVFlinger,
503 "slot {} was queued without requesting "
504 "a buffer",
505 slot);
506 return Status::BadValue;
507 }
508
509 LOG_DEBUG(Service_NVFlinger,
510 "slot={} frame={} time={} crop=[{},{},{},{}] transform={} scale={}", slot,
511 core->frame_counter + 1, timestamp, crop.Left(), crop.Top(), crop.Right(),
512 crop.Bottom(), transform, scaling_mode);
513
514 const std::shared_ptr<GraphicBuffer>& graphic_buffer(slots[slot].graphic_buffer);
515 Common::Rectangle<s32> buffer_rect(graphic_buffer->Width(), graphic_buffer->Height());
516 Common::Rectangle<s32> cropped_rect;
517 [[maybe_unused]] const bool unused = crop.Intersect(buffer_rect, &cropped_rect);
518
519 if (cropped_rect != crop) {
520 LOG_ERROR(Service_NVFlinger, "crop rect is not contained within the buffer in slot {}",
521 slot);
522 return Status::BadValue;
523 }
524
525 slots[slot].fence = fence;
526 slots[slot].buffer_state = BufferState::Queued;
527 ++core->frame_counter;
528 slots[slot].frame_number = core->frame_counter;
529
530 item.acquire_called = slots[slot].acquire_called;
531 item.graphic_buffer = slots[slot].graphic_buffer;
532 item.crop = crop;
533 item.transform = transform & ~NativeWindowTransform::InverseDisplay;
534 item.transform_to_display_inverse =
535 (transform & NativeWindowTransform::InverseDisplay) != NativeWindowTransform::None;
536 item.scaling_mode = static_cast<u32>(scaling_mode);
537 item.timestamp = timestamp;
538 item.is_auto_timestamp = is_auto_timestamp;
539 item.frame_number = core->frame_counter;
540 item.slot = slot;
541 item.fence = fence;
542 item.is_droppable = core->dequeue_buffer_cannot_block || async;
543 item.swap_interval = swap_interval;
544 sticky_transform = sticky_transform_;
545
546 if (core->queue.empty()) {
547 // When the queue is empty, we can simply queue this buffer
548 core->queue.push_back(item);
549 frameAvailableListener = core->consumer_listener;
550 } else {
551 // When the queue is not empty, we need to look at the front buffer
552 // state to see if we need to replace it
553 auto front(core->queue.begin());
554
555 if (front->is_droppable) {
556 // If the front queued buffer is still being tracked, we first
557 // mark it as freed
558 if (core->StillTracking(*front)) {
559 slots[front->slot].buffer_state = BufferState::Free;
560 // Reset the frame number of the freed buffer so that it is the first in line to
561 // be dequeued again
562 slots[front->slot].frame_number = 0;
563 }
564 // Overwrite the droppable buffer with the incoming one
565 *front = item;
566 frameReplacedListener = core->consumer_listener;
567 } else {
568 core->queue.push_back(item);
569 frameAvailableListener = core->consumer_listener;
570 }
571 }
572
573 core->buffer_has_been_queued = true;
574 core->SignalDequeueCondition();
575 output->Inflate(core->default_width, core->default_height, core->transform_hint,
576 static_cast<u32>(core->queue.size()));
577
578 // Take a ticket for the callback functions
579 callback_ticket = next_callback_ticket++;
580 }
581
582 // Don't send the GraphicBuffer through the callback, and don't send the slot number, since the
583 // consumer shouldn't need it
584 item.graphic_buffer.reset();
585 item.slot = BufferItem::INVALID_BUFFER_SLOT;
586
587 // Call back without the main BufferQueue lock held, but with the callback lock held so we can
588 // ensure that callbacks occur in order
589 {
590 std::scoped_lock lock(callback_mutex);
591 while (callback_ticket != current_callback_ticket) {
592 callback_condition.wait(callback_mutex);
593 }
594
595 if (frameAvailableListener != nullptr) {
596 frameAvailableListener->OnFrameAvailable(item);
597 } else if (frameReplacedListener != nullptr) {
598 frameReplacedListener->OnFrameReplaced(item);
599 }
600
601 ++current_callback_ticket;
602 callback_condition.notify_all();
603 }
604
605 return Status::NoError;
606}
607
608void BufferQueueProducer::CancelBuffer(s32 slot, const Fence& fence) {
609 LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
610
611 std::scoped_lock lock(core->mutex);
612
613 if (core->is_abandoned) {
614 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
615 return;
616 }
617
618 if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
619 LOG_ERROR(Service_NVFlinger, "slot index {} out of range [0, {})", slot,
620 BufferQueueDefs::NUM_BUFFER_SLOTS);
621 return;
622 } else if (slots[slot].buffer_state != BufferState::Dequeued) {
623 LOG_ERROR(Service_NVFlinger, "slot {} is not owned by the producer (state = {})", slot,
624 slots[slot].buffer_state);
625 return;
626 }
627
628 slots[slot].buffer_state = BufferState::Free;
629 slots[slot].frame_number = 0;
630 slots[slot].fence = fence;
631
632 core->SignalDequeueCondition();
633 buffer_wait_event->GetWritableEvent().Signal();
634}
635
636Status BufferQueueProducer::Query(NativeWindow what, s32* out_value) {
637 std::scoped_lock lock(core->mutex);
638
639 if (out_value == nullptr) {
640 LOG_ERROR(Service_NVFlinger, "outValue was nullptr");
641 return Status::BadValue;
642 }
643
644 if (core->is_abandoned) {
645 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
646 return Status::NoInit;
647 }
648
649 u32 value{};
650 switch (what) {
651 case NativeWindow::Width:
652 value = core->default_width;
653 break;
654 case NativeWindow::Height:
655 value = core->default_height;
656 break;
657 case NativeWindow::Format:
658 value = static_cast<u32>(core->default_buffer_format);
659 break;
660 case NativeWindow::MinUndequeedBuffers:
661 value = core->GetMinUndequeuedBufferCountLocked(false);
662 break;
663 case NativeWindow::StickyTransform:
664 value = sticky_transform;
665 break;
666 case NativeWindow::ConsumerRunningBehind:
667 value = (core->queue.size() > 1);
668 break;
669 case NativeWindow::ConsumerUsageBits:
670 value = core->consumer_usage_bit;
671 break;
672 case NativeWindow::BufferAge:
673 if (core->buffer_age > INT32_MAX) {
674 value = 0;
675 } else {
676 value = static_cast<u32>(core->buffer_age);
677 }
678 break;
679 default:
680 UNREACHABLE();
681 return Status::BadValue;
682 }
683
684 LOG_DEBUG(Service_NVFlinger, "what = {}, value = {}", what, value);
685
686 *out_value = static_cast<s32>(value);
687
688 return Status::NoError;
689}
690
691Status BufferQueueProducer::Connect(const std::shared_ptr<IProducerListener>& listener,
692 NativeWindowApi api, bool producer_controlled_by_app,
693 QueueBufferOutput* output) {
694 std::scoped_lock lock(core->mutex);
695
696 LOG_DEBUG(Service_NVFlinger, "api = {} producer_controlled_by_app = {}", api,
697 producer_controlled_by_app);
698
699 if (core->is_abandoned) {
700 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
701 return Status::NoInit;
702 }
703
704 if (core->consumer_listener == nullptr) {
705 LOG_ERROR(Service_NVFlinger, "BufferQueue has no consumer");
706 return Status::NoInit;
707 }
708
709 if (output == nullptr) {
710 LOG_ERROR(Service_NVFlinger, "output was nullptr");
711 return Status::BadValue;
712 }
713
714 if (core->connected_api != NativeWindowApi::NoConnectedApi) {
715 LOG_ERROR(Service_NVFlinger, "already connected (cur = {} req = {})", core->connected_api,
716 api);
717 return Status::BadValue;
718 }
719
720 Status status = Status::NoError;
721 switch (api) {
722 case NativeWindowApi::Egl:
723 case NativeWindowApi::Cpu:
724 case NativeWindowApi::Media:
725 case NativeWindowApi::Camera:
726 core->connected_api = api;
727 output->Inflate(core->default_width, core->default_height, core->transform_hint,
728 static_cast<u32>(core->queue.size()));
729 core->connected_producer_listener = listener;
730 break;
731 default:
732 LOG_ERROR(Service_NVFlinger, "unknown api = {}", api);
733 status = Status::BadValue;
734 break;
735 }
736
737 core->buffer_has_been_queued = false;
738 core->dequeue_buffer_cannot_block =
739 core->consumer_controlled_by_app && producer_controlled_by_app;
740 core->allow_allocation = true;
741
742 return status;
743}
744
745Status BufferQueueProducer::Disconnect(NativeWindowApi api) {
746 LOG_DEBUG(Service_NVFlinger, "api = {}", api);
747
748 Status status = Status::NoError;
749 std::shared_ptr<IConsumerListener> listener;
750
751 {
752 std::scoped_lock lock(core->mutex);
753
754 core->WaitWhileAllocatingLocked();
755
756 if (core->is_abandoned) {
757 // Disconnecting after the surface has been abandoned is a no-op.
758 return Status::NoError;
759 }
760
761 switch (api) {
762 case NativeWindowApi::Egl:
763 case NativeWindowApi::Cpu:
764 case NativeWindowApi::Media:
765 case NativeWindowApi::Camera:
766 if (core->connected_api == api) {
767 core->FreeAllBuffersLocked();
768 core->connected_producer_listener = nullptr;
769 core->connected_api = NativeWindowApi::NoConnectedApi;
770 core->SignalDequeueCondition();
771 buffer_wait_event->GetWritableEvent().Signal();
772 listener = core->consumer_listener;
773 } else if (core->connected_api != NativeWindowApi::NoConnectedApi) {
774 LOG_ERROR(Service_NVFlinger, "still connected to another api (cur = {} req = {})",
775 core->connected_api, api);
776 status = Status::BadValue;
777 }
778 break;
779 default:
780 LOG_ERROR(Service_NVFlinger, "unknown api = {}", api);
781 status = Status::BadValue;
782 break;
783 }
784 }
785
786 // Call back without lock held
787 if (listener != nullptr) {
788 listener->OnBuffersReleased();
789 }
790
791 return status;
792}
793
794Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot,
795 const std::shared_ptr<GraphicBuffer>& buffer) {
796 LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
797
798 if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
799 return Status::BadValue;
800 }
801
802 std::scoped_lock lock(core->mutex);
803
804 slots[slot] = {};
805 slots[slot].graphic_buffer = buffer;
806 slots[slot].frame_number = 0;
807
808 // Most games preallocate a buffer and pass a valid buffer here. However, it is possible for
809 // this to be called with an empty buffer, Naruto Ultimate Ninja Storm is a game that does this.
810 if (buffer) {
811 slots[slot].is_preallocated = true;
812
813 core->override_max_buffer_count = core->GetPreallocatedBufferCountLocked();
814 core->default_width = buffer->Width();
815 core->default_height = buffer->Height();
816 core->default_buffer_format = buffer->Format();
817 }
818
819 core->SignalDequeueCondition();
820 buffer_wait_event->GetWritableEvent().Signal();
821
822 return Status::NoError;
823}
824
825void BufferQueueProducer::Transact(Kernel::HLERequestContext& ctx, TransactionId code, u32 flags) {
826 Status status{Status::NoError};
827 Parcel parcel_in{ctx.ReadBuffer()};
828 Parcel parcel_out{};
829
830 switch (code) {
831 case TransactionId::Connect: {
832 const auto enable_listener = parcel_in.Read<bool>();
833 const auto api = parcel_in.Read<NativeWindowApi>();
834 const auto producer_controlled_by_app = parcel_in.Read<bool>();
835
836 UNIMPLEMENTED_IF_MSG(enable_listener, "Listener is unimplemented!");
837
838 std::shared_ptr<IProducerListener> listener;
839 QueueBufferOutput output{};
840
841 status = Connect(listener, api, producer_controlled_by_app, &output);
842
843 parcel_out.Write(output);
844 break;
845 }
846 case TransactionId::SetPreallocatedBuffer: {
847 const auto slot = parcel_in.Read<s32>();
848 const auto buffer = parcel_in.ReadObject<GraphicBuffer>();
849
850 status = SetPreallocatedBuffer(slot, buffer);
851 break;
852 }
853 case TransactionId::DequeueBuffer: {
854 const auto is_async = parcel_in.Read<bool>();
855 const auto width = parcel_in.Read<u32>();
856 const auto height = parcel_in.Read<u32>();
857 const auto pixel_format = parcel_in.Read<PixelFormat>();
858 const auto usage = parcel_in.Read<u32>();
859
860 s32 slot{};
861 Fence fence{};
862
863 status = DequeueBuffer(&slot, &fence, is_async, width, height, pixel_format, usage);
864
865 parcel_out.Write(slot);
866 parcel_out.WriteObject(&fence);
867 break;
868 }
869 case TransactionId::RequestBuffer: {
870 const auto slot = parcel_in.Read<s32>();
871
872 std::shared_ptr<GraphicBuffer> buf;
873
874 status = RequestBuffer(slot, &buf);
875
876 parcel_out.WriteObject(buf);
877 break;
878 }
879 case TransactionId::QueueBuffer: {
880 const auto slot = parcel_in.Read<s32>();
881
882 QueueBufferInput input{parcel_in};
883 QueueBufferOutput output;
884
885 status = QueueBuffer(slot, input, &output);
886
887 parcel_out.Write(output);
888 break;
889 }
890 case TransactionId::Query: {
891 const auto what = parcel_in.Read<NativeWindow>();
892
893 s32 value{};
894
895 status = Query(what, &value);
896
897 parcel_out.Write(value);
898 break;
899 }
900 case TransactionId::CancelBuffer: {
901 const auto slot = parcel_in.Read<s32>();
902 const auto fence = parcel_in.ReadFlattened<Fence>();
903
904 CancelBuffer(slot, fence);
905 break;
906 }
907 case TransactionId::Disconnect: {
908 const auto api = parcel_in.Read<NativeWindowApi>();
909
910 status = Disconnect(api);
911 break;
912 }
913 case TransactionId::DetachBuffer: {
914 const auto slot = parcel_in.Read<s32>();
915
916 status = DetachBuffer(slot);
917 break;
918 }
919 case TransactionId::SetBufferCount: {
920 const auto buffer_count = parcel_in.Read<s32>();
921
922 status = SetBufferCount(buffer_count);
923 break;
924 }
925 case TransactionId::GetBufferHistory:
926 LOG_WARNING(Service_NVFlinger, "(STUBBED) called, transaction=GetBufferHistory");
927 break;
928 default:
929 ASSERT_MSG(false, "Unimplemented TransactionId {}", code);
930 break;
931 }
932
933 parcel_out.Write(status);
934
935 ctx.WriteBuffer(parcel_out.Serialize());
936}
937
938Kernel::KReadableEvent& BufferQueueProducer::GetNativeHandle() {
939 return buffer_wait_event->GetReadableEvent();
940}
941
942} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.h b/src/core/hle/service/nvflinger/buffer_queue_producer.h
new file mode 100644
index 000000000..77fdcae8e
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_queue_producer.h
@@ -0,0 +1,83 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueProducer.h
6
7#pragma once
8
9#include <condition_variable>
10#include <memory>
11#include <mutex>
12
13#include "common/common_funcs.h"
14#include "core/hle/service/nvdrv/nvdata.h"
15#include "core/hle/service/nvflinger/binder.h"
16#include "core/hle/service/nvflinger/buffer_queue_defs.h"
17#include "core/hle/service/nvflinger/buffer_slot.h"
18#include "core/hle/service/nvflinger/graphic_buffer_producer.h"
19#include "core/hle/service/nvflinger/pixel_format.h"
20#include "core/hle/service/nvflinger/status.h"
21#include "core/hle/service/nvflinger/window.h"
22
23namespace Kernel {
24class KernelCore;
25class KEvent;
26class KReadableEvent;
27class KWritableEvent;
28} // namespace Kernel
29
30namespace Service::KernelHelpers {
31class ServiceContext;
32} // namespace Service::KernelHelpers
33
34namespace Service::android {
35
36class BufferQueueCore;
37class IProducerListener;
38
39class BufferQueueProducer final : public IBinder {
40public:
41 explicit BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_,
42 std::shared_ptr<BufferQueueCore> buffer_queue_core_);
43 ~BufferQueueProducer();
44
45 void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code, u32 flags) override;
46
47 Kernel::KReadableEvent& GetNativeHandle() override;
48
49public:
50 Status RequestBuffer(s32 slot, std::shared_ptr<GraphicBuffer>* buf);
51 Status SetBufferCount(s32 buffer_count);
52 Status DequeueBuffer(s32* out_slot, android::Fence* out_fence, bool async, u32 width,
53 u32 height, PixelFormat format, u32 usage);
54 Status DetachBuffer(s32 slot);
55 Status DetachNextBuffer(std::shared_ptr<GraphicBuffer>* out_buffer, Fence* out_fence);
56 Status AttachBuffer(s32* outSlot, const std::shared_ptr<GraphicBuffer>& buffer);
57 Status QueueBuffer(s32 slot, const QueueBufferInput& input, QueueBufferOutput* output);
58 void CancelBuffer(s32 slot, const Fence& fence);
59 Status Query(NativeWindow what, s32* out_value);
60 Status Connect(const std::shared_ptr<IProducerListener>& listener, NativeWindowApi api,
61 bool producer_controlled_by_app, QueueBufferOutput* output);
62
63 Status Disconnect(NativeWindowApi api);
64 Status SetPreallocatedBuffer(s32 slot, const std::shared_ptr<GraphicBuffer>& buffer);
65
66private:
67 BufferQueueProducer(const BufferQueueProducer&) = delete;
68
69 Status WaitForFreeSlotThenRelock(bool async, s32* found, Status* returnFlags) const;
70
71 Kernel::KEvent* buffer_wait_event{};
72 Service::KernelHelpers::ServiceContext& service_context;
73
74 std::shared_ptr<BufferQueueCore> core;
75 BufferQueueDefs::SlotsType& slots;
76 u32 sticky_transform{};
77 std::mutex callback_mutex;
78 s32 next_callback_ticket{};
79 s32 current_callback_ticket{};
80 std::condition_variable_any callback_condition;
81};
82
83} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_slot.h b/src/core/hle/service/nvflinger/buffer_slot.h
new file mode 100644
index 000000000..e3ea58910
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_slot.h
@@ -0,0 +1,39 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferSlot.h
6
7#pragma once
8
9#include <memory>
10
11#include "common/common_types.h"
12#include "core/hle/service/nvflinger/ui/fence.h"
13
14namespace Service::android {
15
16class GraphicBuffer;
17
18enum class BufferState : u32 {
19 Free = 0,
20 Dequeued = 1,
21 Queued = 2,
22 Acquired = 3,
23};
24
25struct BufferSlot final {
26 constexpr BufferSlot() = default;
27
28 std::shared_ptr<GraphicBuffer> graphic_buffer;
29 BufferState buffer_state{BufferState::Free};
30 bool request_buffer_called{};
31 u64 frame_number{};
32 Fence fence;
33 bool acquire_called{};
34 bool needs_cleanup_on_release{};
35 bool attached_by_consumer{};
36 bool is_preallocated{};
37};
38
39} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_transform_flags.h b/src/core/hle/service/nvflinger/buffer_transform_flags.h
new file mode 100644
index 000000000..e8e6300e3
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_transform_flags.h
@@ -0,0 +1,25 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3
4#pragma once
5
6#include "common/common_types.h"
7
8namespace Service::android {
9
10enum class BufferTransformFlags : u32 {
11 /// No transform flags are set
12 Unset = 0x00,
13 /// Flip source image horizontally (around the vertical axis)
14 FlipH = 0x01,
15 /// Flip source image vertically (around the horizontal axis)
16 FlipV = 0x02,
17 /// Rotate source image 90 degrees clockwise
18 Rotate90 = 0x04,
19 /// Rotate source image 180 degrees
20 Rotate180 = 0x03,
21 /// Rotate source image 270 degrees clockwise
22 Rotate270 = 0x07,
23};
24
25} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/consumer_base.cpp b/src/core/hle/service/nvflinger/consumer_base.cpp
new file mode 100644
index 000000000..be65a3f88
--- /dev/null
+++ b/src/core/hle/service/nvflinger/consumer_base.cpp
@@ -0,0 +1,130 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2010 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/ConsumerBase.cpp
6
7#include "common/assert.h"
8#include "common/logging/log.h"
9#include "core/hle/service/nvflinger/buffer_item.h"
10#include "core/hle/service/nvflinger/buffer_queue_consumer.h"
11#include "core/hle/service/nvflinger/buffer_queue_core.h"
12#include "core/hle/service/nvflinger/consumer_base.h"
13#include "core/hle/service/nvflinger/ui/graphic_buffer.h"
14
15namespace Service::android {
16
17ConsumerBase::ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_)
18 : consumer{std::move(consumer_)} {}
19
20ConsumerBase::~ConsumerBase() {
21 std::scoped_lock lock(mutex);
22
23 ASSERT_MSG(is_abandoned, "consumer is not abandoned!");
24}
25
26void ConsumerBase::Connect(bool controlled_by_app) {
27 consumer->Connect(shared_from_this(), controlled_by_app);
28}
29
30void ConsumerBase::FreeBufferLocked(s32 slot_index) {
31 LOG_DEBUG(Service_NVFlinger, "slot_index={}", slot_index);
32
33 slots[slot_index].graphic_buffer = nullptr;
34 slots[slot_index].fence = Fence::NoFence();
35 slots[slot_index].frame_number = 0;
36}
37
38void ConsumerBase::OnFrameAvailable(const BufferItem& item) {
39 std::scoped_lock lock(mutex);
40 LOG_DEBUG(Service_NVFlinger, "called");
41}
42
43void ConsumerBase::OnFrameReplaced(const BufferItem& item) {
44 std::scoped_lock lock(mutex);
45 LOG_DEBUG(Service_NVFlinger, "called");
46}
47
48void ConsumerBase::OnBuffersReleased() {
49 std::scoped_lock lock(mutex);
50 LOG_DEBUG(Service_NVFlinger, "called");
51}
52
53void ConsumerBase::OnSidebandStreamChanged() {}
54
55Status ConsumerBase::AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when,
56 u64 max_frame_number) {
57 if (is_abandoned) {
58 LOG_ERROR(Service_NVFlinger, "consumer is abandoned!");
59 return Status::NoInit;
60 }
61
62 Status err = consumer->AcquireBuffer(item, present_when, max_frame_number);
63 if (err != Status::NoError) {
64 return err;
65 }
66
67 if (item->graphic_buffer != nullptr) {
68 if (slots[item->slot].graphic_buffer != nullptr) {
69 FreeBufferLocked(item->slot);
70 }
71 slots[item->slot].graphic_buffer = item->graphic_buffer;
72 }
73
74 slots[item->slot].frame_number = item->frame_number;
75 slots[item->slot].fence = item->fence;
76
77 LOG_DEBUG(Service_NVFlinger, "slot={}", item->slot);
78
79 return Status::NoError;
80}
81
82Status ConsumerBase::AddReleaseFenceLocked(s32 slot,
83 const std::shared_ptr<GraphicBuffer> graphic_buffer,
84 const Fence& fence) {
85 LOG_DEBUG(Service_NVFlinger, "slot={}", slot);
86
87 // If consumer no longer tracks this graphic_buffer, we can safely
88 // drop this fence, as it will never be received by the producer.
89
90 if (!StillTracking(slot, graphic_buffer)) {
91 return Status::NoError;
92 }
93
94 slots[slot].fence = fence;
95
96 return Status::NoError;
97}
98
99Status ConsumerBase::ReleaseBufferLocked(s32 slot,
100 const std::shared_ptr<GraphicBuffer> graphic_buffer) {
101 // If consumer no longer tracks this graphic_buffer (we received a new
102 // buffer on the same slot), the buffer producer is definitely no longer
103 // tracking it.
104
105 if (!StillTracking(slot, graphic_buffer)) {
106 return Status::NoError;
107 }
108
109 LOG_DEBUG(Service_NVFlinger, "slot={}", slot);
110 Status err = consumer->ReleaseBuffer(slot, slots[slot].frame_number, slots[slot].fence);
111 if (err == Status::StaleBufferSlot) {
112 FreeBufferLocked(slot);
113 }
114
115 slots[slot].fence = Fence::NoFence();
116
117 return err;
118}
119
120bool ConsumerBase::StillTracking(s32 slot,
121 const std::shared_ptr<GraphicBuffer> graphic_buffer) const {
122 if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
123 return false;
124 }
125
126 return (slots[slot].graphic_buffer != nullptr &&
127 slots[slot].graphic_buffer->Handle() == graphic_buffer->Handle());
128}
129
130} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/consumer_base.h b/src/core/hle/service/nvflinger/consumer_base.h
new file mode 100644
index 000000000..9ab949420
--- /dev/null
+++ b/src/core/hle/service/nvflinger/consumer_base.h
@@ -0,0 +1,61 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2010 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/ConsumerBase.h
6
7#pragma once
8
9#include <array>
10#include <chrono>
11#include <memory>
12#include <mutex>
13
14#include "common/common_types.h"
15#include "core/hle/service/nvflinger/buffer_queue_defs.h"
16#include "core/hle/service/nvflinger/consumer_listener.h"
17#include "core/hle/service/nvflinger/status.h"
18
19namespace Service::android {
20
21class BufferItem;
22class BufferQueueConsumer;
23
24class ConsumerBase : public IConsumerListener, public std::enable_shared_from_this<ConsumerBase> {
25public:
26 void Connect(bool controlled_by_app);
27
28protected:
29 explicit ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_);
30 virtual ~ConsumerBase();
31
32 virtual void OnFrameAvailable(const BufferItem& item) override;
33 virtual void OnFrameReplaced(const BufferItem& item) override;
34 virtual void OnBuffersReleased() override;
35 virtual void OnSidebandStreamChanged() override;
36
37 void FreeBufferLocked(s32 slot_index);
38 Status AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when,
39 u64 max_frame_number = 0);
40 Status ReleaseBufferLocked(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer);
41 bool StillTracking(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer) const;
42 Status AddReleaseFenceLocked(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer,
43 const Fence& fence);
44
45 struct Slot final {
46 std::shared_ptr<GraphicBuffer> graphic_buffer;
47 Fence fence;
48 u64 frame_number{};
49 };
50
51protected:
52 std::array<Slot, BufferQueueDefs::NUM_BUFFER_SLOTS> slots;
53
54 bool is_abandoned{};
55
56 std::unique_ptr<BufferQueueConsumer> consumer;
57
58 mutable std::mutex mutex;
59};
60
61} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/consumer_listener.h b/src/core/hle/service/nvflinger/consumer_listener.h
new file mode 100644
index 000000000..b6d1c3e9a
--- /dev/null
+++ b/src/core/hle/service/nvflinger/consumer_listener.h
@@ -0,0 +1,26 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/IConsumerListener.h
6
7#pragma once
8
9namespace Service::android {
10
11class BufferItem;
12
13/// ConsumerListener is the interface through which the BufferQueue notifies the consumer of events
14/// that the consumer may wish to react to.
15class IConsumerListener {
16public:
17 IConsumerListener() = default;
18 virtual ~IConsumerListener() = default;
19
20 virtual void OnFrameAvailable(const BufferItem& item) = 0;
21 virtual void OnFrameReplaced(const BufferItem& item) = 0;
22 virtual void OnBuffersReleased() = 0;
23 virtual void OnSidebandStreamChanged() = 0;
24};
25
26}; // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/graphic_buffer_producer.cpp b/src/core/hle/service/nvflinger/graphic_buffer_producer.cpp
new file mode 100644
index 000000000..d4da98ddb
--- /dev/null
+++ b/src/core/hle/service/nvflinger/graphic_buffer_producer.cpp
@@ -0,0 +1,20 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2010 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/IGraphicBufferProducer.cpp
6
7#pragma once
8
9#include "core/hle/service/nvflinger/graphic_buffer_producer.h"
10#include "core/hle/service/nvflinger/parcel.h"
11
12namespace Service::android {
13
14QueueBufferInput::QueueBufferInput(Parcel& parcel) {
15 parcel.ReadFlattened(*this);
16}
17
18QueueBufferOutput::QueueBufferOutput() = default;
19
20} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/graphic_buffer_producer.h b/src/core/hle/service/nvflinger/graphic_buffer_producer.h
new file mode 100644
index 000000000..98d27871c
--- /dev/null
+++ b/src/core/hle/service/nvflinger/graphic_buffer_producer.h
@@ -0,0 +1,76 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2010 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/IGraphicBufferProducer.h
6
7#pragma once
8
9#include "common/common_funcs.h"
10#include "common/common_types.h"
11#include "common/math_util.h"
12#include "core/hle/service/nvflinger/ui/fence.h"
13#include "core/hle/service/nvflinger/window.h"
14
15namespace Service::android {
16
17class Parcel;
18
19#pragma pack(push, 1)
20struct QueueBufferInput final {
21 explicit QueueBufferInput(Parcel& parcel);
22
23 void Deflate(s64* timestamp_, bool* is_auto_timestamp_, Common::Rectangle<s32>* crop_,
24 NativeWindowScalingMode* scaling_mode_, NativeWindowTransform* transform_,
25 u32* sticky_transform_, bool* async_, s32* swap_interval_, Fence* fence_) const {
26 *timestamp_ = timestamp;
27 *is_auto_timestamp_ = static_cast<bool>(is_auto_timestamp);
28 *crop_ = crop;
29 *scaling_mode_ = scaling_mode;
30 *transform_ = transform;
31 *sticky_transform_ = sticky_transform;
32 *async_ = static_cast<bool>(async);
33 *swap_interval_ = swap_interval;
34 *fence_ = fence;
35 }
36
37private:
38 s64 timestamp{};
39 s32 is_auto_timestamp{};
40 Common::Rectangle<s32> crop{};
41 NativeWindowScalingMode scaling_mode{};
42 NativeWindowTransform transform{};
43 u32 sticky_transform{};
44 s32 async{};
45 s32 swap_interval{};
46 Fence fence{};
47};
48#pragma pack(pop)
49static_assert(sizeof(QueueBufferInput) == 84, "QueueBufferInput has wrong size");
50
51struct QueueBufferOutput final {
52 QueueBufferOutput();
53
54 void Deflate(u32* width_, u32* height_, u32* transform_hint_, u32* num_pending_buffers_) const {
55 *width_ = width;
56 *height_ = height;
57 *transform_hint_ = transform_hint;
58 *num_pending_buffers_ = num_pending_buffers;
59 }
60
61 void Inflate(u32 width_, u32 height_, u32 transform_hint_, u32 num_pending_buffers_) {
62 width = width_;
63 height = height_;
64 transform_hint = transform_hint_;
65 num_pending_buffers = num_pending_buffers_;
66 }
67
68private:
69 u32 width{};
70 u32 height{};
71 u32 transform_hint{};
72 u32 num_pending_buffers{};
73};
74static_assert(sizeof(QueueBufferOutput) == 16, "QueueBufferOutput has wrong size");
75
76} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/hos_binder_driver_server.cpp b/src/core/hle/service/nvflinger/hos_binder_driver_server.cpp
new file mode 100644
index 000000000..0c937d682
--- /dev/null
+++ b/src/core/hle/service/nvflinger/hos_binder_driver_server.cpp
@@ -0,0 +1,36 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3
4#include <mutex>
5
6#include "common/common_types.h"
7#include "core/hle/service/nvflinger/hos_binder_driver_server.h"
8
9namespace Service::NVFlinger {
10
11HosBinderDriverServer::HosBinderDriverServer(Core::System& system_)
12 : service_context(system_, "HosBinderDriverServer") {}
13
14HosBinderDriverServer::~HosBinderDriverServer() {}
15
16u64 HosBinderDriverServer::RegisterProducer(std::unique_ptr<android::IBinder>&& binder) {
17 std::lock_guard lk{lock};
18
19 last_id++;
20
21 producers[last_id] = std::move(binder);
22
23 return last_id;
24}
25
26android::IBinder* HosBinderDriverServer::TryGetProducer(u64 id) {
27 std::lock_guard lk{lock};
28
29 if (auto search = producers.find(id); search != producers.end()) {
30 return search->second.get();
31 }
32
33 return {};
34}
35
36} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/hos_binder_driver_server.h b/src/core/hle/service/nvflinger/hos_binder_driver_server.h
new file mode 100644
index 000000000..cbca87fa0
--- /dev/null
+++ b/src/core/hle/service/nvflinger/hos_binder_driver_server.h
@@ -0,0 +1,37 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3
4#pragma once
5
6#include <memory>
7#include <mutex>
8#include <unordered_map>
9
10#include "common/common_types.h"
11#include "core/hle/service/kernel_helpers.h"
12#include "core/hle/service/nvflinger/binder.h"
13
14namespace Core {
15class System;
16}
17
18namespace Service::NVFlinger {
19
20class HosBinderDriverServer final {
21public:
22 explicit HosBinderDriverServer(Core::System& system_);
23 ~HosBinderDriverServer();
24
25 u64 RegisterProducer(std::unique_ptr<android::IBinder>&& binder);
26
27 android::IBinder* TryGetProducer(u64 id);
28
29private:
30 KernelHelpers::ServiceContext service_context;
31
32 std::unordered_map<u64, std::unique_ptr<android::IBinder>> producers;
33 std::mutex lock;
34 u64 last_id{};
35};
36
37} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 01e69de30..76ce1fbfd 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -1,6 +1,5 @@
1// Copyright 2018 yuzu emulator team 1// SPDX-License-Identifier: GPL-3.0-or-later
2// Licensed under GPLv2 or any later version 2// Copyright 2021 yuzu Emulator Project
3// Refer to the license.txt file included.
4 3
5#include <algorithm> 4#include <algorithm>
6#include <optional> 5#include <optional>
@@ -16,8 +15,11 @@
16#include "core/hle/kernel/k_readable_event.h" 15#include "core/hle/kernel/k_readable_event.h"
17#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" 16#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
18#include "core/hle/service/nvdrv/nvdrv.h" 17#include "core/hle/service/nvdrv/nvdrv.h"
19#include "core/hle/service/nvflinger/buffer_queue.h" 18#include "core/hle/service/nvflinger/buffer_item_consumer.h"
19#include "core/hle/service/nvflinger/buffer_queue_core.h"
20#include "core/hle/service/nvflinger/hos_binder_driver_server.h"
20#include "core/hle/service/nvflinger/nvflinger.h" 21#include "core/hle/service/nvflinger/nvflinger.h"
22#include "core/hle/service/nvflinger/ui/graphic_buffer.h"
21#include "core/hle/service/vi/display/vi_display.h" 23#include "core/hle/service/vi/display/vi_display.h"
22#include "core/hle/service/vi/layer/vi_layer.h" 24#include "core/hle/service/vi/layer/vi_layer.h"
23#include "video_core/gpu.h" 25#include "video_core/gpu.h"
@@ -53,13 +55,14 @@ void NVFlinger::SplitVSync(std::stop_token stop_token) {
53 } 55 }
54} 56}
55 57
56NVFlinger::NVFlinger(Core::System& system_) 58NVFlinger::NVFlinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_)
57 : system(system_), service_context(system_, "nvflinger") { 59 : system(system_), service_context(system_, "nvflinger"),
58 displays.emplace_back(0, "Default", service_context, system); 60 hos_binder_driver_server(hos_binder_driver_server_) {
59 displays.emplace_back(1, "External", service_context, system); 61 displays.emplace_back(0, "Default", hos_binder_driver_server, service_context, system);
60 displays.emplace_back(2, "Edid", service_context, system); 62 displays.emplace_back(1, "External", hos_binder_driver_server, service_context, system);
61 displays.emplace_back(3, "Internal", service_context, system); 63 displays.emplace_back(2, "Edid", hos_binder_driver_server, service_context, system);
62 displays.emplace_back(4, "Null", service_context, system); 64 displays.emplace_back(3, "Internal", hos_binder_driver_server, service_context, system);
65 displays.emplace_back(4, "Null", hos_binder_driver_server, service_context, system);
63 guard = std::make_shared<std::mutex>(); 66 guard = std::make_shared<std::mutex>();
64 67
65 // Schedule the screen composition events 68 // Schedule the screen composition events
@@ -83,12 +86,15 @@ NVFlinger::NVFlinger(Core::System& system_)
83} 86}
84 87
85NVFlinger::~NVFlinger() { 88NVFlinger::~NVFlinger() {
86 for (auto& buffer_queue : buffer_queues) {
87 buffer_queue->Disconnect();
88 }
89 if (!system.IsMulticore()) { 89 if (!system.IsMulticore()) {
90 system.CoreTiming().UnscheduleEvent(composition_event, 0); 90 system.CoreTiming().UnscheduleEvent(composition_event, 0);
91 } 91 }
92
93 for (auto& display : displays) {
94 for (size_t layer = 0; layer < display.GetNumLayers(); ++layer) {
95 display.GetLayer(layer).Core().NotifyShutdown();
96 }
97 }
92} 98}
93 99
94void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) { 100void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {
@@ -125,10 +131,8 @@ std::optional<u64> NVFlinger::CreateLayer(u64 display_id) {
125} 131}
126 132
127void NVFlinger::CreateLayerAtId(VI::Display& display, u64 layer_id) { 133void NVFlinger::CreateLayerAtId(VI::Display& display, u64 layer_id) {
128 const u32 buffer_queue_id = next_buffer_queue_id++; 134 const auto buffer_id = next_buffer_queue_id++;
129 buffer_queues.emplace_back( 135 display.CreateLayer(layer_id, buffer_id);
130 std::make_unique<BufferQueue>(system.Kernel(), buffer_queue_id, layer_id, service_context));
131 display.CreateLayer(layer_id, *buffer_queues.back());
132} 136}
133 137
134void NVFlinger::CloseLayer(u64 layer_id) { 138void NVFlinger::CloseLayer(u64 layer_id) {
@@ -147,7 +151,7 @@ std::optional<u32> NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) {
147 return std::nullopt; 151 return std::nullopt;
148 } 152 }
149 153
150 return layer->GetBufferQueue().GetId(); 154 return layer->GetBinderId();
151} 155}
152 156
153Kernel::KReadableEvent* NVFlinger::FindVsyncEvent(u64 display_id) { 157Kernel::KReadableEvent* NVFlinger::FindVsyncEvent(u64 display_id) {
@@ -161,18 +165,6 @@ Kernel::KReadableEvent* NVFlinger::FindVsyncEvent(u64 display_id) {
161 return &display->GetVSyncEvent(); 165 return &display->GetVSyncEvent();
162} 166}
163 167
164BufferQueue* NVFlinger::FindBufferQueue(u32 id) {
165 const auto lock_guard = Lock();
166 const auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(),
167 [id](const auto& queue) { return queue->GetId() == id; });
168
169 if (itr == buffer_queues.end()) {
170 return nullptr;
171 }
172
173 return itr->get();
174}
175
176VI::Display* NVFlinger::FindDisplay(u64 display_id) { 168VI::Display* NVFlinger::FindDisplay(u64 display_id) {
177 const auto itr = 169 const auto itr =
178 std::find_if(displays.begin(), displays.end(), 170 std::find_if(displays.begin(), displays.end(),
@@ -246,23 +238,22 @@ void NVFlinger::Compose() {
246 238
247 // TODO(Subv): Support more than 1 layer. 239 // TODO(Subv): Support more than 1 layer.
248 VI::Layer& layer = display.GetLayer(0); 240 VI::Layer& layer = display.GetLayer(0);
249 auto& buffer_queue = layer.GetBufferQueue();
250 241
251 // Search for a queued buffer and acquire it 242 android::BufferItem buffer{};
252 auto buffer = buffer_queue.AcquireBuffer(); 243 const auto status = layer.GetConsumer().AcquireBuffer(&buffer, {}, false);
253 244
254 if (!buffer) { 245 if (status != android::Status::NoError) {
255 continue; 246 continue;
256 } 247 }
257 248
258 const auto& igbp_buffer = buffer->get().igbp_buffer; 249 const auto& igbp_buffer = *buffer.graphic_buffer;
259 250
260 if (!system.IsPoweredOn()) { 251 if (!system.IsPoweredOn()) {
261 return; // We are likely shutting down 252 return; // We are likely shutting down
262 } 253 }
263 254
264 auto& gpu = system.GPU(); 255 auto& gpu = system.GPU();
265 const auto& multi_fence = buffer->get().multi_fence; 256 const auto& multi_fence = buffer.fence;
266 guard->unlock(); 257 guard->unlock();
267 for (u32 fence_id = 0; fence_id < multi_fence.num_fences; fence_id++) { 258 for (u32 fence_id = 0; fence_id < multi_fence.num_fences; fence_id++) {
268 const auto& fence = multi_fence.fences[fence_id]; 259 const auto& fence = multi_fence.fences[fence_id];
@@ -278,12 +269,18 @@ void NVFlinger::Compose() {
278 auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>("/dev/nvdisp_disp0"); 269 auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>("/dev/nvdisp_disp0");
279 ASSERT(nvdisp); 270 ASSERT(nvdisp);
280 271
281 nvdisp->flip(igbp_buffer.gpu_buffer_id, igbp_buffer.offset, igbp_buffer.external_format, 272 Common::Rectangle<int> crop_rect{
282 igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride, 273 static_cast<int>(buffer.crop.Left()), static_cast<int>(buffer.crop.Top()),
283 buffer->get().transform, buffer->get().crop_rect); 274 static_cast<int>(buffer.crop.Right()), static_cast<int>(buffer.crop.Bottom())};
275
276 nvdisp->flip(igbp_buffer.BufferId(), igbp_buffer.Offset(), igbp_buffer.ExternalFormat(),
277 igbp_buffer.Width(), igbp_buffer.Height(), igbp_buffer.Stride(),
278 static_cast<android::BufferTransformFlags>(buffer.transform), crop_rect);
279
280 swap_interval = buffer.swap_interval;
284 281
285 swap_interval = buffer->get().swap_interval; 282 auto fence = android::Fence::NoFence();
286 buffer_queue.ReleaseBuffer(buffer->get().slot); 283 layer.GetConsumer().ReleaseBuffer(buffer, fence);
287 } 284 }
288} 285}
289 286
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index 7935cf773..ed160f6f9 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -1,6 +1,5 @@
1// Copyright 2018 yuzu emulator team 1// SPDX-License-Identifier: GPL-3.0-or-later
2// Licensed under GPLv2 or any later version 2// Copyright 2021 yuzu Emulator Project
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
@@ -37,13 +36,16 @@ class Display;
37class Layer; 36class Layer;
38} // namespace Service::VI 37} // namespace Service::VI
39 38
40namespace Service::NVFlinger { 39namespace Service::android {
40class BufferQueueCore;
41class BufferQueueProducer;
42} // namespace Service::android
41 43
42class BufferQueue; 44namespace Service::NVFlinger {
43 45
44class NVFlinger final { 46class NVFlinger final {
45public: 47public:
46 explicit NVFlinger(Core::System& system_); 48 explicit NVFlinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_);
47 ~NVFlinger(); 49 ~NVFlinger();
48 50
49 /// Sets the NVDrv module instance to use to send buffers to the GPU. 51 /// Sets the NVDrv module instance to use to send buffers to the GPU.
@@ -72,9 +74,6 @@ public:
72 /// If an invalid display ID is provided, then nullptr is returned. 74 /// If an invalid display ID is provided, then nullptr is returned.
73 [[nodiscard]] Kernel::KReadableEvent* FindVsyncEvent(u64 display_id); 75 [[nodiscard]] Kernel::KReadableEvent* FindVsyncEvent(u64 display_id);
74 76
75 /// Obtains a buffer queue identified by the ID.
76 [[nodiscard]] BufferQueue* FindBufferQueue(u32 id);
77
78 /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when 77 /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when
79 /// finished. 78 /// finished.
80 void Compose(); 79 void Compose();
@@ -82,6 +81,12 @@ public:
82 [[nodiscard]] s64 GetNextTicks() const; 81 [[nodiscard]] s64 GetNextTicks() const;
83 82
84private: 83private:
84 struct Layer {
85 std::unique_ptr<android::BufferQueueCore> core;
86 std::unique_ptr<android::BufferQueueProducer> producer;
87 };
88
89private:
85 [[nodiscard]] std::unique_lock<std::mutex> Lock() const { 90 [[nodiscard]] std::unique_lock<std::mutex> Lock() const {
86 return std::unique_lock{*guard}; 91 return std::unique_lock{*guard};
87 } 92 }
@@ -111,7 +116,6 @@ private:
111 std::shared_ptr<Nvidia::Module> nvdrv; 116 std::shared_ptr<Nvidia::Module> nvdrv;
112 117
113 std::list<VI::Display> displays; 118 std::list<VI::Display> displays;
114 std::vector<std::unique_ptr<BufferQueue>> buffer_queues;
115 119
116 /// Id to use for the next layer that is created, this counter is shared among all displays. 120 /// Id to use for the next layer that is created, this counter is shared among all displays.
117 u64 next_layer_id = 1; 121 u64 next_layer_id = 1;
@@ -131,6 +135,8 @@ private:
131 std::jthread vsync_thread; 135 std::jthread vsync_thread;
132 136
133 KernelHelpers::ServiceContext service_context; 137 KernelHelpers::ServiceContext service_context;
138
139 HosBinderDriverServer& hos_binder_driver_server;
134}; 140};
135 141
136} // namespace Service::NVFlinger 142} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/parcel.h b/src/core/hle/service/nvflinger/parcel.h
new file mode 100644
index 000000000..aa36e6479
--- /dev/null
+++ b/src/core/hle/service/nvflinger/parcel.h
@@ -0,0 +1,172 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3
4#pragma once
5
6#include <memory>
7#include <vector>
8
9#include "common/alignment.h"
10#include "common/assert.h"
11#include "common/common_types.h"
12
13namespace Service::android {
14
15class Parcel final {
16public:
17 static constexpr std::size_t DefaultBufferSize = 0x40;
18
19 Parcel() : buffer(DefaultBufferSize) {}
20
21 template <typename T>
22 explicit Parcel(const T& out_data) : buffer(DefaultBufferSize) {
23 Write(out_data);
24 }
25
26 explicit Parcel(std::vector<u8> in_data) : buffer(std::move(in_data)) {
27 DeserializeHeader();
28 [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
29 }
30
31 template <typename T>
32 void Read(T& val) {
33 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
34 ASSERT(read_index + sizeof(T) <= buffer.size());
35
36 std::memcpy(&val, buffer.data() + read_index, sizeof(T));
37 read_index += sizeof(T);
38 read_index = Common::AlignUp(read_index, 4);
39 }
40
41 template <typename T>
42 T Read() {
43 T val;
44 Read(val);
45 return val;
46 }
47
48 template <typename T>
49 void ReadFlattened(T& val) {
50 const auto flattened_size = Read<s64>();
51 ASSERT(sizeof(T) == flattened_size);
52 Read(val);
53 }
54
55 template <typename T>
56 T ReadFlattened() {
57 T val;
58 ReadFlattened(val);
59 return val;
60 }
61
62 template <typename T>
63 T ReadUnaligned() {
64 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
65 ASSERT(read_index + sizeof(T) <= buffer.size());
66
67 T val;
68 std::memcpy(&val, buffer.data() + read_index, sizeof(T));
69 read_index += sizeof(T);
70 return val;
71 }
72
73 template <typename T>
74 const std::shared_ptr<T> ReadObject() {
75 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
76
77 const auto is_valid{Read<bool>()};
78
79 if (is_valid) {
80 auto result = std::make_shared<T>();
81 ReadFlattened(*result);
82 return result;
83 }
84
85 return {};
86 }
87
88 std::u16string ReadInterfaceToken() {
89 [[maybe_unused]] const u32 unknown = Read<u32>();
90 const u32 length = Read<u32>();
91
92 std::u16string token;
93 token.reserve(length + 1);
94
95 for (u32 ch = 0; ch < length + 1; ++ch) {
96 token.push_back(ReadUnaligned<u16>());
97 }
98
99 read_index = Common::AlignUp(read_index, 4);
100
101 return token;
102 }
103
104 template <typename T>
105 void Write(const T& val) {
106 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
107
108 if (buffer.size() < write_index + sizeof(T)) {
109 buffer.resize(buffer.size() + sizeof(T) + DefaultBufferSize);
110 }
111
112 std::memcpy(buffer.data() + write_index, &val, sizeof(T));
113 write_index += sizeof(T);
114 write_index = Common::AlignUp(write_index, 4);
115 }
116
117 template <typename T>
118 void WriteObject(const T* ptr) {
119 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
120
121 if (!ptr) {
122 Write<u32>(0);
123 return;
124 }
125
126 Write<u32>(1);
127 Write<s64>(sizeof(T));
128 Write(*ptr);
129 }
130
131 template <typename T>
132 void WriteObject(const std::shared_ptr<T> ptr) {
133 WriteObject(ptr.get());
134 }
135
136 void DeserializeHeader() {
137 ASSERT(buffer.size() > sizeof(Header));
138
139 Header header{};
140 std::memcpy(&header, buffer.data(), sizeof(Header));
141
142 read_index = header.data_offset;
143 }
144
145 std::vector<u8> Serialize() const {
146 ASSERT(read_index == 0);
147
148 Header header{};
149 header.data_size = static_cast<u32>(write_index - sizeof(Header));
150 header.data_offset = sizeof(Header);
151 header.objects_size = 4;
152 header.objects_offset = static_cast<u32>(sizeof(Header) + header.data_size);
153 std::memcpy(buffer.data(), &header, sizeof(Header));
154
155 return buffer;
156 }
157
158private:
159 struct Header {
160 u32 data_size;
161 u32 data_offset;
162 u32 objects_size;
163 u32 objects_offset;
164 };
165 static_assert(sizeof(Header) == 16, "ParcelHeader has wrong size");
166
167 mutable std::vector<u8> buffer;
168 std::size_t read_index = 0;
169 std::size_t write_index = sizeof(Header);
170};
171
172} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/pixel_format.h b/src/core/hle/service/nvflinger/pixel_format.h
new file mode 100644
index 000000000..8a77d8bea
--- /dev/null
+++ b/src/core/hle/service/nvflinger/pixel_format.h
@@ -0,0 +1,21 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3
4#pragma once
5
6#include "common/common_types.h"
7
8namespace Service::android {
9
10enum class PixelFormat : u32 {
11 NoFormat = 0,
12 Rgba8888 = 1,
13 Rgbx8888 = 2,
14 Rgb888 = 3,
15 Rgb565 = 4,
16 Bgra8888 = 5,
17 Rgba5551 = 6,
18 Rgba4444 = 7,
19};
20
21} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/producer_listener.h b/src/core/hle/service/nvflinger/producer_listener.h
new file mode 100644
index 000000000..468e06431
--- /dev/null
+++ b/src/core/hle/service/nvflinger/producer_listener.h
@@ -0,0 +1,16 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/IProducerListener.h
6
7#pragma once
8
9namespace Service::android {
10
11class IProducerListener {
12public:
13 virtual void OnBufferReleased() = 0;
14};
15
16} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/status.h b/src/core/hle/service/nvflinger/status.h
new file mode 100644
index 000000000..a003eda89
--- /dev/null
+++ b/src/core/hle/service/nvflinger/status.h
@@ -0,0 +1,28 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3
4#pragma once
5
6#include "common/common_funcs.h"
7#include "common/common_types.h"
8
9namespace Service::android {
10
11enum class Status : s32 {
12 None = 0,
13 NoError = 0,
14 StaleBufferSlot = 1,
15 NoBufferAvailable = 2,
16 PresentLater = 3,
17 WouldBlock = -11,
18 NoMemory = -12,
19 Busy = -16,
20 NoInit = -19,
21 BadValue = -22,
22 InvalidOperation = -37,
23 BufferNeedsReallocation = 1,
24 ReleaseAllBuffers = 2,
25};
26DECLARE_ENUM_FLAG_OPERATORS(Status);
27
28} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/ui/fence.h b/src/core/hle/service/nvflinger/ui/fence.h
new file mode 100644
index 000000000..4a74e26a3
--- /dev/null
+++ b/src/core/hle/service/nvflinger/ui/fence.h
@@ -0,0 +1,32 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2012 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/ui/Fence.h
6
7#pragma once
8
9#include <array>
10
11#include "common/common_types.h"
12#include "core/hle/service/nvdrv/nvdata.h"
13
14namespace Service::android {
15
16class Fence {
17public:
18 constexpr Fence() = default;
19
20 static constexpr Fence NoFence() {
21 Fence fence;
22 fence.fences[0].id = -1;
23 return fence;
24 }
25
26public:
27 u32 num_fences{};
28 std::array<Service::Nvidia::NvFence, 4> fences{};
29};
30static_assert(sizeof(Fence) == 36, "Fence has wrong size");
31
32} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/ui/graphic_buffer.h b/src/core/hle/service/nvflinger/ui/graphic_buffer.h
new file mode 100644
index 000000000..7abbf78ba
--- /dev/null
+++ b/src/core/hle/service/nvflinger/ui/graphic_buffer.h
@@ -0,0 +1,100 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2007 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/ui/GraphicBuffer.h
6
7#pragma once
8
9#include "common/common_funcs.h"
10#include "common/common_types.h"
11#include "core/hle/service/nvflinger/pixel_format.h"
12
13namespace Service::android {
14
15class GraphicBuffer final {
16public:
17 constexpr GraphicBuffer() = default;
18
19 constexpr GraphicBuffer(u32 width_, u32 height_, PixelFormat format_, u32 usage_)
20 : width{static_cast<s32>(width_)}, height{static_cast<s32>(height_)}, format{format_},
21 usage{static_cast<s32>(usage_)} {}
22
23 constexpr u32 Width() const {
24 return static_cast<u32>(width);
25 }
26
27 constexpr u32 Height() const {
28 return static_cast<u32>(height);
29 }
30
31 constexpr u32 Stride() const {
32 return static_cast<u32>(stride);
33 }
34
35 constexpr u32 Usage() const {
36 return static_cast<u32>(usage);
37 }
38
39 constexpr PixelFormat Format() const {
40 return format;
41 }
42
43 constexpr u32 BufferId() const {
44 return buffer_id;
45 }
46
47 constexpr PixelFormat ExternalFormat() const {
48 return external_format;
49 }
50
51 constexpr u32 Handle() const {
52 return handle;
53 }
54
55 constexpr u32 Offset() const {
56 return offset;
57 }
58
59 constexpr bool NeedsReallocation(u32 width_, u32 height_, PixelFormat format_,
60 u32 usage_) const {
61 if (static_cast<s32>(width_) != width) {
62 return true;
63 }
64
65 if (static_cast<s32>(height_) != height) {
66 return true;
67 }
68
69 if (format_ != format) {
70 return true;
71 }
72
73 if ((static_cast<u32>(usage) & usage_) != usage_) {
74 return true;
75 }
76
77 return false;
78 }
79
80private:
81 u32 magic{};
82 s32 width{};
83 s32 height{};
84 s32 stride{};
85 PixelFormat format{};
86 s32 usage{};
87 INSERT_PADDING_WORDS(1);
88 u32 index{};
89 INSERT_PADDING_WORDS(3);
90 u32 buffer_id{};
91 INSERT_PADDING_WORDS(6);
92 PixelFormat external_format{};
93 INSERT_PADDING_WORDS(10);
94 u32 handle{};
95 u32 offset{};
96 INSERT_PADDING_WORDS(60);
97};
98static_assert(sizeof(GraphicBuffer) == 0x16C, "GraphicBuffer has wrong size");
99
100} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/window.h b/src/core/hle/service/nvflinger/window.h
new file mode 100644
index 000000000..e26f8160e
--- /dev/null
+++ b/src/core/hle/service/nvflinger/window.h
@@ -0,0 +1,53 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3
4#pragma once
5
6#include "common/common_funcs.h"
7#include "common/common_types.h"
8
9namespace Service::android {
10
11/// Attributes queryable with Query
12enum class NativeWindow : s32 {
13 Width = 0,
14 Height = 1,
15 Format = 2,
16 MinUndequeedBuffers = 3,
17 QueuesToWindowComposer = 4,
18 ConcreteType = 5,
19 DefaultWidth = 6,
20 DefaultHeight = 7,
21 TransformHint = 8,
22 ConsumerRunningBehind = 9,
23 ConsumerUsageBits = 10,
24 StickyTransform = 11,
25 DefaultDataSpace = 12,
26 BufferAge = 13,
27};
28
29/// Parameter for Connect/Disconnect
30enum class NativeWindowApi : s32 {
31 NoConnectedApi = 0,
32 Egl = 1,
33 Cpu = 2,
34 Media = 3,
35 Camera = 4,
36};
37
38/// Scaling mode parameter for QueueBuffer
39enum class NativeWindowScalingMode : s32 {
40 Freeze = 0,
41 ScaleToWindow = 1,
42 ScaleCrop = 2,
43 NoScaleCrop = 3,
44};
45
46/// Transform parameter for QueueBuffer
47enum class NativeWindowTransform : u32 {
48 None = 0x0,
49 InverseDisplay = 0x08,
50};
51DECLARE_ENUM_FLAG_OPERATORS(NativeWindowTransform);
52
53} // namespace Service::android
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index eb1138313..ab3286db9 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -49,6 +49,7 @@
49#include "core/hle/service/npns/npns.h" 49#include "core/hle/service/npns/npns.h"
50#include "core/hle/service/ns/ns.h" 50#include "core/hle/service/ns/ns.h"
51#include "core/hle/service/nvdrv/nvdrv.h" 51#include "core/hle/service/nvdrv/nvdrv.h"
52#include "core/hle/service/nvflinger/hos_binder_driver_server.h"
52#include "core/hle/service/nvflinger/nvflinger.h" 53#include "core/hle/service/nvflinger/nvflinger.h"
53#include "core/hle/service/olsc/olsc.h" 54#include "core/hle/service/olsc/olsc.h"
54#include "core/hle/service/pcie/pcie.h" 55#include "core/hle/service/pcie/pcie.h"
@@ -230,7 +231,8 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& sessi
230 231
231/// Initialize Services 232/// Initialize Services
232Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) 233Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system)
233 : nv_flinger{std::make_unique<NVFlinger::NVFlinger>(system)} { 234 : hos_binder_driver_server{std::make_unique<NVFlinger::HosBinderDriverServer>(system)},
235 nv_flinger{std::make_unique<NVFlinger::NVFlinger>(system, *hos_binder_driver_server)} {
234 236
235 // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it 237 // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it
236 // here and pass it into the respective InstallInterfaces functions. 238 // here and pass it into the respective InstallInterfaces functions.
@@ -290,7 +292,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
290 SSL::InstallInterfaces(*sm, system); 292 SSL::InstallInterfaces(*sm, system);
291 Time::InstallInterfaces(system); 293 Time::InstallInterfaces(system);
292 USB::InstallInterfaces(*sm, system); 294 USB::InstallInterfaces(*sm, system);
293 VI::InstallInterfaces(*sm, system, *nv_flinger); 295 VI::InstallInterfaces(*sm, system, *nv_flinger, *hos_binder_driver_server);
294 WLAN::InstallInterfaces(*sm, system); 296 WLAN::InstallInterfaces(*sm, system);
295} 297}
296 298
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index c9d6b879d..b9ab2c465 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -33,8 +33,9 @@ class FileSystemController;
33} 33}
34 34
35namespace NVFlinger { 35namespace NVFlinger {
36class HosBinderDriverServer;
36class NVFlinger; 37class NVFlinger;
37} 38} // namespace NVFlinger
38 39
39namespace SM { 40namespace SM {
40class ServiceManager; 41class ServiceManager;
@@ -236,6 +237,7 @@ public:
236 ~Services(); 237 ~Services();
237 238
238private: 239private:
240 std::unique_ptr<NVFlinger::HosBinderDriverServer> hos_binder_driver_server;
239 std::unique_ptr<NVFlinger::NVFlinger> nv_flinger; 241 std::unique_ptr<NVFlinger::NVFlinger> nv_flinger;
240}; 242};
241 243
diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp
index b7705c02a..558022511 100644
--- a/src/core/hle/service/vi/display/vi_display.cpp
+++ b/src/core/hle/service/vi/display/vi_display.cpp
@@ -13,14 +13,34 @@
13#include "core/hle/kernel/k_readable_event.h" 13#include "core/hle/kernel/k_readable_event.h"
14#include "core/hle/kernel/k_writable_event.h" 14#include "core/hle/kernel/k_writable_event.h"
15#include "core/hle/service/kernel_helpers.h" 15#include "core/hle/service/kernel_helpers.h"
16#include "core/hle/service/nvflinger/buffer_item_consumer.h"
17#include "core/hle/service/nvflinger/buffer_queue_consumer.h"
18#include "core/hle/service/nvflinger/buffer_queue_core.h"
19#include "core/hle/service/nvflinger/buffer_queue_producer.h"
20#include "core/hle/service/nvflinger/hos_binder_driver_server.h"
16#include "core/hle/service/vi/display/vi_display.h" 21#include "core/hle/service/vi/display/vi_display.h"
17#include "core/hle/service/vi/layer/vi_layer.h" 22#include "core/hle/service/vi/layer/vi_layer.h"
18 23
19namespace Service::VI { 24namespace Service::VI {
20 25
21Display::Display(u64 id, std::string name_, KernelHelpers::ServiceContext& service_context_, 26struct BufferQueue {
22 Core::System& system_) 27 std::shared_ptr<android::BufferQueueCore> core;
23 : display_id{id}, name{std::move(name_)}, service_context{service_context_} { 28 std::unique_ptr<android::BufferQueueProducer> producer;
29 std::unique_ptr<android::BufferQueueConsumer> consumer;
30};
31
32static BufferQueue CreateBufferQueue(KernelHelpers::ServiceContext& service_context) {
33 auto buffer_queue_core = std::make_shared<android::BufferQueueCore>();
34 return {buffer_queue_core,
35 std::make_unique<android::BufferQueueProducer>(service_context, buffer_queue_core),
36 std::make_unique<android::BufferQueueConsumer>(buffer_queue_core)};
37}
38
39Display::Display(u64 id, std::string name_,
40 NVFlinger::HosBinderDriverServer& hos_binder_driver_server_,
41 KernelHelpers::ServiceContext& service_context_, Core::System& system_)
42 : display_id{id}, name{std::move(name_)}, hos_binder_driver_server{hos_binder_driver_server_},
43 service_context{service_context_} {
24 vsync_event = service_context.CreateEvent(fmt::format("Display VSync Event {}", id)); 44 vsync_event = service_context.CreateEvent(fmt::format("Display VSync Event {}", id));
25} 45}
26 46
@@ -44,21 +64,29 @@ void Display::SignalVSyncEvent() {
44 vsync_event->GetWritableEvent().Signal(); 64 vsync_event->GetWritableEvent().Signal();
45} 65}
46 66
47void Display::CreateLayer(u64 layer_id, NVFlinger::BufferQueue& buffer_queue) { 67void Display::CreateLayer(u64 layer_id, u32 binder_id) {
48 // TODO(Subv): Support more than 1 layer.
49 ASSERT_MSG(layers.empty(), "Only one layer is supported per display at the moment"); 68 ASSERT_MSG(layers.empty(), "Only one layer is supported per display at the moment");
50 69
51 layers.emplace_back(std::make_shared<Layer>(layer_id, buffer_queue)); 70 auto [core, producer, consumer] = CreateBufferQueue(service_context);
71
72 auto buffer_item_consumer = std::make_shared<android::BufferItemConsumer>(std::move(consumer));
73 buffer_item_consumer->Connect(false);
74
75 layers.emplace_back(std::make_unique<Layer>(layer_id, binder_id, *core, *producer,
76 std::move(buffer_item_consumer)));
77
78 hos_binder_driver_server.RegisterProducer(std::move(producer));
52} 79}
53 80
54void Display::CloseLayer(u64 layer_id) { 81void Display::CloseLayer(u64 layer_id) {
55 std::erase_if(layers, [layer_id](const auto& layer) { return layer->GetID() == layer_id; }); 82 std::erase_if(layers,
83 [layer_id](const auto& layer) { return layer->GetLayerId() == layer_id; });
56} 84}
57 85
58Layer* Display::FindLayer(u64 layer_id) { 86Layer* Display::FindLayer(u64 layer_id) {
59 const auto itr = 87 const auto itr =
60 std::find_if(layers.begin(), layers.end(), [layer_id](const std::shared_ptr<Layer>& layer) { 88 std::find_if(layers.begin(), layers.end(), [layer_id](const std::unique_ptr<Layer>& layer) {
61 return layer->GetID() == layer_id; 89 return layer->GetLayerId() == layer_id;
62 }); 90 });
63 91
64 if (itr == layers.end()) { 92 if (itr == layers.end()) {
@@ -70,8 +98,8 @@ Layer* Display::FindLayer(u64 layer_id) {
70 98
71const Layer* Display::FindLayer(u64 layer_id) const { 99const Layer* Display::FindLayer(u64 layer_id) const {
72 const auto itr = 100 const auto itr =
73 std::find_if(layers.begin(), layers.end(), [layer_id](const std::shared_ptr<Layer>& layer) { 101 std::find_if(layers.begin(), layers.end(), [layer_id](const std::unique_ptr<Layer>& layer) {
74 return layer->GetID() == layer_id; 102 return layer->GetLayerId() == layer_id;
75 }); 103 });
76 104
77 if (itr == layers.end()) { 105 if (itr == layers.end()) {
diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h
index 329f4ba86..e93d084ee 100644
--- a/src/core/hle/service/vi/display/vi_display.h
+++ b/src/core/hle/service/vi/display/vi_display.h
@@ -15,12 +15,17 @@ namespace Kernel {
15class KEvent; 15class KEvent;
16} 16}
17 17
18namespace Service::NVFlinger { 18namespace Service::android {
19class BufferQueue; 19class BufferQueueProducer;
20} 20}
21
21namespace Service::KernelHelpers { 22namespace Service::KernelHelpers {
22class ServiceContext; 23class ServiceContext;
23} // namespace Service::KernelHelpers 24}
25
26namespace Service::NVFlinger {
27class HosBinderDriverServer;
28}
24 29
25namespace Service::VI { 30namespace Service::VI {
26 31
@@ -35,12 +40,13 @@ public:
35 /// Constructs a display with a given unique ID and name. 40 /// Constructs a display with a given unique ID and name.
36 /// 41 ///
37 /// @param id The unique ID for this display. 42 /// @param id The unique ID for this display.
43 /// @param hos_binder_driver_server_ NVFlinger HOSBinderDriver server instance.
38 /// @param service_context_ The ServiceContext for the owning service. 44 /// @param service_context_ The ServiceContext for the owning service.
39 /// @param name_ The name for this display. 45 /// @param name_ The name for this display.
40 /// @param system_ The global system instance. 46 /// @param system_ The global system instance.
41 /// 47 ///
42 Display(u64 id, std::string name_, KernelHelpers::ServiceContext& service_context_, 48 Display(u64 id, std::string name_, NVFlinger::HosBinderDriverServer& hos_binder_driver_server_,
43 Core::System& system_); 49 KernelHelpers::ServiceContext& service_context_, Core::System& system_);
44 ~Display(); 50 ~Display();
45 51
46 /// Gets the unique ID assigned to this display. 52 /// Gets the unique ID assigned to this display.
@@ -64,6 +70,10 @@ public:
64 /// Gets a layer for this display based off an index. 70 /// Gets a layer for this display based off an index.
65 const Layer& GetLayer(std::size_t index) const; 71 const Layer& GetLayer(std::size_t index) const;
66 72
73 std::size_t GetNumLayers() const {
74 return layers.size();
75 }
76
67 /// Gets the readable vsync event. 77 /// Gets the readable vsync event.
68 Kernel::KReadableEvent& GetVSyncEvent(); 78 Kernel::KReadableEvent& GetVSyncEvent();
69 79
@@ -72,10 +82,10 @@ public:
72 82
73 /// Creates and adds a layer to this display with the given ID. 83 /// Creates and adds a layer to this display with the given ID.
74 /// 84 ///
75 /// @param layer_id The ID to assign to the created layer. 85 /// @param layer_id The ID to assign to the created layer.
76 /// @param buffer_queue The buffer queue for the layer instance to use. 86 /// @param binder_id The ID assigned to the buffer queue.
77 /// 87 ///
78 void CreateLayer(u64 layer_id, NVFlinger::BufferQueue& buffer_queue); 88 void CreateLayer(u64 layer_id, u32 binder_id);
79 89
80 /// Closes and removes a layer from this display with the given ID. 90 /// Closes and removes a layer from this display with the given ID.
81 /// 91 ///
@@ -104,9 +114,10 @@ public:
104private: 114private:
105 u64 display_id; 115 u64 display_id;
106 std::string name; 116 std::string name;
117 NVFlinger::HosBinderDriverServer& hos_binder_driver_server;
107 KernelHelpers::ServiceContext& service_context; 118 KernelHelpers::ServiceContext& service_context;
108 119
109 std::vector<std::shared_ptr<Layer>> layers; 120 std::vector<std::unique_ptr<Layer>> layers;
110 Kernel::KEvent* vsync_event{}; 121 Kernel::KEvent* vsync_event{};
111}; 122};
112 123
diff --git a/src/core/hle/service/vi/layer/vi_layer.cpp b/src/core/hle/service/vi/layer/vi_layer.cpp
index 9bc382587..93858e91f 100644
--- a/src/core/hle/service/vi/layer/vi_layer.cpp
+++ b/src/core/hle/service/vi/layer/vi_layer.cpp
@@ -6,7 +6,11 @@
6 6
7namespace Service::VI { 7namespace Service::VI {
8 8
9Layer::Layer(u64 id, NVFlinger::BufferQueue& queue) : layer_id{id}, buffer_queue{queue} {} 9Layer::Layer(u64 layer_id_, u32 binder_id_, android::BufferQueueCore& core_,
10 android::BufferQueueProducer& binder_,
11 std::shared_ptr<android::BufferItemConsumer>&& consumer_)
12 : layer_id{layer_id_}, binder_id{binder_id_}, core{core_}, binder{binder_}, consumer{std::move(
13 consumer_)} {}
10 14
11Layer::~Layer() = default; 15Layer::~Layer() = default;
12 16
diff --git a/src/core/hle/service/vi/layer/vi_layer.h b/src/core/hle/service/vi/layer/vi_layer.h
index ebdd85505..c28b14450 100644
--- a/src/core/hle/service/vi/layer/vi_layer.h
+++ b/src/core/hle/service/vi/layer/vi_layer.h
@@ -4,11 +4,15 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <memory>
8
7#include "common/common_types.h" 9#include "common/common_types.h"
8 10
9namespace Service::NVFlinger { 11namespace Service::android {
10class BufferQueue; 12class BufferItemConsumer;
11} 13class BufferQueueCore;
14class BufferQueueProducer;
15} // namespace Service::android
12 16
13namespace Service::VI { 17namespace Service::VI {
14 18
@@ -17,10 +21,13 @@ class Layer {
17public: 21public:
18 /// Constructs a layer with a given ID and buffer queue. 22 /// Constructs a layer with a given ID and buffer queue.
19 /// 23 ///
20 /// @param id The ID to assign to this layer. 24 /// @param layer_id_ The ID to assign to this layer.
21 /// @param queue The buffer queue for this layer to use. 25 /// @param binder_id_ The binder ID to assign to this layer.
26 /// @param binder_ The buffer producer queue for this layer to use.
22 /// 27 ///
23 Layer(u64 id, NVFlinger::BufferQueue& queue); 28 Layer(u64 layer_id_, u32 binder_id_, android::BufferQueueCore& core_,
29 android::BufferQueueProducer& binder_,
30 std::shared_ptr<android::BufferItemConsumer>&& consumer_);
24 ~Layer(); 31 ~Layer();
25 32
26 Layer(const Layer&) = delete; 33 Layer(const Layer&) = delete;
@@ -30,23 +37,47 @@ public:
30 Layer& operator=(Layer&&) = delete; 37 Layer& operator=(Layer&&) = delete;
31 38
32 /// Gets the ID for this layer. 39 /// Gets the ID for this layer.
33 u64 GetID() const { 40 u64 GetLayerId() const {
34 return layer_id; 41 return layer_id;
35 } 42 }
36 43
44 /// Gets the binder ID for this layer.
45 u32 GetBinderId() const {
46 return binder_id;
47 }
48
37 /// Gets a reference to the buffer queue this layer is using. 49 /// Gets a reference to the buffer queue this layer is using.
38 NVFlinger::BufferQueue& GetBufferQueue() { 50 android::BufferQueueProducer& GetBufferQueue() {
39 return buffer_queue; 51 return binder;
40 } 52 }
41 53
42 /// Gets a const reference to the buffer queue this layer is using. 54 /// Gets a const reference to the buffer queue this layer is using.
43 const NVFlinger::BufferQueue& GetBufferQueue() const { 55 const android::BufferQueueProducer& GetBufferQueue() const {
44 return buffer_queue; 56 return binder;
57 }
58
59 android::BufferItemConsumer& GetConsumer() {
60 return *consumer;
61 }
62
63 const android::BufferItemConsumer& GetConsumer() const {
64 return *consumer;
65 }
66
67 android::BufferQueueCore& Core() {
68 return core;
69 }
70
71 const android::BufferQueueCore& Core() const {
72 return core;
45 } 73 }
46 74
47private: 75private:
48 u64 layer_id; 76 const u64 layer_id;
49 NVFlinger::BufferQueue& buffer_queue; 77 const u32 binder_id;
78 android::BufferQueueCore& core;
79 android::BufferQueueProducer& binder;
80 std::shared_ptr<android::BufferItemConsumer> consumer;
50}; 81};
51 82
52} // namespace Service::VI 83} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 75ee3e5e4..430cbc546 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -22,8 +22,11 @@
22#include "core/hle/kernel/k_readable_event.h" 22#include "core/hle/kernel/k_readable_event.h"
23#include "core/hle/kernel/k_thread.h" 23#include "core/hle/kernel/k_thread.h"
24#include "core/hle/service/nvdrv/nvdata.h" 24#include "core/hle/service/nvdrv/nvdata.h"
25#include "core/hle/service/nvflinger/buffer_queue.h" 25#include "core/hle/service/nvflinger/binder.h"
26#include "core/hle/service/nvflinger/buffer_queue_producer.h"
27#include "core/hle/service/nvflinger/hos_binder_driver_server.h"
26#include "core/hle/service/nvflinger/nvflinger.h" 28#include "core/hle/service/nvflinger/nvflinger.h"
29#include "core/hle/service/nvflinger/parcel.h"
27#include "core/hle/service/service.h" 30#include "core/hle/service/service.h"
28#include "core/hle/service/vi/vi.h" 31#include "core/hle/service/vi/vi.h"
29#include "core/hle/service/vi/vi_m.h" 32#include "core/hle/service/vi/vi_m.h"
@@ -57,447 +60,24 @@ struct DisplayInfo {
57}; 60};
58static_assert(sizeof(DisplayInfo) == 0x60, "DisplayInfo has wrong size"); 61static_assert(sizeof(DisplayInfo) == 0x60, "DisplayInfo has wrong size");
59 62
60class Parcel { 63class NativeWindow final {
61public: 64public:
62 // This default size was chosen arbitrarily. 65 constexpr explicit NativeWindow(u32 id_) : id{id_} {}
63 static constexpr std::size_t DefaultBufferSize = 0x40;
64 Parcel() : buffer(DefaultBufferSize) {}
65 explicit Parcel(std::vector<u8> data) : buffer(std::move(data)) {}
66 virtual ~Parcel() = default;
67
68 template <typename T>
69 T Read() {
70 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
71 ASSERT(read_index + sizeof(T) <= buffer.size());
72
73 T val;
74 std::memcpy(&val, buffer.data() + read_index, sizeof(T));
75 read_index += sizeof(T);
76 read_index = Common::AlignUp(read_index, 4);
77 return val;
78 }
79
80 template <typename T>
81 T ReadUnaligned() {
82 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
83 ASSERT(read_index + sizeof(T) <= buffer.size());
84
85 T val;
86 std::memcpy(&val, buffer.data() + read_index, sizeof(T));
87 read_index += sizeof(T);
88 return val;
89 }
90
91 std::vector<u8> ReadBlock(std::size_t length) {
92 ASSERT(read_index + length <= buffer.size());
93 const u8* const begin = buffer.data() + read_index;
94 const u8* const end = begin + length;
95 std::vector<u8> data(begin, end);
96 read_index += length;
97 read_index = Common::AlignUp(read_index, 4);
98 return data;
99 }
100
101 std::u16string ReadInterfaceToken() {
102 [[maybe_unused]] const u32 unknown = Read<u32_le>();
103 const u32 length = Read<u32_le>();
104
105 std::u16string token{};
106
107 for (u32 ch = 0; ch < length + 1; ++ch) {
108 token.push_back(ReadUnaligned<u16_le>());
109 }
110
111 read_index = Common::AlignUp(read_index, 4);
112
113 return token;
114 }
115
116 template <typename T>
117 void Write(const T& val) {
118 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
119
120 if (buffer.size() < write_index + sizeof(T)) {
121 buffer.resize(buffer.size() + sizeof(T) + DefaultBufferSize);
122 }
123
124 std::memcpy(buffer.data() + write_index, &val, sizeof(T));
125 write_index += sizeof(T);
126 write_index = Common::AlignUp(write_index, 4);
127 }
128
129 template <typename T>
130 void WriteObject(const T& val) {
131 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
132
133 const u32_le size = static_cast<u32>(sizeof(val));
134 Write(size);
135 // TODO(Subv): Support file descriptors.
136 Write<u32_le>(0); // Fd count.
137 Write(val);
138 }
139
140 void Deserialize() {
141 ASSERT(buffer.size() > sizeof(Header));
142
143 Header header{};
144 std::memcpy(&header, buffer.data(), sizeof(Header));
145
146 read_index = header.data_offset;
147 DeserializeData();
148 }
149
150 std::vector<u8> Serialize() {
151 ASSERT(read_index == 0);
152 write_index = sizeof(Header);
153
154 SerializeData();
155
156 Header header{};
157 header.data_size = static_cast<u32_le>(write_index - sizeof(Header));
158 header.data_offset = sizeof(Header);
159 header.objects_size = 4;
160 header.objects_offset = static_cast<u32>(sizeof(Header) + header.data_size);
161 std::memcpy(buffer.data(), &header, sizeof(Header));
162
163 return buffer;
164 }
165
166protected:
167 virtual void SerializeData() {}
168
169 virtual void DeserializeData() {}
170
171private:
172 struct Header {
173 u32_le data_size;
174 u32_le data_offset;
175 u32_le objects_size;
176 u32_le objects_offset;
177 };
178 static_assert(sizeof(Header) == 16, "ParcelHeader has wrong size");
179
180 std::vector<u8> buffer;
181 std::size_t read_index = 0;
182 std::size_t write_index = 0;
183};
184
185class NativeWindow : public Parcel {
186public:
187 explicit NativeWindow(u32 id) {
188 data.id = id;
189 }
190 ~NativeWindow() override = default;
191
192protected:
193 void SerializeData() override {
194 Write(data);
195 }
196
197private:
198 struct Data {
199 u32_le magic = 2;
200 u32_le process_id = 1;
201 u32_le id;
202 INSERT_PADDING_WORDS(3);
203 std::array<u8, 8> dispdrv = {'d', 'i', 's', 'p', 'd', 'r', 'v', '\0'};
204 INSERT_PADDING_WORDS(2);
205 };
206 static_assert(sizeof(Data) == 0x28, "ParcelData has wrong size");
207
208 Data data{};
209};
210
211class IGBPConnectRequestParcel : public Parcel {
212public:
213 explicit IGBPConnectRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) {
214 Deserialize();
215 }
216
217 void DeserializeData() override {
218 [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
219 data = Read<Data>();
220 }
221
222 struct Data {
223 u32_le unk;
224 u32_le api;
225 u32_le producer_controlled_by_app;
226 };
227
228 Data data;
229};
230
231class IGBPConnectResponseParcel : public Parcel {
232public:
233 explicit IGBPConnectResponseParcel(u32 width, u32 height) {
234 data.width = width;
235 data.height = height;
236 }
237 ~IGBPConnectResponseParcel() override = default;
238
239protected:
240 void SerializeData() override {
241 Write(data);
242 }
243
244private:
245 struct Data {
246 u32_le width;
247 u32_le height;
248 u32_le transform_hint;
249 u32_le num_pending_buffers;
250 u32_le status;
251 };
252 static_assert(sizeof(Data) == 20, "ParcelData has wrong size");
253
254 Data data{};
255};
256
257/// Represents a parcel containing one int '0' as its data
258/// Used by DetachBuffer and Disconnect
259class IGBPEmptyResponseParcel : public Parcel {
260protected:
261 void SerializeData() override {
262 Write(data);
263 }
264
265private:
266 struct Data {
267 u32_le unk_0{};
268 };
269
270 Data data{};
271};
272
273class IGBPSetPreallocatedBufferRequestParcel : public Parcel {
274public:
275 explicit IGBPSetPreallocatedBufferRequestParcel(std::vector<u8> buffer_)
276 : Parcel(std::move(buffer_)) {
277 Deserialize();
278 }
279
280 void DeserializeData() override {
281 [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
282 data = Read<Data>();
283 if (data.contains_object != 0) {
284 buffer_container = Read<BufferContainer>();
285 }
286 }
287
288 struct Data {
289 u32_le slot;
290 u32_le contains_object;
291 };
292
293 struct BufferContainer {
294 u32_le graphic_buffer_length;
295 INSERT_PADDING_WORDS(1);
296 NVFlinger::IGBPBuffer buffer{};
297 };
298
299 Data data{};
300 BufferContainer buffer_container{};
301};
302
303class IGBPSetPreallocatedBufferResponseParcel : public Parcel {
304protected:
305 void SerializeData() override {
306 // TODO(Subv): Find out what this means
307 Write<u32>(0);
308 }
309};
310
311class IGBPCancelBufferRequestParcel : public Parcel {
312public:
313 explicit IGBPCancelBufferRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) {
314 Deserialize();
315 }
316
317 void DeserializeData() override {
318 [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
319 data = Read<Data>();
320 }
321
322 struct Data {
323 u32_le slot;
324 Service::Nvidia::MultiFence multi_fence;
325 };
326
327 Data data;
328};
329
330class IGBPCancelBufferResponseParcel : public Parcel {
331protected:
332 void SerializeData() override {
333 Write<u32>(0); // Success
334 }
335};
336
337class IGBPDequeueBufferRequestParcel : public Parcel {
338public:
339 explicit IGBPDequeueBufferRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) {
340 Deserialize();
341 }
342
343 void DeserializeData() override {
344 [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
345 data = Read<Data>();
346 }
347
348 struct Data {
349 u32_le pixel_format;
350 u32_le width;
351 u32_le height;
352 u32_le get_frame_timestamps;
353 u32_le usage;
354 };
355
356 Data data;
357};
358
359class IGBPDequeueBufferResponseParcel : public Parcel {
360public:
361 explicit IGBPDequeueBufferResponseParcel(u32 slot_, Nvidia::MultiFence& multi_fence_)
362 : slot(slot_), multi_fence(multi_fence_) {}
363
364protected:
365 void SerializeData() override {
366 Write(slot);
367 Write<u32_le>(1);
368 WriteObject(multi_fence);
369 Write<u32_le>(0);
370 }
371
372 u32_le slot;
373 Service::Nvidia::MultiFence multi_fence;
374};
375
376class IGBPRequestBufferRequestParcel : public Parcel {
377public:
378 explicit IGBPRequestBufferRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) {
379 Deserialize();
380 }
381
382 void DeserializeData() override {
383 [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
384 slot = Read<u32_le>();
385 }
386
387 u32_le slot;
388};
389
390class IGBPRequestBufferResponseParcel : public Parcel {
391public:
392 explicit IGBPRequestBufferResponseParcel(NVFlinger::IGBPBuffer buffer_) : buffer(buffer_) {}
393 ~IGBPRequestBufferResponseParcel() override = default;
394
395protected:
396 void SerializeData() override {
397 // TODO(Subv): Figure out what this value means, writing non-zero here will make libnx
398 // try to read an IGBPBuffer object from the parcel.
399 Write<u32_le>(1);
400 WriteObject(buffer);
401 Write<u32_le>(0);
402 }
403
404 NVFlinger::IGBPBuffer buffer;
405};
406
407class IGBPQueueBufferRequestParcel : public Parcel {
408public:
409 explicit IGBPQueueBufferRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) {
410 Deserialize();
411 }
412
413 void DeserializeData() override {
414 [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
415 data = Read<Data>();
416 }
417
418 struct Data {
419 u32_le slot;
420 INSERT_PADDING_WORDS(3);
421 u32_le timestamp;
422 s32_le is_auto_timestamp;
423 s32_le crop_top;
424 s32_le crop_left;
425 s32_le crop_right;
426 s32_le crop_bottom;
427 s32_le scaling_mode;
428 NVFlinger::BufferQueue::BufferTransformFlags transform;
429 u32_le sticky_transform;
430 INSERT_PADDING_WORDS(1);
431 u32_le swap_interval;
432 Service::Nvidia::MultiFence multi_fence;
433
434 Common::Rectangle<int> GetCropRect() const {
435 return {crop_left, crop_top, crop_right, crop_bottom};
436 }
437 };
438 static_assert(sizeof(Data) == 96, "ParcelData has wrong size");
439
440 Data data;
441};
442
443class IGBPQueueBufferResponseParcel : public Parcel {
444public:
445 explicit IGBPQueueBufferResponseParcel(u32 width, u32 height) {
446 data.width = width;
447 data.height = height;
448 }
449 ~IGBPQueueBufferResponseParcel() override = default;
450
451protected:
452 void SerializeData() override {
453 Write(data);
454 }
455
456private:
457 struct Data {
458 u32_le width;
459 u32_le height;
460 u32_le transform_hint;
461 u32_le num_pending_buffers;
462 u32_le status;
463 };
464 static_assert(sizeof(Data) == 20, "ParcelData has wrong size");
465
466 Data data{};
467};
468
469class IGBPQueryRequestParcel : public Parcel {
470public:
471 explicit IGBPQueryRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) {
472 Deserialize();
473 }
474
475 void DeserializeData() override {
476 [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
477 type = Read<u32_le>();
478 }
479
480 u32 type;
481};
482
483class IGBPQueryResponseParcel : public Parcel {
484public:
485 explicit IGBPQueryResponseParcel(u32 value_) : value{value_} {}
486 ~IGBPQueryResponseParcel() override = default;
487
488protected:
489 void SerializeData() override {
490 Write(value);
491 }
492 66
493private: 67private:
494 u32_le value; 68 const u32 magic = 2;
69 const u32 process_id = 1;
70 const u32 id;
71 INSERT_PADDING_WORDS(3);
72 std::array<u8, 8> dispdrv = {'d', 'i', 's', 'p', 'd', 'r', 'v', '\0'};
73 INSERT_PADDING_WORDS(2);
495}; 74};
75static_assert(sizeof(NativeWindow) == 0x28, "NativeWindow has wrong size");
496 76
497class IHOSBinderDriver final : public ServiceFramework<IHOSBinderDriver> { 77class IHOSBinderDriver final : public ServiceFramework<IHOSBinderDriver> {
498public: 78public:
499 explicit IHOSBinderDriver(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_) 79 explicit IHOSBinderDriver(Core::System& system_, NVFlinger::HosBinderDriverServer& server_)
500 : ServiceFramework{system_, "IHOSBinderDriver"}, nv_flinger(nv_flinger_) { 80 : ServiceFramework{system_, "IHOSBinderDriver"}, server(server_) {
501 static const FunctionInfo functions[] = { 81 static const FunctionInfo functions[] = {
502 {0, &IHOSBinderDriver::TransactParcel, "TransactParcel"}, 82 {0, &IHOSBinderDriver::TransactParcel, "TransactParcel"},
503 {1, &IHOSBinderDriver::AdjustRefcount, "AdjustRefcount"}, 83 {1, &IHOSBinderDriver::AdjustRefcount, "AdjustRefcount"},
@@ -508,147 +88,16 @@ public:
508 } 88 }
509 89
510private: 90private:
511 enum class TransactionId {
512 RequestBuffer = 1,
513 SetBufferCount = 2,
514 DequeueBuffer = 3,
515 DetachBuffer = 4,
516 DetachNextBuffer = 5,
517 AttachBuffer = 6,
518 QueueBuffer = 7,
519 CancelBuffer = 8,
520 Query = 9,
521 Connect = 10,
522 Disconnect = 11,
523
524 AllocateBuffers = 13,
525 SetPreallocatedBuffer = 14,
526
527 GetBufferHistory = 17
528 };
529
530 void TransactParcel(Kernel::HLERequestContext& ctx) { 91 void TransactParcel(Kernel::HLERequestContext& ctx) {
531 IPC::RequestParser rp{ctx}; 92 IPC::RequestParser rp{ctx};
532 const u32 id = rp.Pop<u32>(); 93 const u32 id = rp.Pop<u32>();
533 const auto transaction = static_cast<TransactionId>(rp.Pop<u32>()); 94 const auto transaction = static_cast<android::TransactionId>(rp.Pop<u32>());
534 const u32 flags = rp.Pop<u32>(); 95 const u32 flags = rp.Pop<u32>();
535 96
536 LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id, 97 LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id,
537 transaction, flags); 98 transaction, flags);
538 99
539 auto& buffer_queue = *nv_flinger.FindBufferQueue(id); 100 server.TryGetProducer(id)->Transact(ctx, transaction, flags);
540
541 switch (transaction) {
542 case TransactionId::Connect: {
543 IGBPConnectRequestParcel request{ctx.ReadBuffer()};
544 IGBPConnectResponseParcel response{static_cast<u32>(DisplayResolution::UndockedWidth),
545 static_cast<u32>(DisplayResolution::UndockedHeight)};
546
547 buffer_queue.Connect();
548
549 ctx.WriteBuffer(response.Serialize());
550 break;
551 }
552 case TransactionId::SetPreallocatedBuffer: {
553 IGBPSetPreallocatedBufferRequestParcel request{ctx.ReadBuffer()};
554
555 buffer_queue.SetPreallocatedBuffer(request.data.slot, request.buffer_container.buffer);
556
557 IGBPSetPreallocatedBufferResponseParcel response{};
558 ctx.WriteBuffer(response.Serialize());
559 break;
560 }
561 case TransactionId::DequeueBuffer: {
562 IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()};
563 const u32 width{request.data.width};
564 const u32 height{request.data.height};
565
566 do {
567 if (auto result = buffer_queue.DequeueBuffer(width, height); result) {
568 // Buffer is available
569 IGBPDequeueBufferResponseParcel response{result->first, *result->second};
570 ctx.WriteBuffer(response.Serialize());
571 break;
572 }
573 } while (buffer_queue.IsConnected());
574
575 break;
576 }
577 case TransactionId::RequestBuffer: {
578 IGBPRequestBufferRequestParcel request{ctx.ReadBuffer()};
579
580 auto& buffer = buffer_queue.RequestBuffer(request.slot);
581 IGBPRequestBufferResponseParcel response{buffer};
582 ctx.WriteBuffer(response.Serialize());
583
584 break;
585 }
586 case TransactionId::QueueBuffer: {
587 IGBPQueueBufferRequestParcel request{ctx.ReadBuffer()};
588
589 buffer_queue.QueueBuffer(request.data.slot, request.data.transform,
590 request.data.GetCropRect(), request.data.swap_interval,
591 request.data.multi_fence);
592
593 IGBPQueueBufferResponseParcel response{1280, 720};
594 ctx.WriteBuffer(response.Serialize());
595 break;
596 }
597 case TransactionId::Query: {
598 IGBPQueryRequestParcel request{ctx.ReadBuffer()};
599
600 const u32 value =
601 buffer_queue.Query(static_cast<NVFlinger::BufferQueue::QueryType>(request.type));
602
603 IGBPQueryResponseParcel response{value};
604 ctx.WriteBuffer(response.Serialize());
605 break;
606 }
607 case TransactionId::CancelBuffer: {
608 IGBPCancelBufferRequestParcel request{ctx.ReadBuffer()};
609
610 buffer_queue.CancelBuffer(request.data.slot, request.data.multi_fence);
611
612 IGBPCancelBufferResponseParcel response{};
613 ctx.WriteBuffer(response.Serialize());
614 break;
615 }
616 case TransactionId::Disconnect: {
617 LOG_WARNING(Service_VI, "(STUBBED) called, transaction=Disconnect");
618 const auto buffer = ctx.ReadBuffer();
619
620 buffer_queue.Disconnect();
621
622 IGBPEmptyResponseParcel response{};
623 ctx.WriteBuffer(response.Serialize());
624 break;
625 }
626 case TransactionId::DetachBuffer: {
627 const auto buffer = ctx.ReadBuffer();
628
629 IGBPEmptyResponseParcel response{};
630 ctx.WriteBuffer(response.Serialize());
631 break;
632 }
633 case TransactionId::SetBufferCount: {
634 LOG_WARNING(Service_VI, "(STUBBED) called, transaction=SetBufferCount");
635 [[maybe_unused]] const auto buffer = ctx.ReadBuffer();
636
637 IGBPEmptyResponseParcel response{};
638 ctx.WriteBuffer(response.Serialize());
639 break;
640 }
641 case TransactionId::GetBufferHistory: {
642 LOG_WARNING(Service_VI, "(STUBBED) called, transaction=GetBufferHistory");
643 [[maybe_unused]] const auto buffer = ctx.ReadBuffer();
644
645 IGBPEmptyResponseParcel response{};
646 ctx.WriteBuffer(response.Serialize());
647 break;
648 }
649 default:
650 ASSERT_MSG(false, "Unimplemented");
651 }
652 101
653 IPC::ResponseBuilder rb{ctx, 2}; 102 IPC::ResponseBuilder rb{ctx, 2};
654 rb.Push(ResultSuccess); 103 rb.Push(ResultSuccess);
@@ -674,13 +123,13 @@ private:
674 123
675 LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown); 124 LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown);
676 125
677 // TODO(Subv): Find out what this actually is.
678 IPC::ResponseBuilder rb{ctx, 2, 1}; 126 IPC::ResponseBuilder rb{ctx, 2, 1};
679 rb.Push(ResultSuccess); 127 rb.Push(ResultSuccess);
680 rb.PushCopyObjects(nv_flinger.FindBufferQueue(id)->GetBufferWaitEvent()); 128 rb.PushCopyObjects(server.TryGetProducer(id)->GetNativeHandle());
681 } 129 }
682 130
683 NVFlinger::NVFlinger& nv_flinger; 131private:
132 NVFlinger::HosBinderDriverServer& server;
684}; 133};
685 134
686class ISystemDisplayService final : public ServiceFramework<ISystemDisplayService> { 135class ISystemDisplayService final : public ServiceFramework<ISystemDisplayService> {
@@ -937,7 +386,40 @@ private:
937 386
938class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> { 387class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> {
939public: 388public:
940 explicit IApplicationDisplayService(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_); 389 IApplicationDisplayService(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_,
390 NVFlinger::HosBinderDriverServer& hos_binder_driver_server_)
391 : ServiceFramework{system_, "IApplicationDisplayService"}, nv_flinger{nv_flinger_},
392 hos_binder_driver_server{hos_binder_driver_server_} {
393
394 static const FunctionInfo functions[] = {
395 {100, &IApplicationDisplayService::GetRelayService, "GetRelayService"},
396 {101, &IApplicationDisplayService::GetSystemDisplayService, "GetSystemDisplayService"},
397 {102, &IApplicationDisplayService::GetManagerDisplayService,
398 "GetManagerDisplayService"},
399 {103, &IApplicationDisplayService::GetIndirectDisplayTransactionService,
400 "GetIndirectDisplayTransactionService"},
401 {1000, &IApplicationDisplayService::ListDisplays, "ListDisplays"},
402 {1010, &IApplicationDisplayService::OpenDisplay, "OpenDisplay"},
403 {1011, &IApplicationDisplayService::OpenDefaultDisplay, "OpenDefaultDisplay"},
404 {1020, &IApplicationDisplayService::CloseDisplay, "CloseDisplay"},
405 {1101, &IApplicationDisplayService::SetDisplayEnabled, "SetDisplayEnabled"},
406 {1102, &IApplicationDisplayService::GetDisplayResolution, "GetDisplayResolution"},
407 {2020, &IApplicationDisplayService::OpenLayer, "OpenLayer"},
408 {2021, &IApplicationDisplayService::CloseLayer, "CloseLayer"},
409 {2030, &IApplicationDisplayService::CreateStrayLayer, "CreateStrayLayer"},
410 {2031, &IApplicationDisplayService::DestroyStrayLayer, "DestroyStrayLayer"},
411 {2101, &IApplicationDisplayService::SetLayerScalingMode, "SetLayerScalingMode"},
412 {2102, &IApplicationDisplayService::ConvertScalingMode, "ConvertScalingMode"},
413 {2450, &IApplicationDisplayService::GetIndirectLayerImageMap,
414 "GetIndirectLayerImageMap"},
415 {2451, nullptr, "GetIndirectLayerImageCropMap"},
416 {2460, &IApplicationDisplayService::GetIndirectLayerImageRequiredMemoryInfo,
417 "GetIndirectLayerImageRequiredMemoryInfo"},
418 {5202, &IApplicationDisplayService::GetDisplayVsyncEvent, "GetDisplayVsyncEvent"},
419 {5203, nullptr, "GetDisplayVsyncEventForDebug"},
420 };
421 RegisterHandlers(functions);
422 }
941 423
942private: 424private:
943 enum class ConvertedScaleMode : u64 { 425 enum class ConvertedScaleMode : u64 {
@@ -961,7 +443,7 @@ private:
961 443
962 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 444 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
963 rb.Push(ResultSuccess); 445 rb.Push(ResultSuccess);
964 rb.PushIpcInterface<IHOSBinderDriver>(system, nv_flinger); 446 rb.PushIpcInterface<IHOSBinderDriver>(system, hos_binder_driver_server);
965 } 447 }
966 448
967 void GetSystemDisplayService(Kernel::HLERequestContext& ctx) { 449 void GetSystemDisplayService(Kernel::HLERequestContext& ctx) {
@@ -985,7 +467,7 @@ private:
985 467
986 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 468 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
987 rb.Push(ResultSuccess); 469 rb.Push(ResultSuccess);
988 rb.PushIpcInterface<IHOSBinderDriver>(system, nv_flinger); 470 rb.PushIpcInterface<IHOSBinderDriver>(system, hos_binder_driver_server);
989 } 471 }
990 472
991 void OpenDisplay(Kernel::HLERequestContext& ctx) { 473 void OpenDisplay(Kernel::HLERequestContext& ctx) {
@@ -1089,7 +571,7 @@ private:
1089 void ListDisplays(Kernel::HLERequestContext& ctx) { 571 void ListDisplays(Kernel::HLERequestContext& ctx) {
1090 LOG_WARNING(Service_VI, "(STUBBED) called"); 572 LOG_WARNING(Service_VI, "(STUBBED) called");
1091 573
1092 DisplayInfo display_info; 574 const DisplayInfo display_info;
1093 ctx.WriteBuffer(&display_info, sizeof(DisplayInfo)); 575 ctx.WriteBuffer(&display_info, sizeof(DisplayInfo));
1094 IPC::ResponseBuilder rb{ctx, 4}; 576 IPC::ResponseBuilder rb{ctx, 4};
1095 rb.Push(ResultSuccess); 577 rb.Push(ResultSuccess);
@@ -1124,8 +606,8 @@ private:
1124 return; 606 return;
1125 } 607 }
1126 608
1127 NativeWindow native_window{*buffer_queue_id}; 609 const auto parcel = android::Parcel{NativeWindow{*buffer_queue_id}};
1128 const auto buffer_size = ctx.WriteBuffer(native_window.Serialize()); 610 const auto buffer_size = ctx.WriteBuffer(parcel.Serialize());
1129 611
1130 IPC::ResponseBuilder rb{ctx, 4}; 612 IPC::ResponseBuilder rb{ctx, 4};
1131 rb.Push(ResultSuccess); 613 rb.Push(ResultSuccess);
@@ -1170,8 +652,8 @@ private:
1170 return; 652 return;
1171 } 653 }
1172 654
1173 NativeWindow native_window{*buffer_queue_id}; 655 const auto parcel = android::Parcel{NativeWindow{*buffer_queue_id}};
1174 const auto buffer_size = ctx.WriteBuffer(native_window.Serialize()); 656 const auto buffer_size = ctx.WriteBuffer(parcel.Serialize());
1175 657
1176 IPC::ResponseBuilder rb{ctx, 6}; 658 IPC::ResponseBuilder rb{ctx, 6};
1177 rb.Push(ResultSuccess); 659 rb.Push(ResultSuccess);
@@ -1287,39 +769,9 @@ private:
1287 } 769 }
1288 770
1289 NVFlinger::NVFlinger& nv_flinger; 771 NVFlinger::NVFlinger& nv_flinger;
772 NVFlinger::HosBinderDriverServer& hos_binder_driver_server;
1290}; 773};
1291 774
1292IApplicationDisplayService::IApplicationDisplayService(Core::System& system_,
1293 NVFlinger::NVFlinger& nv_flinger_)
1294 : ServiceFramework{system_, "IApplicationDisplayService"}, nv_flinger{nv_flinger_} {
1295 static const FunctionInfo functions[] = {
1296 {100, &IApplicationDisplayService::GetRelayService, "GetRelayService"},
1297 {101, &IApplicationDisplayService::GetSystemDisplayService, "GetSystemDisplayService"},
1298 {102, &IApplicationDisplayService::GetManagerDisplayService, "GetManagerDisplayService"},
1299 {103, &IApplicationDisplayService::GetIndirectDisplayTransactionService,
1300 "GetIndirectDisplayTransactionService"},
1301 {1000, &IApplicationDisplayService::ListDisplays, "ListDisplays"},
1302 {1010, &IApplicationDisplayService::OpenDisplay, "OpenDisplay"},
1303 {1011, &IApplicationDisplayService::OpenDefaultDisplay, "OpenDefaultDisplay"},
1304 {1020, &IApplicationDisplayService::CloseDisplay, "CloseDisplay"},
1305 {1101, &IApplicationDisplayService::SetDisplayEnabled, "SetDisplayEnabled"},
1306 {1102, &IApplicationDisplayService::GetDisplayResolution, "GetDisplayResolution"},
1307 {2020, &IApplicationDisplayService::OpenLayer, "OpenLayer"},
1308 {2021, &IApplicationDisplayService::CloseLayer, "CloseLayer"},
1309 {2030, &IApplicationDisplayService::CreateStrayLayer, "CreateStrayLayer"},
1310 {2031, &IApplicationDisplayService::DestroyStrayLayer, "DestroyStrayLayer"},
1311 {2101, &IApplicationDisplayService::SetLayerScalingMode, "SetLayerScalingMode"},
1312 {2102, &IApplicationDisplayService::ConvertScalingMode, "ConvertScalingMode"},
1313 {2450, &IApplicationDisplayService::GetIndirectLayerImageMap, "GetIndirectLayerImageMap"},
1314 {2451, nullptr, "GetIndirectLayerImageCropMap"},
1315 {2460, &IApplicationDisplayService::GetIndirectLayerImageRequiredMemoryInfo,
1316 "GetIndirectLayerImageRequiredMemoryInfo"},
1317 {5202, &IApplicationDisplayService::GetDisplayVsyncEvent, "GetDisplayVsyncEvent"},
1318 {5203, nullptr, "GetDisplayVsyncEventForDebug"},
1319 };
1320 RegisterHandlers(functions);
1321}
1322
1323static bool IsValidServiceAccess(Permission permission, Policy policy) { 775static bool IsValidServiceAccess(Permission permission, Policy policy) {
1324 if (permission == Permission::User) { 776 if (permission == Permission::User) {
1325 return policy == Policy::User; 777 return policy == Policy::User;
@@ -1333,7 +785,9 @@ static bool IsValidServiceAccess(Permission permission, Policy policy) {
1333} 785}
1334 786
1335void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, Core::System& system, 787void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, Core::System& system,
1336 NVFlinger::NVFlinger& nv_flinger, Permission permission) { 788 NVFlinger::NVFlinger& nv_flinger,
789 NVFlinger::HosBinderDriverServer& hos_binder_driver_server,
790 Permission permission) {
1337 IPC::RequestParser rp{ctx}; 791 IPC::RequestParser rp{ctx};
1338 const auto policy = rp.PopEnum<Policy>(); 792 const auto policy = rp.PopEnum<Policy>();
1339 793
@@ -1346,14 +800,18 @@ void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, Core::System&
1346 800
1347 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 801 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
1348 rb.Push(ResultSuccess); 802 rb.Push(ResultSuccess);
1349 rb.PushIpcInterface<IApplicationDisplayService>(system, nv_flinger); 803 rb.PushIpcInterface<IApplicationDisplayService>(system, nv_flinger, hos_binder_driver_server);
1350} 804}
1351 805
1352void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system, 806void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system,
1353 NVFlinger::NVFlinger& nv_flinger) { 807 NVFlinger::NVFlinger& nv_flinger,
1354 std::make_shared<VI_M>(system, nv_flinger)->InstallAsService(service_manager); 808 NVFlinger::HosBinderDriverServer& hos_binder_driver_server) {
1355 std::make_shared<VI_S>(system, nv_flinger)->InstallAsService(service_manager); 809 std::make_shared<VI_M>(system, nv_flinger, hos_binder_driver_server)
1356 std::make_shared<VI_U>(system, nv_flinger)->InstallAsService(service_manager); 810 ->InstallAsService(service_manager);
811 std::make_shared<VI_S>(system, nv_flinger, hos_binder_driver_server)
812 ->InstallAsService(service_manager);
813 std::make_shared<VI_U>(system, nv_flinger, hos_binder_driver_server)
814 ->InstallAsService(service_manager);
1357} 815}
1358 816
1359} // namespace Service::VI 817} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi.h b/src/core/hle/service/vi/vi.h
index 2fd7f8e61..d68f2646b 100644
--- a/src/core/hle/service/vi/vi.h
+++ b/src/core/hle/service/vi/vi.h
@@ -15,8 +15,9 @@ class HLERequestContext;
15} 15}
16 16
17namespace Service::NVFlinger { 17namespace Service::NVFlinger {
18class HosBinderDriverServer;
18class NVFlinger; 19class NVFlinger;
19} 20} // namespace Service::NVFlinger
20 21
21namespace Service::SM { 22namespace Service::SM {
22class ServiceManager; 23class ServiceManager;
@@ -47,11 +48,14 @@ enum class Policy {
47 48
48namespace detail { 49namespace detail {
49void GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, Core::System& system, 50void GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, Core::System& system,
50 NVFlinger::NVFlinger& nv_flinger, Permission permission); 51 NVFlinger::NVFlinger& nv_flinger,
52 NVFlinger::HosBinderDriverServer& hos_binder_driver_server,
53 Permission permission);
51} // namespace detail 54} // namespace detail
52 55
53/// Registers all VI services with the specified service manager. 56/// Registers all VI services with the specified service manager.
54void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system, 57void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system,
55 NVFlinger::NVFlinger& nv_flinger); 58 NVFlinger::NVFlinger& nv_flinger,
59 NVFlinger::HosBinderDriverServer& hos_binder_driver_server);
56 60
57} // namespace Service::VI 61} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_m.cpp b/src/core/hle/service/vi/vi_m.cpp
index 87db1c416..be0255f3d 100644
--- a/src/core/hle/service/vi/vi_m.cpp
+++ b/src/core/hle/service/vi/vi_m.cpp
@@ -8,8 +8,10 @@
8 8
9namespace Service::VI { 9namespace Service::VI {
10 10
11VI_M::VI_M(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_) 11VI_M::VI_M(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_,
12 : ServiceFramework{system_, "vi:m"}, nv_flinger{nv_flinger_} { 12 NVFlinger::HosBinderDriverServer& hos_binder_driver_server_)
13 : ServiceFramework{system_, "vi:m"}, nv_flinger{nv_flinger_}, hos_binder_driver_server{
14 hos_binder_driver_server_} {
13 static const FunctionInfo functions[] = { 15 static const FunctionInfo functions[] = {
14 {2, &VI_M::GetDisplayService, "GetDisplayService"}, 16 {2, &VI_M::GetDisplayService, "GetDisplayService"},
15 {3, nullptr, "GetDisplayServiceWithProxyNameExchange"}, 17 {3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
@@ -22,7 +24,8 @@ VI_M::~VI_M() = default;
22void VI_M::GetDisplayService(Kernel::HLERequestContext& ctx) { 24void VI_M::GetDisplayService(Kernel::HLERequestContext& ctx) {
23 LOG_DEBUG(Service_VI, "called"); 25 LOG_DEBUG(Service_VI, "called");
24 26
25 detail::GetDisplayServiceImpl(ctx, system, nv_flinger, Permission::Manager); 27 detail::GetDisplayServiceImpl(ctx, system, nv_flinger, hos_binder_driver_server,
28 Permission::Manager);
26} 29}
27 30
28} // namespace Service::VI 31} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_m.h b/src/core/hle/service/vi/vi_m.h
index d79c41beb..efbd34e09 100644
--- a/src/core/hle/service/vi/vi_m.h
+++ b/src/core/hle/service/vi/vi_m.h
@@ -15,20 +15,23 @@ class HLERequestContext;
15} 15}
16 16
17namespace Service::NVFlinger { 17namespace Service::NVFlinger {
18class HosBinderDriverServer;
18class NVFlinger; 19class NVFlinger;
19} 20} // namespace Service::NVFlinger
20 21
21namespace Service::VI { 22namespace Service::VI {
22 23
23class VI_M final : public ServiceFramework<VI_M> { 24class VI_M final : public ServiceFramework<VI_M> {
24public: 25public:
25 explicit VI_M(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_); 26 explicit VI_M(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_,
27 NVFlinger::HosBinderDriverServer& hos_binder_driver_server_);
26 ~VI_M() override; 28 ~VI_M() override;
27 29
28private: 30private:
29 void GetDisplayService(Kernel::HLERequestContext& ctx); 31 void GetDisplayService(Kernel::HLERequestContext& ctx);
30 32
31 NVFlinger::NVFlinger& nv_flinger; 33 NVFlinger::NVFlinger& nv_flinger;
34 NVFlinger::HosBinderDriverServer& hos_binder_driver_server;
32}; 35};
33 36
34} // namespace Service::VI 37} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_s.cpp b/src/core/hle/service/vi/vi_s.cpp
index 5cd22f7df..7996a6811 100644
--- a/src/core/hle/service/vi/vi_s.cpp
+++ b/src/core/hle/service/vi/vi_s.cpp
@@ -8,8 +8,10 @@
8 8
9namespace Service::VI { 9namespace Service::VI {
10 10
11VI_S::VI_S(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_) 11VI_S::VI_S(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_,
12 : ServiceFramework{system_, "vi:s"}, nv_flinger{nv_flinger_} { 12 NVFlinger::HosBinderDriverServer& hos_binder_driver_server_)
13 : ServiceFramework{system_, "vi:s"}, nv_flinger{nv_flinger_}, hos_binder_driver_server{
14 hos_binder_driver_server_} {
13 static const FunctionInfo functions[] = { 15 static const FunctionInfo functions[] = {
14 {1, &VI_S::GetDisplayService, "GetDisplayService"}, 16 {1, &VI_S::GetDisplayService, "GetDisplayService"},
15 {3, nullptr, "GetDisplayServiceWithProxyNameExchange"}, 17 {3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
@@ -22,7 +24,8 @@ VI_S::~VI_S() = default;
22void VI_S::GetDisplayService(Kernel::HLERequestContext& ctx) { 24void VI_S::GetDisplayService(Kernel::HLERequestContext& ctx) {
23 LOG_DEBUG(Service_VI, "called"); 25 LOG_DEBUG(Service_VI, "called");
24 26
25 detail::GetDisplayServiceImpl(ctx, system, nv_flinger, Permission::System); 27 detail::GetDisplayServiceImpl(ctx, system, nv_flinger, hos_binder_driver_server,
28 Permission::System);
26} 29}
27 30
28} // namespace Service::VI 31} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_s.h b/src/core/hle/service/vi/vi_s.h
index 5f1f8f290..3812c5061 100644
--- a/src/core/hle/service/vi/vi_s.h
+++ b/src/core/hle/service/vi/vi_s.h
@@ -15,20 +15,23 @@ class HLERequestContext;
15} 15}
16 16
17namespace Service::NVFlinger { 17namespace Service::NVFlinger {
18class HosBinderDriverServer;
18class NVFlinger; 19class NVFlinger;
19} 20} // namespace Service::NVFlinger
20 21
21namespace Service::VI { 22namespace Service::VI {
22 23
23class VI_S final : public ServiceFramework<VI_S> { 24class VI_S final : public ServiceFramework<VI_S> {
24public: 25public:
25 explicit VI_S(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_); 26 explicit VI_S(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_,
27 NVFlinger::HosBinderDriverServer& hos_binder_driver_server_);
26 ~VI_S() override; 28 ~VI_S() override;
27 29
28private: 30private:
29 void GetDisplayService(Kernel::HLERequestContext& ctx); 31 void GetDisplayService(Kernel::HLERequestContext& ctx);
30 32
31 NVFlinger::NVFlinger& nv_flinger; 33 NVFlinger::NVFlinger& nv_flinger;
34 NVFlinger::HosBinderDriverServer& hos_binder_driver_server;
32}; 35};
33 36
34} // namespace Service::VI 37} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_u.cpp b/src/core/hle/service/vi/vi_u.cpp
index 0079d51f0..57c888313 100644
--- a/src/core/hle/service/vi/vi_u.cpp
+++ b/src/core/hle/service/vi/vi_u.cpp
@@ -8,8 +8,10 @@
8 8
9namespace Service::VI { 9namespace Service::VI {
10 10
11VI_U::VI_U(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_) 11VI_U::VI_U(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_,
12 : ServiceFramework{system_, "vi:u"}, nv_flinger{nv_flinger_} { 12 NVFlinger::HosBinderDriverServer& hos_binder_driver_server_)
13 : ServiceFramework{system_, "vi:u"}, nv_flinger{nv_flinger_}, hos_binder_driver_server{
14 hos_binder_driver_server_} {
13 static const FunctionInfo functions[] = { 15 static const FunctionInfo functions[] = {
14 {0, &VI_U::GetDisplayService, "GetDisplayService"}, 16 {0, &VI_U::GetDisplayService, "GetDisplayService"},
15 {1, nullptr, "GetDisplayServiceWithProxyNameExchange"}, 17 {1, nullptr, "GetDisplayServiceWithProxyNameExchange"},
@@ -22,7 +24,8 @@ VI_U::~VI_U() = default;
22void VI_U::GetDisplayService(Kernel::HLERequestContext& ctx) { 24void VI_U::GetDisplayService(Kernel::HLERequestContext& ctx) {
23 LOG_DEBUG(Service_VI, "called"); 25 LOG_DEBUG(Service_VI, "called");
24 26
25 detail::GetDisplayServiceImpl(ctx, system, nv_flinger, Permission::User); 27 detail::GetDisplayServiceImpl(ctx, system, nv_flinger, hos_binder_driver_server,
28 Permission::User);
26} 29}
27 30
28} // namespace Service::VI 31} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_u.h b/src/core/hle/service/vi/vi_u.h
index 8e3885c73..b08e56576 100644
--- a/src/core/hle/service/vi/vi_u.h
+++ b/src/core/hle/service/vi/vi_u.h
@@ -15,20 +15,23 @@ class HLERequestContext;
15} 15}
16 16
17namespace Service::NVFlinger { 17namespace Service::NVFlinger {
18class HosBinderDriverServer;
18class NVFlinger; 19class NVFlinger;
19} 20} // namespace Service::NVFlinger
20 21
21namespace Service::VI { 22namespace Service::VI {
22 23
23class VI_U final : public ServiceFramework<VI_U> { 24class VI_U final : public ServiceFramework<VI_U> {
24public: 25public:
25 explicit VI_U(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_); 26 explicit VI_U(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_,
27 NVFlinger::HosBinderDriverServer& hos_binder_driver_server_);
26 ~VI_U() override; 28 ~VI_U() override;
27 29
28private: 30private:
29 void GetDisplayService(Kernel::HLERequestContext& ctx); 31 void GetDisplayService(Kernel::HLERequestContext& ctx);
30 32
31 NVFlinger::NVFlinger& nv_flinger; 33 NVFlinger::NVFlinger& nv_flinger;
34 NVFlinger::HosBinderDriverServer& hos_binder_driver_server;
32}; 35};
33 36
34} // namespace Service::VI 37} // namespace Service::VI
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp
index d3cbb14a9..cb47d253c 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp
@@ -2,6 +2,8 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <bit>
6
5#include "shader_recompiler/backend/spirv/emit_spirv.h" 7#include "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" 8#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7#include "shader_recompiler/backend/spirv/spirv_emit_context.h" 9#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
index 3c2a5e16f..aa7082978 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
@@ -2,6 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <bit>
5#include <tuple> 6#include <tuple>
6#include <utility> 7#include <utility>
7 8
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
index 53be98ced..28f6a6184 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
@@ -4,6 +4,7 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <array> 6#include <array>
7#include <bit>
7#include <climits> 8#include <climits>
8 9
9#include <boost/container/static_vector.hpp> 10#include <boost/container/static_vector.hpp>
diff --git a/src/shader_recompiler/frontend/ir/microinstruction.cpp b/src/shader_recompiler/frontend/ir/microinstruction.cpp
index 631446cf7..4a2564f47 100644
--- a/src/shader_recompiler/frontend/ir/microinstruction.cpp
+++ b/src/shader_recompiler/frontend/ir/microinstruction.cpp
@@ -326,6 +326,11 @@ void Inst::AddPhiOperand(Block* predecessor, const Value& value) {
326 phi_args.emplace_back(predecessor, value); 326 phi_args.emplace_back(predecessor, value);
327} 327}
328 328
329void Inst::ErasePhiOperand(size_t index) {
330 const auto operand_it{phi_args.begin() + static_cast<ptrdiff_t>(index)};
331 phi_args.erase(operand_it);
332}
333
329void Inst::OrderPhiArgs() { 334void Inst::OrderPhiArgs() {
330 if (op != Opcode::Phi) { 335 if (op != Opcode::Phi) {
331 throw LogicError("{} is not a Phi instruction", op); 336 throw LogicError("{} is not a Phi instruction", op);
diff --git a/src/shader_recompiler/frontend/ir/value.h b/src/shader_recompiler/frontend/ir/value.h
index 947579852..14f6e55bc 100644
--- a/src/shader_recompiler/frontend/ir/value.h
+++ b/src/shader_recompiler/frontend/ir/value.h
@@ -179,9 +179,13 @@ public:
179 179
180 /// Get a pointer to the block of a phi argument. 180 /// Get a pointer to the block of a phi argument.
181 [[nodiscard]] Block* PhiBlock(size_t index) const; 181 [[nodiscard]] Block* PhiBlock(size_t index) const;
182
182 /// Add phi operand to a phi instruction. 183 /// Add phi operand to a phi instruction.
183 void AddPhiOperand(Block* predecessor, const Value& value); 184 void AddPhiOperand(Block* predecessor, const Value& value);
184 185
186 // Erase the phi operand at the given index.
187 void ErasePhiOperand(size_t index);
188
185 /// Orders the Phi arguments from farthest away to nearest. 189 /// Orders the Phi arguments from farthest away to nearest.
186 void OrderPhiArgs(); 190 void OrderPhiArgs();
187 191
diff --git a/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp b/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp
index 400836301..6697fde85 100644
--- a/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp
+++ b/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp
@@ -2,24 +2,104 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm>
6
7#include <boost/container/small_vector.hpp>
8
5#include "shader_recompiler/frontend/ir/basic_block.h" 9#include "shader_recompiler/frontend/ir/basic_block.h"
6#include "shader_recompiler/frontend/ir/value.h" 10#include "shader_recompiler/frontend/ir/value.h"
7#include "shader_recompiler/ir_opt/passes.h" 11#include "shader_recompiler/ir_opt/passes.h"
8 12
9namespace Shader::Optimization { 13namespace Shader::Optimization {
10 14namespace {
11void DeadCodeEliminationPass(IR::Program& program) { 15template <bool TEST_USES>
16void DeadInstElimination(IR::Block* const block) {
12 // We iterate over the instructions in reverse order. 17 // We iterate over the instructions in reverse order.
13 // This is because removing an instruction reduces the number of uses for earlier instructions. 18 // This is because removing an instruction reduces the number of uses for earlier instructions.
14 for (IR::Block* const block : program.post_order_blocks) { 19 auto it{block->end()};
15 auto it{block->end()}; 20 while (it != block->begin()) {
16 while (it != block->begin()) { 21 --it;
17 --it; 22 if constexpr (TEST_USES) {
18 if (!it->HasUses() && !it->MayHaveSideEffects()) { 23 if (it->HasUses() || it->MayHaveSideEffects()) {
19 it->Invalidate(); 24 continue;
20 it = block->Instructions().erase(it); 25 }
26 }
27 it->Invalidate();
28 it = block->Instructions().erase(it);
29 }
30}
31
32void DeletedPhiArgElimination(IR::Program& program, std::span<const IR::Block*> dead_blocks) {
33 for (IR::Block* const block : program.blocks) {
34 for (IR::Inst& phi : *block) {
35 if (!IR::IsPhi(phi)) {
36 continue;
37 }
38 for (size_t i = 0; i < phi.NumArgs(); ++i) {
39 if (std::ranges::find(dead_blocks, phi.PhiBlock(i)) == dead_blocks.end()) {
40 continue;
41 }
42 // Phi operand at this index is an unreachable block
43 phi.ErasePhiOperand(i);
44 --i;
45 }
46 }
47 }
48}
49
50void DeadBranchElimination(IR::Program& program) {
51 boost::container::small_vector<const IR::Block*, 3> dead_blocks;
52 const auto begin_it{program.syntax_list.begin()};
53 for (auto node_it = begin_it; node_it != program.syntax_list.end(); ++node_it) {
54 if (node_it->type != IR::AbstractSyntaxNode::Type::If) {
55 continue;
56 }
57 IR::Inst* const cond_ref{node_it->data.if_node.cond.Inst()};
58 const IR::U1 cond{cond_ref->Arg(0)};
59 if (!cond.IsImmediate()) {
60 continue;
61 }
62 if (cond.U1()) {
63 continue;
64 }
65 // False immediate condition. Remove condition ref, erase the entire branch.
66 cond_ref->Invalidate();
67 // Account for nested if-statements within the if(false) branch
68 u32 nested_ifs{1u};
69 while (node_it->type != IR::AbstractSyntaxNode::Type::EndIf || nested_ifs > 0) {
70 node_it = program.syntax_list.erase(node_it);
71 switch (node_it->type) {
72 case IR::AbstractSyntaxNode::Type::If:
73 ++nested_ifs;
74 break;
75 case IR::AbstractSyntaxNode::Type::EndIf:
76 --nested_ifs;
77 break;
78 case IR::AbstractSyntaxNode::Type::Block: {
79 IR::Block* const block{node_it->data.block};
80 DeadInstElimination<false>(block);
81 dead_blocks.push_back(block);
82 break;
83 }
84 default:
85 break;
21 } 86 }
22 } 87 }
88 // Erase EndIf node of the if(false) branch
89 node_it = program.syntax_list.erase(node_it);
90 // Account for loop increment
91 --node_it;
92 }
93 if (!dead_blocks.empty()) {
94 DeletedPhiArgElimination(program, std::span(dead_blocks.data(), dead_blocks.size()));
95 }
96}
97} // namespace
98
99void DeadCodeEliminationPass(IR::Program& program) {
100 DeadBranchElimination(program);
101 for (IR::Block* const block : program.post_order_blocks) {
102 DeadInstElimination<true>(block);
23 } 103 }
24} 104}
25 105
diff --git a/src/video_core/buffer_cache/buffer_base.h b/src/video_core/buffer_cache/buffer_base.h
index be2113f5a..10975884b 100644
--- a/src/video_core/buffer_cache/buffer_base.h
+++ b/src/video_core/buffer_cache/buffer_base.h
@@ -212,7 +212,7 @@ public:
212 void FlushCachedWrites() noexcept { 212 void FlushCachedWrites() noexcept {
213 flags &= ~BufferFlagBits::CachedWrites; 213 flags &= ~BufferFlagBits::CachedWrites;
214 const u64 num_words = NumWords(); 214 const u64 num_words = NumWords();
215 const u64* const cached_words = Array<Type::CachedCPU>(); 215 u64* const cached_words = Array<Type::CachedCPU>();
216 u64* const untracked_words = Array<Type::Untracked>(); 216 u64* const untracked_words = Array<Type::Untracked>();
217 u64* const cpu_words = Array<Type::CPU>(); 217 u64* const cpu_words = Array<Type::CPU>();
218 for (u64 word_index = 0; word_index < num_words; ++word_index) { 218 for (u64 word_index = 0; word_index < num_words; ++word_index) {
@@ -220,6 +220,7 @@ public:
220 NotifyRasterizer<false>(word_index, untracked_words[word_index], cached_bits); 220 NotifyRasterizer<false>(word_index, untracked_words[word_index], cached_bits);
221 untracked_words[word_index] |= cached_bits; 221 untracked_words[word_index] |= cached_bits;
222 cpu_words[word_index] |= cached_bits; 222 cpu_words[word_index] |= cached_bits;
223 cached_words[word_index] = 0;
223 } 224 }
224 } 225 }
225 226
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index 200d792dd..21bfb76a4 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -76,8 +76,9 @@ class BufferCache {
76 76
77 static constexpr BufferId NULL_BUFFER_ID{0}; 77 static constexpr BufferId NULL_BUFFER_ID{0};
78 78
79 static constexpr u64 EXPECTED_MEMORY = 512_MiB; 79 static constexpr s64 DEFAULT_EXPECTED_MEMORY = 512_MiB;
80 static constexpr u64 CRITICAL_MEMORY = 1_GiB; 80 static constexpr s64 DEFAULT_CRITICAL_MEMORY = 1_GiB;
81 static constexpr s64 TARGET_THRESHOLD = 4_GiB;
81 82
82 using Maxwell = Tegra::Engines::Maxwell3D::Regs; 83 using Maxwell = Tegra::Engines::Maxwell3D::Regs;
83 84
@@ -436,6 +437,8 @@ private:
436 Common::LeastRecentlyUsedCache<LRUItemParams> lru_cache; 437 Common::LeastRecentlyUsedCache<LRUItemParams> lru_cache;
437 u64 frame_tick = 0; 438 u64 frame_tick = 0;
438 u64 total_used_memory = 0; 439 u64 total_used_memory = 0;
440 u64 minimum_memory = 0;
441 u64 critical_memory = 0;
439 442
440 std::array<BufferId, ((1ULL << 39) >> PAGE_BITS)> page_table; 443 std::array<BufferId, ((1ULL << 39) >> PAGE_BITS)> page_table;
441}; 444};
@@ -451,11 +454,30 @@ BufferCache<P>::BufferCache(VideoCore::RasterizerInterface& rasterizer_,
451 // Ensure the first slot is used for the null buffer 454 // Ensure the first slot is used for the null buffer
452 void(slot_buffers.insert(runtime, NullBufferParams{})); 455 void(slot_buffers.insert(runtime, NullBufferParams{}));
453 common_ranges.clear(); 456 common_ranges.clear();
457
458 if (!runtime.CanReportMemoryUsage()) {
459 minimum_memory = DEFAULT_EXPECTED_MEMORY;
460 critical_memory = DEFAULT_CRITICAL_MEMORY;
461 return;
462 }
463
464 const s64 device_memory = static_cast<s64>(runtime.GetDeviceLocalMemory());
465 const s64 min_spacing_expected = device_memory - 1_GiB - 512_MiB;
466 const s64 min_spacing_critical = device_memory - 1_GiB;
467 const s64 mem_threshold = std::min(device_memory, TARGET_THRESHOLD);
468 const s64 min_vacancy_expected = (6 * mem_threshold) / 10;
469 const s64 min_vacancy_critical = (3 * mem_threshold) / 10;
470 minimum_memory = static_cast<u64>(
471 std::max(std::min(device_memory - min_vacancy_expected, min_spacing_expected),
472 DEFAULT_EXPECTED_MEMORY));
473 critical_memory = static_cast<u64>(
474 std::max(std::min(device_memory - min_vacancy_critical, min_spacing_critical),
475 DEFAULT_CRITICAL_MEMORY));
454} 476}
455 477
456template <class P> 478template <class P>
457void BufferCache<P>::RunGarbageCollector() { 479void BufferCache<P>::RunGarbageCollector() {
458 const bool aggressive_gc = total_used_memory >= CRITICAL_MEMORY; 480 const bool aggressive_gc = total_used_memory >= critical_memory;
459 const u64 ticks_to_destroy = aggressive_gc ? 60 : 120; 481 const u64 ticks_to_destroy = aggressive_gc ? 60 : 120;
460 int num_iterations = aggressive_gc ? 64 : 32; 482 int num_iterations = aggressive_gc ? 64 : 32;
461 const auto clean_up = [this, &num_iterations](BufferId buffer_id) { 483 const auto clean_up = [this, &num_iterations](BufferId buffer_id) {
@@ -486,7 +508,11 @@ void BufferCache<P>::TickFrame() {
486 const bool skip_preferred = hits * 256 < shots * 251; 508 const bool skip_preferred = hits * 256 < shots * 251;
487 uniform_buffer_skip_cache_size = skip_preferred ? DEFAULT_SKIP_CACHE_SIZE : 0; 509 uniform_buffer_skip_cache_size = skip_preferred ? DEFAULT_SKIP_CACHE_SIZE : 0;
488 510
489 if (total_used_memory >= EXPECTED_MEMORY) { 511 // If we can obtain the memory info, use it instead of the estimate.
512 if (runtime.CanReportMemoryUsage()) {
513 total_used_memory = runtime.GetDeviceMemoryUsage();
514 }
515 if (total_used_memory >= minimum_memory) {
490 RunGarbageCollector(); 516 RunGarbageCollector();
491 } 517 }
492 ++frame_tick; 518 ++frame_tick;
diff --git a/src/video_core/framebuffer_config.h b/src/video_core/framebuffer_config.h
index b1d455e30..93349bb78 100644
--- a/src/video_core/framebuffer_config.h
+++ b/src/video_core/framebuffer_config.h
@@ -6,41 +6,22 @@
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "common/math_util.h" 8#include "common/math_util.h"
9#include "core/hle/service/nvflinger/buffer_transform_flags.h"
10#include "core/hle/service/nvflinger/pixel_format.h"
9 11
10namespace Tegra { 12namespace Tegra {
13
11/** 14/**
12 * Struct describing framebuffer configuration 15 * Struct describing framebuffer configuration
13 */ 16 */
14struct FramebufferConfig { 17struct FramebufferConfig {
15 enum class PixelFormat : u32 {
16 A8B8G8R8_UNORM = 1,
17 RGB565_UNORM = 4,
18 B8G8R8A8_UNORM = 5,
19 };
20
21 enum class TransformFlags : u32 {
22 /// No transform flags are set
23 Unset = 0x00,
24 /// Flip source image horizontally (around the vertical axis)
25 FlipH = 0x01,
26 /// Flip source image vertically (around the horizontal axis)
27 FlipV = 0x02,
28 /// Rotate source image 90 degrees clockwise
29 Rotate90 = 0x04,
30 /// Rotate source image 180 degrees
31 Rotate180 = 0x03,
32 /// Rotate source image 270 degrees clockwise
33 Rotate270 = 0x07,
34 };
35
36 VAddr address{}; 18 VAddr address{};
37 u32 offset{}; 19 u32 offset{};
38 u32 width{}; 20 u32 width{};
39 u32 height{}; 21 u32 height{};
40 u32 stride{}; 22 u32 stride{};
41 PixelFormat pixel_format{}; 23 Service::android::PixelFormat pixel_format{};
42 24 Service::android::BufferTransformFlags transform_flags{};
43 TransformFlags transform_flags{};
44 Common::Rectangle<int> crop_rect; 25 Common::Rectangle<int> crop_rect;
45}; 26};
46 27
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index 26b8ea233..97c029140 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -8,6 +8,7 @@
8 8
9#include "common/bit_field.h" 9#include "common/bit_field.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "core/hle/service/nvdrv/nvdata.h"
11#include "video_core/cdma_pusher.h" 12#include "video_core/cdma_pusher.h"
12#include "video_core/framebuffer_config.h" 13#include "video_core/framebuffer_config.h"
13 14
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
index d4dd10bb6..f1f7b384b 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
@@ -135,6 +135,20 @@ BufferCacheRuntime::BufferCacheRuntime(const Device& device_)
135 buffer.Create(); 135 buffer.Create();
136 glNamedBufferData(buffer.handle, 0x10'000, nullptr, GL_STREAM_COPY); 136 glNamedBufferData(buffer.handle, 0x10'000, nullptr, GL_STREAM_COPY);
137 } 137 }
138
139 device_access_memory = [this]() -> u64 {
140 if (device.CanReportMemoryUsage()) {
141 return device.GetCurrentDedicatedVideoMemory() + 512_MiB;
142 }
143 return 2_GiB; // Return minimum requirements
144 }();
145}
146
147u64 BufferCacheRuntime::GetDeviceMemoryUsage() const {
148 if (device.CanReportMemoryUsage()) {
149 return device_access_memory - device.GetCurrentDedicatedVideoMemory();
150 }
151 return 2_GiB;
138} 152}
139 153
140void BufferCacheRuntime::CopyBuffer(Buffer& dst_buffer, Buffer& src_buffer, 154void BufferCacheRuntime::CopyBuffer(Buffer& dst_buffer, Buffer& src_buffer,
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h
index 7287731b6..a8699f28c 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.h
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.h
@@ -89,6 +89,8 @@ public:
89 void BindImageBuffer(Buffer& buffer, u32 offset, u32 size, 89 void BindImageBuffer(Buffer& buffer, u32 offset, u32 size,
90 VideoCore::Surface::PixelFormat format); 90 VideoCore::Surface::PixelFormat format);
91 91
92 u64 GetDeviceMemoryUsage() const;
93
92 void BindFastUniformBuffer(size_t stage, u32 binding_index, u32 size) { 94 void BindFastUniformBuffer(size_t stage, u32 binding_index, u32 size) {
93 const GLuint handle = fast_uniforms[stage][binding_index].handle; 95 const GLuint handle = fast_uniforms[stage][binding_index].handle;
94 const GLsizeiptr gl_size = static_cast<GLsizeiptr>(size); 96 const GLsizeiptr gl_size = static_cast<GLsizeiptr>(size);
@@ -151,6 +153,14 @@ public:
151 use_storage_buffers = use_storage_buffers_; 153 use_storage_buffers = use_storage_buffers_;
152 } 154 }
153 155
156 u64 GetDeviceLocalMemory() const {
157 return device_access_memory;
158 }
159
160 bool CanReportMemoryUsage() const {
161 return device.CanReportMemoryUsage();
162 }
163
154private: 164private:
155 static constexpr std::array PABO_LUT{ 165 static constexpr std::array PABO_LUT{
156 GL_VERTEX_PROGRAM_PARAMETER_BUFFER_NV, GL_TESS_CONTROL_PROGRAM_PARAMETER_BUFFER_NV, 166 GL_VERTEX_PROGRAM_PARAMETER_BUFFER_NV, GL_TESS_CONTROL_PROGRAM_PARAMETER_BUFFER_NV,
@@ -184,6 +194,8 @@ private:
184 std::array<OGLBuffer, VideoCommon::NUM_COMPUTE_UNIFORM_BUFFERS> copy_compute_uniforms; 194 std::array<OGLBuffer, VideoCommon::NUM_COMPUTE_UNIFORM_BUFFERS> copy_compute_uniforms;
185 195
186 u32 index_buffer_offset = 0; 196 u32 index_buffer_offset = 0;
197
198 u64 device_access_memory;
187}; 199};
188 200
189struct BufferCacheParams { 201struct BufferCacheParams {
diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp
index 715cd3a48..656dd7eb0 100644
--- a/src/video_core/renderer_opengl/gl_device.cpp
+++ b/src/video_core/renderer_opengl/gl_device.cpp
@@ -13,12 +13,15 @@
13 13
14#include <glad/glad.h> 14#include <glad/glad.h>
15 15
16#include "common/literals.h"
16#include "common/logging/log.h" 17#include "common/logging/log.h"
17#include "common/settings.h" 18#include "common/settings.h"
18#include "shader_recompiler/stage.h" 19#include "shader_recompiler/stage.h"
19#include "video_core/renderer_opengl/gl_device.h" 20#include "video_core/renderer_opengl/gl_device.h"
20#include "video_core/renderer_opengl/gl_resource_manager.h" 21#include "video_core/renderer_opengl/gl_resource_manager.h"
21 22
23using namespace Common::Literals;
24
22namespace OpenGL { 25namespace OpenGL {
23namespace { 26namespace {
24constexpr std::array LIMIT_UBOS = { 27constexpr std::array LIMIT_UBOS = {
@@ -165,6 +168,7 @@ Device::Device() {
165 has_sparse_texture_2 = GLAD_GL_ARB_sparse_texture2; 168 has_sparse_texture_2 = GLAD_GL_ARB_sparse_texture2;
166 warp_size_potentially_larger_than_guest = !is_nvidia && !is_intel; 169 warp_size_potentially_larger_than_guest = !is_nvidia && !is_intel;
167 need_fastmath_off = is_nvidia; 170 need_fastmath_off = is_nvidia;
171 can_report_memory = GLAD_GL_NVX_gpu_memory_info;
168 172
169 // At the moment of writing this, only Nvidia's driver optimizes BufferSubData on exclusive 173 // At the moment of writing this, only Nvidia's driver optimizes BufferSubData on exclusive
170 // uniform buffers as "push constants" 174 // uniform buffers as "push constants"
@@ -276,4 +280,10 @@ void main() {
276})"); 280})");
277} 281}
278 282
283u64 Device::GetCurrentDedicatedVideoMemory() const {
284 GLint cur_avail_mem_kb = 0;
285 glGetIntegerv(GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX, &cur_avail_mem_kb);
286 return static_cast<u64>(cur_avail_mem_kb) * 1_KiB;
287}
288
279} // namespace OpenGL 289} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h
index 95c2e8d38..9bb0b9148 100644
--- a/src/video_core/renderer_opengl/gl_device.h
+++ b/src/video_core/renderer_opengl/gl_device.h
@@ -20,6 +20,8 @@ public:
20 20
21 [[nodiscard]] std::string GetVendorName() const; 21 [[nodiscard]] std::string GetVendorName() const;
22 22
23 u64 GetCurrentDedicatedVideoMemory() const;
24
23 u32 GetMaxUniformBuffers(Shader::Stage stage) const noexcept { 25 u32 GetMaxUniformBuffers(Shader::Stage stage) const noexcept {
24 return max_uniform_buffers[static_cast<size_t>(stage)]; 26 return max_uniform_buffers[static_cast<size_t>(stage)];
25 } 27 }
@@ -168,6 +170,10 @@ public:
168 return vendor_name == "ATI Technologies Inc."; 170 return vendor_name == "ATI Technologies Inc.";
169 } 171 }
170 172
173 bool CanReportMemoryUsage() const {
174 return can_report_memory;
175 }
176
171private: 177private:
172 static bool TestVariableAoffi(); 178 static bool TestVariableAoffi();
173 static bool TestPreciseBug(); 179 static bool TestPreciseBug();
@@ -210,6 +216,7 @@ private:
210 bool need_fastmath_off{}; 216 bool need_fastmath_off{};
211 bool has_cbuf_ftou_bug{}; 217 bool has_cbuf_ftou_bug{};
212 bool has_bool_ref_bug{}; 218 bool has_bool_ref_bug{};
219 bool can_report_memory{};
213 220
214 std::string vendor_name; 221 std::string vendor_name;
215}; 222};
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 4d632d211..e6f9ece8b 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -15,8 +15,9 @@
15#include "common/logging/log.h" 15#include "common/logging/log.h"
16#include "common/math_util.h" 16#include "common/math_util.h"
17#include "common/microprofile.h" 17#include "common/microprofile.h"
18#include "common/scope_exit.h"
18#include "common/settings.h" 19#include "common/settings.h"
19#include "core/memory.h" 20
20#include "video_core/engines/kepler_compute.h" 21#include "video_core/engines/kepler_compute.h"
21#include "video_core/engines/maxwell_3d.h" 22#include "video_core/engines/maxwell_3d.h"
22#include "video_core/memory_manager.h" 23#include "video_core/memory_manager.h"
@@ -210,6 +211,7 @@ void RasterizerOpenGL::Clear() {
210void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) { 211void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
211 MICROPROFILE_SCOPE(OpenGL_Drawing); 212 MICROPROFILE_SCOPE(OpenGL_Drawing);
212 213
214 SCOPE_EXIT({ gpu.TickWork(); });
213 query_cache.UpdateCounters(); 215 query_cache.UpdateCounters();
214 216
215 GraphicsPipeline* const pipeline{shader_cache.CurrentGraphicsPipeline()}; 217 GraphicsPipeline* const pipeline{shader_cache.CurrentGraphicsPipeline()};
@@ -265,8 +267,6 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
265 267
266 ++num_queued_commands; 268 ++num_queued_commands;
267 has_written_global_memory |= pipeline->WritesGlobalMemory(); 269 has_written_global_memory |= pipeline->WritesGlobalMemory();
268
269 gpu.TickWork();
270} 270}
271 271
272void RasterizerOpenGL::DispatchCompute() { 272void RasterizerOpenGL::DispatchCompute() {
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index 3c1f79a27..8f9a65beb 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -484,6 +484,13 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, ProgramManager&
484 rescale_read_fbos[i].Create(); 484 rescale_read_fbos[i].Create();
485 } 485 }
486 } 486 }
487
488 device_access_memory = [this]() -> u64 {
489 if (device.CanReportMemoryUsage()) {
490 return device.GetCurrentDedicatedVideoMemory() + 512_MiB;
491 }
492 return 2_GiB; // Return minimum requirements
493 }();
487} 494}
488 495
489TextureCacheRuntime::~TextureCacheRuntime() = default; 496TextureCacheRuntime::~TextureCacheRuntime() = default;
@@ -500,13 +507,11 @@ ImageBufferMap TextureCacheRuntime::DownloadStagingBuffer(size_t size) {
500 return download_buffers.RequestMap(size, false); 507 return download_buffers.RequestMap(size, false);
501} 508}
502 509
503u64 TextureCacheRuntime::GetDeviceLocalMemory() const { 510u64 TextureCacheRuntime::GetDeviceMemoryUsage() const {
504 if (GLAD_GL_NVX_gpu_memory_info) { 511 if (device.CanReportMemoryUsage()) {
505 GLint cur_avail_mem_kb = 0; 512 return device_access_memory - device.GetCurrentDedicatedVideoMemory();
506 glGetIntegerv(GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, &cur_avail_mem_kb);
507 return static_cast<u64>(cur_avail_mem_kb) * 1_KiB;
508 } 513 }
509 return 2_GiB; // Return minimum requirements 514 return 2_GiB;
510} 515}
511 516
512void TextureCacheRuntime::CopyImage(Image& dst_image, Image& src_image, 517void TextureCacheRuntime::CopyImage(Image& dst_image, Image& src_image,
@@ -686,6 +691,7 @@ Image::Image(TextureCacheRuntime& runtime_, const VideoCommon::ImageInfo& info_,
686 } 691 }
687 if (IsConverted(runtime->device, info.format, info.type)) { 692 if (IsConverted(runtime->device, info.format, info.type)) {
688 flags |= ImageFlagBits::Converted; 693 flags |= ImageFlagBits::Converted;
694 flags |= ImageFlagBits::CostlyLoad;
689 gl_internal_format = IsPixelFormatSRGB(info.format) ? GL_SRGB8_ALPHA8 : GL_RGBA8; 695 gl_internal_format = IsPixelFormatSRGB(info.format) ? GL_SRGB8_ALPHA8 : GL_RGBA8;
690 gl_format = GL_RGBA; 696 gl_format = GL_RGBA;
691 gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; 697 gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h
index 7f425631f..53088b66e 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.h
+++ b/src/video_core/renderer_opengl/gl_texture_cache.h
@@ -10,6 +10,7 @@
10#include <glad/glad.h> 10#include <glad/glad.h>
11 11
12#include "shader_recompiler/shader_info.h" 12#include "shader_recompiler/shader_info.h"
13#include "video_core/renderer_opengl/gl_device.h"
13#include "video_core/renderer_opengl/gl_resource_manager.h" 14#include "video_core/renderer_opengl/gl_resource_manager.h"
14#include "video_core/renderer_opengl/util_shaders.h" 15#include "video_core/renderer_opengl/util_shaders.h"
15#include "video_core/texture_cache/image_view_base.h" 16#include "video_core/texture_cache/image_view_base.h"
@@ -21,7 +22,6 @@ struct ResolutionScalingInfo;
21 22
22namespace OpenGL { 23namespace OpenGL {
23 24
24class Device;
25class ProgramManager; 25class ProgramManager;
26class StateTracker; 26class StateTracker;
27 27
@@ -83,7 +83,15 @@ public:
83 83
84 ImageBufferMap DownloadStagingBuffer(size_t size); 84 ImageBufferMap DownloadStagingBuffer(size_t size);
85 85
86 u64 GetDeviceLocalMemory() const; 86 u64 GetDeviceLocalMemory() const {
87 return device_access_memory;
88 }
89
90 u64 GetDeviceMemoryUsage() const;
91
92 bool CanReportMemoryUsage() const {
93 return device.CanReportMemoryUsage();
94 }
87 95
88 bool ShouldReinterpret([[maybe_unused]] Image& dst, [[maybe_unused]] Image& src) { 96 bool ShouldReinterpret([[maybe_unused]] Image& dst, [[maybe_unused]] Image& src) {
89 return true; 97 return true;
@@ -172,6 +180,7 @@ private:
172 std::array<OGLFramebuffer, 4> rescale_draw_fbos; 180 std::array<OGLFramebuffer, 4> rescale_draw_fbos;
173 std::array<OGLFramebuffer, 4> rescale_read_fbos; 181 std::array<OGLFramebuffer, 4> rescale_read_fbos;
174 const Settings::ResolutionScalingInfo& resolution; 182 const Settings::ResolutionScalingInfo& resolution;
183 u64 device_access_memory;
175}; 184};
176 185
177class Image : public VideoCommon::ImageBase { 186class Image : public VideoCommon::ImageBase {
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 795c97831..f8f29013a 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -323,12 +323,12 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
323 323
324 GLint internal_format; 324 GLint internal_format;
325 switch (framebuffer.pixel_format) { 325 switch (framebuffer.pixel_format) {
326 case Tegra::FramebufferConfig::PixelFormat::A8B8G8R8_UNORM: 326 case Service::android::PixelFormat::Rgba8888:
327 internal_format = GL_RGBA8; 327 internal_format = GL_RGBA8;
328 texture.gl_format = GL_RGBA; 328 texture.gl_format = GL_RGBA;
329 texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; 329 texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
330 break; 330 break;
331 case Tegra::FramebufferConfig::PixelFormat::RGB565_UNORM: 331 case Service::android::PixelFormat::Rgb565:
332 internal_format = GL_RGB565; 332 internal_format = GL_RGB565;
333 texture.gl_format = GL_RGB; 333 texture.gl_format = GL_RGB;
334 texture.gl_type = GL_UNSIGNED_SHORT_5_6_5; 334 texture.gl_type = GL_UNSIGNED_SHORT_5_6_5;
@@ -464,8 +464,8 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
464 const auto& texcoords = screen_info.display_texcoords; 464 const auto& texcoords = screen_info.display_texcoords;
465 auto left = texcoords.left; 465 auto left = texcoords.left;
466 auto right = texcoords.right; 466 auto right = texcoords.right;
467 if (framebuffer_transform_flags != Tegra::FramebufferConfig::TransformFlags::Unset) { 467 if (framebuffer_transform_flags != Service::android::BufferTransformFlags::Unset) {
468 if (framebuffer_transform_flags == Tegra::FramebufferConfig::TransformFlags::FlipV) { 468 if (framebuffer_transform_flags == Service::android::BufferTransformFlags::FlipV) {
469 // Flip the framebuffer vertically 469 // Flip the framebuffer vertically
470 left = texcoords.right; 470 left = texcoords.right;
471 right = texcoords.left; 471 right = texcoords.left;
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index 35706cf05..aa206878b 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -46,7 +46,7 @@ struct TextureInfo {
46 GLsizei height; 46 GLsizei height;
47 GLenum gl_format; 47 GLenum gl_format;
48 GLenum gl_type; 48 GLenum gl_type;
49 Tegra::FramebufferConfig::PixelFormat pixel_format; 49 Service::android::PixelFormat pixel_format;
50}; 50};
51 51
52/// Structure used for storing information about the display target for the Switch screen 52/// Structure used for storing information about the display target for the Switch screen
@@ -135,7 +135,7 @@ private:
135 std::vector<u8> gl_framebuffer_data; 135 std::vector<u8> gl_framebuffer_data;
136 136
137 /// Used for transforming the framebuffer orientation 137 /// Used for transforming the framebuffer orientation
138 Tegra::FramebufferConfig::TransformFlags framebuffer_transform_flags{}; 138 Service::android::BufferTransformFlags framebuffer_transform_flags{};
139 Common::Rectangle<int> framebuffer_crop_rect; 139 Common::Rectangle<int> framebuffer_crop_rect;
140}; 140};
141 141
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
index 0ec85682b..d893c1952 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
@@ -94,11 +94,11 @@ std::size_t GetSizeInBytes(const Tegra::FramebufferConfig& framebuffer) {
94 94
95VkFormat GetFormat(const Tegra::FramebufferConfig& framebuffer) { 95VkFormat GetFormat(const Tegra::FramebufferConfig& framebuffer) {
96 switch (framebuffer.pixel_format) { 96 switch (framebuffer.pixel_format) {
97 case Tegra::FramebufferConfig::PixelFormat::A8B8G8R8_UNORM: 97 case Service::android::PixelFormat::Rgba8888:
98 return VK_FORMAT_A8B8G8R8_UNORM_PACK32; 98 return VK_FORMAT_A8B8G8R8_UNORM_PACK32;
99 case Tegra::FramebufferConfig::PixelFormat::RGB565_UNORM: 99 case Service::android::PixelFormat::Rgb565:
100 return VK_FORMAT_R5G6B5_UNORM_PACK16; 100 return VK_FORMAT_R5G6B5_UNORM_PACK16;
101 case Tegra::FramebufferConfig::PixelFormat::B8G8R8A8_UNORM: 101 case Service::android::PixelFormat::Bgra8888:
102 return VK_FORMAT_B8G8R8A8_UNORM; 102 return VK_FORMAT_B8G8R8A8_UNORM;
103 default: 103 default:
104 UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}", 104 UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}",
@@ -1390,9 +1390,9 @@ void VKBlitScreen::SetVertexData(BufferData& data, const Tegra::FramebufferConfi
1390 auto right = texcoords.right; 1390 auto right = texcoords.right;
1391 1391
1392 switch (framebuffer_transform_flags) { 1392 switch (framebuffer_transform_flags) {
1393 case Tegra::FramebufferConfig::TransformFlags::Unset: 1393 case Service::android::BufferTransformFlags::Unset:
1394 break; 1394 break;
1395 case Tegra::FramebufferConfig::TransformFlags::FlipV: 1395 case Service::android::BufferTransformFlags::FlipV:
1396 // Flip the framebuffer vertically 1396 // Flip the framebuffer vertically
1397 left = texcoords.right; 1397 left = texcoords.right;
1398 right = texcoords.left; 1398 right = texcoords.left;
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
index 5ffd93499..def838c34 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
@@ -141,6 +141,18 @@ StagingBufferRef BufferCacheRuntime::DownloadStagingBuffer(size_t size) {
141 return staging_pool.Request(size, MemoryUsage::Download); 141 return staging_pool.Request(size, MemoryUsage::Download);
142} 142}
143 143
144u64 BufferCacheRuntime::GetDeviceLocalMemory() const {
145 return device.GetDeviceLocalMemory();
146}
147
148u64 BufferCacheRuntime::GetDeviceMemoryUsage() const {
149 return device.GetDeviceMemoryUsage();
150}
151
152bool BufferCacheRuntime::CanReportMemoryUsage() const {
153 return device.CanReportMemoryUsage();
154}
155
144void BufferCacheRuntime::Finish() { 156void BufferCacheRuntime::Finish() {
145 scheduler.Finish(); 157 scheduler.Finish();
146} 158}
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h
index 1ee0d8420..d7fdd18ff 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.h
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h
@@ -65,6 +65,12 @@ public:
65 65
66 void Finish(); 66 void Finish();
67 67
68 u64 GetDeviceLocalMemory() const;
69
70 u64 GetDeviceMemoryUsage() const;
71
72 bool CanReportMemoryUsage() const;
73
68 [[nodiscard]] StagingBufferRef UploadStagingBuffer(size_t size); 74 [[nodiscard]] StagingBufferRef UploadStagingBuffer(size_t size);
69 75
70 [[nodiscard]] StagingBufferRef DownloadStagingBuffer(size_t size); 76 [[nodiscard]] StagingBufferRef DownloadStagingBuffer(size_t size);
diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
index 5d5329abf..64a58304b 100644
--- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
+++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
@@ -118,7 +118,7 @@ StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& mem
118 .image = nullptr, 118 .image = nullptr,
119 .buffer = *stream_buffer, 119 .buffer = *stream_buffer,
120 }; 120 };
121 const auto memory_properties = device.GetPhysical().GetMemoryProperties(); 121 const auto memory_properties = device.GetPhysical().GetMemoryProperties().memoryProperties;
122 VkMemoryAllocateInfo stream_memory_info{ 122 VkMemoryAllocateInfo stream_memory_info{
123 .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, 123 .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
124 .pNext = make_dedicated ? &dedicated_info : nullptr, 124 .pNext = make_dedicated ? &dedicated_info : nullptr,
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index 0d0e02a6e..2c2ccc7c6 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -1189,6 +1189,14 @@ u64 TextureCacheRuntime::GetDeviceLocalMemory() const {
1189 return device.GetDeviceLocalMemory(); 1189 return device.GetDeviceLocalMemory();
1190} 1190}
1191 1191
1192u64 TextureCacheRuntime::GetDeviceMemoryUsage() const {
1193 return device.GetDeviceMemoryUsage();
1194}
1195
1196bool TextureCacheRuntime::CanReportMemoryUsage() const {
1197 return device.CanReportMemoryUsage();
1198}
1199
1192void TextureCacheRuntime::TickFrame() {} 1200void TextureCacheRuntime::TickFrame() {}
1193 1201
1194Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu_addr_, 1202Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu_addr_,
@@ -1203,6 +1211,7 @@ Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu
1203 } else { 1211 } else {
1204 flags |= VideoCommon::ImageFlagBits::Converted; 1212 flags |= VideoCommon::ImageFlagBits::Converted;
1205 } 1213 }
1214 flags |= VideoCommon::ImageFlagBits::CostlyLoad;
1206 } 1215 }
1207 if (runtime->device.HasDebuggingToolAttached()) { 1216 if (runtime->device.HasDebuggingToolAttached()) {
1208 original_image.SetObjectNameEXT(VideoCommon::Name(*this).c_str()); 1217 original_image.SetObjectNameEXT(VideoCommon::Name(*this).c_str());
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h
index c81130dd2..cb15b4a1c 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.h
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.h
@@ -55,6 +55,10 @@ public:
55 55
56 u64 GetDeviceLocalMemory() const; 56 u64 GetDeviceLocalMemory() const;
57 57
58 u64 GetDeviceMemoryUsage() const;
59
60 bool CanReportMemoryUsage() const;
61
58 void BlitImage(Framebuffer* dst_framebuffer, ImageView& dst, ImageView& src, 62 void BlitImage(Framebuffer* dst_framebuffer, ImageView& dst, ImageView& src,
59 const Region2D& dst_region, const Region2D& src_region, 63 const Region2D& dst_region, const Region2D& src_region,
60 Tegra::Engines::Fermi2D::Filter filter, 64 Tegra::Engines::Fermi2D::Filter filter,
diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp
index a36015c8c..5f428d35d 100644
--- a/src/video_core/surface.cpp
+++ b/src/video_core/surface.cpp
@@ -190,13 +190,13 @@ PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format)
190 } 190 }
191} 191}
192 192
193PixelFormat PixelFormatFromGPUPixelFormat(Tegra::FramebufferConfig::PixelFormat format) { 193PixelFormat PixelFormatFromGPUPixelFormat(Service::android::PixelFormat format) {
194 switch (format) { 194 switch (format) {
195 case Tegra::FramebufferConfig::PixelFormat::A8B8G8R8_UNORM: 195 case Service::android::PixelFormat::Rgba8888:
196 return PixelFormat::A8B8G8R8_UNORM; 196 return PixelFormat::A8B8G8R8_UNORM;
197 case Tegra::FramebufferConfig::PixelFormat::RGB565_UNORM: 197 case Service::android::PixelFormat::Rgb565:
198 return PixelFormat::R5G6B5_UNORM; 198 return PixelFormat::R5G6B5_UNORM;
199 case Tegra::FramebufferConfig::PixelFormat::B8G8R8A8_UNORM: 199 case Service::android::PixelFormat::Bgra8888:
200 return PixelFormat::B8G8R8A8_UNORM; 200 return PixelFormat::B8G8R8A8_UNORM;
201 default: 201 default:
202 UNIMPLEMENTED_MSG("Unimplemented format={}", format); 202 UNIMPLEMENTED_MSG("Unimplemented format={}", format);
diff --git a/src/video_core/surface.h b/src/video_core/surface.h
index 33e8d24ab..5704cf16a 100644
--- a/src/video_core/surface.h
+++ b/src/video_core/surface.h
@@ -460,7 +460,7 @@ PixelFormat PixelFormatFromDepthFormat(Tegra::DepthFormat format);
460 460
461PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format); 461PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format);
462 462
463PixelFormat PixelFormatFromGPUPixelFormat(Tegra::FramebufferConfig::PixelFormat format); 463PixelFormat PixelFormatFromGPUPixelFormat(Service::android::PixelFormat format);
464 464
465SurfaceType GetFormatType(PixelFormat pixel_format); 465SurfaceType GetFormatType(PixelFormat pixel_format);
466 466
diff --git a/src/video_core/texture_cache/image_base.h b/src/video_core/texture_cache/image_base.h
index 89c111c00..dd0106432 100644
--- a/src/video_core/texture_cache/image_base.h
+++ b/src/video_core/texture_cache/image_base.h
@@ -33,11 +33,12 @@ enum class ImageFlagBits : u32 {
33 ///< garbage collection priority 33 ///< garbage collection priority
34 Alias = 1 << 11, ///< This image has aliases and has priority on garbage 34 Alias = 1 << 11, ///< This image has aliases and has priority on garbage
35 ///< collection 35 ///< collection
36 CostlyLoad = 1 << 12, ///< Protected from low-tier GC as it is costly to load back.
36 37
37 // Rescaler 38 // Rescaler
38 Rescaled = 1 << 12, 39 Rescaled = 1 << 13,
39 CheckingRescalable = 1 << 13, 40 CheckingRescalable = 1 << 14,
40 IsRescalable = 1 << 14, 41 IsRescalable = 1 << 15,
41}; 42};
42DECLARE_ENUM_FLAG_OPERATORS(ImageFlagBits) 43DECLARE_ENUM_FLAG_OPERATORS(ImageFlagBits)
43 44
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 72eeb8bbd..efc1c4525 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -50,14 +50,20 @@ TextureCache<P>::TextureCache(Runtime& runtime_, VideoCore::RasterizerInterface&
50 void(slot_samplers.insert(runtime, sampler_descriptor)); 50 void(slot_samplers.insert(runtime, sampler_descriptor));
51 51
52 if constexpr (HAS_DEVICE_MEMORY_INFO) { 52 if constexpr (HAS_DEVICE_MEMORY_INFO) {
53 const auto device_memory = runtime.GetDeviceLocalMemory(); 53 const s64 device_memory = static_cast<s64>(runtime.GetDeviceLocalMemory());
54 const u64 possible_expected_memory = (device_memory * 4) / 10; 54 const s64 min_spacing_expected = device_memory - 1_GiB - 512_MiB;
55 const u64 possible_critical_memory = (device_memory * 7) / 10; 55 const s64 min_spacing_critical = device_memory - 1_GiB;
56 expected_memory = std::max(possible_expected_memory, DEFAULT_EXPECTED_MEMORY - 256_MiB); 56 const s64 mem_threshold = std::min(device_memory, TARGET_THRESHOLD);
57 critical_memory = std::max(possible_critical_memory, DEFAULT_CRITICAL_MEMORY - 512_MiB); 57 const s64 min_vacancy_expected = (6 * mem_threshold) / 10;
58 minimum_memory = 0; 58 const s64 min_vacancy_critical = (3 * mem_threshold) / 10;
59 expected_memory = static_cast<u64>(
60 std::max(std::min(device_memory - min_vacancy_expected, min_spacing_expected),
61 DEFAULT_EXPECTED_MEMORY));
62 critical_memory = static_cast<u64>(
63 std::max(std::min(device_memory - min_vacancy_critical, min_spacing_critical),
64 DEFAULT_CRITICAL_MEMORY));
65 minimum_memory = static_cast<u64>((device_memory - mem_threshold) / 2);
59 } else { 66 } else {
60 // On OpenGL we can be more conservatives as the driver takes care.
61 expected_memory = DEFAULT_EXPECTED_MEMORY + 512_MiB; 67 expected_memory = DEFAULT_EXPECTED_MEMORY + 512_MiB;
62 critical_memory = DEFAULT_CRITICAL_MEMORY + 1_GiB; 68 critical_memory = DEFAULT_CRITICAL_MEMORY + 1_GiB;
63 minimum_memory = 0; 69 minimum_memory = 0;
@@ -66,18 +72,21 @@ TextureCache<P>::TextureCache(Runtime& runtime_, VideoCore::RasterizerInterface&
66 72
67template <class P> 73template <class P>
68void TextureCache<P>::RunGarbageCollector() { 74void TextureCache<P>::RunGarbageCollector() {
69 const bool high_priority_mode = total_used_memory >= expected_memory; 75 bool high_priority_mode = total_used_memory >= expected_memory;
70 const bool aggressive_mode = total_used_memory >= critical_memory; 76 bool aggressive_mode = total_used_memory >= critical_memory;
71 const u64 ticks_to_destroy = aggressive_mode ? 10ULL : high_priority_mode ? 25ULL : 100ULL; 77 const u64 ticks_to_destroy = aggressive_mode ? 10ULL : high_priority_mode ? 25ULL : 50ULL;
72 size_t num_iterations = aggressive_mode ? 300 : (high_priority_mode ? 50 : 10); 78 size_t num_iterations = aggressive_mode ? 40 : (high_priority_mode ? 20 : 10);
73 const auto clean_up = [this, &num_iterations, high_priority_mode](ImageId image_id) { 79 const auto clean_up = [this, &num_iterations, &high_priority_mode,
80 &aggressive_mode](ImageId image_id) {
74 if (num_iterations == 0) { 81 if (num_iterations == 0) {
75 return true; 82 return true;
76 } 83 }
77 --num_iterations; 84 --num_iterations;
78 auto& image = slot_images[image_id]; 85 auto& image = slot_images[image_id];
79 const bool must_download = image.IsSafeDownload(); 86 const bool must_download =
80 if (!high_priority_mode && must_download) { 87 image.IsSafeDownload() && False(image.flags & ImageFlagBits::BadOverlap);
88 if (!high_priority_mode &&
89 (must_download || True(image.flags & ImageFlagBits::CostlyLoad))) {
81 return false; 90 return false;
82 } 91 }
83 if (must_download) { 92 if (must_download) {
@@ -92,6 +101,18 @@ void TextureCache<P>::RunGarbageCollector() {
92 } 101 }
93 UnregisterImage(image_id); 102 UnregisterImage(image_id);
94 DeleteImage(image_id, image.scale_tick > frame_tick + 5); 103 DeleteImage(image_id, image.scale_tick > frame_tick + 5);
104 if (total_used_memory < critical_memory) {
105 if (aggressive_mode) {
106 // Sink the aggresiveness.
107 num_iterations >>= 2;
108 aggressive_mode = false;
109 return false;
110 }
111 if (high_priority_mode && total_used_memory < expected_memory) {
112 num_iterations >>= 1;
113 high_priority_mode = false;
114 }
115 }
95 return false; 116 return false;
96 }; 117 };
97 lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, clean_up); 118 lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, clean_up);
@@ -99,6 +120,10 @@ void TextureCache<P>::RunGarbageCollector() {
99 120
100template <class P> 121template <class P>
101void TextureCache<P>::TickFrame() { 122void TextureCache<P>::TickFrame() {
123 // If we can obtain the memory info, use it instead of the estimate.
124 if (runtime.CanReportMemoryUsage()) {
125 total_used_memory = runtime.GetDeviceMemoryUsage();
126 }
102 if (total_used_memory > minimum_memory) { 127 if (total_used_memory > minimum_memory) {
103 RunGarbageCollector(); 128 RunGarbageCollector();
104 } 129 }
@@ -106,6 +131,7 @@ void TextureCache<P>::TickFrame() {
106 sentenced_framebuffers.Tick(); 131 sentenced_framebuffers.Tick();
107 sentenced_image_view.Tick(); 132 sentenced_image_view.Tick();
108 runtime.TickFrame(); 133 runtime.TickFrame();
134 critical_gc = 0;
109 ++frame_tick; 135 ++frame_tick;
110} 136}
111 137
@@ -1052,6 +1078,11 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
1052 1078
1053 for (const ImageId overlap_id : overlap_ids) { 1079 for (const ImageId overlap_id : overlap_ids) {
1054 Image& overlap = slot_images[overlap_id]; 1080 Image& overlap = slot_images[overlap_id];
1081 if (True(overlap.flags & ImageFlagBits::GpuModified)) {
1082 new_image.flags |= ImageFlagBits::GpuModified;
1083 new_image.modification_tick =
1084 std::max(overlap.modification_tick, new_image.modification_tick);
1085 }
1055 if (overlap.info.num_samples != new_image.info.num_samples) { 1086 if (overlap.info.num_samples != new_image.info.num_samples) {
1056 LOG_WARNING(HW_GPU, "Copying between images with different samples is not implemented"); 1087 LOG_WARNING(HW_GPU, "Copying between images with different samples is not implemented");
1057 } else { 1088 } else {
@@ -1414,6 +1445,10 @@ void TextureCache<P>::RegisterImage(ImageId image_id) {
1414 tentative_size = EstimatedDecompressedSize(tentative_size, image.info.format); 1445 tentative_size = EstimatedDecompressedSize(tentative_size, image.info.format);
1415 } 1446 }
1416 total_used_memory += Common::AlignUp(tentative_size, 1024); 1447 total_used_memory += Common::AlignUp(tentative_size, 1024);
1448 if (total_used_memory > critical_memory && critical_gc < GC_EMERGENCY_COUNTS) {
1449 RunGarbageCollector();
1450 critical_gc++;
1451 }
1417 image.lru_index = lru_cache.Insert(image_id, frame_tick); 1452 image.lru_index = lru_cache.Insert(image_id, frame_tick);
1418 1453
1419 ForEachGPUPage(image.gpu_addr, image.guest_size_bytes, 1454 ForEachGPUPage(image.gpu_addr, image.guest_size_bytes,
@@ -1704,6 +1739,9 @@ void TextureCache<P>::SynchronizeAliases(ImageId image_id) {
1704 most_recent_tick = std::max(most_recent_tick, aliased_image.modification_tick); 1739 most_recent_tick = std::max(most_recent_tick, aliased_image.modification_tick);
1705 aliased_images.push_back(&aliased); 1740 aliased_images.push_back(&aliased);
1706 any_rescaled |= True(aliased_image.flags & ImageFlagBits::Rescaled); 1741 any_rescaled |= True(aliased_image.flags & ImageFlagBits::Rescaled);
1742 if (True(aliased_image.flags & ImageFlagBits::GpuModified)) {
1743 image.flags |= ImageFlagBits::GpuModified;
1744 }
1707 } 1745 }
1708 } 1746 }
1709 if (aliased_images.empty()) { 1747 if (aliased_images.empty()) {
diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h
index 647ca0730..b1324edf3 100644
--- a/src/video_core/texture_cache/texture_cache_base.h
+++ b/src/video_core/texture_cache/texture_cache_base.h
@@ -59,8 +59,10 @@ class TextureCache {
59 /// True when the API can provide info about the memory of the device. 59 /// True when the API can provide info about the memory of the device.
60 static constexpr bool HAS_DEVICE_MEMORY_INFO = P::HAS_DEVICE_MEMORY_INFO; 60 static constexpr bool HAS_DEVICE_MEMORY_INFO = P::HAS_DEVICE_MEMORY_INFO;
61 61
62 static constexpr u64 DEFAULT_EXPECTED_MEMORY = 1_GiB; 62 static constexpr s64 TARGET_THRESHOLD = 4_GiB;
63 static constexpr u64 DEFAULT_CRITICAL_MEMORY = 2_GiB; 63 static constexpr s64 DEFAULT_EXPECTED_MEMORY = 1_GiB + 125_MiB;
64 static constexpr s64 DEFAULT_CRITICAL_MEMORY = 1_GiB + 625_MiB;
65 static constexpr size_t GC_EMERGENCY_COUNTS = 2;
64 66
65 using Runtime = typename P::Runtime; 67 using Runtime = typename P::Runtime;
66 using Image = typename P::Image; 68 using Image = typename P::Image;
@@ -372,6 +374,7 @@ private:
372 u64 minimum_memory; 374 u64 minimum_memory;
373 u64 expected_memory; 375 u64 expected_memory;
374 u64 critical_memory; 376 u64 critical_memory;
377 size_t critical_gc;
375 378
376 SlotVector<Image> slot_images; 379 SlotVector<Image> slot_images;
377 SlotVector<ImageMapView> slot_map_views; 380 SlotVector<ImageMapView> slot_map_views;
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index 8c2e9e7eb..4a5de9ddf 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -12,12 +12,14 @@
12#include <vector> 12#include <vector>
13 13
14#include "common/assert.h" 14#include "common/assert.h"
15#include "common/literals.h"
15#include "common/settings.h" 16#include "common/settings.h"
16#include "video_core/vulkan_common/nsight_aftermath_tracker.h" 17#include "video_core/vulkan_common/nsight_aftermath_tracker.h"
17#include "video_core/vulkan_common/vulkan_device.h" 18#include "video_core/vulkan_common/vulkan_device.h"
18#include "video_core/vulkan_common/vulkan_wrapper.h" 19#include "video_core/vulkan_common/vulkan_wrapper.h"
19 20
20namespace Vulkan { 21namespace Vulkan {
22using namespace Common::Literals;
21namespace { 23namespace {
22namespace Alternatives { 24namespace Alternatives {
23constexpr std::array STENCIL8_UINT{ 25constexpr std::array STENCIL8_UINT{
@@ -596,6 +598,11 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
596 } 598 }
597 logical = vk::Device::Create(physical, queue_cis, extensions, first_next, dld); 599 logical = vk::Device::Create(physical, queue_cis, extensions, first_next, dld);
598 600
601 is_integrated = properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;
602 is_virtual = properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU;
603 is_non_gpu = properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_OTHER ||
604 properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU;
605
599 CollectPhysicalMemoryInfo(); 606 CollectPhysicalMemoryInfo();
600 CollectTelemetryParameters(); 607 CollectTelemetryParameters();
601 CollectToolingInfo(); 608 CollectToolingInfo();
@@ -990,6 +997,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
990 test(has_khr_swapchain_mutable_format, VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME, 997 test(has_khr_swapchain_mutable_format, VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME,
991 false); 998 false);
992 test(has_ext_line_rasterization, VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME, false); 999 test(has_ext_line_rasterization, VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME, false);
1000 test(ext_memory_budget, VK_EXT_MEMORY_BUDGET_EXTENSION_NAME, true);
993 if (Settings::values.enable_nsight_aftermath) { 1001 if (Settings::values.enable_nsight_aftermath) {
994 test(nv_device_diagnostics_config, VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME, 1002 test(nv_device_diagnostics_config, VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME,
995 true); 1003 true);
@@ -1002,7 +1010,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
1002 VkPhysicalDeviceFeatures2KHR features{}; 1010 VkPhysicalDeviceFeatures2KHR features{};
1003 features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR; 1011 features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR;
1004 1012
1005 VkPhysicalDeviceProperties2KHR physical_properties; 1013 VkPhysicalDeviceProperties2KHR physical_properties{};
1006 physical_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR; 1014 physical_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR;
1007 1015
1008 if (has_khr_shader_float16_int8) { 1016 if (has_khr_shader_float16_int8) {
@@ -1272,15 +1280,50 @@ void Device::CollectTelemetryParameters() {
1272 vendor_name = driver.driverName; 1280 vendor_name = driver.driverName;
1273} 1281}
1274 1282
1283u64 Device::GetDeviceMemoryUsage() const {
1284 VkPhysicalDeviceMemoryBudgetPropertiesEXT budget;
1285 budget.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT;
1286 budget.pNext = nullptr;
1287 physical.GetMemoryProperties(&budget);
1288 u64 result{};
1289 for (const size_t heap : valid_heap_memory) {
1290 result += budget.heapUsage[heap];
1291 }
1292 return result;
1293}
1294
1275void Device::CollectPhysicalMemoryInfo() { 1295void Device::CollectPhysicalMemoryInfo() {
1276 const auto mem_properties = physical.GetMemoryProperties(); 1296 VkPhysicalDeviceMemoryBudgetPropertiesEXT budget{};
1297 budget.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT;
1298 const auto mem_info = physical.GetMemoryProperties(ext_memory_budget ? &budget : nullptr);
1299 const auto& mem_properties = mem_info.memoryProperties;
1277 const size_t num_properties = mem_properties.memoryHeapCount; 1300 const size_t num_properties = mem_properties.memoryHeapCount;
1278 device_access_memory = 0; 1301 device_access_memory = 0;
1302 u64 device_initial_usage = 0;
1303 u64 local_memory = 0;
1279 for (size_t element = 0; element < num_properties; ++element) { 1304 for (size_t element = 0; element < num_properties; ++element) {
1280 if ((mem_properties.memoryHeaps[element].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0) { 1305 const bool is_heap_local =
1281 device_access_memory += mem_properties.memoryHeaps[element].size; 1306 (mem_properties.memoryHeaps[element].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0;
1307 if (!is_integrated && !is_heap_local) {
1308 continue;
1282 } 1309 }
1310 valid_heap_memory.push_back(element);
1311 if (is_heap_local) {
1312 local_memory += mem_properties.memoryHeaps[element].size;
1313 }
1314 if (ext_memory_budget) {
1315 device_initial_usage += budget.heapUsage[element];
1316 device_access_memory += budget.heapBudget[element];
1317 continue;
1318 }
1319 device_access_memory += mem_properties.memoryHeaps[element].size;
1320 }
1321 if (!is_integrated) {
1322 return;
1283 } 1323 }
1324 const s64 available_memory = static_cast<s64>(device_access_memory - device_initial_usage);
1325 device_access_memory = static_cast<u64>(std::max<s64>(
1326 std::min<s64>(available_memory - 8_GiB, 4_GiB), static_cast<s64>(local_memory)));
1284} 1327}
1285 1328
1286void Device::CollectToolingInfo() { 1329void Device::CollectToolingInfo() {
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h
index 1c7c18bcf..2d709d069 100644
--- a/src/video_core/vulkan_common/vulkan_device.h
+++ b/src/video_core/vulkan_common/vulkan_device.h
@@ -341,6 +341,12 @@ public:
341 return device_access_memory; 341 return device_access_memory;
342 } 342 }
343 343
344 bool CanReportMemoryUsage() const {
345 return ext_memory_budget;
346 }
347
348 u64 GetDeviceMemoryUsage() const;
349
344 u32 GetSetsPerPool() const { 350 u32 GetSetsPerPool() const {
345 return sets_per_pool; 351 return sets_per_pool;
346 } 352 }
@@ -421,6 +427,9 @@ private:
421 bool is_topology_list_restart_supported{}; ///< Support for primitive restart with list 427 bool is_topology_list_restart_supported{}; ///< Support for primitive restart with list
422 ///< topologies. 428 ///< topologies.
423 bool is_patch_list_restart_supported{}; ///< Support for primitive restart with list patch. 429 bool is_patch_list_restart_supported{}; ///< Support for primitive restart with list patch.
430 bool is_integrated{}; ///< Is GPU an iGPU.
431 bool is_virtual{}; ///< Is GPU a virtual GPU.
432 bool is_non_gpu{}; ///< Is SoftwareRasterizer, FPGA, non-GPU device.
424 bool nv_viewport_swizzle{}; ///< Support for VK_NV_viewport_swizzle. 433 bool nv_viewport_swizzle{}; ///< Support for VK_NV_viewport_swizzle.
425 bool nv_viewport_array2{}; ///< Support for VK_NV_viewport_array2. 434 bool nv_viewport_array2{}; ///< Support for VK_NV_viewport_array2.
426 bool nv_geometry_shader_passthrough{}; ///< Support for VK_NV_geometry_shader_passthrough. 435 bool nv_geometry_shader_passthrough{}; ///< Support for VK_NV_geometry_shader_passthrough.
@@ -445,6 +454,7 @@ private:
445 bool ext_shader_atomic_int64{}; ///< Support for VK_KHR_shader_atomic_int64. 454 bool ext_shader_atomic_int64{}; ///< Support for VK_KHR_shader_atomic_int64.
446 bool ext_conservative_rasterization{}; ///< Support for VK_EXT_conservative_rasterization. 455 bool ext_conservative_rasterization{}; ///< Support for VK_EXT_conservative_rasterization.
447 bool ext_provoking_vertex{}; ///< Support for VK_EXT_provoking_vertex. 456 bool ext_provoking_vertex{}; ///< Support for VK_EXT_provoking_vertex.
457 bool ext_memory_budget{}; ///< Support for VK_EXT_memory_budget.
448 bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config. 458 bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config.
449 bool has_broken_cube_compatibility{}; ///< Has broken cube compatiblity bit 459 bool has_broken_cube_compatibility{}; ///< Has broken cube compatiblity bit
450 bool has_renderdoc{}; ///< Has RenderDoc attached 460 bool has_renderdoc{}; ///< Has RenderDoc attached
@@ -456,6 +466,7 @@ private:
456 // Telemetry parameters 466 // Telemetry parameters
457 std::string vendor_name; ///< Device's driver name. 467 std::string vendor_name; ///< Device's driver name.
458 std::vector<std::string> supported_extensions; ///< Reported Vulkan extensions. 468 std::vector<std::string> supported_extensions; ///< Reported Vulkan extensions.
469 std::vector<size_t> valid_heap_memory; ///< Heaps used.
459 470
460 /// Format properties dictionary. 471 /// Format properties dictionary.
461 std::unordered_map<VkFormat, VkFormatProperties> format_properties; 472 std::unordered_map<VkFormat, VkFormatProperties> format_properties;
diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
index 300a61205..e6e97b332 100644
--- a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
+++ b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
@@ -227,7 +227,7 @@ void MemoryCommit::Release() {
227} 227}
228 228
229MemoryAllocator::MemoryAllocator(const Device& device_, bool export_allocations_) 229MemoryAllocator::MemoryAllocator(const Device& device_, bool export_allocations_)
230 : device{device_}, properties{device_.GetPhysical().GetMemoryProperties()}, 230 : device{device_}, properties{device_.GetPhysical().GetMemoryProperties().memoryProperties},
231 export_allocations{export_allocations_}, 231 export_allocations{export_allocations_},
232 buffer_image_granularity{ 232 buffer_image_granularity{
233 device_.GetPhysical().GetProperties().limits.bufferImageGranularity} {} 233 device_.GetPhysical().GetProperties().limits.bufferImageGranularity} {}
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp
index a794f16dd..742cc39da 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.cpp
+++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp
@@ -237,8 +237,8 @@ bool Load(VkInstance instance, InstanceDispatch& dld) noexcept {
237 return X(vkCreateDevice) && X(vkDestroyDevice) && X(vkDestroyDevice) && 237 return X(vkCreateDevice) && X(vkDestroyDevice) && X(vkDestroyDevice) &&
238 X(vkEnumerateDeviceExtensionProperties) && X(vkEnumeratePhysicalDevices) && 238 X(vkEnumerateDeviceExtensionProperties) && X(vkEnumeratePhysicalDevices) &&
239 X(vkGetDeviceProcAddr) && X(vkGetPhysicalDeviceFormatProperties) && 239 X(vkGetDeviceProcAddr) && X(vkGetPhysicalDeviceFormatProperties) &&
240 X(vkGetPhysicalDeviceMemoryProperties) && X(vkGetPhysicalDeviceProperties) && 240 X(vkGetPhysicalDeviceMemoryProperties) && X(vkGetPhysicalDeviceMemoryProperties2) &&
241 X(vkGetPhysicalDeviceQueueFamilyProperties); 241 X(vkGetPhysicalDeviceProperties) && X(vkGetPhysicalDeviceQueueFamilyProperties);
242#undef X 242#undef X
243} 243}
244 244
@@ -926,9 +926,12 @@ std::vector<VkPresentModeKHR> PhysicalDevice::GetSurfacePresentModesKHR(
926 return modes; 926 return modes;
927} 927}
928 928
929VkPhysicalDeviceMemoryProperties PhysicalDevice::GetMemoryProperties() const noexcept { 929VkPhysicalDeviceMemoryProperties2 PhysicalDevice::GetMemoryProperties(
930 VkPhysicalDeviceMemoryProperties properties; 930 void* next_structures) const noexcept {
931 dld->vkGetPhysicalDeviceMemoryProperties(physical_device, &properties); 931 VkPhysicalDeviceMemoryProperties2 properties{};
932 properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2;
933 properties.pNext = next_structures;
934 dld->vkGetPhysicalDeviceMemoryProperties2(physical_device, &properties);
932 return properties; 935 return properties;
933} 936}
934 937
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h
index 53bac627f..0a5f9931c 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.h
+++ b/src/video_core/vulkan_common/vulkan_wrapper.h
@@ -172,6 +172,7 @@ struct InstanceDispatch {
172 PFN_vkGetPhysicalDeviceFeatures2KHR vkGetPhysicalDeviceFeatures2KHR{}; 172 PFN_vkGetPhysicalDeviceFeatures2KHR vkGetPhysicalDeviceFeatures2KHR{};
173 PFN_vkGetPhysicalDeviceFormatProperties vkGetPhysicalDeviceFormatProperties{}; 173 PFN_vkGetPhysicalDeviceFormatProperties vkGetPhysicalDeviceFormatProperties{};
174 PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties{}; 174 PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties{};
175 PFN_vkGetPhysicalDeviceMemoryProperties2 vkGetPhysicalDeviceMemoryProperties2{};
175 PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties{}; 176 PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties{};
176 PFN_vkGetPhysicalDeviceProperties2KHR vkGetPhysicalDeviceProperties2KHR{}; 177 PFN_vkGetPhysicalDeviceProperties2KHR vkGetPhysicalDeviceProperties2KHR{};
177 PFN_vkGetPhysicalDeviceQueueFamilyProperties vkGetPhysicalDeviceQueueFamilyProperties{}; 178 PFN_vkGetPhysicalDeviceQueueFamilyProperties vkGetPhysicalDeviceQueueFamilyProperties{};
@@ -950,7 +951,8 @@ public:
950 951
951 std::vector<VkPresentModeKHR> GetSurfacePresentModesKHR(VkSurfaceKHR) const; 952 std::vector<VkPresentModeKHR> GetSurfacePresentModesKHR(VkSurfaceKHR) const;
952 953
953 VkPhysicalDeviceMemoryProperties GetMemoryProperties() const noexcept; 954 VkPhysicalDeviceMemoryProperties2 GetMemoryProperties(
955 void* next_structures = nullptr) const noexcept;
954 956
955private: 957private:
956 VkPhysicalDevice physical_device = nullptr; 958 VkPhysicalDevice physical_device = nullptr;
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 4b943c6ba..d2e735f48 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -775,6 +775,7 @@ void Config::ReadUIValues() {
775 ReadBasicSetting(UISettings::values.pause_when_in_background); 775 ReadBasicSetting(UISettings::values.pause_when_in_background);
776 ReadBasicSetting(UISettings::values.mute_when_in_background); 776 ReadBasicSetting(UISettings::values.mute_when_in_background);
777 ReadBasicSetting(UISettings::values.hide_mouse); 777 ReadBasicSetting(UISettings::values.hide_mouse);
778 ReadBasicSetting(UISettings::values.disable_web_applet);
778 779
779 qt_config->endGroup(); 780 qt_config->endGroup();
780} 781}
@@ -1308,6 +1309,7 @@ void Config::SaveUIValues() {
1308 WriteBasicSetting(UISettings::values.pause_when_in_background); 1309 WriteBasicSetting(UISettings::values.pause_when_in_background);
1309 WriteBasicSetting(UISettings::values.mute_when_in_background); 1310 WriteBasicSetting(UISettings::values.mute_when_in_background);
1310 WriteBasicSetting(UISettings::values.hide_mouse); 1311 WriteBasicSetting(UISettings::values.hide_mouse);
1312 WriteBasicSetting(UISettings::values.disable_web_applet);
1311 1313
1312 qt_config->endGroup(); 1314 qt_config->endGroup();
1313} 1315}
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index c1cf4050c..bd50f7a68 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -59,6 +59,13 @@ void ConfigureDebug::SetConfiguration() {
59 ui->disable_loop_safety_checks->setChecked( 59 ui->disable_loop_safety_checks->setChecked(
60 Settings::values.disable_shader_loop_safety_checks.GetValue()); 60 Settings::values.disable_shader_loop_safety_checks.GetValue());
61 ui->extended_logging->setChecked(Settings::values.extended_logging.GetValue()); 61 ui->extended_logging->setChecked(Settings::values.extended_logging.GetValue());
62
63#ifdef YUZU_USE_QT_WEB_ENGINE
64 ui->disable_web_applet->setChecked(UISettings::values.disable_web_applet.GetValue());
65#else
66 ui->disable_web_applet->setEnabled(false);
67 ui->disable_web_applet->setText(QString::fromUtf8("Web applet not compiled"));
68#endif
62} 69}
63 70
64void ConfigureDebug::ApplyConfiguration() { 71void ConfigureDebug::ApplyConfiguration() {
@@ -80,6 +87,7 @@ void ConfigureDebug::ApplyConfiguration() {
80 ui->disable_loop_safety_checks->isChecked(); 87 ui->disable_loop_safety_checks->isChecked();
81 Settings::values.disable_macro_jit = ui->disable_macro_jit->isChecked(); 88 Settings::values.disable_macro_jit = ui->disable_macro_jit->isChecked();
82 Settings::values.extended_logging = ui->extended_logging->isChecked(); 89 Settings::values.extended_logging = ui->extended_logging->isChecked();
90 UISettings::values.disable_web_applet = ui->disable_web_applet->isChecked();
83 Debugger::ToggleConsole(); 91 Debugger::ToggleConsole();
84 Common::Log::Filter filter; 92 Common::Log::Filter filter;
85 filter.ParseFilterString(Settings::values.log_filter.GetValue()); 93 filter.ParseFilterString(Settings::values.log_filter.GetValue());
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index 4dd870855..edb525e82 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -8,49 +8,49 @@
8 <property name="title"> 8 <property name="title">
9 <string>Logging</string> 9 <string>Logging</string>
10 </property> 10 </property>
11 <layout class="QGridLayout" name="gridLayout_1"> 11 <layout class="QGridLayout" name="gridLayout_1">
12 <item row="0" column="0" colspan="2"> 12 <item row="0" column="0" colspan="2">
13 <layout class="QHBoxLayout" name="horizontalLayout_1"> 13 <layout class="QHBoxLayout" name="horizontalLayout_1">
14 <item> 14 <item>
15 <widget class="QLabel" name="label_1"> 15 <widget class="QLabel" name="label_1">
16 <property name="text">
17 <string>Global Log Filter</string>
18 </property>
19 </widget>
20 </item>
21 <item>
22 <widget class="QLineEdit" name="log_filter_edit"/>
23 </item>
24 </layout>
25 </item>
26 <item row="1" column="0">
27 <widget class="QCheckBox" name="toggle_console">
28 <property name="text">
29 <string>Show Log in Console</string>
30 </property>
31 </widget>
32 </item>
33 <item row="1" column="1">
34 <widget class="QPushButton" name="open_log_button">
35 <property name="text">
36 <string>Open Log Location</string>
37 </property>
38 </widget>
39 </item>
40 <item row="2" column="0">
41 <widget class="QCheckBox" name="extended_logging">
42 <property name="enabled">
43 <bool>true</bool>
44 </property>
45 <property name="toolTip">
46 <string>When checked, the max size of the log increases from 100 MB to 1 GB</string>
47 </property>
48 <property name="text"> 16 <property name="text">
49 <string>Enable Extended Logging**</string> 17 <string>Global Log Filter</string>
50 </property> 18 </property>
51 </widget> 19 </widget>
52 </item> 20 </item>
53 </layout> 21 <item>
22 <widget class="QLineEdit" name="log_filter_edit"/>
23 </item>
24 </layout>
25 </item>
26 <item row="1" column="0">
27 <widget class="QCheckBox" name="toggle_console">
28 <property name="text">
29 <string>Show Log in Console</string>
30 </property>
31 </widget>
32 </item>
33 <item row="1" column="1">
34 <widget class="QPushButton" name="open_log_button">
35 <property name="text">
36 <string>Open Log Location</string>
37 </property>
38 </widget>
39 </item>
40 <item row="2" column="0">
41 <widget class="QCheckBox" name="extended_logging">
42 <property name="enabled">
43 <bool>true</bool>
44 </property>
45 <property name="toolTip">
46 <string>When checked, the max size of the log increases from 100 MB to 1 GB</string>
47 </property>
48 <property name="text">
49 <string>Enable Extended Logging**</string>
50 </property>
51 </widget>
52 </item>
53 </layout>
54 </widget> 54 </widget>
55 </item> 55 </item>
56 <item> 56 <item>
@@ -183,7 +183,7 @@
183 <string>Advanced</string> 183 <string>Advanced</string>
184 </property> 184 </property>
185 <layout class="QGridLayout" name="gridLayout_4"> 185 <layout class="QGridLayout" name="gridLayout_4">
186 <item> row="0" column="0"> 186 <item row="0" column="0">
187 <widget class="QCheckBox" name="quest_flag"> 187 <widget class="QCheckBox" name="quest_flag">
188 <property name="text"> 188 <property name="text">
189 <string>Kiosk (Quest) Mode</string> 189 <string>Kiosk (Quest) Mode</string>
@@ -218,6 +218,13 @@
218 </property> 218 </property>
219 </widget> 219 </widget>
220 </item> 220 </item>
221 <item row="2" column="1">
222 <widget class="QCheckBox" name="disable_web_applet">
223 <property name="text">
224 <string>Disable Web Applet**</string>
225 </property>
226 </widget>
227 </item>
221 </layout> 228 </layout>
222 </widget> 229 </widget>
223 </item> 230 </item>
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index f2182d901..62d15f8cd 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -293,8 +293,6 @@ GMainWindow::GMainWindow()
293 293
294 MigrateConfigFiles(); 294 MigrateConfigFiles();
295 295
296 ui->action_Fullscreen->setChecked(false);
297
298#if defined(HAVE_SDL2) && !defined(_WIN32) 296#if defined(HAVE_SDL2) && !defined(_WIN32)
299 SDL_InitSubSystem(SDL_INIT_VIDEO); 297 SDL_InitSubSystem(SDL_INIT_VIDEO);
300 // SDL disables the screen saver by default, and setting the hint 298 // SDL disables the screen saver by default, and setting the hint
@@ -312,17 +310,20 @@ GMainWindow::GMainWindow()
312 } 310 }
313 311
314 QString game_path; 312 QString game_path;
313 bool has_gamepath = false;
314 bool is_fullscreen = false;
315 315
316 for (int i = 1; i < args.size(); ++i) { 316 for (int i = 1; i < args.size(); ++i) {
317 // Preserves drag/drop functionality 317 // Preserves drag/drop functionality
318 if (args.size() == 2 && !args[1].startsWith(QChar::fromLatin1('-'))) { 318 if (args.size() == 2 && !args[1].startsWith(QChar::fromLatin1('-'))) {
319 game_path = args[1]; 319 game_path = args[1];
320 has_gamepath = true;
320 break; 321 break;
321 } 322 }
322 323
323 // Launch game in fullscreen mode 324 // Launch game in fullscreen mode
324 if (args[i] == QStringLiteral("-f")) { 325 if (args[i] == QStringLiteral("-f")) {
325 ui->action_Fullscreen->setChecked(true); 326 is_fullscreen = true;
326 continue; 327 continue;
327 } 328 }
328 329
@@ -365,9 +366,15 @@ GMainWindow::GMainWindow()
365 } 366 }
366 367
367 game_path = args[++i]; 368 game_path = args[++i];
369 has_gamepath = true;
368 } 370 }
369 } 371 }
370 372
373 // Override fullscreen setting if gamepath or argument is provided
374 if (has_gamepath || is_fullscreen) {
375 ui->action_Fullscreen->setChecked(is_fullscreen);
376 }
377
371 if (!game_path.isEmpty()) { 378 if (!game_path.isEmpty()) {
372 BootGame(game_path); 379 BootGame(game_path);
373 } 380 }
@@ -582,7 +589,7 @@ void GMainWindow::WebBrowserOpenWebPage(const std::string& main_url,
582#ifdef YUZU_USE_QT_WEB_ENGINE 589#ifdef YUZU_USE_QT_WEB_ENGINE
583 590
584 // Raw input breaks with the web applet, Disable web applets if enabled 591 // Raw input breaks with the web applet, Disable web applets if enabled
585 if (disable_web_applet || Settings::values.enable_raw_input) { 592 if (UISettings::values.disable_web_applet || Settings::values.enable_raw_input) {
586 emit WebBrowserClosed(Service::AM::Applets::WebExitReason::WindowClosed, 593 emit WebBrowserClosed(Service::AM::Applets::WebExitReason::WindowClosed,
587 "http://localhost/"); 594 "http://localhost/");
588 return; 595 return;
@@ -647,12 +654,12 @@ void GMainWindow::WebBrowserOpenWebPage(const std::string& main_url,
647 connect(exit_action, &QAction::triggered, this, [this, &web_browser_view] { 654 connect(exit_action, &QAction::triggered, this, [this, &web_browser_view] {
648 const auto result = QMessageBox::warning( 655 const auto result = QMessageBox::warning(
649 this, tr("Disable Web Applet"), 656 this, tr("Disable Web Applet"),
650 tr("Disabling the web applet will cause it to not be shown again for the rest of the " 657 tr("Disabling the web applet can lead to undefined behavior and should only be used "
651 "emulated session. This can lead to undefined behavior and should only be used with " 658 "with Super Mario 3D All-Stars. Are you sure you want to disable the web "
652 "Super Mario 3D All-Stars. Are you sure you want to disable the web applet?"), 659 "applet?\n(This can be re-enabled in the Debug settings.)"),
653 QMessageBox::Yes | QMessageBox::No); 660 QMessageBox::Yes | QMessageBox::No);
654 if (result == QMessageBox::Yes) { 661 if (result == QMessageBox::Yes) {
655 disable_web_applet = true; 662 UISettings::values.disable_web_applet = true;
656 web_browser_view.SetFinished(true); 663 web_browser_view.SetFinished(true);
657 } 664 }
658 }); 665 });
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 7659c8000..ab95a7518 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -398,9 +398,6 @@ private:
398 // Last game booted, used for multi-process apps 398 // Last game booted, used for multi-process apps
399 QString last_filename_booted; 399 QString last_filename_booted;
400 400
401 // Disables the web applet for the rest of the emulated session
402 bool disable_web_applet{};
403
404 // Applets 401 // Applets
405 QtSoftwareKeyboardDialog* software_keyboard = nullptr; 402 QtSoftwareKeyboardDialog* software_keyboard = nullptr;
406 403
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h
index 06e8b46da..cc5aee382 100644
--- a/src/yuzu/uisettings.h
+++ b/src/yuzu/uisettings.h
@@ -114,6 +114,7 @@ struct Values {
114 114
115 bool configuration_applied; 115 bool configuration_applied;
116 bool reset_to_defaults; 116 bool reset_to_defaults;
117 Settings::BasicSetting<bool> disable_web_applet{true, "disable_web_applet"};
117}; 118};
118 119
119extern Values values; 120extern Values values;
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 14bf82f39..ab12dd15d 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -74,6 +74,7 @@ static void PrintVersion() {
74int main(int argc, char** argv) { 74int main(int argc, char** argv) {
75 Common::Log::Initialize(); 75 Common::Log::Initialize();
76 Common::Log::SetColorConsoleBackendEnabled(true); 76 Common::Log::SetColorConsoleBackendEnabled(true);
77 Common::Log::Start();
77 Common::DetachedTasks detached_tasks; 78 Common::DetachedTasks detached_tasks;
78 79
79 int option_index = 0; 80 int option_index = 0;