Compare commits

..

106 Commits

Author SHA1 Message Date
Subv
51d4c772a2 RasterizerCache: Swizzle to a staging buffer before copying it to memory when flushing a texture.
This gives us slightly increased memory safety, writing to out of bounds memory will be caught at the point of write instead of several calls down the line as memory corruption
2018-09-21 13:53:13 -05:00
Subv
2fd06acc8f GPU/DMA: Fixed Tiled->Linear transfers.
We no longer write to out of bounds memory anymore.
2018-09-21 13:53:12 -05:00
Subv
bdb3920753 GPU/DMA: Copy the requested amount of data when doing Linear->Linear 2D transfers. 2018-09-21 13:53:12 -05:00
Subv
bb9eeba670 GPU/DMA: Fixed the Linear->Tiled and Linear->Linear transfer modes.
This fixes the loading bar in Has-Been Heroes.

The Tiled->Tiled transfer mode is not implemented yet and will assert.
2018-09-21 13:53:12 -05:00
Subv
47826fd090 RasterizerGL: Flush dirty cached objects before removing them from the cache.
There might be a case where we draw to a framebuffer (thus making it dirty) and then proceed to overwrite only a portion of it from the CPU. The current code would cause the rest of the modified framebuffer to be discarded.
2018-09-21 13:53:11 -05:00
Subv
b87b8db879 GPU/Engines: Flush and invalidate source/dest regions in Fermi2D and KeplerMemory copies. 2018-09-21 13:53:11 -05:00
Subv
23e1dd3c4d RasterizerCache: Do not flush surfaces that have not been modified.
This should bring the performance closer to what it was before flushing was implemented.
2018-09-21 13:53:10 -05:00
Subv
acfc8d7fc9 RasterizerCache: Convert the S8Z24 format from Z24S8 back to S8Z24 when flushing. 2018-09-21 13:53:10 -05:00
Subv
81afcf3f4e RasterizerCache: Allow flushing compressed formats. 2018-09-21 13:53:10 -05:00
Subv
b27ba353d4 RasterizerCache: Log problematic formats when flushing a surface. 2018-09-21 13:53:09 -05:00
Subv
8474a5adf7 GPU/DMA: Implemented source offset copies for unswizzling Tiled->Linear transfers.
This is used by nouveau to implement glReadPixels.
2018-09-21 13:53:09 -05:00
Subv
ca7eb39b86 RasterizerCache: Remove the glGetError call after a surface flush 2018-09-21 13:53:08 -05:00
Subv
393f2418c5 GPU/DMA: Flush the source memory region before a DMA transfer and invalidate the destination region after the transfer. 2018-09-21 13:53:08 -05:00
Subv
d0a814eaca GPU/DMA: Pass the current rasterizer as a variable when constructing the DMA engine. 2018-09-21 13:53:06 -05:00
Subv
047bbe0881 RasterizerCache: Re-introduced the code to flush the various resource caches. 2018-09-21 13:53:06 -05:00
Subv
181aca9af0 RasterizerCache: Reintroduced code for flushing OpenGL surfaces back to memory.
This is required for a proper implementation of the DMA and 2D engines.
2018-09-21 13:53:05 -05:00
bunnei
4f186de069 Merge pull request #1379 from lioncash/bitwise
gl_stream_buffer: Fix use of bitwise OR instead of logical OR in Map()
2018-09-21 14:02:00 -04:00
Lioncash
ba02dd9ebc gl_stream_buffer: Fix use of bitwise OR instead of logical OR in Map()
This was very likely intended to be a logical OR based off the
conditioning and testing of inversion in one case.

Even if this was intentional, this is the kind of non-obvious thing one
should be clarifying with a comment.
2018-09-21 07:59:03 -04:00
David
1db1e013e0 Added support for uncompressed NSOs (#1374)
* Added support for uncompressed NSOs

* Moved compressed section check to NsoHeader
2018-09-21 00:39:30 -04:00
James Rowe
8606995515 Merge pull request #1225 from tech4me/travis-windows
travis: running mingw build on travis ci
2018-09-20 21:39:15 -06:00
bunnei
000da01c1d Merge pull request #1337 from DarkLordZach/create-fs-cmd
yuzu-cmd: Add filesystem service initializer call
2018-09-20 23:36:11 -04:00
bunnei
0285ddfbd4 Merge pull request #1372 from lioncash/thread
kernel/thread: Use owner_process when setting the page table in SetupMainThread()
2018-09-20 23:35:29 -04:00
bunnei
072053ab95 Merge pull request #1371 from lioncash/fwd-arm
arm_interface: Replace kernel vm_manager include with a forward declaration
2018-09-20 23:35:06 -04:00
bunnei
a373dddbd9 Merge pull request #1375 from Subv/gl_clear
RasterizerGL: Use the correct framebuffer when clearing via the CLEAR_BUFFERS register.
2018-09-20 23:34:39 -04:00
Subv
9cd5c61fcf RasterizerGL: Use the correct framebuffer when clearing via the CLEAR_BUFFERS register.
Previously we were clearing the default backbuffer framebuffer.

Found thanks to a Piglit test :)
2018-09-20 22:31:53 -05:00
bunnei
c1c59617ad Merge pull request #1364 from lioncash/content
file-sys: Default heavy-weight class destructors in the cpp file
2018-09-20 23:31:31 -04:00
bunnei
cf97d00eb6 Merge pull request #1367 from lioncash/plural
game_list: Handle plurals within setFilterResult() better
2018-09-20 23:30:32 -04:00
bunnei
b0b57c21e6 Merge pull request #1368 from ogniK5377/nifm-fix
Added IRequest::Submit
2018-09-20 23:30:11 -04:00
bunnei
a0db7e2cf9 Merge pull request #1352 from lioncash/sharing
ring_buffer: Use std::hardware_destructive_interference_size to determine alignment size for avoiding false sharing
2018-09-20 23:29:10 -04:00
bunnei
9f92533cc2 Merge pull request #1373 from ogniK5377/revert-nifm
Revert GetRequestState
2018-09-20 22:19:58 -04:00
David Marcec
b918925bd5 Revert GetRequestState
Even though setting this value to 3 is more correct. We break more games than we fix due to missing implementations. We should keep this as 0 for the time being
2018-09-21 12:15:49 +10:00
Lioncash
05aa4aa01a kernel/thread: Use owner_process when setting the page table in SetupMainThread()
The owning process of a thread is required to exist before the thread,
so we can enforce this API-wise by using a reference. We can also avoid
the reliance on the system instance by using that parameter to access
the page table that needs to be set.
2018-09-20 21:10:00 -04:00
Lioncash
9b8fc2b689 arm_interface: Replace kernel vm_manager include with a forward declaration
Avoids an unnecessary inclusion and also uncovers three places where
indirect inclusions were relied upon, which allows us to also resolve
those.
2018-09-20 19:35:36 -04:00
Mat M
f237265187 Merge pull request #1370 from Hedges/GDBClean
Correct endianness of BRK
2018-09-20 17:25:56 -04:00
Mat M
1e35d8fa8f Merge pull request #1362 from MerryMage/dynarmic
externals: Update dynarmic to 171d116
2018-09-20 17:22:42 -04:00
Jarek Syrylak
74f966404d Correct endianness of BKPT 2018-09-20 22:03:57 +01:00
MerryMage
ef31487624 arm_dynarmic: Halt when BRK encountered 2018-09-20 19:12:42 +01:00
David Marcec
3f49725a51 Fixed submit 2018-09-21 00:51:13 +10:00
David Marcec
cfc9fe4460 Added IRequest::Submit
This fixes updated versions of SMO. Currently unable to test as I don't have an updated version
2018-09-21 00:47:30 +10:00
bunnei
8fe9572271 Merge pull request #1358 from DarkLordZach/temp-storage
savedata_factory: Add TemporaryStorage SaveDataType
2018-09-20 10:13:38 -04:00
bunnei
0931a711de Merge pull request #1363 from lioncash/control
control_metadata: Move language name array definition to the cpp file
2018-09-20 10:13:23 -04:00
bunnei
b02a1e38fa Merge pull request #1361 from lioncash/nax
xts_archive/nax: Minor interface changes
2018-09-20 10:13:10 -04:00
bunnei
e57aa62b11 Merge pull request #1366 from ogniK5377/splat-fix
Implemented NTC & IEnsureNetworkClockAvailabilityService
2018-09-20 10:12:35 -04:00
Lioncash
e980e90d6e game_list: Handle plurals within setFilterResult() better
Qt provides an overload of tr() that operates on quantities in relation
to pluralization. This also allows the translation to adapt based on the
target language rules better.

For example, the previous code would result in an incorrect translation
for the French language (which doesn't use the pluralized version of
"result" in the case of a total of zero. While in English it's
correct to use the pluralized version of "result", that is, "results"

---

For example:

English: "0 results"

French: "0 résultat" (uses the singular form)

In French, the noun being counted is singular if the quantity is 0 or 1.
In English, on the other hand, if the noun being counted has a quantity
of 0 or N > 1, then the noun is pluralized.

---

For another example in a language that has different counting methods
than the above, consider English and Irish. Irish has a special form of
of a grammatical number called a dual. Which alters how a word is
written when N of something is 2. This won't appear in this case with a
direct number "2", but it would change if we ever used "Two" to refer to
two of something. For example:

English: "Zero results"

Irish: "Toradh ar bith"

English: "One result"

Irish: "Toradh amháin"

English: "Two results"

Irish: "Dhá thorthaí" <- Dual case

Which is an important distinction to make between singular and plural,
because in other situations, "two" on its own would be written as "dó"
in Irish. There's also a few other cases where the order the words are
placed *and* whether or not the plural or singular variant of the word
is used *and* whether or not the word is placed after or between a set
of numbers can vary. Counting in Irish also differs depending on whether or not
you're counting things (like above) or counting people, in which case an
entirely different set of numbers are used.

It's not important for this case, but it's provided as an example as to why one
should never assume the placement of values in text will be like that of
English or other languages. Some languages have very different ways to
represent counting, and breaking up the translated string like this
isn't advisable because it makes it extremely difficult to get right
depending on what language a translator is translating text into due to
the ambiguity of the strings being presented for translation.

In this case a translator would see three fragmented strings on
Transifex (and not necessarily grouped beside one another, but even
then, it would still be annoying to decipher):

- "of"
- "result"
- "results"

There is no way a translator is going to know what those sets of words
are actually used for unless they look at the code to see what is being
done with them (which they shouldn't have to do).
2018-09-20 01:35:50 -04:00
David Marcec
583137709f Removed unneeded event clear 2018-09-20 15:16:08 +10:00
David Marcec
335e9d18ae Implemented NTC & IEnsureNetworkClockAvailabilityService
Needed because of the recent nim fixes
2018-09-20 15:14:07 +10:00
Lioncash
05ef9dfc10 file-sys: Default heavy-weight class destructors in the cpp file
Several classes have a lot of non-trivial members within them, or don't
but likely should have the destructor defaulted in the cpp file for
future-proofing/being more friendly to forward declarations.

Leaving the destructor unspecified allows the compiler to inline the
destruction code all over the place, which is generally undesirable from
a code bloat perspective.
2018-09-19 19:34:08 -04:00
Lioncash
3146502a12 control_metadata: Remove unnecessary else within GetLanguageEntry()
There's no need to indent the code here, given the if case contains a
return statement at the end of it.
2018-09-19 19:02:06 -04:00
Lioncash
05d49962b6 control_metadata: Move language name array definition to the cpp file
This was used in two different translation units
(deconstructed_rom_directory and patch_manager). This means we'd be
pointlessly duplicating the whole array twice due to it being defined
within the header.
2018-09-19 18:57:26 -04:00
MerryMage
3b4a158230 arm_dynarmic: Support BKPT instruction 2018-09-19 21:00:38 +01:00
MerryMage
321eb0b6b0 externals: Update dynarmic to 171d116
171d116 A64: Implement SCVTF, UCVTF (vector, fixed-point), scalar variant
f221bb0 emit_x64_floating_point: Reduce fallback LUT code in EmitFPToFixed
eb123e2 A64: Implement FCVTZS, FCVTZU, UCVTF, SCVTF (vector, fixed-point), vector variant
487d37a A64: Implement UQSHL's vector immediate and register variants
f698933 ir: Add opcodes for unsigned saturating left shifts
7148e66 A64/translate/impl: Make signatures consistent for unimplemented by-element SIMD variants
fdde4ca A64: Implement BRK
b1490db A64/imm: Add full range of comparison operators to Imm template
1ec40ef IR: Add fbits argument to FPVectorFrom{Signed,Unsigned}Fixed
d6d5e98 A64: Implement SCVTF, UCVTF (scalar, fixed-point)
6513595 opcodes.inc: Align columns to a tabstop of 4
6b0d2b5 IR: Add fbits argument to FixedToFP-related opcodes
c4b3831 A64: Implement SQSHL's vector immediate variant
e0d8d2d A64: Implement SQSHL's vector register variant
5327625 ir: Add opcodes for left signed saturated shifts
9705252 branch: Make variables const where applicable
650946e move_wide: Make variables const where applicable
62b3a6d load_store_register_unprivileged: Make variables const where applicable
3add1c7 load_store_register_immediate: Place conditional bodies on their own line
2fc4088 load_store_load_literal: Make variables const where applicable
b2c1462 data_processing_logical: Move datasize declarations after early-exit conditionals
028028f data_processing_conditional_select: Make variables const where applicable
c66042d data_processing_addsub: Move datasize declarations after early-exit conditionals
6bc546e data_processing_bitfield: Move datasize variables after early-exit conditionals
2aad5fa A64: Implement CLS's vector variant
6c877ff emit_x64_vector: Make EmitVectorUnsignedSaturatedAccumulateSigned() internally linked
4b5926d perf_map: Use std::string_view instead of std::string for PerfMapRegister()
7445947 A64: Implement SQRDMULH (vector), vector variant
03b80f2 A64: Implement SQDMULL (vector), vector variant
4a2c596 IR: Add VectorSignedSaturatedDoublingMultiplyLong
59dc33e emit_x64_vector: Changes to VectorSignedSaturatedDoublingMultiply
bbaebeb IR: Implement Vector{Signed,Unsigned}Multiply{16,32}
baac5a8 backend_x64/a64_interface: Re-enable the constant folding pass
e78ca19 emit_x64_vector_floating_point: Hardware FMA implementation for RSqrtStepFused
8a5ae9a emit_x64_vector_floating_point: Hardware FMA implementation of FPVectorRecipStepFused
39818f9 emit_x64_floating_point: Hardware FMA implementation of FPRSqrtStepFused
3d0a0b4 emit_x64_floating_point: Hardware FMA implementation of FPRecipStepFused{32,64}
2293dff emit_x64_vector: SSE implementation of VectorSignedSaturatedAccumulateUnsigned{8,16,32}
2047683 emit_x64_vector: Correct static asserts for < 64-bit type checks in saturated accumulate fallbacks
55e9e40 emit_x64_vector: EmitVectorSignedSaturatedAccumulateUnsigned64: SSE implementation
1076651 emit_x64_vector: Simplify fpsr_qc related code
4039030 A64: Implement CLZ's vector variant
0bb908f ir: Add opcodes for vector CLZ operations
3b13259 A64/translate: VectorZeroUpper for V(64) stores
1931d44 simd_two_register_misc: FNEG (vector) with Q == 0 had dirty upper
a0790f0 emit_x64_vector: Remove unnecessary [[maybe_unused]] attributes
b0e1eb5 A64: Implement USQADD's scalar and vector variants
28424c7 ir: Add opcodes form unsigned saturated accumulations of signed values
9923ea0 A64: Implement SUQADD's scalar and vector variants
4c0adbb ir: Add opcodes for signed saturated accumulations of unsigned values
799bfed A64: Implement SMLAL{2}, SMLSL{2}, UMLAL{2}, and UMLSL{2}'s vector by-element variants
94451ec A64: Implement UMULL{2}'s vector by-element variant
45867de A64: Implement SMULL{2}'s vector by-element variant
0235793 ir/value: Replace includes with forward declarations
450f721 ir/cond: Migrate to C++17 nested namespace specifiers
e649988 CMakeLists: Add missing cond.h header to file listing
d20e769 A64: Implement URSQRTE
4f3bde5 ir: Add opcodes for performing unsigned reciprocal square root estimates
cfeeaec A64: Implement URECPE
622b60e ir: Add opcodes for unsigned reciprocal estimate
d17599a Update Xbyak to 5.71
f7c26e9 Squashed 'externals/xbyak/' changes from 671fc805..1de435ed
8782b69 travis: Make macOS build with Xcode 9.4.1
b575b23 A64: Implement SQNEG's scalar and vector variant
06062a9 A64: Add opcodes for signed saturating negations
1c40579 emit_x64_vector: Simplify "position == 0" case for EmitVectorExtract()
e335050 emit_x64_vector: Simplify "position == 0" case for EmitVectorExtractLower()
8b13421 A64: Implement SQDMULH's by-element scalar variant
9122a6e A64: Implement SQDMULH's by-element vector variant
176e60e backend/x64: Do not clear fast_dispatch_table if not enabled
2018-09-19 20:31:39 +01:00
Lioncash
2e5f0e5024 xts_archive: Remove unused variables from CalculateHMAC256()
These variables aren't used, which still has an impact, as std::vector
cannot be optimized away by the compiler (it's constructor and
destructor are both non-trivial), so this was just wasting memory.
2018-09-19 14:23:13 -04:00
Lioncash
2752183883 xts_archive: Make AsNCA() return a std::unique_ptr instead of a std::shared_ptr
std::shared_ptr isn't strictly necessary here and is only ever used in
contexts where the object doesn't depend on being shared. This also
makes the interface more flexible, as it's possible to create a
std::shared_ptr from a std::unique_ptr (std::shared_ptr has a
constructor that accepts a std::unique_ptr), but not the other way
around.
2018-09-19 14:22:37 -04:00
Lioncash
45195a51a7 nax: Avoid re-parsing NAX data with GetFileType()
An instance of the NAX apploader already has an existing NAX instance in
memory. Calling directly into IdentifyType() directly would re-parse the
whole file again into yet another NAX instance, only to toss it away
again.

This gets rid of unnecessary/redundant file parsing and allocations.
2018-09-19 14:22:37 -04:00
Lioncash
c8c4105659 nax: Avoid unnecessary calls to AsNCA() in IdentifyType()
AsNCA() allocates an NCA instance every time it's called. In the current
manner it's used, it's quite inefficient as it's making a redundant
allocation.

We can just amend the order of the conditionals to make it easier to
just call it once.
2018-09-19 14:22:37 -04:00
Lioncash
f272261c21 xts_archive: Ensure NAX's type member is always initialized
Ensures that the member always has a deterministic value.
2018-09-19 14:22:37 -04:00
Lioncash
92a98a8b19 xts_archive: Amend initializer order of NAX's constructor
Orders the initializer list in the same order the members would be
initialized. Avoids compiler warnings.
2018-09-19 14:22:30 -04:00
tech4me
189cf0da7b Update MinGWCross.cmake to lowercase 2018-09-19 14:22:14 -04:00
tech4me
a8f54f96fc travis: running mingw build on travis ci
This commit also fixed a broken cmake dependency with unicorn
2018-09-19 14:22:14 -04:00
David
0432af5ad1 Reworked incorrect nifm stubs (#1355)
* Reworked incorrect nifm stubs

Need confirmation on `CreateTemporaryNetworkProfile`, unsure which game uses it but according to reversing. It should return a uuid which we currently don't do.

Any 0 client id is considered an invalid client id.

GetRequestState 0 is considered invalid.

* Fixups for nifm
2018-09-19 11:59:01 -04:00
bunnei
52fb86c65c Merge pull request #1356 from degasus/hotfix
gl_rasterizer: Fix StartAddress handling with indexed draw calls.
2018-09-19 11:58:22 -04:00
bunnei
8dff92c5f6 Merge pull request #1359 from ogniK5377/nes
Fixed GetAccountId stub, Added error code for OpenDirectory and added ActivateNpadWithRevision
2018-09-19 10:01:36 -04:00
bunnei
5fbc354261 Merge pull request #1353 from ogniK5377/remove-MakeBuilder
Removed the use of rp.MakeBuilder in the yuzu codebase
2018-09-19 10:00:23 -04:00
David Marcec
cbc7ad8f6d Fixed GetAccountId stub, Added error code for OpenDirectory and added ActivateNpadWithRevision
With these, `Nintendo Entertainment System - Nintendo Switch Online` loads
2018-09-19 23:25:00 +10:00
Zach Hilman
9bba103791 yuzu-cmd: Add call to CreateFactories
Ensures all relevant filesystem objects are initialized and eliminates a crash related to the RegisteredCache.
2018-09-19 09:09:11 -04:00
Zach Hilman
dfaea55be5 savedata_factory: Add TemporaryStorage SaveDataType
Seems to be used by NSO NES Emulator
2018-09-19 09:06:22 -04:00
Markus Wick
f465e4aaf2 gl_rasterizer: Fix StartAddress handling with indexed draw calls.
We uploaded the wrong data before. So the offset on the host GPU pointer may work for the first vertices, the last ones run out bounds.
Let's just offset the upload instead.
2018-09-19 09:22:30 +02:00
David Marcec
08819ec70a Removed MakeBuilder as it's not needed anymore 2018-09-19 15:13:56 +10:00
David Marcec
6a0612f2bf Removed the use of rp.MakeBuilder
Due to keeping the code style consistent in the yuzu codebase. `rb = rp.MakeBuilder(...)` was replaced with `rb{ctx, ...}`
2018-09-19 15:09:59 +10:00
Lioncash
ab6dfa4fa5 ring_buffer: Use std::atomic_size_t in a static assert
Avoids the need to repeat "std::" twice
2018-09-18 23:36:04 -04:00
Lioncash
c51f8563a6 ring_buffer: Use std::hardware_destructive_interference_size to determine alignment size for avoiding false sharing
MSVC 19.11 (A.K.A. VS 15.3)'s C++ standard library implements P0154R1
(http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0154r1.html)
which defines two new constants within the <new> header, std::hardware_destructive_interference_size
and std::hardware_constructive_interference_size.

std::hardware_destructive_interference_size defines the minimum
recommended offset between two concurrently-accessed objects to avoid
performance degradation due to contention introduced by the
implementation (with the lower-bound being at least alignof(max_align_t)).
In other words, the minimum offset between objects necessary to avoid
false-sharing.

std::hardware_constructive_interference_size on the other hand defines
the maximum recommended size of contiguous memory occupied by two
objects accessed wth temporal locality by concurrent threads (also
defined to be at least alignof(max_align_t)). In other words the maximum
size to promote true-sharing.

So we can simply use this facility to determine the ideal alignment
size. Unfortunately, only MSVC supports this right now, so we need to
enclose it within an ifdef for the time being.
2018-09-18 23:35:57 -04:00
bunnei
b33ce787b7 Merge pull request #1348 from ogniK5377/GetImageSize
Implemented IProfile::GetImageSize
2018-09-18 22:15:18 -04:00
bunnei
d85130d7be Merge pull request #1319 from lioncash/audio
audio_core: Replace includes with forward declarations where applicable.
2018-09-18 22:14:26 -04:00
bunnei
c9942fe46e Merge pull request #1351 from ogniK5377/GetDefaultDisplayResolution
Implemented GetDefaultDisplayResolution
2018-09-18 22:13:28 -04:00
bunnei
bf957d5345 Merge pull request #1341 from lioncash/dependency
core/core_cpu: Replace exclusive monitor include with forward declaration
2018-09-18 22:13:05 -04:00
bunnei
c768535463 Merge pull request #1346 from lioncash/svc
svc_wrap: Convert the PARAM macro into a function
2018-09-18 22:12:47 -04:00
bunnei
e9abbcae85 Merge pull request #1350 from ogniK5377/Six-Axis-Stub
Stubbed ActivateConsoleSixAxisSensor & StartConsoleSixAxisSensor
2018-09-18 22:12:12 -04:00
bunnei
bd88d4108f Merge pull request #1342 from lioncash/trunc
gl_shader_decompiler: Avoid truncation warnings within LD_A and ST_A code
2018-09-18 22:11:48 -04:00
bunnei
0284cbe7ec Merge pull request #1279 from FernandoS27/csetp
shader_decompiler: Implemented (Partialy) Control Codes and CSETP
2018-09-18 22:10:48 -04:00
bunnei
6415f81bb8 Merge pull request #1299 from FernandoS27/texture-sanatize
shader_decompiler: Asserts for Texture Instructions
2018-09-18 22:10:09 -04:00
Philippe Babin
9155c8daeb Invalid default value of username in yuzu_cmd (#1334)
* Fix bug where default username value for yuzu_cmd create an userprofile with uninitialize data as username

* Fix format

* Apply code review changes

* Remove nullptr check
2018-09-18 19:58:28 -04:00
bunnei
733c47623b Merge pull request #1343 from lioncash/mutex
kernel/svc: Handle invalid address cases within svcArbitrateLock() and svcArbitrateUnlock()
2018-09-18 14:25:00 -04:00
bunnei
c2cf784376 Merge pull request #1344 from lioncash/arm
arm_interface: Remove ARM11-isms from the CPU interface
2018-09-18 14:24:20 -04:00
bunnei
c7a4bf5074 Merge pull request #1345 from lioncash/write
arm_dynarmic: Correct ExclusiveWrite128()'s operation
2018-09-18 14:23:57 -04:00
David Marcec
de9604d63e Added ActivateGesture 2018-09-19 01:13:58 +10:00
David Marcec
1a2d33eeb4 Implemented GetDefaultDisplayResolution 2018-09-19 01:10:16 +10:00
David Marcec
a8b1c7763b Added StopSixAxisSensor 2018-09-19 00:56:02 +10:00
David Marcec
717889e93c Stubbed ActivateConsoleSixAxisSensor & StartConsoleSixAxisSensor 2018-09-19 00:52:20 +10:00
David Marcec
528e5cee67 Implemented GetImageSize 2018-09-19 00:16:52 +10:00
Lioncash
f85ab0a123 svc_wrap: Convert the PARAM macro into a function
This can just be a regular function, getting rid of the need to also
explicitly undef the define at the end of the file. Given FuncReturn()
was already converted into a function, it's #undef can also be removed.
2018-09-18 04:27:38 -04:00
Lioncash
ead2a4eeb4 arm_dynarmic: Correct ExclusiveWrite128()'s operation
Previously the second half of the value being written would overwrite
the first half. Thankfully this wasn't a bug that was being encountered,
as the function is currently unused.
2018-09-18 03:56:32 -04:00
Lioncash
b51e7e0288 arm_interface: Remove ARM11-isms from the CPU interface
This modifies the CPU interface to more accurately match an
AArch64-supporting CPU as opposed to an ARM11 one. Two of the methods
don't even make sense to keep around for this interface, as Adv Simd is
used, rather than the VFP in the primary execution state. This is
essentially a modernization change that should have occurred from the
get-go.
2018-09-18 03:20:04 -04:00
FernandoS27
567a5524b9 Implemented Internal Flags 2018-09-17 20:50:54 -04:00
Lioncash
9a8dbba1e5 gl_shader_decompiler: Avoid truncation warnings within LD_A and ST_A code
These are internally stored as u64 values, so using u32 here causes
truncation warnings. Instead, we can just use u64 and preserve the bit
width.
2018-09-17 19:25:55 -04:00
bunnei
fafc80d72e Merge pull request #1290 from FernandoS27/shader-header
Implemented (Partialy) Shader Header
2018-09-17 18:53:14 -04:00
Lioncash
4a84986bc3 core/core_cpu: Replace exclusive monitor include with forward declaration
We don't need to include this as a dependency within the header. A
regular forward declaration will suffice here.
2018-09-17 18:16:55 -04:00
FernandoS27
e4bb759c4b Implemented I2I.CC on the NEU control code, used by SMO 2018-09-17 17:42:46 -04:00
FernandoS27
e2ac8fb36d Implemented CSETP 2018-09-17 17:42:44 -04:00
FernandoS27
aac77bbd18 Implemented Control Codes 2018-09-17 17:42:43 -04:00
Lioncash
f7308a6c25 time_stretch: Remove unused <array> include
This isn't used within this header and isn't necessary.
2018-09-17 15:08:34 -04:00
Lioncash
2fd124bc93 stream: Replace includes with forward declarations where applicable
Avoids propagating includes in headers where it's not necessary to do
so.
2018-09-17 15:08:34 -04:00
Lioncash
1adbcd54fe audio_renderer: Replace includes with forward declarations where applicable
Avoids including unnecessary headers within the audio_renderer.h header,
lessening the likelihood of needing to rebuild source files including
this header if they ever change.

Given std::vector allows forward declaring contained types, we can move
VoiceState to the cpp file and hide the implementation entirely.
2018-09-17 15:08:30 -04:00
FernandoS27
31e52113b3 Added asserts for texture misc modes to texture instructions 2018-09-17 12:56:36 -04:00
FernandoS27
55a4756766 Added texture misc modes to texture instructions 2018-09-17 12:51:05 -04:00
FernandoS27
2b48cfd44b Replace old FragmentHeader for the new Header 2018-09-11 12:48:19 -04:00
FernandoS27
e926757c8f Implemented (Partialy) Shader Header 2018-09-11 12:34:27 -04:00
99 changed files with 1608 additions and 430 deletions

View File

@@ -29,6 +29,19 @@ matrix:
script: "./.travis/macos/build.sh"
after_success: "./.travis/macos/upload.sh"
cache: ccache
- os: linux
env: NAME="MinGW build"
sudo: required
dist: trusty
services: docker
addons:
apt:
packages:
- p7zip-full
install: "./.travis/linux-mingw/deps.sh"
script: "./.travis/linux-mingw/build.sh"
after_success: "./.travis/linux-mingw/upload.sh"
cache: ccache
deploy:
provider: releases

View File

@@ -11,6 +11,9 @@ if [ -z $TRAVIS_TAG ]; then
RELEASE_NAME=head
else
RELEASE_NAME=$(echo $TRAVIS_TAG | cut -d- -f1)
if [ "$NAME" = "MinGW build" ]; then
RELEASE_NAME="${RELEASE_NAME}-mingw"
fi
fi
mv "$REV_NAME" $RELEASE_NAME

3
.travis/linux-mingw/build.sh Executable file
View File

@@ -0,0 +1,3 @@
#!/bin/bash -ex
mkdir "$HOME/.ccache" || true
docker run --env-file .travis/common/travis-ci.env -v $(pwd):/yuzu -v "$HOME/.ccache":/root/.ccache ubuntu:18.04 /bin/bash -ex /yuzu/.travis/linux-mingw/docker.sh

3
.travis/linux-mingw/deps.sh Executable file
View File

@@ -0,0 +1,3 @@
#!/bin/sh -ex
docker pull ubuntu:18.04

59
.travis/linux-mingw/docker.sh Executable file
View File

@@ -0,0 +1,59 @@
#!/bin/bash -ex
cd /yuzu
MINGW_PACKAGES="sdl2-mingw-w64 qt5base-mingw-w64 qt5tools-mingw-w64 libsamplerate-mingw-w64 qt5multimedia-mingw-w64"
apt-get update
apt-get install -y gpg wget git python3-pip python ccache g++-mingw-w64-x86-64 gcc-mingw-w64-x86-64 mingw-w64-tools cmake
echo 'deb http://ppa.launchpad.net/tobydox/mingw-w64/ubuntu bionic main ' > /etc/apt/sources.list.d/extras.list
apt-key adv --keyserver keyserver.ubuntu.com --recv '72931B477E22FEFD47F8DECE02FE5F12ADDE29B2'
apt-get update
apt-get install -y ${MINGW_PACKAGES}
# fix a problem in current MinGW headers
wget -q https://raw.githubusercontent.com/Alexpux/mingw-w64/d0d7f784833bbb0b2d279310ddc6afb52fe47a46/mingw-w64-headers/crt/errno.h -O /usr/x86_64-w64-mingw32/include/errno.h
# override Travis CI unreasonable ccache size
echo 'max_size = 3.0G' > "$HOME/.ccache/ccache.conf"
# Dirty hack to trick unicorn makefile into believing we are in a MINGW system
mv /bin/uname /bin/uname1 && echo -e '#!/bin/sh\necho MINGW64' >> /bin/uname
chmod +x /bin/uname
# Dirty hack to trick unicorn makefile into believing we have cmd
echo '' >> /bin/cmd
chmod +x /bin/cmd
mkdir build && cd build
cmake .. -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DYUZU_USE_BUNDLED_UNICORN=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DCMAKE_BUILD_TYPE=Release
make -j4
# Clean up the dirty hacks
rm /bin/uname && mv /bin/uname1 /bin/uname
rm /bin/cmd
ccache -s
echo "Tests skipped"
#ctest -VV -C Release
echo 'Prepare binaries...'
cd ..
mkdir package
QT_PLATFORM_DLL_PATH='/usr/x86_64-w64-mingw32/lib/qt5/plugins/platforms/'
find build/ -name "yuzu*.exe" -exec cp {} 'package' \;
# copy Qt plugins
mkdir package/platforms
cp "${QT_PLATFORM_DLL_PATH}/qwindows.dll" package/platforms/
cp -rv "${QT_PLATFORM_DLL_PATH}/../mediaservice/" package/
cp -rv "${QT_PLATFORM_DLL_PATH}/../imageformats/" package/
rm -f package/mediaservice/*d.dll
for i in package/*.exe; do
# we need to process pdb here, however, cv2pdb
# does not work here, so we just simply strip all the debug symbols
x86_64-w64-mingw32-strip "${i}"
done
pip3 install pefile
python3 .travis/linux-mingw/scan_dll.py package/*.exe "package/"

View File

@@ -0,0 +1,106 @@
import pefile
import sys
import re
import os
import queue
import shutil
# constant definitions
KNOWN_SYS_DLLS = ['WINMM.DLL', 'MSVCRT.DLL', 'VERSION.DLL', 'MPR.DLL',
'DWMAPI.DLL', 'UXTHEME.DLL', 'DNSAPI.DLL', 'IPHLPAPI.DLL']
# below is for Ubuntu 18.04 with specified PPA enabled, if you are using
# other distro or different repositories, change the following accordingly
DLL_PATH = [
'/usr/x86_64-w64-mingw32/bin/',
'/usr/x86_64-w64-mingw32/lib/',
'/usr/lib/gcc/x86_64-w64-mingw32/7.3-posix/'
]
missing = []
def parse_imports(file_name):
results = []
pe = pefile.PE(file_name, fast_load=True)
pe.parse_data_directories()
for entry in pe.DIRECTORY_ENTRY_IMPORT:
current = entry.dll.decode()
current_u = current.upper() # b/c Windows is often case insensitive
# here we filter out system dlls
# dll w/ names like *32.dll are likely to be system dlls
if current_u.upper() not in KNOWN_SYS_DLLS and not re.match(string=current_u, pattern=r'.*32\.DLL'):
results.append(current)
return results
def parse_imports_recursive(file_name, path_list=[]):
q = queue.Queue() # create a FIFO queue
# file_name can be a string or a list for the convience
if isinstance(file_name, str):
q.put(file_name)
elif isinstance(file_name, list):
for i in file_name:
q.put(i)
full_list = []
while q.qsize():
current = q.get_nowait()
print('> %s' % current)
deps = parse_imports(current)
# if this dll does not have any import, ignore it
if not deps:
continue
for dep in deps:
# the dependency already included in the list, skip
if dep in full_list:
continue
# find the requested dll in the provided paths
full_path = find_dll(dep)
if not full_path:
missing.append(dep)
continue
full_list.append(dep)
q.put(full_path)
path_list.append(full_path)
return full_list
def find_dll(name):
for path in DLL_PATH:
for root, _, files in os.walk(path):
for f in files:
if name.lower() == f.lower():
return os.path.join(root, f)
def deploy(name, dst, dry_run=False):
dlls_path = []
parse_imports_recursive(name, dlls_path)
for dll_entry in dlls_path:
if not dry_run:
shutil.copy(dll_entry, dst)
else:
print('[Dry-Run] Copy %s to %s' % (dll_entry, dst))
print('Deploy completed.')
return dlls_path
def main():
if len(sys.argv) < 3:
print('Usage: %s [files to examine ...] [target deploy directory]')
return 1
to_deploy = sys.argv[1:-1]
tgt_dir = sys.argv[-1]
if not os.path.isdir(tgt_dir):
print('%s is not a directory.' % tgt_dir)
return 1
print('Scanning dependencies...')
deploy(to_deploy, tgt_dir)
if missing:
print('Following DLLs are not found: %s' % ('\n'.join(missing)))
return 0
if __name__ == '__main__':
main()

13
.travis/linux-mingw/upload.sh Executable file
View File

@@ -0,0 +1,13 @@
#!/bin/bash -ex
. .travis/common/pre-upload.sh
REV_NAME="yuzu-windows-mingw-${GITDATE}-${GITREV}"
ARCHIVE_NAME="${REV_NAME}.tar.gz"
COMPRESSION_FLAGS="-czvf"
mkdir "$REV_NAME"
# get around the permission issues
cp -r package/* "$REV_NAME"
. .travis/common/post-upload.sh

View File

@@ -6,7 +6,9 @@ apt-get install --no-install-recommends -y build-essential git libqt5opengl5-dev
cd /yuzu
mkdir build && cd build
cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -G Ninja
cmake .. -DYUZU_USE_BUNDLED_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -G Ninja
ninja
ccache -s
ctest -VV -C Release

View File

@@ -9,7 +9,9 @@ export PATH="/usr/local/opt/ccache/libexec:$PATH"
mkdir build && cd build
cmake --version
cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON
cmake .. -DYUZU_USE_BUNDLED_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON
make -j4
ccache -s
ctest -VV -C Release

View File

@@ -269,10 +269,18 @@ if (YUZU_USE_BUNDLED_UNICORN)
find_package(PythonInterp 2.7 REQUIRED)
add_custom_command(OUTPUT ${LIBUNICORN_LIBRARY}
COMMAND ${CMAKE_COMMAND} -E env UNICORN_ARCHS="aarch64" PYTHON="${PYTHON_EXECUTABLE}" /bin/sh make.sh macos-universal-no
WORKING_DIRECTORY ${UNICORN_PREFIX}
)
if (MINGW)
add_custom_command(OUTPUT ${LIBUNICORN_LIBRARY}
COMMAND ${CMAKE_COMMAND} -E env UNICORN_ARCHS="aarch64" PYTHON="${PYTHON_EXECUTABLE}" /bin/sh make.sh cross-win64
WORKING_DIRECTORY ${UNICORN_PREFIX}
)
else()
add_custom_command(OUTPUT ${LIBUNICORN_LIBRARY}
COMMAND ${CMAKE_COMMAND} -E env UNICORN_ARCHS="aarch64" PYTHON="${PYTHON_EXECUTABLE}" /bin/sh make.sh macos-universal-no
WORKING_DIRECTORY ${UNICORN_PREFIX}
)
endif()
# ALL makes this custom target build every time
# but it won't actually build if LIBUNICORN_LIBRARY is up to date
add_custom_target(unicorn-build ALL
@@ -286,6 +294,7 @@ endif()
if (UNICORN_FOUND)
add_library(unicorn INTERFACE)
add_dependencies(unicorn unicorn-build)
target_link_libraries(unicorn INTERFACE "${LIBUNICORN_LIBRARY}")
target_include_directories(unicorn INTERFACE "${LIBUNICORN_INCLUDE_DIR}")
else()

View File

@@ -0,0 +1,54 @@
set(MINGW_PREFIX /usr/x86_64-w64-mingw32/)
set(CMAKE_SYSTEM_NAME Windows)
set(CMAKE_SYSTEM_PROCESSOR x86_64)
# Actually a hack, w/o this will cause some strange errors
set(CMAKE_HOST_WIN32 TRUE)
set(CMAKE_FIND_ROOT_PATH ${MINGW_PREFIX})
set(SDL2_PATH ${MINGW_PREFIX})
set(MINGW_TOOL_PREFIX ${CMAKE_SYSTEM_PROCESSOR}-w64-mingw32-)
# Specify the cross compiler
set(CMAKE_C_COMPILER ${MINGW_TOOL_PREFIX}gcc-posix)
set(CMAKE_CXX_COMPILER ${MINGW_TOOL_PREFIX}g++-posix)
set(CMAKE_RC_COMPILER ${MINGW_TOOL_PREFIX}windres)
# Mingw tools
set(STRIP ${MINGW_TOOL_PREFIX}strip)
set(WINDRES ${MINGW_TOOL_PREFIX}windres)
set(ENV{PKG_CONFIG} ${MINGW_TOOL_PREFIX}pkg-config)
# ccache wrapper
option(USE_CCACHE "Use ccache for compilation" OFF)
if(USE_CCACHE)
find_program(CCACHE ccache)
if(CCACHE)
message(STATUS "Using ccache found in PATH")
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ${CCACHE})
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ${CCACHE})
else(CCACHE)
message(WARNING "USE_CCACHE enabled, but no ccache found")
endif(CCACHE)
endif(USE_CCACHE)
# Search for programs in the build host directories
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# Echo modified cmake vars to screen for debugging purposes
if(NOT DEFINED ENV{MINGW_DEBUG_INFO})
message("")
message("Custom cmake vars: (blank = system default)")
message("-----------------------------------------")
message("* CMAKE_C_COMPILER : ${CMAKE_C_COMPILER}")
message("* CMAKE_CXX_COMPILER : ${CMAKE_CXX_COMPILER}")
message("* CMAKE_RC_COMPILER : ${CMAKE_RC_COMPILER}")
message("* WINDRES : ${WINDRES}")
message("* ENV{PKG_CONFIG} : $ENV{PKG_CONFIG}")
message("* STRIP : ${STRIP}")
message("* USE_CCACHE : ${USE_CCACHE}")
message("")
# So that the debug info only appears once
set(ENV{MINGW_DEBUG_INFO} SHOWN)
endif()

View File

@@ -163,13 +163,3 @@ artifacts:
name: build
type: zip
deploy:
provider: GitHub
release: $(appveyor_repo_tag_name)
auth_token:
secure: QqePPnXbkzmXct5c8hZ2X5AbsthbI6cS1Sr+VBzcD8oUOIjfWJJKXVAQGUbQAbb0
artifact: update,build
draft: false
prerelease: false
on:
appveyor_repo_tag: true

View File

@@ -3,9 +3,12 @@
// Refer to the license.txt file included.
#include "audio_core/algorithm/interpolate.h"
#include "audio_core/audio_out.h"
#include "audio_core/audio_renderer.h"
#include "audio_core/codec.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/hle/kernel/event.h"
#include "core/memory.h"
namespace AudioCore {
@@ -13,6 +16,41 @@ namespace AudioCore {
constexpr u32 STREAM_SAMPLE_RATE{48000};
constexpr u32 STREAM_NUM_CHANNELS{2};
class AudioRenderer::VoiceState {
public:
bool IsPlaying() const {
return is_in_use && info.play_state == PlayState::Started;
}
const VoiceOutStatus& GetOutStatus() const {
return out_status;
}
const VoiceInfo& GetInfo() const {
return info;
}
VoiceInfo& Info() {
return info;
}
void SetWaveIndex(std::size_t index);
std::vector<s16> DequeueSamples(std::size_t sample_count);
void UpdateState();
void RefreshBuffer();
private:
bool is_in_use{};
bool is_refresh_pending{};
std::size_t wave_index{};
std::size_t offset{};
Codec::ADPCMState adpcm_state{};
InterpolationState interp_state{};
std::vector<s16> samples;
VoiceOutStatus out_status{};
VoiceInfo info{};
};
AudioRenderer::AudioRenderer(AudioRendererParameter params,
Kernel::SharedPtr<Kernel::Event> buffer_event)
: worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count) {
@@ -27,6 +65,8 @@ AudioRenderer::AudioRenderer(AudioRendererParameter params,
QueueMixedBuffer(2);
}
AudioRenderer::~AudioRenderer() = default;
u32 AudioRenderer::GetSampleRate() const {
return worker_params.sample_rate;
}

View File

@@ -8,16 +8,20 @@
#include <memory>
#include <vector>
#include "audio_core/algorithm/interpolate.h"
#include "audio_core/audio_out.h"
#include "audio_core/codec.h"
#include "audio_core/stream.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/object.h"
namespace Kernel {
class Event;
}
namespace AudioCore {
class AudioOut;
enum class PlayState : u8 {
Started = 0,
Stopped = 1,
@@ -158,6 +162,8 @@ static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size
class AudioRenderer {
public:
AudioRenderer(AudioRendererParameter params, Kernel::SharedPtr<Kernel::Event> buffer_event);
~AudioRenderer();
std::vector<u8> UpdateAudioRenderer(const std::vector<u8>& input_params);
void QueueMixedBuffer(Buffer::Tag tag);
void ReleaseAndQueueBuffers();
@@ -166,45 +172,12 @@ public:
u32 GetMixBufferCount() const;
private:
class VoiceState {
public:
bool IsPlaying() const {
return is_in_use && info.play_state == PlayState::Started;
}
const VoiceOutStatus& GetOutStatus() const {
return out_status;
}
const VoiceInfo& GetInfo() const {
return info;
}
VoiceInfo& Info() {
return info;
}
void SetWaveIndex(std::size_t index);
std::vector<s16> DequeueSamples(std::size_t sample_count);
void UpdateState();
void RefreshBuffer();
private:
bool is_in_use{};
bool is_refresh_pending{};
std::size_t wave_index{};
std::size_t offset{};
Codec::ADPCMState adpcm_state{};
InterpolationState interp_state{};
std::vector<s16> samples;
VoiceOutStatus out_status{};
VoiceInfo info{};
};
class VoiceState;
AudioRendererParameter worker_params;
Kernel::SharedPtr<Kernel::Event> buffer_event;
std::vector<VoiceState> voices;
std::unique_ptr<AudioCore::AudioOut> audio_out;
std::unique_ptr<AudioOut> audio_out;
AudioCore::StreamPtr stream;
};

View File

@@ -7,6 +7,7 @@
#include "audio_core/sink.h"
#include "audio_core/sink_details.h"
#include "audio_core/sink_stream.h"
#include "audio_core/stream.h"
#include "common/assert.h"
#include "common/logging/log.h"

View File

@@ -11,13 +11,16 @@
#include <queue>
#include "audio_core/buffer.h"
#include "audio_core/sink_stream.h"
#include "common/assert.h"
#include "common/common_types.h"
#include "core/core_timing.h"
namespace CoreTiming {
struct EventType;
}
namespace AudioCore {
class SinkStream;
/**
* Represents an audio stream, which is a sequence of queued buffers, to be outputed by AudioOut
*/

View File

@@ -4,7 +4,6 @@
#pragma once
#include <array>
#include <cstddef>
#include <SoundTouch.h>
#include "common/common_types.h"

View File

@@ -9,6 +9,7 @@
#include <atomic>
#include <cstddef>
#include <cstring>
#include <new>
#include <type_traits>
#include <vector>
#include "common/common_types.h"
@@ -29,7 +30,7 @@ class RingBuffer {
static_assert(capacity < std::numeric_limits<std::size_t>::max() / 2 / granularity);
static_assert((capacity & (capacity - 1)) == 0, "capacity must be a power of two");
// Ensure lock-free.
static_assert(std::atomic<std::size_t>::is_always_lock_free);
static_assert(std::atomic_size_t::is_always_lock_free);
public:
/// Pushes slots into the ring buffer
@@ -102,8 +103,15 @@ public:
private:
// It is important to align the below variables for performance reasons:
// Having them on the same cache-line would result in false-sharing between them.
alignas(128) std::atomic<std::size_t> m_read_index{0};
alignas(128) std::atomic<std::size_t> m_write_index{0};
// TODO: Remove this ifdef whenever clang and GCC support
// std::hardware_destructive_interference_size.
#if defined(_MSC_VER) && _MSC_VER >= 1911
alignas(std::hardware_destructive_interference_size) std::atomic_size_t m_read_index{0};
alignas(std::hardware_destructive_interference_size) std::atomic_size_t m_write_index{0};
#else
alignas(128) std::atomic_size_t m_read_index{0};
alignas(128) std::atomic_size_t m_write_index{0};
#endif
std::array<T, granularity * capacity> m_data;
};

View File

@@ -6,11 +6,14 @@
#include <array>
#include "common/common_types.h"
#include "core/hle/kernel/vm_manager.h"
namespace Kernel {
enum class VMAPermission : u8;
}
namespace Core {
/// Generic ARM11 CPU interface
/// Generic ARMv8 CPU interface
class ARM_Interface : NonCopyable {
public:
virtual ~ARM_Interface() {}
@@ -19,9 +22,9 @@ public:
std::array<u64, 31> cpu_registers;
u64 sp;
u64 pc;
u64 cpsr;
std::array<u128, 32> fpu_registers;
u64 fpscr;
u64 pstate;
std::array<u128, 32> vector_registers;
u64 fpcr;
};
/// Runs the CPU until an event happens
@@ -69,42 +72,50 @@ public:
*/
virtual void SetReg(int index, u64 value) = 0;
virtual u128 GetExtReg(int index) const = 0;
virtual void SetExtReg(int index, u128 value) = 0;
/**
* Gets the value of a specified vector register.
*
* @param index The index of the vector register.
* @return the value within the vector register.
*/
virtual u128 GetVectorReg(int index) const = 0;
/**
* Gets the value of a VFP register
* @param index Register index (0-31)
* @return Returns the value in the register
* Sets a given value into a vector register.
*
* @param index The index of the vector register.
* @param value The new value to place in the register.
*/
virtual u32 GetVFPReg(int index) const = 0;
virtual void SetVectorReg(int index, u128 value) = 0;
/**
* Sets a VFP register to the given value
* @param index Register index (0-31)
* @param value Value to set register to
* Get the current PSTATE register
* @return Returns the value of the PSTATE register
*/
virtual void SetVFPReg(int index, u32 value) = 0;
virtual u32 GetPSTATE() const = 0;
/**
* Get the current CPSR register
* @return Returns the value of the CPSR register
* Set the current PSTATE register
* @param pstate Value to set PSTATE to
*/
virtual u32 GetCPSR() const = 0;
/**
* Set the current CPSR register
* @param cpsr Value to set CPSR to
*/
virtual void SetCPSR(u32 cpsr) = 0;
virtual void SetPSTATE(u32 pstate) = 0;
virtual VAddr GetTlsAddress() const = 0;
virtual void SetTlsAddress(VAddr address) = 0;
/**
* Gets the value within the TPIDR_EL0 (read/write software thread ID) register.
*
* @return the value within the register.
*/
virtual u64 GetTPIDR_EL0() const = 0;
/**
* Sets a new value within the TPIDR_EL0 (read/write software thread ID) register.
*
* @param value The new value to place in the register.
*/
virtual void SetTPIDR_EL0(u64 value) = 0;
/**
@@ -119,6 +130,7 @@ public:
*/
virtual void LoadContext(const ThreadContext& ctx) = 0;
/// Clears the exclusive monitor's state.
virtual void ClearExclusiveState() = 0;
/// Prepare core for thread reschedule (if needed to correctly handle state)

View File

@@ -12,8 +12,10 @@
#include "core/core.h"
#include "core/core_cpu.h"
#include "core/core_timing.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/svc.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/memory.h"
namespace Core {
@@ -79,6 +81,17 @@ public:
case Dynarmic::A64::Exception::SendEventLocal:
case Dynarmic::A64::Exception::Yield:
return;
case Dynarmic::A64::Exception::Breakpoint:
if (GDBStub::IsServerEnabled()) {
parent.jit->HaltExecution();
parent.SetPC(pc);
Kernel::Thread* thread = Kernel::GetCurrentThread();
parent.SaveContext(thread->context);
GDBStub::Break();
GDBStub::SendTrap(thread, 5);
return;
}
[[fallthrough]];
default:
ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:X})",
static_cast<std::size_t>(exception), pc);
@@ -194,29 +207,20 @@ void ARM_Dynarmic::SetReg(int index, u64 value) {
jit->SetRegister(index, value);
}
u128 ARM_Dynarmic::GetExtReg(int index) const {
u128 ARM_Dynarmic::GetVectorReg(int index) const {
return jit->GetVector(index);
}
void ARM_Dynarmic::SetExtReg(int index, u128 value) {
void ARM_Dynarmic::SetVectorReg(int index, u128 value) {
jit->SetVector(index, value);
}
u32 ARM_Dynarmic::GetVFPReg(int /*index*/) const {
UNIMPLEMENTED();
return {};
}
void ARM_Dynarmic::SetVFPReg(int /*index*/, u32 /*value*/) {
UNIMPLEMENTED();
}
u32 ARM_Dynarmic::GetCPSR() const {
u32 ARM_Dynarmic::GetPSTATE() const {
return jit->GetPstate();
}
void ARM_Dynarmic::SetCPSR(u32 cpsr) {
jit->SetPstate(cpsr);
void ARM_Dynarmic::SetPSTATE(u32 pstate) {
jit->SetPstate(pstate);
}
u64 ARM_Dynarmic::GetTlsAddress() const {
@@ -239,18 +243,18 @@ void ARM_Dynarmic::SaveContext(ThreadContext& ctx) {
ctx.cpu_registers = jit->GetRegisters();
ctx.sp = jit->GetSP();
ctx.pc = jit->GetPC();
ctx.cpsr = jit->GetPstate();
ctx.fpu_registers = jit->GetVectors();
ctx.fpscr = jit->GetFpcr();
ctx.pstate = jit->GetPstate();
ctx.vector_registers = jit->GetVectors();
ctx.fpcr = jit->GetFpcr();
}
void ARM_Dynarmic::LoadContext(const ThreadContext& ctx) {
jit->SetRegisters(ctx.cpu_registers);
jit->SetSP(ctx.sp);
jit->SetPC(ctx.pc);
jit->SetPstate(static_cast<u32>(ctx.cpsr));
jit->SetVectors(ctx.fpu_registers);
jit->SetFpcr(static_cast<u32>(ctx.fpscr));
jit->SetPstate(static_cast<u32>(ctx.pstate));
jit->SetVectors(ctx.vector_registers);
jit->SetFpcr(static_cast<u32>(ctx.fpcr));
}
void ARM_Dynarmic::PrepareReschedule() {
@@ -304,8 +308,8 @@ bool DynarmicExclusiveMonitor::ExclusiveWrite64(std::size_t core_index, VAddr va
bool DynarmicExclusiveMonitor::ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) {
return monitor.DoExclusiveOperation(core_index, vaddr, 16, [&] {
Memory::Write64(vaddr, value[0]);
Memory::Write64(vaddr, value[1]);
Memory::Write64(vaddr + 0, value[0]);
Memory::Write64(vaddr + 8, value[1]);
});
}

View File

@@ -12,6 +12,10 @@
#include "core/arm/exclusive_monitor.h"
#include "core/arm/unicorn/arm_unicorn.h"
namespace Memory {
struct PageTable;
}
namespace Core {
class ARM_Dynarmic_Callbacks;
@@ -29,14 +33,12 @@ public:
u64 GetPC() const override;
u64 GetReg(int index) const override;
void SetReg(int index, u64 value) override;
u128 GetExtReg(int index) const override;
void SetExtReg(int index, u128 value) override;
u32 GetVFPReg(int index) const override;
void SetVFPReg(int index, u32 value) override;
u32 GetCPSR() const override;
u128 GetVectorReg(int index) const override;
void SetVectorReg(int index, u128 value) override;
u32 GetPSTATE() const override;
void SetPSTATE(u32 pstate) override;
void Run() override;
void Step() override;
void SetCPSR(u32 cpsr) override;
VAddr GetTlsAddress() const override;
void SetTlsAddress(VAddr address) override;
void SetTPIDR_EL0(u64 value) override;

View File

@@ -131,33 +131,24 @@ void ARM_Unicorn::SetReg(int regn, u64 val) {
CHECKED(uc_reg_write(uc, treg, &val));
}
u128 ARM_Unicorn::GetExtReg(int /*index*/) const {
u128 ARM_Unicorn::GetVectorReg(int /*index*/) const {
UNIMPLEMENTED();
static constexpr u128 res{};
return res;
}
void ARM_Unicorn::SetExtReg(int /*index*/, u128 /*value*/) {
void ARM_Unicorn::SetVectorReg(int /*index*/, u128 /*value*/) {
UNIMPLEMENTED();
}
u32 ARM_Unicorn::GetVFPReg(int /*index*/) const {
UNIMPLEMENTED();
return {};
}
void ARM_Unicorn::SetVFPReg(int /*index*/, u32 /*value*/) {
UNIMPLEMENTED();
}
u32 ARM_Unicorn::GetCPSR() const {
u32 ARM_Unicorn::GetPSTATE() const {
u64 nzcv{};
CHECKED(uc_reg_read(uc, UC_ARM64_REG_NZCV, &nzcv));
return static_cast<u32>(nzcv);
}
void ARM_Unicorn::SetCPSR(u32 cpsr) {
u64 nzcv = cpsr;
void ARM_Unicorn::SetPSTATE(u32 pstate) {
u64 nzcv = pstate;
CHECKED(uc_reg_write(uc, UC_ARM64_REG_NZCV, &nzcv));
}
@@ -219,7 +210,7 @@ void ARM_Unicorn::SaveContext(ThreadContext& ctx) {
CHECKED(uc_reg_read(uc, UC_ARM64_REG_SP, &ctx.sp));
CHECKED(uc_reg_read(uc, UC_ARM64_REG_PC, &ctx.pc));
CHECKED(uc_reg_read(uc, UC_ARM64_REG_NZCV, &ctx.cpsr));
CHECKED(uc_reg_read(uc, UC_ARM64_REG_NZCV, &ctx.pstate));
for (auto i = 0; i < 29; ++i) {
uregs[i] = UC_ARM64_REG_X0 + i;
@@ -234,7 +225,7 @@ void ARM_Unicorn::SaveContext(ThreadContext& ctx) {
for (int i = 0; i < 32; ++i) {
uregs[i] = UC_ARM64_REG_Q0 + i;
tregs[i] = &ctx.fpu_registers[i];
tregs[i] = &ctx.vector_registers[i];
}
CHECKED(uc_reg_read_batch(uc, uregs, tregs, 32));
@@ -246,7 +237,7 @@ void ARM_Unicorn::LoadContext(const ThreadContext& ctx) {
CHECKED(uc_reg_write(uc, UC_ARM64_REG_SP, &ctx.sp));
CHECKED(uc_reg_write(uc, UC_ARM64_REG_PC, &ctx.pc));
CHECKED(uc_reg_write(uc, UC_ARM64_REG_NZCV, &ctx.cpsr));
CHECKED(uc_reg_write(uc, UC_ARM64_REG_NZCV, &ctx.pstate));
for (int i = 0; i < 29; ++i) {
uregs[i] = UC_ARM64_REG_X0 + i;
@@ -261,7 +252,7 @@ void ARM_Unicorn::LoadContext(const ThreadContext& ctx) {
for (auto i = 0; i < 32; ++i) {
uregs[i] = UC_ARM64_REG_Q0 + i;
tregs[i] = (void*)&ctx.fpu_registers[i];
tregs[i] = (void*)&ctx.vector_registers[i];
}
CHECKED(uc_reg_write_batch(uc, uregs, tregs, 32));

View File

@@ -22,12 +22,10 @@ public:
u64 GetPC() const override;
u64 GetReg(int index) const override;
void SetReg(int index, u64 value) override;
u128 GetExtReg(int index) const override;
void SetExtReg(int index, u128 value) override;
u32 GetVFPReg(int index) const override;
void SetVFPReg(int index, u32 value) override;
u32 GetCPSR() const override;
void SetCPSR(u32 cpsr) override;
u128 GetVectorReg(int index) const override;
void SetVectorReg(int index, u128 value) override;
u32 GetPSTATE() const override;
void SetPSTATE(u32 pstate) override;
VAddr GetTlsAddress() const override;
void SetTlsAddress(VAddr address) override;
void SetTPIDR_EL0(u64 value) override;

View File

@@ -9,6 +9,7 @@
#ifdef ARCHITECTURE_x86_64
#include "core/arm/dynarmic/arm_dynarmic.h"
#endif
#include "core/arm/exclusive_monitor.h"
#include "core/arm/unicorn/arm_unicorn.h"
#include "core/core_cpu.h"
#include "core/core_timing.h"
@@ -66,6 +67,8 @@ Cpu::Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
scheduler = std::make_shared<Kernel::Scheduler>(arm_interface.get());
}
Cpu::~Cpu() = default;
std::shared_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(std::size_t num_cores) {
if (Settings::values.use_cpu_jit) {
#ifdef ARCHITECTURE_x86_64

View File

@@ -6,11 +6,10 @@
#include <atomic>
#include <condition_variable>
#include <cstddef>
#include <memory>
#include <mutex>
#include <string>
#include "common/common_types.h"
#include "core/arm/exclusive_monitor.h"
namespace Kernel {
class Scheduler;
@@ -19,6 +18,7 @@ class Scheduler;
namespace Core {
class ARM_Interface;
class ExclusiveMonitor;
constexpr unsigned NUM_CPU_CORES{4};
@@ -43,6 +43,7 @@ class Cpu {
public:
Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
std::shared_ptr<CpuBarrier> cpu_barrier, std::size_t core_index);
~Cpu();
void RunLoop(bool tight_loop = true);

View File

@@ -463,6 +463,8 @@ NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_off
status = Loader::ResultStatus::Success;
}
NCA::~NCA() = default;
Loader::ResultStatus NCA::GetStatus() const {
return status;
}

View File

@@ -81,6 +81,8 @@ class NCA : public ReadOnlyVfsDirectory {
public:
explicit NCA(VirtualFile file, VirtualFile bktr_base_romfs = nullptr,
u64 bktr_base_ivfc_offset = 0);
~NCA() override;
Loader::ResultStatus GetStatus() const;
std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;

View File

@@ -8,6 +8,14 @@
namespace FileSys {
const std::array<const char*, 15> LANGUAGE_NAMES = {
"AmericanEnglish", "BritishEnglish", "Japanese",
"French", "German", "LatinAmericanSpanish",
"Spanish", "Italian", "Dutch",
"CanadianFrench", "Portugese", "Russian",
"Korean", "Taiwanese", "Chinese",
};
std::string LanguageEntry::GetApplicationName() const {
return Common::StringFromFixedZeroTerminatedBuffer(application_name.data(), 0x200);
}
@@ -20,18 +28,20 @@ NACP::NACP(VirtualFile file) : raw(std::make_unique<RawNACP>()) {
file->ReadObject(raw.get());
}
NACP::~NACP() = default;
const LanguageEntry& NACP::GetLanguageEntry(Language language) const {
if (language != Language::Default) {
return raw->language_entries.at(static_cast<u8>(language));
} else {
for (const auto& language_entry : raw->language_entries) {
if (!language_entry.GetApplicationName().empty())
return language_entry;
}
// Fallback to English
return GetLanguageEntry(Language::AmericanEnglish);
}
for (const auto& language_entry : raw->language_entries) {
if (!language_entry.GetApplicationName().empty())
return language_entry;
}
// Fallback to English
return GetLanguageEntry(Language::AmericanEnglish);
}
std::string NACP::GetApplicationName(Language language) const {

View File

@@ -66,18 +66,15 @@ enum class Language : u8 {
Default = 255,
};
static constexpr std::array<const char*, 15> LANGUAGE_NAMES = {
"AmericanEnglish", "BritishEnglish", "Japanese",
"French", "German", "LatinAmericanSpanish",
"Spanish", "Italian", "Dutch",
"CanadianFrench", "Portugese", "Russian",
"Korean", "Taiwanese", "Chinese"};
extern const std::array<const char*, 15> LANGUAGE_NAMES;
// A class representing the format used by NX metadata files, typically named Control.nacp.
// These store application name, dev name, title id, and other miscellaneous data.
class NACP {
public:
explicit NACP(VirtualFile file);
~NACP();
const LanguageEntry& GetLanguageEntry(Language language = Language::Default) const;
std::string GetApplicationName(Language language = Language::Default) const;
std::string GetDeveloperName(Language language = Language::Default) const;

View File

@@ -51,6 +51,8 @@ CNMT::CNMT(CNMTHeader header, OptionalHeader opt_header, std::vector<ContentReco
: header(std::move(header)), opt_header(std::move(opt_header)),
content_records(std::move(content_records)), meta_records(std::move(meta_records)) {}
CNMT::~CNMT() = default;
u64 CNMT::GetTitleID() const {
return header.title_id;
}

View File

@@ -87,6 +87,7 @@ public:
explicit CNMT(VirtualFile file);
CNMT(CNMTHeader header, OptionalHeader opt_header, std::vector<ContentRecord> content_records,
std::vector<MetaRecord> meta_records);
~CNMT();
u64 GetTitleID() const;
u32 GetTitleVersion() const;

View File

@@ -72,6 +72,8 @@ PartitionFilesystem::PartitionFilesystem(std::shared_ptr<VfsFile> file) {
status = Loader::ResultStatus::Success;
}
PartitionFilesystem::~PartitionFilesystem() = default;
Loader::ResultStatus PartitionFilesystem::GetStatus() const {
return status;
}

View File

@@ -25,6 +25,8 @@ namespace FileSys {
class PartitionFilesystem : public ReadOnlyVfsDirectory {
public:
explicit PartitionFilesystem(std::shared_ptr<VfsFile> file);
~PartitionFilesystem() override;
Loader::ResultStatus GetStatus() const;
std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;

View File

@@ -41,6 +41,8 @@ std::string FormatPatchTypeName(PatchType type) {
PatchManager::PatchManager(u64 title_id) : title_id(title_id) {}
PatchManager::~PatchManager() = default;
VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id);

View File

@@ -34,6 +34,7 @@ std::string FormatPatchTypeName(PatchType type);
class PatchManager {
public:
explicit PatchManager(u64 title_id);
~PatchManager();
// Currently tracked ExeFS patches:
// - Game Updates

View File

@@ -12,6 +12,10 @@
namespace FileSys {
ProgramMetadata::ProgramMetadata() = default;
ProgramMetadata::~ProgramMetadata() = default;
Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {
std::size_t total_size = static_cast<std::size_t>(file->GetSize());
if (total_size < sizeof(Header))

View File

@@ -36,6 +36,9 @@ enum class ProgramFilePermission : u64 {
*/
class ProgramMetadata {
public:
ProgramMetadata();
~ProgramMetadata();
Loader::ResultStatus Load(VirtualFile file);
bool Is64BitProgram() const;

View File

@@ -28,6 +28,8 @@ RomFSFactory::RomFSFactory(Loader::AppLoader& app_loader) {
ivfc_offset = app_loader.ReadRomFSIVFCOffset();
}
RomFSFactory::~RomFSFactory() = default;
ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() {
if (!updatable)
return MakeResult<VirtualFile>(file);

View File

@@ -30,6 +30,7 @@ enum class StorageId : u8 {
class RomFSFactory {
public:
explicit RomFSFactory(Loader::AppLoader& app_loader);
~RomFSFactory();
ResultVal<VirtualFile> OpenCurrentProcess();
ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type);

View File

@@ -20,6 +20,8 @@ std::string SaveDataDescriptor::DebugInfo() const {
SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) {}
SaveDataFactory::~SaveDataFactory() = default;
ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, SaveDataDescriptor meta) {
if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) {
if (meta.zero_1 != 0) {
@@ -85,10 +87,10 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ
switch (space) {
case SaveDataSpaceId::NandSystem:
out = "/system/save/";
out = "/system/";
break;
case SaveDataSpaceId::NandUser:
out = "/user/save/";
out = "/user/";
break;
default:
ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space));
@@ -96,9 +98,12 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ
switch (type) {
case SaveDataType::SystemSaveData:
return fmt::format("{}{:016X}/{:016X}{:016X}", out, save_id, user_id[1], user_id[0]);
return fmt::format("{}save/{:016X}/{:016X}{:016X}", out, save_id, user_id[1], user_id[0]);
case SaveDataType::SaveData:
return fmt::format("{}{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0],
return fmt::format("{}save/{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0],
title_id);
case SaveDataType::TemporaryStorage:
return fmt::format("{}temp/{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0],
title_id);
default:
ASSERT_MSG(false, "Unrecognized SaveDataType: {:02X}", static_cast<u8>(type));

View File

@@ -48,6 +48,7 @@ static_assert(sizeof(SaveDataDescriptor) == 0x40, "SaveDataDescriptor has incorr
class SaveDataFactory {
public:
explicit SaveDataFactory(VirtualDir dir);
~SaveDataFactory();
ResultVal<VirtualDir> Open(SaveDataSpaceId space, SaveDataDescriptor meta);

View File

@@ -24,7 +24,7 @@ enum class ContentRecordType : u8;
class NSP : public ReadOnlyVfsDirectory {
public:
explicit NSP(VirtualFile file);
~NSP();
~NSP() override;
Loader::ResultStatus GetStatus() const;
Loader::ResultStatus GetProgramStatus(u64 title_id) const;

View File

@@ -27,6 +27,8 @@ ConcatenatedVfsFile::ConcatenatedVfsFile(std::vector<VirtualFile> files_, std::s
}
}
ConcatenatedVfsFile::~ConcatenatedVfsFile() = default;
std::string ConcatenatedVfsFile::GetName() const {
if (files.empty())
return "";

View File

@@ -22,6 +22,8 @@ class ConcatenatedVfsFile : public VfsFile {
ConcatenatedVfsFile(std::vector<VirtualFile> files, std::string name);
public:
~ConcatenatedVfsFile() override;
std::string GetName() const override;
std::size_t GetSize() const override;
bool Resize(std::size_t new_size) override;

View File

@@ -14,6 +14,8 @@ OffsetVfsFile::OffsetVfsFile(std::shared_ptr<VfsFile> file_, std::size_t size_,
: file(file_), offset(offset_), size(size_), name(std::move(name_)),
parent(parent_ == nullptr ? file->GetContainingDirectory() : std::move(parent_)) {}
OffsetVfsFile::~OffsetVfsFile() = default;
std::string OffsetVfsFile::GetName() const {
return name.empty() ? file->GetName() : name;
}

View File

@@ -19,6 +19,7 @@ class OffsetVfsFile : public VfsFile {
public:
OffsetVfsFile(std::shared_ptr<VfsFile> file, std::size_t size, std::size_t offset = 0,
std::string new_name = "", VirtualDir new_parent = nullptr);
~OffsetVfsFile() override;
std::string GetName() const override;
std::size_t GetSize() const override;

View File

@@ -13,6 +13,8 @@ VectorVfsDirectory::VectorVfsDirectory(std::vector<VirtualFile> files_,
: files(std::move(files_)), dirs(std::move(dirs_)), parent(std::move(parent_)),
name(std::move(name_)) {}
VectorVfsDirectory::~VectorVfsDirectory() = default;
std::vector<std::shared_ptr<VfsFile>> VectorVfsDirectory::GetFiles() const {
return files;
}

View File

@@ -15,6 +15,7 @@ public:
explicit VectorVfsDirectory(std::vector<VirtualFile> files = {},
std::vector<VirtualDir> dirs = {}, std::string name = "",
VirtualDir parent = nullptr);
~VectorVfsDirectory() override;
std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;

View File

@@ -30,9 +30,6 @@ static bool CalculateHMAC256(Destination* out, const SourceKey* key, std::size_t
mbedtls_md_context_t context;
mbedtls_md_init(&context);
const auto key_f = reinterpret_cast<const u8*>(key);
const std::vector<u8> key_v(key_f, key_f + key_length);
if (mbedtls_md_setup(&context, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 1) ||
mbedtls_md_hmac_starts(&context, reinterpret_cast<const u8*>(key), key_length) ||
mbedtls_md_hmac_update(&context, reinterpret_cast<const u8*>(data), data_length) ||
@@ -45,7 +42,7 @@ static bool CalculateHMAC256(Destination* out, const SourceKey* key, std::size_t
return true;
}
NAX::NAX(VirtualFile file_) : file(std::move(file_)), header(std::make_unique<NAXHeader>()) {
NAX::NAX(VirtualFile file_) : header(std::make_unique<NAXHeader>()), file(std::move(file_)) {
std::string path = FileUtil::SanitizePath(file->GetFullPath());
static const std::regex nax_path_regex("/registered/(000000[0-9A-F]{2})/([0-9A-F]{32})\\.nca",
std::regex_constants::ECMAScript |
@@ -65,13 +62,15 @@ NAX::NAX(VirtualFile file_) : file(std::move(file_)), header(std::make_unique<NA
}
NAX::NAX(VirtualFile file_, std::array<u8, 0x10> nca_id)
: file(std::move(file_)), header(std::make_unique<NAXHeader>()) {
: header(std::make_unique<NAXHeader>()), file(std::move(file_)) {
Core::Crypto::SHA256Hash hash{};
mbedtls_sha256(nca_id.data(), nca_id.size(), hash.data(), 0);
status = Parse(fmt::format("/registered/000000{:02X}/{}.nca", hash[0],
Common::HexArrayToString(nca_id, false)));
}
NAX::~NAX() = default;
Loader::ResultStatus NAX::Parse(std::string_view path) {
if (file->ReadObject(header.get()) != sizeof(NAXHeader))
return Loader::ResultStatus::ErrorBadNAXHeader;
@@ -138,9 +137,9 @@ VirtualFile NAX::GetDecrypted() const {
return dec_file;
}
std::shared_ptr<NCA> NAX::AsNCA() const {
std::unique_ptr<NCA> NAX::AsNCA() const {
if (type == NAXContentType::NCA)
return std::make_shared<NCA>(GetDecrypted());
return std::make_unique<NCA>(GetDecrypted());
return nullptr;
}

View File

@@ -33,12 +33,13 @@ class NAX : public ReadOnlyVfsDirectory {
public:
explicit NAX(VirtualFile file);
explicit NAX(VirtualFile file, std::array<u8, 0x10> nca_id);
~NAX() override;
Loader::ResultStatus GetStatus() const;
VirtualFile GetDecrypted() const;
std::shared_ptr<NCA> AsNCA() const;
std::unique_ptr<NCA> AsNCA() const;
NAXContentType GetContentType() const;
@@ -60,7 +61,7 @@ private:
VirtualFile file;
Loader::ResultStatus status;
NAXContentType type;
NAXContentType type{};
VirtualFile dec_file;

View File

@@ -65,9 +65,9 @@ constexpr u32 MSG_WAITALL = 8;
constexpr u32 LR_REGISTER = 30;
constexpr u32 SP_REGISTER = 31;
constexpr u32 PC_REGISTER = 32;
constexpr u32 CPSR_REGISTER = 33;
constexpr u32 PSTATE_REGISTER = 33;
constexpr u32 UC_ARM64_REG_Q0 = 34;
constexpr u32 FPSCR_REGISTER = 66;
constexpr u32 FPCR_REGISTER = 66;
// TODO/WiP - Used while working on support for FPU
constexpr u32 TODO_DUMMY_REG_997 = 997;
@@ -116,7 +116,7 @@ constexpr char target_xml[] =
<reg name="pc" bitsize="64" type="code_ptr"/>
<flags id="cpsr_flags" size="4">
<flags id="pstate_flags" size="4">
<field name="SP" start="0" end="0"/>
<field name="" start="1" end="1"/>
<field name="EL" start="2" end="3"/>
@@ -135,7 +135,7 @@ constexpr char target_xml[] =
<field name="Z" start="30" end="30"/>
<field name="N" start="31" end="31"/>
</flags>
<reg name="cpsr" bitsize="32" type="cpsr_flags"/>
<reg name="pstate" bitsize="32" type="pstate_flags"/>
</feature>
<feature name="org.gnu.gdb.aarch64.fpu">
</feature>
@@ -227,10 +227,10 @@ static u64 RegRead(std::size_t id, Kernel::Thread* thread = nullptr) {
return thread->context.sp;
} else if (id == PC_REGISTER) {
return thread->context.pc;
} else if (id == CPSR_REGISTER) {
return thread->context.cpsr;
} else if (id > CPSR_REGISTER && id < FPSCR_REGISTER) {
return thread->context.fpu_registers[id - UC_ARM64_REG_Q0][0];
} else if (id == PSTATE_REGISTER) {
return thread->context.pstate;
} else if (id > PSTATE_REGISTER && id < FPCR_REGISTER) {
return thread->context.vector_registers[id - UC_ARM64_REG_Q0][0];
} else {
return 0;
}
@@ -247,10 +247,10 @@ static void RegWrite(std::size_t id, u64 val, Kernel::Thread* thread = nullptr)
thread->context.sp = val;
} else if (id == PC_REGISTER) {
thread->context.pc = val;
} else if (id == CPSR_REGISTER) {
thread->context.cpsr = val;
} else if (id > CPSR_REGISTER && id < FPSCR_REGISTER) {
thread->context.fpu_registers[id - (CPSR_REGISTER + 1)][0] = val;
} else if (id == PSTATE_REGISTER) {
thread->context.pstate = val;
} else if (id > PSTATE_REGISTER && id < FPCR_REGISTER) {
thread->context.vector_registers[id - (PSTATE_REGISTER + 1)][0] = val;
}
}
@@ -781,11 +781,11 @@ static void ReadRegister() {
LongToGdbHex(reply, RegRead(id, current_thread));
} else if (id == PC_REGISTER) {
LongToGdbHex(reply, RegRead(id, current_thread));
} else if (id == CPSR_REGISTER) {
IntToGdbHex(reply, (u32)RegRead(id, current_thread));
} else if (id >= UC_ARM64_REG_Q0 && id < FPSCR_REGISTER) {
} else if (id == PSTATE_REGISTER) {
IntToGdbHex(reply, static_cast<u32>(RegRead(id, current_thread)));
} else if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) {
LongToGdbHex(reply, RegRead(id, current_thread));
} else if (id == FPSCR_REGISTER) {
} else if (id == FPCR_REGISTER) {
LongToGdbHex(reply, RegRead(TODO_DUMMY_REG_998, current_thread));
} else {
LongToGdbHex(reply, RegRead(TODO_DUMMY_REG_997, current_thread));
@@ -811,7 +811,7 @@ static void ReadRegisters() {
bufptr += 16;
IntToGdbHex(bufptr, (u32)RegRead(CPSR_REGISTER, current_thread));
IntToGdbHex(bufptr, static_cast<u32>(RegRead(PSTATE_REGISTER, current_thread)));
bufptr += 8;
@@ -843,11 +843,11 @@ static void WriteRegister() {
RegWrite(id, GdbHexToLong(buffer_ptr), current_thread);
} else if (id == PC_REGISTER) {
RegWrite(id, GdbHexToLong(buffer_ptr), current_thread);
} else if (id == CPSR_REGISTER) {
} else if (id == PSTATE_REGISTER) {
RegWrite(id, GdbHexToInt(buffer_ptr), current_thread);
} else if (id >= UC_ARM64_REG_Q0 && id < FPSCR_REGISTER) {
} else if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) {
RegWrite(id, GdbHexToLong(buffer_ptr), current_thread);
} else if (id == FPSCR_REGISTER) {
} else if (id == FPCR_REGISTER) {
RegWrite(TODO_DUMMY_REG_998, GdbHexToLong(buffer_ptr), current_thread);
} else {
RegWrite(TODO_DUMMY_REG_997, GdbHexToLong(buffer_ptr), current_thread);
@@ -866,16 +866,16 @@ static void WriteRegisters() {
if (command_buffer[0] != 'G')
return SendReply("E01");
for (u32 i = 0, reg = 0; reg <= FPSCR_REGISTER; i++, reg++) {
for (u32 i = 0, reg = 0; reg <= FPCR_REGISTER; i++, reg++) {
if (reg <= SP_REGISTER) {
RegWrite(reg, GdbHexToLong(buffer_ptr + i * 16), current_thread);
} else if (reg == PC_REGISTER) {
RegWrite(PC_REGISTER, GdbHexToLong(buffer_ptr + i * 16), current_thread);
} else if (reg == CPSR_REGISTER) {
RegWrite(CPSR_REGISTER, GdbHexToInt(buffer_ptr + i * 16), current_thread);
} else if (reg >= UC_ARM64_REG_Q0 && reg < FPSCR_REGISTER) {
} else if (reg == PSTATE_REGISTER) {
RegWrite(PSTATE_REGISTER, GdbHexToInt(buffer_ptr + i * 16), current_thread);
} else if (reg >= UC_ARM64_REG_Q0 && reg < FPCR_REGISTER) {
RegWrite(reg, GdbHexToLong(buffer_ptr + i * 16), current_thread);
} else if (reg == FPSCR_REGISTER) {
} else if (reg == FPCR_REGISTER) {
RegWrite(TODO_DUMMY_REG_998, GdbHexToLong(buffer_ptr + i * 16), current_thread);
} else {
UNIMPLEMENTED();
@@ -995,7 +995,7 @@ static bool CommitBreakpoint(BreakpointType type, VAddr addr, u64 len) {
breakpoint.addr = addr;
breakpoint.len = len;
Memory::ReadBlock(addr, breakpoint.inst.data(), breakpoint.inst.size());
static constexpr std::array<u8, 4> btrap{{0xd4, 0x20, 0x7d, 0x0}};
static constexpr std::array<u8, 4> btrap{{0x00, 0x7d, 0x20, 0xd4}};
Memory::WriteBlock(addr, btrap.data(), btrap.size());
Core::System::GetInstance().InvalidateCpuInstructionCaches();
p.insert({addr, breakpoint});

View File

@@ -290,13 +290,6 @@ public:
Skip(CommandIdSize, false);
}
ResponseBuilder MakeBuilder(u32 normal_params_size, u32 num_handles_to_copy,
u32 num_handles_to_move,
ResponseBuilder::Flags flags = ResponseBuilder::Flags::None) const {
return ResponseBuilder{*context, normal_params_size, num_handles_to_copy,
num_handles_to_move, flags};
}
template <typename T>
T Pop();

View File

@@ -16,6 +16,7 @@
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/result.h"
#include "core/memory.h"
namespace Kernel {

View File

@@ -125,7 +125,7 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
vm_manager.LogLayout();
status = ProcessStatus::Running;
Kernel::SetupMainThread(kernel, entry_point, main_thread_priority, this);
Kernel::SetupMainThread(kernel, entry_point, main_thread_priority, *this);
}
void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) {

View File

@@ -13,7 +13,9 @@
namespace Kernel {
#define PARAM(n) Core::CurrentArmInterface().GetReg(n)
static inline u64 Param(int n) {
return Core::CurrentArmInterface().GetReg(n);
}
/**
* HLE a function return from the current ARM userland process
@@ -28,23 +30,23 @@ static inline void FuncReturn(u64 res) {
template <ResultCode func(u64)>
void SvcWrap() {
FuncReturn(func(PARAM(0)).raw);
FuncReturn(func(Param(0)).raw);
}
template <ResultCode func(u32)>
void SvcWrap() {
FuncReturn(func((u32)PARAM(0)).raw);
FuncReturn(func((u32)Param(0)).raw);
}
template <ResultCode func(u32, u32)>
void SvcWrap() {
FuncReturn(func((u32)PARAM(0), (u32)PARAM(1)).raw);
FuncReturn(func((u32)Param(0), (u32)Param(1)).raw);
}
template <ResultCode func(u32*, u32)>
void SvcWrap() {
u32 param_1 = 0;
u32 retval = func(&param_1, (u32)PARAM(1)).raw;
u32 retval = func(&param_1, (u32)Param(1)).raw;
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval);
}
@@ -52,39 +54,39 @@ void SvcWrap() {
template <ResultCode func(u32*, u64)>
void SvcWrap() {
u32 param_1 = 0;
u32 retval = func(&param_1, PARAM(1)).raw;
u32 retval = func(&param_1, Param(1)).raw;
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval);
}
template <ResultCode func(u64, s32)>
void SvcWrap() {
FuncReturn(func(PARAM(0), (s32)PARAM(1)).raw);
FuncReturn(func(Param(0), (s32)Param(1)).raw);
}
template <ResultCode func(u64*, u64)>
void SvcWrap() {
u64 param_1 = 0;
u32 retval = func(&param_1, PARAM(1)).raw;
u32 retval = func(&param_1, Param(1)).raw;
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval);
}
template <ResultCode func(u32, u64)>
void SvcWrap() {
FuncReturn(func((u32)(PARAM(0) & 0xFFFFFFFF), PARAM(1)).raw);
FuncReturn(func((u32)(Param(0) & 0xFFFFFFFF), Param(1)).raw);
}
template <ResultCode func(u32, u32, u64)>
void SvcWrap() {
FuncReturn(func((u32)(PARAM(0) & 0xFFFFFFFF), (u32)(PARAM(1) & 0xFFFFFFFF), PARAM(2)).raw);
FuncReturn(func((u32)(Param(0) & 0xFFFFFFFF), (u32)(Param(1) & 0xFFFFFFFF), Param(2)).raw);
}
template <ResultCode func(u32, u32*, u64*)>
void SvcWrap() {
u32 param_1 = 0;
u64 param_2 = 0;
ResultCode retval = func((u32)(PARAM(2) & 0xFFFFFFFF), &param_1, &param_2);
ResultCode retval = func((u32)(Param(2) & 0xFFFFFFFF), &param_1, &param_2);
Core::CurrentArmInterface().SetReg(1, param_1);
Core::CurrentArmInterface().SetReg(2, param_2);
FuncReturn(retval.raw);
@@ -93,46 +95,46 @@ void SvcWrap() {
template <ResultCode func(u64, u64, u32, u32)>
void SvcWrap() {
FuncReturn(
func(PARAM(0), PARAM(1), (u32)(PARAM(3) & 0xFFFFFFFF), (u32)(PARAM(3) & 0xFFFFFFFF)).raw);
func(Param(0), Param(1), (u32)(Param(3) & 0xFFFFFFFF), (u32)(Param(3) & 0xFFFFFFFF)).raw);
}
template <ResultCode func(u32, u64, u32)>
void SvcWrap() {
FuncReturn(func((u32)PARAM(0), PARAM(1), (u32)PARAM(2)).raw);
FuncReturn(func((u32)Param(0), Param(1), (u32)Param(2)).raw);
}
template <ResultCode func(u64, u64, u64)>
void SvcWrap() {
FuncReturn(func(PARAM(0), PARAM(1), PARAM(2)).raw);
FuncReturn(func(Param(0), Param(1), Param(2)).raw);
}
template <ResultCode func(u32, u64, u64, u32)>
void SvcWrap() {
FuncReturn(func((u32)PARAM(0), PARAM(1), PARAM(2), (u32)PARAM(3)).raw);
FuncReturn(func((u32)Param(0), Param(1), Param(2), (u32)Param(3)).raw);
}
template <ResultCode func(u32, u64, u64)>
void SvcWrap() {
FuncReturn(func((u32)PARAM(0), PARAM(1), PARAM(2)).raw);
FuncReturn(func((u32)Param(0), Param(1), Param(2)).raw);
}
template <ResultCode func(u32*, u64, u64, s64)>
void SvcWrap() {
u32 param_1 = 0;
ResultCode retval = func(&param_1, PARAM(1), (u32)(PARAM(2) & 0xFFFFFFFF), (s64)PARAM(3));
ResultCode retval = func(&param_1, Param(1), (u32)(Param(2) & 0xFFFFFFFF), (s64)Param(3));
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval.raw);
}
template <ResultCode func(u64, u64, u32, s64)>
void SvcWrap() {
FuncReturn(func(PARAM(0), PARAM(1), (u32)PARAM(2), (s64)PARAM(3)).raw);
FuncReturn(func(Param(0), Param(1), (u32)Param(2), (s64)Param(3)).raw);
}
template <ResultCode func(u64*, u64, u64, u64)>
void SvcWrap() {
u64 param_1 = 0;
u32 retval = func(&param_1, PARAM(1), PARAM(2), PARAM(3)).raw;
u32 retval = func(&param_1, Param(1), Param(2), Param(3)).raw;
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval);
}
@@ -141,7 +143,7 @@ template <ResultCode func(u32*, u64, u64, u64, u32, s32)>
void SvcWrap() {
u32 param_1 = 0;
u32 retval =
func(&param_1, PARAM(1), PARAM(2), PARAM(3), (u32)PARAM(4), (s32)(PARAM(5) & 0xFFFFFFFF))
func(&param_1, Param(1), Param(2), Param(3), (u32)Param(4), (s32)(Param(5) & 0xFFFFFFFF))
.raw;
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval);
@@ -151,13 +153,13 @@ template <ResultCode func(MemoryInfo*, PageInfo*, u64)>
void SvcWrap() {
MemoryInfo memory_info = {};
PageInfo page_info = {};
u32 retval = func(&memory_info, &page_info, PARAM(2)).raw;
u32 retval = func(&memory_info, &page_info, Param(2)).raw;
Memory::Write64(PARAM(0), memory_info.base_address);
Memory::Write64(PARAM(0) + 8, memory_info.size);
Memory::Write32(PARAM(0) + 16, memory_info.type);
Memory::Write32(PARAM(0) + 20, memory_info.attributes);
Memory::Write32(PARAM(0) + 24, memory_info.permission);
Memory::Write64(Param(0), memory_info.base_address);
Memory::Write64(Param(0) + 8, memory_info.size);
Memory::Write32(Param(0) + 16, memory_info.type);
Memory::Write32(Param(0) + 20, memory_info.attributes);
Memory::Write32(Param(0) + 24, memory_info.permission);
FuncReturn(retval);
}
@@ -165,7 +167,7 @@ void SvcWrap() {
template <ResultCode func(u32*, u64, u64, u32)>
void SvcWrap() {
u32 param_1 = 0;
u32 retval = func(&param_1, PARAM(1), PARAM(2), (u32)(PARAM(3) & 0xFFFFFFFF)).raw;
u32 retval = func(&param_1, Param(1), Param(2), (u32)(Param(3) & 0xFFFFFFFF)).raw;
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval);
}
@@ -174,7 +176,7 @@ template <ResultCode func(Handle*, u64, u32, u32)>
void SvcWrap() {
u32 param_1 = 0;
u32 retval =
func(&param_1, PARAM(1), (u32)(PARAM(2) & 0xFFFFFFFF), (u32)(PARAM(3) & 0xFFFFFFFF)).raw;
func(&param_1, Param(1), (u32)(Param(2) & 0xFFFFFFFF), (u32)(Param(3) & 0xFFFFFFFF)).raw;
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval);
}
@@ -182,14 +184,14 @@ void SvcWrap() {
template <ResultCode func(u64, u32, s32, s64)>
void SvcWrap() {
FuncReturn(
func(PARAM(0), (u32)(PARAM(1) & 0xFFFFFFFF), (s32)(PARAM(2) & 0xFFFFFFFF), (s64)PARAM(3))
func(Param(0), (u32)(Param(1) & 0xFFFFFFFF), (s32)(Param(2) & 0xFFFFFFFF), (s64)Param(3))
.raw);
}
template <ResultCode func(u64, u32, s32, s32)>
void SvcWrap() {
FuncReturn(func(PARAM(0), (u32)(PARAM(1) & 0xFFFFFFFF), (s32)(PARAM(2) & 0xFFFFFFFF),
(s32)(PARAM(3) & 0xFFFFFFFF))
FuncReturn(func(Param(0), (u32)(Param(1) & 0xFFFFFFFF), (s32)(Param(2) & 0xFFFFFFFF),
(s32)(Param(3) & 0xFFFFFFFF))
.raw);
}
@@ -219,20 +221,17 @@ void SvcWrap() {
template <void func(s64)>
void SvcWrap() {
func((s64)PARAM(0));
func((s64)Param(0));
}
template <void func(u64, u64 len)>
void SvcWrap() {
func(PARAM(0), PARAM(1));
func(Param(0), Param(1));
}
template <void func(u64, u64, u64)>
void SvcWrap() {
func(PARAM(0), PARAM(1), PARAM(2));
func(Param(0), Param(1), Param(2));
}
#undef PARAM
#undef FuncReturn
} // namespace Kernel

View File

@@ -217,8 +217,8 @@ static void ResetThreadContext(Core::ARM_Interface::ThreadContext& context, VAdd
context.cpu_registers[0] = arg;
context.pc = entry_point;
context.sp = stack_top;
context.cpsr = 0;
context.fpscr = 0;
context.pstate = 0;
context.fpcr = 0;
}
ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name, VAddr entry_point,
@@ -311,13 +311,13 @@ void Thread::BoostPriority(u32 priority) {
}
SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 priority,
SharedPtr<Process> owner_process) {
Process& owner_process) {
// Setup page table so we can write to memory
SetCurrentPageTable(&Core::CurrentProcess()->vm_manager.page_table);
SetCurrentPageTable(&owner_process.vm_manager.page_table);
// Initialize new "main" thread
auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0, THREADPROCESSORID_0,
Memory::STACK_AREA_VADDR_END, std::move(owner_process));
Memory::STACK_AREA_VADDR_END, &owner_process);
SharedPtr<Thread> thread = std::move(thread_res).Unwrap();

View File

@@ -281,7 +281,7 @@ private:
* @return A shared pointer to the main thread
*/
SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 priority,
SharedPtr<Process> owner_process);
Process& owner_process);
/**
* Gets the current thread

View File

@@ -34,7 +34,7 @@ public:
static const FunctionInfo functions[] = {
{0, &IProfile::Get, "Get"},
{1, &IProfile::GetBase, "GetBase"},
{10, nullptr, "GetImageSize"},
{10, &IProfile::GetImageSize, "GetImageSize"},
{11, &IProfile::LoadImage, "LoadImage"},
};
RegisterHandlers(functions);
@@ -93,6 +93,14 @@ private:
rb.Push<u32>(jpeg_size);
}
void GetImageSize(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_ACC, "(STUBBED) called");
constexpr u32 jpeg_size = 107;
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(jpeg_size);
}
const ProfileManager& profile_manager;
UUID user_id; ///< The user id this profile refers to.
};
@@ -122,11 +130,10 @@ private:
void GetAccountId(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_ACC, "(STUBBED) called");
// TODO(Subv): Find out what this actually does and implement it. Stub it as an error for
// now since we do not implement NNID. Returning a bogus id here will cause games to send
// invalid IPC requests after ListOpenUsers is called.
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultCode(-1));
// Should return a nintendo account ID
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.PushRaw<u64>(1);
}
};

View File

@@ -25,7 +25,7 @@ const UUID& UUID::Generate() {
ProfileManager::ProfileManager() {
// TODO(ogniK): Create the default user we have for now until loading/saving users is added
auto user_uuid = UUID{1, 0};
CreateNewUser(user_uuid, Settings::values.username);
ASSERT(CreateNewUser(user_uuid, Settings::values.username).IsSuccess());
OpenUser(user_uuid);
}
@@ -91,7 +91,8 @@ ResultCode ProfileManager::CreateNewUser(UUID uuid, const ProfileUsername& usern
/// specifically by allowing an std::string for the username. This is required specifically since
/// we're loading a string straight from the config
ResultCode ProfileManager::CreateNewUser(UUID uuid, const std::string& username) {
ProfileUsername username_output;
ProfileUsername username_output{};
if (username.size() > username_output.size()) {
std::copy_n(username.begin(), username_output.size(), username_output.begin());
} else {

View File

@@ -20,6 +20,7 @@
#include "core/hle/service/nvflinger/nvflinger.h"
#include "core/hle/service/pm/pm.h"
#include "core/hle/service/set/set.h"
#include "core/hle/service/vi/vi.h"
#include "core/settings.h"
namespace Service::AM {
@@ -334,7 +335,7 @@ ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter"
{51, nullptr, "SetVrModeEnabled"},
{52, nullptr, "SwitchLcdBacklight"},
{55, nullptr, "IsInControllerFirmwareUpdateSection"},
{60, nullptr, "GetDefaultDisplayResolution"},
{60, &ICommonStateGetter::GetDefaultDisplayResolution, "GetDefaultDisplayResolution"},
{61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent,
"GetDefaultDisplayResolutionChangeEvent"},
{62, nullptr, "GetHdcpAuthenticationState"},
@@ -393,6 +394,21 @@ void ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent(Kernel::HLEReque
LOG_WARNING(Service_AM, "(STUBBED) called");
}
void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
if (Settings::values.use_docked_mode) {
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth));
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight));
} else {
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth));
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight));
}
LOG_DEBUG(Service_AM, "called");
}
void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) {
const bool use_docked_mode{Settings::values.use_docked_mode};
IPC::ResponseBuilder rb{ctx, 3};
@@ -446,7 +462,7 @@ private:
std::memcpy(&buffer[offset], data.data(), data.size());
IPC::ResponseBuilder rb{rp.MakeBuilder(2, 0, 0)};
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_DEBUG(Service_AM, "called, offset={}", offset);
@@ -462,7 +478,7 @@ private:
ctx.WriteBuffer(buffer.data() + offset, size);
IPC::ResponseBuilder rb{rp.MakeBuilder(2, 0, 0)};
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_DEBUG(Service_AM, "called, offset={}", offset);
@@ -552,7 +568,7 @@ private:
IPC::RequestParser rp{ctx};
storage_stack.push(rp.PopIpcInterface<AM::IStorage>());
IPC::ResponseBuilder rb{rp.MakeBuilder(2, 0, 0)};
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_DEBUG(Service_AM, "called");
@@ -600,7 +616,7 @@ void ILibraryAppletCreator::CreateStorage(Kernel::HLERequestContext& ctx) {
const u64 size{rp.Pop<u64>()};
std::vector<u8> buffer(size);
IPC::ResponseBuilder rb{rp.MakeBuilder(2, 0, 1)};
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<AM::IStorage>(std::move(buffer));

View File

@@ -123,6 +123,7 @@ private:
void GetOperationMode(Kernel::HLERequestContext& ctx);
void GetPerformanceMode(Kernel::HLERequestContext& ctx);
void GetBootMode(Kernel::HLERequestContext& ctx);
void GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx);
Kernel::SharedPtr<Kernel::Event> event;
};

View File

@@ -190,7 +190,7 @@ void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) {
ctx.WriteBuffer(DefaultDevice);
IPC::ResponseBuilder rb = rp.MakeBuilder(3, 0, 0);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(1); // Amount of audio devices

View File

@@ -137,7 +137,7 @@ private:
constexpr std::array<char, 15> audio_interface{{"AudioInterface"}};
ctx.WriteBuffer(audio_interface);
IPC::ResponseBuilder rb = rp.MakeBuilder(3, 0, 0);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(1);
}
@@ -151,7 +151,7 @@ private:
auto file_buffer = ctx.ReadBuffer();
auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0');
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
@@ -162,7 +162,7 @@ private:
constexpr std::array<char, 12> audio_interface{{"AudioDevice"}};
ctx.WriteBuffer(audio_interface);
IPC::ResponseBuilder rb = rp.MakeBuilder(3, 0, 0);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(1);
}

View File

@@ -197,7 +197,7 @@ ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const s
auto dir = GetDirectoryRelativeWrapped(backing, path);
if (dir == nullptr) {
// TODO(DarkLordZach): Find a better error code for this
return ResultCode(-1);
return FileSys::ERROR_PATH_NOT_FOUND;
}
return MakeResult(dir);
}

View File

@@ -313,7 +313,7 @@ public:
{64, nullptr, "DeactivateJoySixAxisSensor"},
{65, nullptr, "GetJoySixAxisSensorLifoHandle"},
{66, &Hid::StartSixAxisSensor, "StartSixAxisSensor"},
{67, nullptr, "StopSixAxisSensor"},
{67, &Hid::StopSixAxisSensor, "StopSixAxisSensor"},
{68, nullptr, "IsSixAxisSensorFusionEnabled"},
{69, nullptr, "EnableSixAxisSensorFusion"},
{70, nullptr, "SetSixAxisSensorFusionParameters"},
@@ -329,7 +329,7 @@ public:
{80, nullptr, "GetGyroscopeZeroDriftMode"},
{81, nullptr, "ResetGyroscopeZeroDriftMode"},
{82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"},
{91, nullptr, "ActivateGesture"},
{91, &Hid::ActivateGesture, "ActivateGesture"},
{100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"},
{101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"},
{102, &Hid::SetSupportedNpadIdType, "SetSupportedNpadIdType"},
@@ -338,7 +338,7 @@ public:
{106, &Hid::AcquireNpadStyleSetUpdateEventHandle, "AcquireNpadStyleSetUpdateEventHandle"},
{107, &Hid::DisconnectNpad, "DisconnectNpad"},
{108, &Hid::GetPlayerLedPattern, "GetPlayerLedPattern"},
{109, nullptr, "ActivateNpadWithRevision"},
{109, &Hid::ActivateNpadWithRevision, "ActivateNpadWithRevision"},
{120, &Hid::SetNpadJoyHoldType, "SetNpadJoyHoldType"},
{121, &Hid::GetNpadJoyHoldType, "GetNpadJoyHoldType"},
{122, &Hid::SetNpadJoyAssignmentModeSingleByDefault, "SetNpadJoyAssignmentModeSingleByDefault"},
@@ -364,8 +364,8 @@ public:
{208, nullptr, "GetActualVibrationGcErmCommand"},
{209, nullptr, "BeginPermitVibrationSession"},
{210, nullptr, "EndPermitVibrationSession"},
{300, nullptr, "ActivateConsoleSixAxisSensor"},
{301, nullptr, "StartConsoleSixAxisSensor"},
{300, &Hid::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"},
{301, &Hid::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"},
{302, nullptr, "StopConsoleSixAxisSensor"},
{303, nullptr, "ActivateSevenSixAxisSensor"},
{304, nullptr, "StartSevenSixAxisSensor"},
@@ -579,6 +579,36 @@ private:
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void StopSixAxisSensor(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void ActivateGesture(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
};
class HidDbg final : public ServiceFramework<HidDbg> {

View File

@@ -31,7 +31,7 @@ public:
{1, &IRequest::GetResult, "GetResult"},
{2, &IRequest::GetSystemEventReadableHandles, "GetSystemEventReadableHandles"},
{3, &IRequest::Cancel, "Cancel"},
{4, nullptr, "Submit"},
{4, &IRequest::Submit, "Submit"},
{5, nullptr, "SetRequirement"},
{6, nullptr, "SetRequirementPreset"},
{8, nullptr, "SetPriority"},
@@ -61,6 +61,12 @@ public:
}
private:
void Submit(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void GetRequestState(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
@@ -114,10 +120,11 @@ public:
private:
void GetClientId(Kernel::HLERequestContext& ctx) {
static constexpr u32 client_id = 1;
LOG_WARNING(Service_NIFM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(0);
rb.Push<u64>(client_id); // Client ID needs to be non zero otherwise it's considered invalid
}
void CreateScanRequest(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
@@ -141,10 +148,16 @@ private:
rb.Push(RESULT_SUCCESS);
}
void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
ASSERT_MSG(ctx.GetReadBufferSize() == 0x17c, "NetworkProfileData is not the correct size");
u128 uuid{};
auto buffer = ctx.ReadBuffer();
std::memcpy(&uuid, buffer.data() + 8, sizeof(u128));
IPC::ResponseBuilder rb{ctx, 6, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<INetworkProfile>();
rb.PushRaw<u128>(uuid);
LOG_DEBUG(Service_NIFM, "called");
}

View File

@@ -2,6 +2,10 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <chrono>
#include <ctime>
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/service/nim/nim.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
@@ -100,19 +104,111 @@ public:
}
};
class IEnsureNetworkClockAvailabilityService final
: public ServiceFramework<IEnsureNetworkClockAvailabilityService> {
public:
IEnsureNetworkClockAvailabilityService()
: ServiceFramework("IEnsureNetworkClockAvailabilityService") {
static const FunctionInfo functions[] = {
{0, &IEnsureNetworkClockAvailabilityService::StartTask, "StartTask"},
{1, &IEnsureNetworkClockAvailabilityService::GetFinishNotificationEvent,
"GetFinishNotificationEvent"},
{2, &IEnsureNetworkClockAvailabilityService::GetResult, "GetResult"},
{3, &IEnsureNetworkClockAvailabilityService::Cancel, "Cancel"},
{4, &IEnsureNetworkClockAvailabilityService::IsProcessing, "IsProcessing"},
{5, &IEnsureNetworkClockAvailabilityService::GetServerTime, "GetServerTime"},
};
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
finished_event =
Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
"IEnsureNetworkClockAvailabilityService:FinishEvent");
}
private:
Kernel::SharedPtr<Kernel::Event> finished_event;
void StartTask(Kernel::HLERequestContext& ctx) {
// No need to connect to the internet, just finish the task straight away.
finished_event->Signal();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_DEBUG(Service_NIM, "called");
}
void GetFinishNotificationEvent(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(finished_event);
LOG_DEBUG(Service_NIM, "called");
}
void GetResult(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_DEBUG(Service_NIM, "called");
}
void Cancel(Kernel::HLERequestContext& ctx) {
finished_event->Clear();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_DEBUG(Service_NIM, "called");
}
void IsProcessing(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.PushRaw<u32>(0); // We instantly process the request
LOG_DEBUG(Service_NIM, "called");
}
void GetServerTime(Kernel::HLERequestContext& ctx) {
const s64 server_time{std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch())
.count()};
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.PushRaw<s64>(server_time);
LOG_DEBUG(Service_NIM, "called");
}
};
class NTC final : public ServiceFramework<NTC> {
public:
explicit NTC() : ServiceFramework{"ntc"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "OpenEnsureNetworkClockAvailabilityService"},
{100, nullptr, "SuspendAutonomicTimeCorrection"},
{101, nullptr, "ResumeAutonomicTimeCorrection"},
{0, &NTC::OpenEnsureNetworkClockAvailabilityService, "OpenEnsureNetworkClockAvailabilityService"},
{100, &NTC::SuspendAutonomicTimeCorrection, "SuspendAutonomicTimeCorrection"},
{101, &NTC::ResumeAutonomicTimeCorrection, "ResumeAutonomicTimeCorrection"},
};
// clang-format on
RegisterHandlers(functions);
}
private:
void OpenEnsureNetworkClockAvailabilityService(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IEnsureNetworkClockAvailabilityService>();
LOG_DEBUG(Service_NIM, "called");
}
// TODO(ogniK): Do we need these?
void SuspendAutonomicTimeCorrection(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_NIM, "(STUBBED) called");
}
void ResumeAutonomicTimeCorrection(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_NIM, "(STUBBED) called");
}
};
void InstallInterfaces(SM::ServiceManager& sm) {

View File

@@ -108,7 +108,7 @@ void SM::GetService(Kernel::HLERequestContext& ctx) {
auto client_port = service_manager->GetServicePort(name);
if (client_port.Failed()) {
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(client_port.Code());
LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, client_port.Code().raw);
if (name.length() == 0)
@@ -121,8 +121,7 @@ void SM::GetService(Kernel::HLERequestContext& ctx) {
ASSERT(session.Succeeded());
if (session.Succeeded()) {
LOG_DEBUG(Service_SM, "called service={} -> session={}", name, (*session)->GetObjectId());
IPC::ResponseBuilder rb =
rp.MakeBuilder(2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles);
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
rb.Push(session.Code());
rb.PushMoveObjects(std::move(session).Unwrap());
}

View File

@@ -71,7 +71,7 @@ private:
LOG_WARNING(Service_SSL, "(STUBBED) called");
IPC::RequestParser rp{ctx};
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}

View File

@@ -650,7 +650,7 @@ private:
u64 layer_id = rp.Pop<u64>();
u64 z_value = rp.Pop<u64>();
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
@@ -658,7 +658,7 @@ private:
IPC::RequestParser rp{ctx};
u64 layer_id = rp.Pop<u64>();
bool visibility = rp.Pop<bool>();
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_VI, "(STUBBED) called, layer_id=0x{:08X}, visibility={}", layer_id,
visibility);
@@ -747,7 +747,7 @@ private:
IPC::RequestParser rp{ctx};
u64 display = rp.Pop<u64>();
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
@@ -761,7 +761,7 @@ private:
u64 layer_id = nv_flinger->CreateLayer(display);
IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push(layer_id);
}
@@ -772,7 +772,7 @@ private:
u32 stack = rp.Pop<u32>();
u64 layer_id = rp.Pop<u64>();
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
@@ -780,7 +780,7 @@ private:
IPC::RequestParser rp{ctx};
u64 layer_id = rp.Pop<u64>();
bool visibility = rp.Pop<bool>();
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_VI, "(STUBBED) called, layer_id=0x{:X}, visibility={}", layer_id,
visibility);
@@ -837,7 +837,7 @@ private:
ASSERT_MSG(name == "Default", "Non-default displays aren't supported yet");
IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(nv_flinger->OpenDisplay(name));
}
@@ -847,7 +847,7 @@ private:
IPC::RequestParser rp{ctx};
u64 display_id = rp.Pop<u64>();
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
@@ -856,7 +856,7 @@ private:
IPC::RequestParser rp{ctx};
u64 display_id = rp.Pop<u64>();
IPC::ResponseBuilder rb = rp.MakeBuilder(6, 0, 0);
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(RESULT_SUCCESS);
if (Settings::values.use_docked_mode) {
@@ -874,7 +874,7 @@ private:
u32 scaling_mode = rp.Pop<u32>();
u64 unknown = rp.Pop<u64>();
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
@@ -882,7 +882,7 @@ private:
IPC::RequestParser rp{ctx};
DisplayInfo display_info;
ctx.WriteBuffer(&display_info, sizeof(DisplayInfo));
IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(1);
LOG_WARNING(Service_VI, "(STUBBED) called");
@@ -903,7 +903,7 @@ private:
u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id);
NativeWindow native_window{buffer_queue_id};
IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(ctx.WriteBuffer(native_window.Serialize()));
}
@@ -922,7 +922,7 @@ private:
u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id);
NativeWindow native_window{buffer_queue_id};
IPC::ResponseBuilder rb = rp.MakeBuilder(6, 0, 0);
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(RESULT_SUCCESS);
rb.Push(layer_id);
rb.Push<u64>(ctx.WriteBuffer(native_window.Serialize()));
@@ -934,7 +934,7 @@ private:
IPC::RequestParser rp{ctx};
u64 layer_id = rp.Pop<u64>();
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
@@ -945,7 +945,7 @@ private:
auto vsync_event = nv_flinger->GetVsyncEvent(display_id);
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 1, 0);
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(vsync_event);
}

View File

@@ -11,6 +11,20 @@
#include "core/loader/nca.h"
namespace Loader {
namespace {
FileType IdentifyTypeImpl(const FileSys::NAX& nax) {
if (nax.GetStatus() != ResultStatus::Success) {
return FileType::Error;
}
const auto nca = nax.AsNCA();
if (nca == nullptr || nca->GetStatus() != ResultStatus::Success) {
return FileType::Error;
}
return FileType::NAX;
}
} // Anonymous namespace
AppLoader_NAX::AppLoader_NAX(FileSys::VirtualFile file)
: AppLoader(file), nax(std::make_unique<FileSys::NAX>(file)),
@@ -19,14 +33,12 @@ AppLoader_NAX::AppLoader_NAX(FileSys::VirtualFile file)
AppLoader_NAX::~AppLoader_NAX() = default;
FileType AppLoader_NAX::IdentifyType(const FileSys::VirtualFile& file) {
FileSys::NAX nax(file);
const FileSys::NAX nax(file);
return IdentifyTypeImpl(nax);
}
if (nax.GetStatus() == ResultStatus::Success && nax.AsNCA() != nullptr &&
nax.AsNCA()->GetStatus() == ResultStatus::Success) {
return FileType::NAX;
}
return FileType::Error;
FileType AppLoader_NAX::GetFileType() {
return IdentifyTypeImpl(*nax);
}
ResultStatus AppLoader_NAX::Load(Kernel::SharedPtr<Kernel::Process>& process) {

View File

@@ -31,9 +31,7 @@ public:
*/
static FileType IdentifyType(const FileSys::VirtualFile& file);
FileType GetFileType() override {
return IdentifyType(file);
}
FileType GetFileType() override;
ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;

View File

@@ -32,11 +32,18 @@ static_assert(sizeof(NsoSegmentHeader) == 0x10, "NsoSegmentHeader has incorrect
struct NsoHeader {
u32_le magic;
INSERT_PADDING_BYTES(0xc);
u32_le version;
INSERT_PADDING_WORDS(1);
u8 flags;
std::array<NsoSegmentHeader, 3> segments; // Text, RoData, Data (in that order)
u32_le bss_size;
INSERT_PADDING_BYTES(0x1c);
std::array<u32_le, 3> segments_compressed_size;
bool IsSegmentCompressed(size_t segment_num) const {
ASSERT_MSG(segment_num < 3, "Invalid segment {}", segment_num);
return ((flags >> segment_num) & 1);
}
};
static_assert(sizeof(NsoHeader) == 0x6c, "NsoHeader has incorrect size.");
static_assert(std::is_trivially_copyable_v<NsoHeader>, "NsoHeader isn't trivially copyable.");
@@ -105,9 +112,11 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base) {
Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create(kernel, "");
std::vector<u8> program_image;
for (std::size_t i = 0; i < nso_header.segments.size(); ++i) {
const std::vector<u8> compressed_data =
std::vector<u8> data =
file->ReadBytes(nso_header.segments_compressed_size[i], nso_header.segments[i].offset);
std::vector<u8> data = DecompressSegment(compressed_data, nso_header.segments[i]);
if (nso_header.IsSegmentCompressed(i)) {
data = DecompressSegment(data, nso_header.segments[i]);
}
program_image.resize(nso_header.segments[i].location);
program_image.insert(program_image.end(), data.begin(), data.end());
codeset->segments[i].addr = nso_header.segments[i].location;

View File

@@ -14,6 +14,7 @@ add_library(video_core STATIC
engines/maxwell_dma.cpp
engines/maxwell_dma.h
engines/shader_bytecode.h
engines/shader_header.h
gpu.cpp
gpu.h
macro_interpreter.cpp

View File

@@ -4,11 +4,13 @@
#include "core/memory.h"
#include "video_core/engines/fermi_2d.h"
#include "video_core/rasterizer_interface.h"
#include "video_core/textures/decoders.h"
namespace Tegra::Engines {
Fermi2D::Fermi2D(MemoryManager& memory_manager) : memory_manager(memory_manager) {}
Fermi2D::Fermi2D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager)
: memory_manager(memory_manager), rasterizer{rasterizer} {}
void Fermi2D::WriteReg(u32 method, u32 value) {
ASSERT_MSG(method < Regs::NUM_REGS,
@@ -52,6 +54,12 @@ void Fermi2D::HandleSurfaceCopy() {
return;
}
rasterizer.FlushRegion(source_cpu, src_bytes_per_pixel * regs.src.width * regs.src.height);
// We have to invalidate the destination region to evict any outdated surfaces from the cache.
// We do this before actually writing the new data because the destination address might contain
// a dirty surface that will have to be written back to memory.
rasterizer.InvalidateRegion(dest_cpu, dst_bytes_per_pixel * regs.dst.width * regs.dst.height);
u8* src_buffer = Memory::GetPointer(source_cpu);
u8* dst_buffer = Memory::GetPointer(dest_cpu);

View File

@@ -12,6 +12,10 @@
#include "video_core/gpu.h"
#include "video_core/memory_manager.h"
namespace VideoCore {
class RasterizerInterface;
}
namespace Tegra::Engines {
#define FERMI2D_REG_INDEX(field_name) \
@@ -19,7 +23,7 @@ namespace Tegra::Engines {
class Fermi2D final {
public:
explicit Fermi2D(MemoryManager& memory_manager);
explicit Fermi2D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager);
~Fermi2D() = default;
/// Write the value to the register identified by method.
@@ -94,6 +98,8 @@ public:
MemoryManager& memory_manager;
private:
VideoCore::RasterizerInterface& rasterizer;
/// Performs the copy from the source surface to the destination surface as configured in the
/// registers.
void HandleSurfaceCopy();

View File

@@ -5,10 +5,14 @@
#include "common/logging/log.h"
#include "core/memory.h"
#include "video_core/engines/kepler_memory.h"
#include "video_core/rasterizer_interface.h"
namespace Tegra::Engines {
KeplerMemory::KeplerMemory(MemoryManager& memory_manager) : memory_manager(memory_manager) {}
KeplerMemory::KeplerMemory(VideoCore::RasterizerInterface& rasterizer,
MemoryManager& memory_manager)
: memory_manager(memory_manager), rasterizer{rasterizer} {}
KeplerMemory::~KeplerMemory() = default;
void KeplerMemory::WriteReg(u32 method, u32 value) {
@@ -37,6 +41,11 @@ void KeplerMemory::ProcessData(u32 data) {
VAddr dest_address =
*memory_manager.GpuToCpuAddress(address + state.write_offset * sizeof(u32));
// We have to invalidate the destination region to evict any outdated surfaces from the cache.
// We do this before actually writing the new data because the destination address might contain
// a dirty surface that will have to be written back to memory.
rasterizer.InvalidateRegion(dest_address, sizeof(u32));
Memory::Write32(dest_address, data);
state.write_offset++;

View File

@@ -11,6 +11,10 @@
#include "common/common_types.h"
#include "video_core/memory_manager.h"
namespace VideoCore {
class RasterizerInterface;
}
namespace Tegra::Engines {
#define KEPLERMEMORY_REG_INDEX(field_name) \
@@ -18,7 +22,7 @@ namespace Tegra::Engines {
class KeplerMemory final {
public:
KeplerMemory(MemoryManager& memory_manager);
KeplerMemory(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager);
~KeplerMemory();
/// Write the value to the register identified by method.
@@ -72,6 +76,7 @@ public:
private:
MemoryManager& memory_manager;
VideoCore::RasterizerInterface& rasterizer;
void ProcessData(u32 data);
};

View File

@@ -4,12 +4,14 @@
#include "core/memory.h"
#include "video_core/engines/maxwell_dma.h"
#include "video_core/rasterizer_interface.h"
#include "video_core/textures/decoders.h"
namespace Tegra {
namespace Engines {
MaxwellDMA::MaxwellDMA(MemoryManager& memory_manager) : memory_manager(memory_manager) {}
MaxwellDMA::MaxwellDMA(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager)
: memory_manager(memory_manager), rasterizer{rasterizer} {}
void MaxwellDMA::WriteReg(u32 method, u32 value) {
ASSERT_MSG(method < Regs::NUM_REGS,
@@ -44,36 +46,79 @@ void MaxwellDMA::HandleCopy() {
ASSERT(regs.exec.query_mode == Regs::QueryMode::None);
ASSERT(regs.exec.query_intr == Regs::QueryIntr::None);
ASSERT(regs.exec.copy_mode == Regs::CopyMode::Unk2);
ASSERT(regs.src_params.pos_x == 0);
ASSERT(regs.src_params.pos_y == 0);
ASSERT(regs.dst_params.pos_x == 0);
ASSERT(regs.dst_params.pos_y == 0);
if (regs.exec.is_dst_linear == regs.exec.is_src_linear) {
std::size_t copy_size = regs.x_count;
if (!regs.exec.is_dst_linear && !regs.exec.is_src_linear) {
// If both the source and the destination are in block layout, assert.
UNREACHABLE_MSG("Tiled->Tiled DMA transfers are not yet implemented");
return;
}
if (regs.exec.is_dst_linear && regs.exec.is_src_linear) {
// When the enable_2d bit is disabled, the copy is performed as if we were copying a 1D
// buffer of length `x_count`, otherwise we copy a 2D buffer of size (x_count, y_count).
if (regs.exec.enable_2d) {
copy_size = copy_size * regs.y_count;
// buffer of length `x_count`, otherwise we copy a 2D image of dimensions (x_count,
// y_count).
if (!regs.exec.enable_2d) {
Memory::CopyBlock(dest_cpu, source_cpu, regs.x_count);
return;
}
Memory::CopyBlock(dest_cpu, source_cpu, copy_size);
// If both the source and the destination are in linear layout, perform a line-by-line
// copy. We're going to take a subrect of size (x_count, y_count) from the source
// rectangle. There is no need to manually flush/invalidate the regions because
// CopyBlock does that for us.
for (u32 line = 0; line < regs.y_count; ++line) {
const VAddr source_line = source_cpu + line * regs.src_pitch;
const VAddr dest_line = dest_cpu + line * regs.dst_pitch;
Memory::CopyBlock(dest_line, source_line, regs.x_count);
}
return;
}
ASSERT(regs.exec.enable_2d == 1);
size_t copy_size = regs.x_count * regs.y_count;
const auto FlushAndInvalidate = [&](u32 src_size, u32 dst_size) {
// TODO(Subv): For now, manually flush the regions until we implement GPU-accelerated
// copying.
rasterizer.FlushRegion(source_cpu, src_size);
// We have to invalidate the destination region to evict any outdated surfaces from the
// cache. We do this before actually writing the new data because the destination address
// might contain a dirty surface that will have to be written back to memory.
rasterizer.InvalidateRegion(dest_cpu, dst_size);
};
u8* src_buffer = Memory::GetPointer(source_cpu);
u8* dst_buffer = Memory::GetPointer(dest_cpu);
if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) {
ASSERT(regs.src_params.size_z == 1);
// If the input is tiled and the output is linear, deswizzle the input and copy it over.
Texture::CopySwizzledData(regs.src_params.size_x, regs.src_params.size_y, 1, 1, src_buffer,
dst_buffer, true, regs.src_params.BlockHeight());
u32 src_bytes_per_pixel = regs.src_pitch / regs.src_params.size_x;
FlushAndInvalidate(regs.src_pitch * regs.src_params.size_y,
copy_size * src_bytes_per_pixel);
Texture::UnswizzleSubrect(regs.x_count, regs.y_count, regs.dst_pitch,
regs.src_params.size_x, src_bytes_per_pixel, source_cpu, dest_cpu,
regs.src_params.BlockHeight(), regs.src_params.pos_x,
regs.src_params.pos_y);
} else {
ASSERT(regs.dst_params.size_z == 1);
ASSERT(regs.src_pitch == regs.x_count);
u32 src_bpp = regs.src_pitch / regs.x_count;
FlushAndInvalidate(regs.src_pitch * regs.y_count,
regs.dst_params.size_x * regs.dst_params.size_y * src_bpp);
// If the input is linear and the output is tiled, swizzle the input and copy it over.
Texture::CopySwizzledData(regs.dst_params.size_x, regs.dst_params.size_y, 1, 1, dst_buffer,
src_buffer, false, regs.dst_params.BlockHeight());
Texture::SwizzleSubrect(regs.x_count, regs.y_count, regs.src_pitch, regs.dst_params.size_x,
src_bpp, dest_cpu, source_cpu, regs.dst_params.BlockHeight());
}
}

View File

@@ -12,11 +12,15 @@
#include "video_core/gpu.h"
#include "video_core/memory_manager.h"
namespace VideoCore {
class RasterizerInterface;
}
namespace Tegra::Engines {
class MaxwellDMA final {
public:
explicit MaxwellDMA(MemoryManager& memory_manager);
explicit MaxwellDMA(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager);
~MaxwellDMA() = default;
/// Write the value to the register identified by method.
@@ -129,6 +133,8 @@ public:
MemoryManager& memory_manager;
private:
VideoCore::RasterizerInterface& rasterizer;
/// Performs the copy from the source buffer to the destination buffer as configured in the
/// registers.
void HandleCopy();

View File

@@ -240,6 +240,41 @@ enum class FlowCondition : u64 {
Fcsm_Tr = 0x1C, // TODO(bunnei): What is this used for?
};
enum class ControlCode : u64 {
F = 0,
LT = 1,
EQ = 2,
LE = 3,
GT = 4,
NE = 5,
GE = 6,
Num = 7,
Nan = 8,
LTU = 9,
EQU = 10,
LEU = 11,
GTU = 12,
NEU = 13,
GEU = 14,
//
OFF = 16,
LO = 17,
SFF = 18,
LS = 19,
HI = 20,
SFT = 21,
HS = 22,
OFT = 23,
CSM_TA = 24,
CSM_TR = 25,
CSM_MX = 26,
FCSM_TA = 27,
FCSM_TR = 28,
FCSM_MX = 29,
RLE = 30,
RGT = 31,
};
enum class PredicateResultMode : u64 {
None = 0x0,
NotZero = 0x3,
@@ -271,6 +306,15 @@ enum class TextureProcessMode : u64 {
LLA = 7 // Load LOD. The A is unknown, does not appear to differ with LL
};
enum class TextureMiscMode : u64 {
DC,
AOFFI, // Uses Offset
NDV,
NODEP,
MZ,
PTP,
};
enum class IpaInterpMode : u64 { Linear = 0, Perspective = 1, Flat = 2, Sc = 3 };
enum class IpaSampleMode : u64 { Default = 0, Centroid = 1, Offset = 2 };
@@ -545,6 +589,15 @@ union Instruction {
BitField<45, 2, PredOperation> op;
} pset;
union {
BitField<0, 3, u64> pred0;
BitField<3, 3, u64> pred3;
BitField<8, 5, ControlCode> cc; // flag in cc
BitField<39, 3, u64> pred39;
BitField<42, 1, u64> neg_pred39;
BitField<45, 4, PredOperation> op; // op with pred39
} csetp;
union {
BitField<39, 3, u64> pred39;
BitField<42, 1, u64> neg_pred;
@@ -590,42 +643,127 @@ union Instruction {
BitField<28, 1, u64> array;
BitField<29, 2, TextureType> texture_type;
BitField<31, 4, u64> component_mask;
BitField<49, 1, u64> nodep_flag;
BitField<50, 1, u64> dc_flag;
BitField<54, 1, u64> aoffi_flag;
BitField<55, 3, TextureProcessMode> process_mode;
bool IsComponentEnabled(std::size_t component) const {
return ((1ull << component) & component_mask) != 0;
}
TextureProcessMode GetTextureProcessMode() const {
return process_mode;
}
bool UsesMiscMode(TextureMiscMode mode) const {
switch (mode) {
case TextureMiscMode::DC:
return dc_flag != 0;
case TextureMiscMode::NODEP:
return nodep_flag != 0;
case TextureMiscMode::AOFFI:
return aoffi_flag != 0;
default:
break;
}
return false;
}
} tex;
union {
BitField<22, 6, TextureQueryType> query_type;
BitField<31, 4, u64> component_mask;
BitField<49, 1, u64> nodep_flag;
bool UsesMiscMode(TextureMiscMode mode) const {
switch (mode) {
case TextureMiscMode::NODEP:
return nodep_flag != 0;
default:
break;
}
return false;
}
} txq;
union {
BitField<28, 1, u64> array;
BitField<29, 2, TextureType> texture_type;
BitField<31, 4, u64> component_mask;
BitField<35, 1, u64> ndv_flag;
BitField<49, 1, u64> nodep_flag;
bool IsComponentEnabled(std::size_t component) const {
return ((1ull << component) & component_mask) != 0;
}
bool UsesMiscMode(TextureMiscMode mode) const {
switch (mode) {
case TextureMiscMode::NDV:
return (ndv_flag != 0);
case TextureMiscMode::NODEP:
return (nodep_flag != 0);
default:
break;
}
return false;
}
} tmml;
union {
BitField<28, 1, u64> array;
BitField<29, 2, TextureType> texture_type;
BitField<35, 1, u64> ndv_flag;
BitField<49, 1, u64> nodep_flag;
BitField<50, 1, u64> dc_flag;
BitField<54, 2, u64> info;
BitField<56, 2, u64> component;
bool UsesMiscMode(TextureMiscMode mode) const {
switch (mode) {
case TextureMiscMode::NDV:
return ndv_flag != 0;
case TextureMiscMode::NODEP:
return nodep_flag != 0;
case TextureMiscMode::DC:
return dc_flag != 0;
case TextureMiscMode::AOFFI:
return info == 1;
case TextureMiscMode::PTP:
return info == 2;
default:
break;
}
return false;
}
} tld4;
union {
BitField<49, 1, u64> nodep_flag;
BitField<50, 1, u64> dc_flag;
BitField<51, 1, u64> aoffi_flag;
BitField<52, 2, u64> component;
bool UsesMiscMode(TextureMiscMode mode) const {
switch (mode) {
case TextureMiscMode::DC:
return dc_flag != 0;
case TextureMiscMode::NODEP:
return nodep_flag != 0;
case TextureMiscMode::AOFFI:
return aoffi_flag != 0;
default:
break;
}
return false;
}
} tld4s;
union {
BitField<0, 8, Register> gpr0;
BitField<28, 8, Register> gpr28;
BitField<49, 1, u64> nodep;
BitField<49, 1, u64> nodep_flag;
BitField<50, 3, u64> component_mask_selector;
BitField<53, 4, u64> texture_info;
@@ -645,6 +783,37 @@ union Instruction {
UNREACHABLE();
}
TextureProcessMode GetTextureProcessMode() const {
switch (texture_info) {
case 0:
case 2:
case 6:
case 8:
case 9:
case 11:
return TextureProcessMode::LZ;
case 3:
case 5:
case 13:
return TextureProcessMode::LL;
default:
break;
}
return TextureProcessMode::None;
}
bool UsesMiscMode(TextureMiscMode mode) const {
switch (mode) {
case TextureMiscMode::DC:
return (texture_info >= 4 && texture_info <= 6) || texture_info == 9;
case TextureMiscMode::NODEP:
return nodep_flag != 0;
default:
break;
}
return false;
}
bool IsArrayTexture() const {
// TEXS only supports Texture2D arrays.
return texture_info >= 7 && texture_info <= 9;
@@ -673,6 +842,7 @@ union Instruction {
} texs;
union {
BitField<49, 1, u64> nodep_flag;
BitField<53, 4, u64> texture_info;
TextureType GetTextureType() const {
@@ -693,6 +863,26 @@ union Instruction {
UNREACHABLE();
}
TextureProcessMode GetTextureProcessMode() const {
if (texture_info == 1 || texture_info == 5 || texture_info == 12)
return TextureProcessMode::LL;
return TextureProcessMode::LZ;
}
bool UsesMiscMode(TextureMiscMode mode) const {
switch (mode) {
case TextureMiscMode::AOFFI:
return texture_info == 12 || texture_info == 4;
case TextureMiscMode::MZ:
return texture_info == 5;
case TextureMiscMode::NODEP:
return nodep_flag != 0;
default:
break;
}
return false;
}
bool IsArrayTexture() const {
// TEXS only supports Texture2D arrays.
return texture_info == 8;
@@ -735,6 +925,7 @@ union Instruction {
BitField<36, 5, u64> index;
} cbuf36;
BitField<47, 1, u64> generates_cc;
BitField<61, 1, u64> is_b_imm;
BitField<60, 1, u64> is_b_gpr;
BitField<59, 1, u64> is_c_gpr;
@@ -859,6 +1050,7 @@ public:
ISET_IMM,
PSETP,
PSET,
CSETP,
XMAD_IMM,
XMAD_CR,
XMAD_RC,
@@ -1095,6 +1287,7 @@ private:
INST("0011011-0101----", Id::ISET_IMM, Type::IntegerSet, "ISET_IMM"),
INST("0101000010001---", Id::PSET, Type::PredicateSetRegister, "PSET"),
INST("0101000010010---", Id::PSETP, Type::PredicateSetPredicate, "PSETP"),
INST("010100001010----", Id::CSETP, Type::PredicateSetPredicate, "CSETP"),
INST("0011011-00------", Id::XMAD_IMM, Type::Xmad, "XMAD_IMM"),
INST("0100111---------", Id::XMAD_CR, Type::Xmad, "XMAD_CR"),
INST("010100010-------", Id::XMAD_RC, Type::Xmad, "XMAD_RC"),

View File

@@ -0,0 +1,103 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
namespace Tegra::Shader {
enum class OutputTopology : u32 {
PointList = 1,
LineStrip = 6,
TriangleStrip = 7,
};
// Documentation in:
// http://download.nvidia.com/open-gpu-doc/Shader-Program-Header/1/Shader-Program-Header.html#ImapTexture
struct Header {
union {
BitField<0, 5, u32> sph_type;
BitField<5, 5, u32> version;
BitField<10, 4, u32> shader_type;
BitField<14, 1, u32> mrt_enable;
BitField<15, 1, u32> kills_pixels;
BitField<16, 1, u32> does_global_store;
BitField<17, 4, u32> sass_version;
BitField<21, 5, u32> reserved;
BitField<26, 1, u32> does_load_or_store;
BitField<27, 1, u32> does_fp64;
BitField<28, 4, u32> stream_out_mask;
} common0;
union {
BitField<0, 24, u32> shader_local_memory_low_size;
BitField<24, 8, u32> per_patch_attribute_count;
} common1;
union {
BitField<0, 24, u32> shader_local_memory_high_size;
BitField<24, 8, u32> threads_per_input_primitive;
} common2;
union {
BitField<0, 24, u32> shader_local_memory_crs_size;
BitField<24, 4, OutputTopology> output_topology;
BitField<28, 4, u32> reserved;
} common3;
union {
BitField<0, 12, u32> max_output_vertices;
BitField<12, 8, u32> store_req_start; // NOTE: not used by geometry shaders.
BitField<24, 4, u32> reserved;
BitField<12, 8, u32> store_req_end; // NOTE: not used by geometry shaders.
} common4;
union {
struct {
INSERT_PADDING_BYTES(3); // ImapSystemValuesA
INSERT_PADDING_BYTES(1); // ImapSystemValuesB
INSERT_PADDING_BYTES(16); // ImapGenericVector[32]
INSERT_PADDING_BYTES(2); // ImapColor
INSERT_PADDING_BYTES(2); // ImapSystemValuesC
INSERT_PADDING_BYTES(5); // ImapFixedFncTexture[10]
INSERT_PADDING_BYTES(1); // ImapReserved
INSERT_PADDING_BYTES(3); // OmapSystemValuesA
INSERT_PADDING_BYTES(1); // OmapSystemValuesB
INSERT_PADDING_BYTES(16); // OmapGenericVector[32]
INSERT_PADDING_BYTES(2); // OmapColor
INSERT_PADDING_BYTES(2); // OmapSystemValuesC
INSERT_PADDING_BYTES(5); // OmapFixedFncTexture[10]
INSERT_PADDING_BYTES(1); // OmapReserved
} vtg;
struct {
INSERT_PADDING_BYTES(3); // ImapSystemValuesA
INSERT_PADDING_BYTES(1); // ImapSystemValuesB
INSERT_PADDING_BYTES(32); // ImapGenericVector[32]
INSERT_PADDING_BYTES(2); // ImapColor
INSERT_PADDING_BYTES(2); // ImapSystemValuesC
INSERT_PADDING_BYTES(10); // ImapFixedFncTexture[10]
INSERT_PADDING_BYTES(2); // ImapReserved
struct {
u32 target;
union {
BitField<0, 1, u32> sample_mask;
BitField<1, 1, u32> depth;
BitField<2, 30, u32> reserved;
};
} omap;
bool IsColorComponentOutputEnabled(u32 render_target, u32 component) const {
const u32 bit = render_target * 4 + component;
return omap.target & (1 << bit);
}
} ps;
};
};
static_assert(sizeof(Header) == 0x50, "Incorrect structure size");
} // namespace Tegra::Shader

View File

@@ -25,10 +25,10 @@ u32 FramebufferConfig::BytesPerPixel(PixelFormat format) {
GPU::GPU(VideoCore::RasterizerInterface& rasterizer) {
memory_manager = std::make_unique<Tegra::MemoryManager>();
maxwell_3d = std::make_unique<Engines::Maxwell3D>(rasterizer, *memory_manager);
fermi_2d = std::make_unique<Engines::Fermi2D>(*memory_manager);
fermi_2d = std::make_unique<Engines::Fermi2D>(rasterizer, *memory_manager);
maxwell_compute = std::make_unique<Engines::MaxwellCompute>();
maxwell_dma = std::make_unique<Engines::MaxwellDMA>(*memory_manager);
kepler_memory = std::make_unique<Engines::KeplerMemory>(*memory_manager);
maxwell_dma = std::make_unique<Engines::MaxwellDMA>(rasterizer, *memory_manager);
kepler_memory = std::make_unique<Engines::KeplerMemory>(rasterizer, *memory_manager);
}
GPU::~GPU() = default;

View File

@@ -17,6 +17,22 @@
template <class T>
class RasterizerCache : NonCopyable {
public:
/// Write any cached resources overlapping the region back to memory (if dirty)
void FlushRegion(Tegra::GPUVAddr addr, size_t size) {
if (size == 0)
return;
const ObjectInterval interval{addr, addr + size};
for (auto& pair : boost::make_iterator_range(object_cache.equal_range(interval))) {
for (auto& cached_object : pair.second) {
if (!cached_object)
continue;
cached_object->Flush();
}
}
}
/// Mark the specified region as being invalidated
void InvalidateRegion(VAddr addr, u64 size) {
if (size == 0)
@@ -71,6 +87,7 @@ protected:
void Unregister(const T& object) {
auto& rasterizer = Core::System::GetInstance().Renderer().Rasterizer();
rasterizer.UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), -1);
object->Flush();
object_cache.subtract({GetInterval(object), ObjectSet{object}});
}

View File

@@ -23,6 +23,9 @@ struct CachedBufferEntry final {
return size;
}
// We do not have to flush this cache as things in it are never modified by us.
void Flush() {}
VAddr addr;
std::size_t size;
GLintptr offset;

View File

@@ -322,6 +322,13 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep
// Used when just a single color attachment is enabled, e.g. for clearing a color buffer
Surface color_surface =
res_cache.GetColorBufferSurface(*single_color_target, preserve_contents);
if (color_surface) {
// Assume that a surface will be written to if it is used as a framebuffer, even if
// the shader doesn't actually write to it.
color_surface->MarkAsDirty();
}
glFramebufferTexture2D(
GL_DRAW_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(*single_color_target), GL_TEXTURE_2D,
@@ -332,6 +339,11 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep
std::array<GLenum, Maxwell::NumRenderTargets> buffers;
for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) {
Surface color_surface = res_cache.GetColorBufferSurface(index, preserve_contents);
if (color_surface) {
// Assume that a surface will be written to if it is used as a framebuffer, even
// if the shader doesn't actually write to it.
color_surface->MarkAsDirty();
}
buffers[index] = GL_COLOR_ATTACHMENT0 + regs.rt_control.GetMap(index);
glFramebufferTexture2D(
GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(index),
@@ -351,6 +363,10 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep
}
if (depth_surface) {
// Assume that a surface will be written to if it is used as a framebuffer, even if
// the shader doesn't actually write to it.
depth_surface->MarkAsDirty();
if (regs.stencil_enable) {
// Attach both depth and stencil
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
@@ -383,7 +399,7 @@ void RasterizerOpenGL::Clear() {
bool use_stencil{};
OpenGLState clear_state;
clear_state.draw.draw_framebuffer = state.draw.draw_framebuffer;
clear_state.draw.draw_framebuffer = framebuffer.handle;
clear_state.color_mask.red_enabled = regs.clear_buffers.R ? GL_TRUE : GL_FALSE;
clear_state.color_mask.green_enabled = regs.clear_buffers.G ? GL_TRUE : GL_FALSE;
clear_state.color_mask.blue_enabled = regs.clear_buffers.B ? GL_TRUE : GL_FALSE;
@@ -484,8 +500,13 @@ void RasterizerOpenGL::DrawArrays() {
GLintptr index_buffer_offset = 0;
if (is_indexed) {
MICROPROFILE_SCOPE(OpenGL_Index);
index_buffer_offset =
buffer_cache.UploadMemory(regs.index_array.StartAddress(), index_buffer_size);
// Adjust the index buffer offset so it points to the first desired index.
auto index_start = regs.index_array.StartAddress();
index_start += static_cast<size_t>(regs.index_array.first) *
static_cast<size_t>(regs.index_array.FormatSizeInBytes());
index_buffer_offset = buffer_cache.UploadMemory(index_start, index_buffer_size);
}
SetupShaders();
@@ -499,10 +520,6 @@ void RasterizerOpenGL::DrawArrays() {
if (is_indexed) {
const GLint base_vertex{static_cast<GLint>(regs.vb_element_base)};
// Adjust the index buffer offset so it points to the first desired index.
index_buffer_offset += static_cast<GLintptr>(regs.index_array.first) *
static_cast<GLintptr>(regs.index_array.FormatSizeInBytes());
if (gpu.state.current_instance > 0) {
glDrawElementsInstancedBaseVertexBaseInstance(
primitive_mode, regs.index_array.count,
@@ -537,9 +554,19 @@ void RasterizerOpenGL::DrawArrays() {
state.Apply();
}
void RasterizerOpenGL::FlushAll() {}
void RasterizerOpenGL::FlushAll() {
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
res_cache.FlushRegion(0, Kernel::VMManager::MAX_ADDRESS);
shader_cache.FlushRegion(0, Kernel::VMManager::MAX_ADDRESS);
buffer_cache.FlushRegion(0, Kernel::VMManager::MAX_ADDRESS);
}
void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size) {}
void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size) {
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
res_cache.FlushRegion(addr, size);
shader_cache.FlushRegion(addr, size);
buffer_cache.FlushRegion(addr, size);
}
void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) {
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
@@ -549,6 +576,7 @@ void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) {
}
void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) {
FlushRegion(addr, size);
InvalidateRegion(addr, size);
}

View File

@@ -265,20 +265,22 @@ void MortonCopy(u32 stride, u32 block_height, u32 height, u8* gl_buffer, std::si
constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / CHAR_BIT;
constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format);
// With the BCn formats (DXT and DXN), each 4x4 tile is swizzled instead of just individual
// pixel values.
const u32 tile_size{IsFormatBCn(format) ? 4U : 1U};
if (morton_to_gl) {
// With the BCn formats (DXT and DXN), each 4x4 tile is swizzled instead of just individual
// pixel values.
const u32 tile_size{IsFormatBCn(format) ? 4U : 1U};
const std::vector<u8> data = Tegra::Texture::UnswizzleTexture(
addr, tile_size, bytes_per_pixel, stride, height, block_height);
const std::size_t size_to_copy{std::min(gl_buffer_size, data.size())};
memcpy(gl_buffer, data.data(), size_to_copy);
} else {
// TODO(bunnei): Assumes the default rendering GOB size of 16 (128 lines). We should
// check the configuration for this and perform more generic un/swizzle
LOG_WARNING(Render_OpenGL, "need to use correct swizzle/GOB parameters!");
VideoCore::MortonCopyPixels128(stride, height, bytes_per_pixel, gl_bytes_per_pixel,
Memory::GetPointer(addr), gl_buffer, morton_to_gl);
std::vector<u8> data(height * stride * bytes_per_pixel);
Tegra::Texture::CopySwizzledData(stride / tile_size, height / tile_size, bytes_per_pixel,
bytes_per_pixel, data.data(), gl_buffer, false,
block_height);
const std::size_t size_to_copy{std::min(gl_buffer_size, data.size())};
memcpy(Memory::GetPointer(addr), data.data(), size_to_copy);
}
}
@@ -357,17 +359,16 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr),
MortonCopy<false, PixelFormat::RGBA16UI>,
MortonCopy<false, PixelFormat::R11FG11FB10F>,
MortonCopy<false, PixelFormat::RGBA32UI>,
// TODO(Subv): Swizzling DXT1/DXT23/DXT45/DXN1/DXN2/BC7U/BC6H_UF16/BC6H_SF16/ASTC_2D_4X4
// formats are not supported
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
MortonCopy<false, PixelFormat::DXT1>,
MortonCopy<false, PixelFormat::DXT23>,
MortonCopy<false, PixelFormat::DXT45>,
MortonCopy<false, PixelFormat::DXN1>,
MortonCopy<false, PixelFormat::DXN2UNORM>,
MortonCopy<false, PixelFormat::DXN2SNORM>,
MortonCopy<false, PixelFormat::BC7U>,
MortonCopy<false, PixelFormat::BC6H_UF16>,
MortonCopy<false, PixelFormat::BC6H_SF16>,
// TODO(Subv): Swizzling ASTC formats are not supported
nullptr,
MortonCopy<false, PixelFormat::G8R8U>,
MortonCopy<false, PixelFormat::G8R8S>,
@@ -503,7 +504,7 @@ CachedSurface::CachedSurface(const SurfaceParams& params)
glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) {
static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height, bool reverse) {
union S8Z24 {
BitField<0, 24, u32> z24;
BitField<24, 8, u32> s8;
@@ -516,16 +517,23 @@ static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) {
};
static_assert(sizeof(Z24S8) == 4, "Z24S8 is incorrect size");
S8Z24 input_pixel{};
Z24S8 output_pixel{};
S8Z24 s8z24_pixel{};
Z24S8 z24s8_pixel{};
constexpr auto bpp{CachedSurface::GetGLBytesPerPixel(PixelFormat::S8Z24)};
for (std::size_t y = 0; y < height; ++y) {
for (std::size_t x = 0; x < width; ++x) {
const std::size_t offset{bpp * (y * width + x)};
std::memcpy(&input_pixel, &data[offset], sizeof(S8Z24));
output_pixel.s8.Assign(input_pixel.s8);
output_pixel.z24.Assign(input_pixel.z24);
std::memcpy(&data[offset], &output_pixel, sizeof(Z24S8));
if (reverse) {
std::memcpy(&z24s8_pixel, &data[offset], sizeof(Z24S8));
s8z24_pixel.s8.Assign(z24s8_pixel.s8);
s8z24_pixel.z24.Assign(z24s8_pixel.z24);
std::memcpy(&data[offset], &s8z24_pixel, sizeof(S8Z24));
} else {
std::memcpy(&s8z24_pixel, &data[offset], sizeof(S8Z24));
z24s8_pixel.s8.Assign(s8z24_pixel.s8);
z24s8_pixel.z24.Assign(s8z24_pixel.z24);
std::memcpy(&data[offset], &z24s8_pixel, sizeof(Z24S8));
}
}
}
}
@@ -561,7 +569,7 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelForma
}
case PixelFormat::S8Z24:
// Convert the S8Z24 depth format to Z24S8, as OpenGL does not support S8Z24.
ConvertS8Z24ToZ24S8(data, width, height);
ConvertS8Z24ToZ24S8(data, width, height, false);
break;
case PixelFormat::G8R8U:
@@ -572,6 +580,30 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelForma
}
}
/**
* Helper function to perform software conversion (as needed) when flushing a buffer from OpenGL to
* Switch memory. This is for Maxwell pixel formats that cannot be represented as-is in OpenGL or
* with typical desktop GPUs.
*/
static void ConvertFormatAsNeeded_FlushGLBuffer(std::vector<u8>& data, PixelFormat pixel_format,
u32 width, u32 height) {
switch (pixel_format) {
case PixelFormat::G8R8U:
case PixelFormat::G8R8S:
case PixelFormat::ASTC_2D_4X4:
case PixelFormat::ASTC_2D_8X8: {
LOG_CRITICAL(HW_GPU, "Conversion of format {} after texture flushing is not implemented",
static_cast<u32>(pixel_format));
UNREACHABLE();
break;
}
case PixelFormat::S8Z24:
// Convert the Z24S8 depth format to S8Z24, as OpenGL does not support S8Z24.
ConvertS8Z24ToZ24S8(data, width, height, true);
break;
}
}
MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 64, 192));
void CachedSurface::LoadGLBuffer() {
ASSERT(params.type != SurfaceType::Fill);
@@ -609,11 +641,70 @@ void CachedSurface::LoadGLBuffer() {
}
ConvertFormatAsNeeded_LoadGLBuffer(gl_buffer, params.pixel_format, params.width, params.height);
dirty = false;
}
MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64));
void CachedSurface::FlushGLBuffer() {
ASSERT_MSG(false, "Unimplemented");
MICROPROFILE_SCOPE(OpenGL_SurfaceFlush);
const auto& rect{params.GetRect()};
// Load data from memory to the surface
const GLint x0 = static_cast<GLint>(rect.left);
const GLint y0 = static_cast<GLint>(rect.bottom);
const size_t buffer_offset =
static_cast<size_t>(static_cast<size_t>(y0) * params.width + static_cast<size_t>(x0)) *
GetGLBytesPerPixel(params.pixel_format);
const u32 bytes_per_pixel = GetGLBytesPerPixel(params.pixel_format);
const u32 copy_size = params.width * params.height * bytes_per_pixel;
gl_buffer.resize(static_cast<size_t>(params.depth) * copy_size);
const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type);
// Ensure no bad interactions with GL_UNPACK_ALIGNMENT
ASSERT(params.width * GetGLBytesPerPixel(params.pixel_format) % 4 == 0);
glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(params.width));
ASSERT(!tuple.compressed);
ASSERT(x0 == 0 && y0 == 0);
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
glGetTextureImage(texture.handle, 0, tuple.format, tuple.type, gl_buffer.size(),
gl_buffer.data());
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
ConvertFormatAsNeeded_FlushGLBuffer(gl_buffer, params.pixel_format, params.width,
params.height);
ASSERT(params.type != SurfaceType::Fill);
const u8* const texture_src_data = Memory::GetPointer(params.addr);
ASSERT(texture_src_data);
if (params.is_tiled) {
// TODO(bunnei): This only swizzles and copies a 2D texture - we do not yet know how to do
// this for 3D textures, etc.
switch (params.target) {
case SurfaceParams::SurfaceTarget::Texture2D:
// Pass impl. to the fallback code below
break;
default:
LOG_CRITICAL(HW_GPU, "Unimplemented tiled unload for target={}",
static_cast<u32>(params.target));
UNREACHABLE();
}
gl_to_morton_fns[static_cast<size_t>(params.pixel_format)](
params.width, params.block_height, params.height, &gl_buffer[buffer_offset], copy_size,
params.addr + buffer_offset);
} else {
Memory::WriteBlock(params.addr + buffer_offset, &gl_buffer[buffer_offset],
gl_buffer.size() - buffer_offset);
}
}
MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 64, 192));

View File

@@ -741,6 +741,19 @@ public:
return params.size_in_bytes;
}
void Flush() {
// There is no need to flush the surface if it hasn't been modified by us.
if (!dirty)
return;
FlushGLBuffer();
dirty = false;
}
void MarkAsDirty() {
dirty = true;
}
const OGLTexture& Texture() const {
return texture;
}
@@ -772,6 +785,7 @@ private:
std::vector<u8> gl_buffer;
SurfaceParams params;
GLenum gl_target;
bool dirty = false;
};
class RasterizerCacheOpenGL final : public RasterizerCache<Surface> {

View File

@@ -32,6 +32,9 @@ public:
return GLShader::MAX_PROGRAM_CODE_LENGTH * sizeof(u64);
}
// We do not have to flush this cache as things in it are never modified by us.
void Flush() {}
/// Gets the shader entries for the shader
const GLShader::ShaderEntries& GetShaderEntries() const {
return entries;

View File

@@ -12,6 +12,7 @@
#include "common/assert.h"
#include "common/common_types.h"
#include "video_core/engines/shader_bytecode.h"
#include "video_core/engines/shader_header.h"
#include "video_core/renderer_opengl/gl_rasterizer.h"
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
@@ -26,7 +27,7 @@ using Tegra::Shader::Sampler;
using Tegra::Shader::SubOp;
constexpr u32 PROGRAM_END = MAX_PROGRAM_CODE_LENGTH;
constexpr u32 PROGRAM_HEADER_SIZE = 0x50;
constexpr u32 PROGRAM_HEADER_SIZE = sizeof(Tegra::Shader::Header);
class DecompileFail : public std::runtime_error {
public:
@@ -235,6 +236,14 @@ private:
const std::string& suffix;
};
enum class InternalFlag : u64 {
ZeroFlag = 0,
CarryFlag = 1,
OverflowFlag = 2,
NaNFlag = 3,
Amount
};
/**
* Used to manage shader registers that are emulated with GLSL. This class keeps track of the state
* of all registers (e.g. whether they are currently being used as Floats or Integers), and
@@ -328,13 +337,19 @@ public:
void SetRegisterToInteger(const Register& reg, bool is_signed, u64 elem,
const std::string& value, u64 dest_num_components,
u64 value_num_components, bool is_saturated = false,
u64 dest_elem = 0, Register::Size size = Register::Size::Word) {
u64 dest_elem = 0, Register::Size size = Register::Size::Word,
bool sets_cc = false) {
ASSERT_MSG(!is_saturated, "Unimplemented");
const std::string func{is_signed ? "intBitsToFloat" : "uintBitsToFloat"};
SetRegister(reg, elem, func + '(' + ConvertIntegerSize(value, size) + ')',
dest_num_components, value_num_components, dest_elem);
if (sets_cc) {
const std::string zero_condition = "( " + ConvertIntegerSize(value, size) + " == 0 )";
SetInternalFlag(InternalFlag::ZeroFlag, zero_condition);
}
}
/**
@@ -351,6 +366,26 @@ public:
shader.AddLine(dest + " = " + src + ';');
}
std::string GetControlCode(const Tegra::Shader::ControlCode cc) const {
switch (cc) {
case Tegra::Shader::ControlCode::NEU:
return "!(" + GetInternalFlag(InternalFlag::ZeroFlag) + ')';
default:
LOG_CRITICAL(HW_GPU, "Unimplemented Control Code {}", static_cast<u32>(cc));
UNREACHABLE();
return "false";
}
}
std::string GetInternalFlag(const InternalFlag ii) const {
const u32 code = static_cast<u32>(ii);
return "internalFlag_" + std::to_string(code) + suffix;
}
void SetInternalFlag(const InternalFlag ii, const std::string& value) const {
shader.AddLine(GetInternalFlag(ii) + " = " + value + ';');
}
/**
* Writes code that does a output attribute assignment to register operation. Output attributes
* are stored as floats, so this may require conversion.
@@ -414,6 +449,12 @@ public:
}
declarations.AddNewLine();
for (u32 ii = 0; ii < static_cast<u64>(InternalFlag::Amount); ii++) {
const InternalFlag code = static_cast<InternalFlag>(ii);
declarations.AddLine("bool " + GetInternalFlag(code) + " = false;");
}
declarations.AddNewLine();
for (const auto element : declr_input_attribute) {
// TODO(bunnei): Use proper number of elements for these
u32 idx =
@@ -674,7 +715,7 @@ public:
u32 main_offset, Maxwell3D::Regs::ShaderStage stage, const std::string& suffix)
: subroutines(subroutines), program_code(program_code), main_offset(main_offset),
stage(stage), suffix(suffix) {
std::memcpy(&header, program_code.data(), sizeof(Tegra::Shader::Header));
Generate(suffix);
}
@@ -688,23 +729,6 @@ public:
}
private:
// Shader program header for a Fragment Shader.
struct FragmentHeader {
INSERT_PADDING_WORDS(5);
INSERT_PADDING_WORDS(13);
u32 enabled_color_outputs;
union {
BitField<0, 1, u32> writes_samplemask;
BitField<1, 1, u32> writes_depth;
};
bool IsColorComponentOutputEnabled(u32 render_target, u32 component) const {
const u32 bit = render_target * 4 + component;
return enabled_color_outputs & (1 << bit);
}
};
static_assert(sizeof(FragmentHeader) == PROGRAM_HEADER_SIZE, "FragmentHeader size is wrong");
/// Gets the Subroutine object corresponding to the specified address.
const Subroutine& GetSubroutine(u32 begin, u32 end) const {
const auto iter = subroutines.find(Subroutine{begin, end, suffix});
@@ -954,8 +978,6 @@ private:
// TEXS has two destination registers and a swizzle. The first two elements in the swizzle
// go into gpr0+0 and gpr0+1, and the rest goes into gpr28+0 and gpr28+1
ASSERT_MSG(instr.texs.nodep == 0, "TEXS nodep not implemented");
std::size_t written_components = 0;
for (u32 component = 0; component < 4; ++component) {
if (!instr.texs.IsComponentEnabled(component)) {
@@ -1010,10 +1032,8 @@ private:
/// Writes the output values from a fragment shader to the corresponding GLSL output variables.
void EmitFragmentOutputsWrite() {
ASSERT(stage == Maxwell3D::Regs::ShaderStage::Fragment);
FragmentHeader header;
std::memcpy(&header, program_code.data(), PROGRAM_HEADER_SIZE);
ASSERT_MSG(header.writes_samplemask == 0, "Samplemask write is unimplemented");
ASSERT_MSG(header.ps.omap.sample_mask == 0, "Samplemask write is unimplemented");
// Write the color outputs using the data in the shader registers, disabled
// rendertargets/components are skipped in the register assignment.
@@ -1022,7 +1042,7 @@ private:
++render_target) {
// TODO(Subv): Figure out how dual-source blending is configured in the Switch.
for (u32 component = 0; component < 4; ++component) {
if (header.IsColorComponentOutputEnabled(render_target, component)) {
if (header.ps.IsColorComponentOutputEnabled(render_target, component)) {
shader.AddLine(fmt::format("FragColor{}[{}] = {};", render_target, component,
regs.GetRegisterAsFloat(current_reg)));
++current_reg;
@@ -1030,7 +1050,7 @@ private:
}
}
if (header.writes_depth) {
if (header.ps.omap.depth) {
// The depth output is always 2 registers after the last color output, and current_reg
// already contains one past the last color register.
@@ -1640,7 +1660,8 @@ private:
}
regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1,
1, instr.alu.saturate_d, 0, instr.conversion.dest_size);
1, instr.alu.saturate_d, 0, instr.conversion.dest_size,
instr.generates_cc.Value() != 0);
break;
}
case OpCode::Id::I2F_R:
@@ -1779,8 +1800,8 @@ private:
Tegra::Shader::IpaMode input_mode{Tegra::Shader::IpaInterpMode::Perspective,
Tegra::Shader::IpaSampleMode::Default};
u32 next_element = instr.attribute.fmt20.element;
u32 next_index = static_cast<u32>(instr.attribute.fmt20.index.Value());
u64 next_element = instr.attribute.fmt20.element;
u64 next_index = static_cast<u64>(instr.attribute.fmt20.index.Value());
const auto LoadNextElement = [&](u32 reg_offset) {
regs.SetRegisterToInputAttibute(instr.gpr0.Value() + reg_offset, next_element,
@@ -1844,8 +1865,8 @@ private:
ASSERT_MSG((instr.attribute.fmt20.immediate.Value() % sizeof(u32)) == 0,
"Unaligned attribute loads are not supported");
u32 next_element = instr.attribute.fmt20.element;
u32 next_index = static_cast<u32>(instr.attribute.fmt20.index.Value());
u64 next_element = instr.attribute.fmt20.element;
u64 next_index = static_cast<u64>(instr.attribute.fmt20.index.Value());
const auto StoreNextElement = [&](u32 reg_offset) {
regs.SetOutputAttributeToRegister(static_cast<Attribute::Index>(next_index),
@@ -1871,6 +1892,13 @@ private:
Tegra::Shader::TextureType texture_type{instr.tex.texture_type};
std::string coord;
ASSERT_MSG(!instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
"NODEP is not implemented");
ASSERT_MSG(!instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI),
"AOFFI is not implemented");
ASSERT_MSG(!instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC),
"DC is not implemented");
switch (texture_type) {
case Tegra::Shader::TextureType::Texture1D: {
const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
@@ -1953,6 +1981,11 @@ private:
Tegra::Shader::TextureType texture_type{instr.texs.GetTextureType()};
bool is_array{instr.texs.IsArrayTexture()};
ASSERT_MSG(!instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
"NODEP is not implemented");
ASSERT_MSG(!instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC),
"DC is not implemented");
switch (texture_type) {
case Tegra::Shader::TextureType::Texture2D: {
if (is_array) {
@@ -1989,6 +2022,13 @@ private:
ASSERT(instr.tlds.IsArrayTexture() == false);
std::string coord;
ASSERT_MSG(!instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
"NODEP is not implemented");
ASSERT_MSG(!instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI),
"AOFFI is not implemented");
ASSERT_MSG(!instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::MZ),
"MZ is not implemented");
switch (instr.tlds.GetTextureType()) {
case Tegra::Shader::TextureType::Texture2D: {
if (instr.tlds.IsArrayTexture()) {
@@ -2017,6 +2057,17 @@ private:
ASSERT(instr.tld4.array == 0);
std::string coord;
ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
"NODEP is not implemented");
ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI),
"AOFFI is not implemented");
ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC),
"DC is not implemented");
ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV),
"NDV is not implemented");
ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::PTP),
"PTP is not implemented");
switch (instr.tld4.texture_type) {
case Tegra::Shader::TextureType::Texture2D: {
const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
@@ -2054,6 +2105,13 @@ private:
break;
}
case OpCode::Id::TLD4S: {
ASSERT_MSG(!instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
"NODEP is not implemented");
ASSERT_MSG(!instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI),
"AOFFI is not implemented");
ASSERT_MSG(!instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC),
"DC is not implemented");
const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
const std::string op_b = regs.GetRegisterAsFloat(instr.gpr20);
// TODO(Subv): Figure out how the sampler type is encoded in the TLD4S instruction.
@@ -2066,6 +2124,9 @@ private:
break;
}
case OpCode::Id::TXQ: {
ASSERT_MSG(!instr.txq.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
"NODEP is not implemented");
// TODO: the new commits on the texture refactor, change the way samplers work.
// Sadly, not all texture instructions specify the type of texture their sampler
// uses. This must be fixed at a later instance.
@@ -2086,6 +2147,11 @@ private:
break;
}
case OpCode::Id::TMML: {
ASSERT_MSG(!instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
"NODEP is not implemented");
ASSERT_MSG(!instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV),
"NDV is not implemented");
const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
const std::string op_b = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
const bool is_array = instr.tmml.array != 0;
@@ -2252,31 +2318,55 @@ private:
break;
}
case OpCode::Type::PredicateSetPredicate: {
const std::string op_a =
GetPredicateCondition(instr.psetp.pred12, instr.psetp.neg_pred12 != 0);
const std::string op_b =
GetPredicateCondition(instr.psetp.pred29, instr.psetp.neg_pred29 != 0);
switch (opcode->GetId()) {
case OpCode::Id::PSETP: {
const std::string op_a =
GetPredicateCondition(instr.psetp.pred12, instr.psetp.neg_pred12 != 0);
const std::string op_b =
GetPredicateCondition(instr.psetp.pred29, instr.psetp.neg_pred29 != 0);
// We can't use the constant predicate as destination.
ASSERT(instr.psetp.pred3 != static_cast<u64>(Pred::UnusedIndex));
// We can't use the constant predicate as destination.
ASSERT(instr.psetp.pred3 != static_cast<u64>(Pred::UnusedIndex));
const std::string second_pred =
GetPredicateCondition(instr.psetp.pred39, instr.psetp.neg_pred39 != 0);
const std::string second_pred =
GetPredicateCondition(instr.psetp.pred39, instr.psetp.neg_pred39 != 0);
const std::string combiner = GetPredicateCombiner(instr.psetp.op);
const std::string combiner = GetPredicateCombiner(instr.psetp.op);
const std::string predicate =
'(' + op_a + ") " + GetPredicateCombiner(instr.psetp.cond) + " (" + op_b + ')';
const std::string predicate =
'(' + op_a + ") " + GetPredicateCombiner(instr.psetp.cond) + " (" + op_b + ')';
// Set the primary predicate to the result of Predicate OP SecondPredicate
SetPredicate(instr.psetp.pred3,
'(' + predicate + ") " + combiner + " (" + second_pred + ')');
// Set the primary predicate to the result of Predicate OP SecondPredicate
SetPredicate(instr.psetp.pred3,
'(' + predicate + ") " + combiner + " (" + second_pred + ')');
if (instr.psetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) {
// Set the secondary predicate to the result of !Predicate OP SecondPredicate,
// if enabled
SetPredicate(instr.psetp.pred0,
"!(" + predicate + ") " + combiner + " (" + second_pred + ')');
if (instr.psetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) {
// Set the secondary predicate to the result of !Predicate OP SecondPredicate,
// if enabled
SetPredicate(instr.psetp.pred0,
"!(" + predicate + ") " + combiner + " (" + second_pred + ')');
}
break;
}
case OpCode::Id::CSETP: {
const std::string pred =
GetPredicateCondition(instr.csetp.pred39, instr.csetp.neg_pred39 != 0);
const std::string combiner = GetPredicateCombiner(instr.csetp.op);
const std::string controlCode = regs.GetControlCode(instr.csetp.cc);
if (instr.csetp.pred3 != static_cast<u64>(Pred::UnusedIndex)) {
SetPredicate(instr.csetp.pred3,
'(' + controlCode + ") " + combiner + " (" + pred + ')');
}
if (instr.csetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) {
SetPredicate(instr.csetp.pred0,
"!(" + controlCode + ") " + combiner + " (" + pred + ')');
}
break;
}
default: {
LOG_CRITICAL(HW_GPU, "Unhandled predicate instruction: {}", opcode->GetName());
UNREACHABLE();
}
}
break;
}
@@ -2666,6 +2756,7 @@ private:
private:
const std::set<Subroutine>& subroutines;
const ProgramCode& program_code;
Tegra::Shader::Header header;
const u32 main_offset;
Maxwell3D::Regs::ShaderStage stage;
const std::string& suffix;

View File

@@ -74,7 +74,7 @@ std::tuple<u8*, GLintptr, bool> OGLStreamBuffer::Map(GLsizeiptr size, GLintptr a
}
}
if (invalidate | !persistent) {
if (invalidate || !persistent) {
GLbitfield flags = GL_MAP_WRITE_BIT | (persistent ? GL_MAP_PERSISTENT_BIT : 0) |
(coherent ? GL_MAP_COHERENT_BIT : GL_MAP_FLUSH_EXPLICIT_BIT) |
(invalidate ? GL_MAP_INVALIDATE_BUFFER_BIT : GL_MAP_UNSYNCHRONIZED_BIT);

View File

@@ -88,6 +88,38 @@ void FastSwizzleData(u32 width, u32 height, u32 bytes_per_pixel, u8* swizzled_da
}
}
void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width,
u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data,
u32 block_height) {
for (u32 line = 0; line < subrect_height; ++line) {
for (u32 x = 0; x < subrect_width; ++x) {
u32 swizzled_offset =
GetSwizzleOffset(x, line, swizzled_width, bytes_per_pixel, block_height);
const VAddr source_line = unswizzled_data + line * source_pitch + x * bytes_per_pixel;
const VAddr dest_addr = swizzled_data + swizzled_offset;
Memory::CopyBlock(dest_addr, source_line, bytes_per_pixel);
}
}
}
void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32 swizzled_width,
u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data,
u32 block_height, u32 offset_x, u32 offset_y) {
for (u32 line = 0; line < subrect_height; ++line) {
for (u32 x = 0; x < subrect_width; ++x) {
u32 swizzled_offset = GetSwizzleOffset(offset_x, line + offset_y, swizzled_width,
bytes_per_pixel, block_height);
const VAddr dest_line = unswizzled_data + line * dest_pitch + x;
const VAddr source_addr = swizzled_data + swizzled_offset;
Memory::CopyBlock(dest_line, source_addr, bytes_per_pixel);
}
}
}
u32 BytesPerPixel(TextureFormat format) {
switch (format) {
case TextureFormat::DXT1:

View File

@@ -26,6 +26,16 @@ std::vector<u8> UnswizzleDepthTexture(VAddr address, DepthFormat format, u32 wid
void CopySwizzledData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel,
u8* swizzled_data, u8* unswizzled_data, bool unswizzle, u32 block_height);
/// Copies an untiled subrectangle into a tiled surface.
void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width,
u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data,
u32 block_height);
/// Copies a tiled subrectangle into a linear surface.
void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32 swizzled_width,
u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data,
u32 block_height, u32 offset_x, u32 offset_y);
/**
* Decodes an unswizzled texture into a A8R8G8B8 texture.
*/

View File

@@ -15,6 +15,7 @@
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/timer.h"
#include "core/hle/kernel/wait_object.h"
#include "core/memory.h"
WaitTreeItem::WaitTreeItem() = default;
WaitTreeItem::~WaitTreeItem() = default;

View File

@@ -89,15 +89,7 @@ bool GameList::SearchField::KeyReleaseEater::eventFilter(QObject* obj, QEvent* e
}
void GameList::SearchField::setFilterResult(int visible, int total) {
QString result_of_text = tr("of");
QString result_text;
if (total == 1) {
result_text = tr("result");
} else {
result_text = tr("results");
}
label_filter_result->setText(
QString("%1 %2 %3 %4").arg(visible).arg(result_of_text).arg(total).arg(result_text));
label_filter_result->setText(tr("%1 of %n result(s)", "", total).arg(visible));
}
void GameList::SearchField::clear() {

View File

@@ -125,6 +125,10 @@ void Config::ReadValues() {
// System
Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false);
Settings::values.username = sdl2_config->Get("System", "username", "yuzu");
if (Settings::values.username.empty()) {
Settings::values.username = "yuzu";
}
// Miscellaneous
Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace");

View File

@@ -176,7 +176,7 @@ use_docked_mode =
# Sets the account username, max length is 32 characters
# yuzu (default)
username =
username = yuzu
# Sets the systems language index
# 0: Japanese, 1: English (default), 2: French, 3: German, 4: Italian, 5: Spanish, 6: Chinese,

View File

@@ -20,8 +20,10 @@
#include "common/string_util.h"
#include "common/telemetry.h"
#include "core/core.h"
#include "core/crypto/key_manager.h"
#include "core/file_sys/vfs_real.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/loader.h"
#include "core/settings.h"
#include "core/telemetry_session.h"
@@ -29,7 +31,6 @@
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
#include <getopt.h>
#include "core/crypto/key_manager.h"
#ifndef _MSC_VER
#include <unistd.h>
#endif
@@ -169,6 +170,7 @@ int main(int argc, char** argv) {
Core::System& system{Core::System::GetInstance()};
system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());
Service::FileSystem::CreateFactories(system.GetFilesystem());
SCOPE_EXIT({ system.Shutdown(); });