Compare commits

...
54 Commits
Author SHA1 Message Date
Luke Usher 1e6e9fd36e ares v141 2024-11-04 10:19:26 +00:00
Fernap 8111bf3163 Fix for SA-1 side DMA to BWRAM banks 0x41+ (#1687)
DDA was incorrectly capped at 16 bits instead of 24, making DMAs to
BWRAM banks 0x41-43 be sent to the corresponding spot in 0x40 instead.
See [the corresponding bug in
bsnes](https://github.com/bsnes-emu/bsnes/issues/309) for some more
context.
2024-11-02 14:26:09 +00:00
jcm c287c1b225 build: macOS back-compatibility fixes (#1682)
While checking out minimum requirements for the new build system, found
a couple small issues worth fixing:

* librashader was not operable on systems pre-macOS 12 with a linker
issue; it can be resolved by setting a minimum deployment target; here
we do that by setting an environment variable on CI.
* While investigating this, I found that librashader's minimum required
macOS seems to be 10.15; this is because `glslang` seems to require
macOS 10.15.
* It wasn't possible to build the Metal driver because of the older SDK
lacking some definitions; however, we can fall back to the
`__MAC_OS_X_VERSION_MAX_ALLOWED` compile definition which we do here.
* Not sure if building on these older systems should really be
considered "supported", but since the fix here was simple enough it
seems fine to throw it in.

paraLLEl-RDP and MoltenVK seem to have [a lot of
issues](https://github.com/user-attachments/assets/088879a9-9153-4b05-a207-212cfac7f831)
on my older system's GeForce 650M. I was able to compile debug MoltenVK
locally, but Xcode GPU capture just hung, so I was unable to figure out
what was going on. Either a driver issue or a MoltenVK issue seems like
the probable culprit. In any case, performance wasn't really good enough
to make me want to investigate further. Other cores seem to work well.

Tested [on my fork's
CI](https://github.com/jcm93/ares/actions/runs/11554703214) to make sure
everything still works.
2024-10-28 15:30:08 +00:00
Giovanni Bajo 863593ed7f n64: remove call to epilogue function from the jit (#1663) 2024-10-28 11:44:11 +00:00
Giovanni Bajo c20d405006 n64: add unusual warning when using CACHE on uncached addresses (#1678)
The CACHE opcode does not work correctly when the specified virtual
address is in an uncached segment. The exact hardware behavior has not
been reversed yet (we know for instance that when doing cache
invalidation, some other cacheline is invalidated instead, but we do not
know the relation yet).

Nonetheless, the behavior is really obscure and all usages should be
considered bugs. Add a debug unusual warning for now (mainly to help
developers, as this is a known instance where Ares doesn't yet follow
hardware behavior).
2024-10-28 11:43:17 +00:00
png183 e1e1bedb2d gba: selectively synchronize CPU and APU, and add option to disable pixel accuracy (#1679)
Similarly to how the S-CPU and S-SMP are handled in the SFC core, it's
possible to run the GBA CPU and APU more asychronously as long as
synchronization is performed on FIFO or APU I/O accesses. From my
testing on Linux, this performs comparably to the performance profile
but without the accuracy downsides, so the performance/accuracy profiles
have been removed (the more accurate behaviour is now always used).

This PR also adds an option to disable pixel accuracy in the GBA core in
exchange for ~10-15% performance benefits, and makes some small PPU
timing improvements that were not feasible to implement in the
performance profile.
2024-10-28 11:43:07 +00:00
Chase Meadors 9f060c769b Allow arbitrary and possibly unaligned writes to RDRAM through gdb (#1639)
We want to allow arbitrary writes through the GDB server to the RDRAM
area. This was done in #1620, but that version has a subtle issue where
the server will still refuse to write or change writes that happen to be
of size `Byte|Half|Word|Dual` if the addressed is not aligned to that
size.
This PR fixes the issue by checking if the write is to RDRAM first,
allowing arbitrary and possibly unaligned writes. Otherwise, the write
is handled through the normal machinery where these issues may be
semantically important.
2024-10-27 15:14:30 +00:00
remutro e7f9af4bbd [N64] allow for pak swapping on controller port 1 (#1672)
This pull request allows for all pak swapping on controller port 1 only.
It also enables support for the transfer pak for the 64DD which was
previously missing.

It is being added for port 1 only now because there is additional work
to manage all the different saves, and gameboy games/saves with the
transfer pak which is a larger effort, and Luke is currently looking
into modifying settings in a more complete manner. So other ports can be
added later. For most people, being able to swap for the first
controller is likely good enough.

This pull request doesn't change any of the current initialization logic
regarding default pak selection that already existed.
2024-10-26 07:53:52 +01:00
jcm 65c34c2ea4 nall: Namespace noinline macro (#1674)
This is a change spun off from continuing build system work.

There, we've found that precompiling the `ares/ares.hpp` header can
speed up compile times for the emulator cores by 30-35%. Doing so,
however, leads to a few collisions:

* Several files in parallel-rdp use the `Hash` type, which variously
resolves to a `Hash` in its own namespace as well as nall's namespace.
* Also in the N64 core, nall's `noinline` macro conflicts with similar
definitions present in `xxhash`.

For parallel-rdp, we can simply skip precompiled headers for the
conflicting files; parallel-rdp does not use anything in the
`ares/ares.hpp` header, so including it there is unnecessary anyway.

This change resolves the second set of collisions by putting nall's
noinline preprocessor macro into its own namespace.

There are many macros remaining that are not namespaced. I did not
proceed in namespacing them for this PR, because there are a lot of
them, and it would be a fairly significant code style change that
probably merits further discussion (not to mention, it will be a decent
amount of work). However, in this instance, there is (or will be) a
material benefit that probably trumps the minor style issue.
2024-10-26 07:53:36 +01:00
png183 9babe6a02b gba: OBJ timing improvements (#1676)
Implements a couple improvements to OBJ-related timings:

- OBJs are now rendered one scanline in advance, as on hardware
- OBJ palette RAM accesses are now performed in `PPU::Objects::run`
rather than `PPU::Objects::scanline`, bringing OBJ PRAM access timings
closer to matching hardware
2024-10-26 07:53:20 +01:00
png183 7e5292cd16 gba: horizontal mosaic fixes (#1669)
Addresses an off-by-one error with BG mosaic, and fixes an oversight in
how OBJ mosaic was handled at x=0.
2024-10-22 11:32:42 +01:00
remutro a8274cd127 [N64] Add support for 40 Winks (Aftermarket) and support for additional supported regions (#1666)
40 Winks was released aftermarket on physical media, and support has
been added for this (does NOT include prototypes). The US version was
tested, but was unable to test the EU release (game Id and pak options
should be the same and should theoretically work).

Additionally, looks like ares was missing some region descriptions, so
these have been added as well based on info located at:
https://n64brew.dev/wiki/ROM_Header#Advanced_Homebrew_ROM_Header
2024-10-18 11:11:07 +01:00
Giovanni Bajo d99b0a3725 n64: use different GDB signals for different exceptions (#1667)
When debugging homebrew with gdb, it might be annoying if gdb stops at
exceptions that are correctly handled by the running application. This
is a bit hard to know so, besides providing some (debatable) default, we
need a way to selectively ignore specific exceptions.

This commit generates different GDB signals for different families of
exceptions. The mapping between MIPS exceptions and UNIX signals is
completely arbitrary; it tries to "sort of" match the meaning of the
exception where possible, and otherwise just use a random signal.

The GDB client can configure which signals should be logged, which
should cause the application to stop, and which should be ignored, using
the "handle" command.

By default, we want to ignore exceptions generated by interrupts (as
those are normally handled by the application) and those handled by the
operating system (like the COP1 exception which both libultra and
libdragon use to speed up context switching). To do so, we assign to
them signals that are ignored by default by gdb (SIGWINCH and SIGURG
respectively).
2024-10-16 06:32:31 +01:00
Luke Usher e43f3f75f7 add ymfm to update-subtrees script 2024-10-08 11:48:21 +01:00
Luke Usher f894a85253 Merge commit '09fbcc4e65efe68eefc6d4f502fd92cb9ee310b4' 2024-10-08 11:33:31 +01:00
Luke Usher 09fbcc4e65 Squashed 'thirdparty/ymfm/' changes from d641a80631..d334ab1a95
d334ab1a95 Fix left-shift signed overflows (UB) (#62)
434e793dc4 Assert that only valid timer numbers are passed to engine_timer_expired().
16e1d4281a Use snprintf instead of sprintf to fix #59. Use std::array for operators to remove the need for array_size.
4e654a3e8f Add virtual destructors to all base abstract classes, to prevent memory leaks (#55)
1a5cc66cb6 Keyon all ops on CSM mode (#56)

git-subtree-dir: thirdparty/ymfm
git-subtree-split: d334ab1a957351ce2a0aa2777b8c704664be3189
2024-10-08 11:33:31 +01:00
Luke Usher 73d90e6e7c Merge commit '38dd6defa99bb03aa5e4dc2fc1f492b2e85aa10c' 2024-10-08 11:33:15 +01:00
Luke Usher 38dd6defa9 Squashed 'thirdparty/slang-shaders/' changes from ec1b855638..263a54c414
263a54c414 Update crt-ntsc-sharp presets (#642)

git-subtree-dir: thirdparty/slang-shaders
git-subtree-split: 263a54c414681b45fd9428143a8320f4024a99ab
2024-10-08 11:33:15 +01:00
Luke Usher 030488aba7 Merge commit 'b2175473f0476248724ba73bb80b71a1c669797d' 2024-10-08 11:33:01 +01:00
Luke Usher b2175473f0 Squashed 'thirdparty/librashader/' changes from 4abd48eb24..cbdbdafecd
cbdbdafecd chore: Release
ffcb8ba1d1 doc: Update BROKEN_SHADERS.md
941275199b build: unpin cc
525c311844 cli: allow raw-id for spirv output
2fe66d958f reflect: put vars to end with running link pass to fix naga ordering
ed8bf637a9 cli: size_of not in prelude
7555bb9024 build: make librashader-reflect features-table more consistent
562e1e5f18 doc(msrv): bump msrv to 1.78
13b44e1639 cargo: revert lockfile version
240aae1bcf chore: Release
95a489ee12 cli(render): allow specifying output dimensions

git-subtree-dir: thirdparty/librashader
git-subtree-split: cbdbdafecdfd6113ef1cdc15e0a4140b46420f0b
2024-10-08 11:33:01 +01:00
Luke Usher bc2a54800f fix sljit after update 2024-10-05 12:56:24 +01:00
Stefan Schlosser 6e182d8542 thirdparty/librashader: activate runtimes in librashader_ld.h 2024-10-05 12:37:24 +01:00
jcm 38f64fa70c ruby/metal: Update to librashader ABI 2 (#1)
We elect to just remove all usage of libra_viewport_t for Metal, since the current rendering path conforms better to recommended Metal patterns than passing the drawable texture directly to librashader would.
2024-10-05 12:34:08 +01:00
Stefan Schlosser 7eb1b28671 ruby/opengl: adapt to librashader ABI 2
The libra_gl_init_context function has been removed. The OpenGL loader used to
create the filter chain, previously passed to libra_gl_init_context, is now
passed as a parameter to the libra_gl_filter_chain_create function.

The libra_output_framebuffer_gl_t and libra_source_image_gl_t structs have been
replaced with libra_image_gl_t.

The position of the viewport parameter in libra_gl_filter_chain_frame has moved
to after the output parameter, and its type has changed from libra_viewport_t to
libra_viewport_t *.

In order to retain the behaviour in ABI 1, output holds the width and height of
the previously used viewport and the viewport itself is removed and replaced
with NULL in the call to _libra.gl_filter_chain_frame.
2024-10-05 12:34:01 +01:00
Stefan Schlosser 42c16831b7 thirdparty/librashader: update SONAME of librashader.so in build-librashader.sh 2024-10-05 12:33:31 +01:00
Luke Usher 5982fa8ae3 Squashed 'thirdparty/slang-shaders/' changes from 693c632bdd..ec1b855638
ec1b855638 Add RF-nes presets to crt-royale-fast folder (#640)
a8e35920c5 Update crt-geom.slang (#639)
901441a496 Update crt-royale-fast and several presets (#638)
33876b3578 ensure testPattern() always has a return condition
5ab3c76109 update patchy-ntsc shaders and presets (#637)
edd2dd3f77 fix some errors identified by librashader scan (#636)
630fe655fe fix mudlord-pal-vhs
49c77cab02 remove another conflicting pass from preset
e1f7c6cc0e remove broken/unused pass from preset
0e29397870 add small, colored border shader (#635)
4ef678cab0 fix bad path
bd0ea94fc8 fix/update glow-trails shaders and presets (#634)
0dd9204791 Update crt-royale-fast and its presets (#633)
24f9468e2e Add crt-royale-pvm-shmup preset (#632)
0a305b879e Update crt-royale-fast and add new presets (#631)
1b2ad364b7 Add edgeNpixels shader (#630)
b9e012f25c Patchy NTSC - Bunch of last minute improvements (#629)
4cf85d3147 New NTSC shader by PlainOldPants (#627)
ef68606c0e Add edge1pixel shader (#628)
0cededfa7f ntsc-simple update to glsl version (#626)
811bb31cdb port tiny_ntsc (#625)
ef702029d8 Fix color clipping in crt-hyllian (#624)
b6b55c3995 Update crt-hyllian (#623)
26e9882eb2 Update crt-hyllian (#622)
b3d795acb9 Add crt-royale-ntsc-composite-fast preset (#620)
111fcedc3b Add crt-royale-fast shaders (#619)
b327343b77 Fix a 7-year old bug in crt-royale-bloom-approx.h (#618)
679a3321c1 Update crt-geom-mini.slang (#617)
f65a4bce69 Sync to koko-aio 1.9.30 (#616)
7d6751a07c update crt-guest to latest (#614)
05a41341be Fix input TF bug & large refactoring; Add box filter AA (#613)
5e702cfdc6 Added flat trinitron TV filters (#611)
1c6f2cd834 Add zfast_crt_geo shader (#612)
46b96cbd39 update crt-geom-mini to glsl version (#610)
959de2bb2e Reverted texture optimizations as they are breaking rendering. (#609)
23f90e3b27 crt-geom-mini update from glsl, crt-pocket fix clamp error (#607)
3796d44482 Update geom.slang (#606)
89dfd9bbd0 Add Geom shader (#605)

git-subtree-dir: thirdparty/slang-shaders
git-subtree-split: ec1b8556388317258376467c1561ecb91a06a466
2024-10-05 12:32:59 +01:00
Luke Usher 5f435345f1 Merge commit '5982fa8ae35f801150b06b8db273b628ea0f7cd9' 2024-10-05 12:32:59 +01:00
Luke Usher 8343f8136c Merge commit '4dcba4224027a101e7d4e21d23ada1f08e7baf2c' 2024-10-05 12:32:27 +01:00
Luke Usher 4dcba42240 Squashed 'thirdparty/librashader/' changes from fff80df5a0..4abd48eb24
4abd48eb24 capi(presets): support `d3d9_hlsl` VID-DRV
0007bd6d98 doc: update preset_ctx_set_runtime doc
72c72dafa0 capi(d3d12): remove unintentional IMAGE_TYPE infix for LIBRA_D3D12_IMAGE_TYPE
0fde712f3c doc: update librashader::reflect docs
2323b18710 capi(ld): add `LIBRA_RUNTIME_D3D9` define commented out
5978f95f76 reflect: make `ShaderPassArtifact` simpler as `PassResource` already includes the source data
3ee5e66c0d presets: make naming more consistent
f189468f6d ci: don't build cli for windows 7
fc47fb4c5a doc: typo
fc7739d9ab ci: update github actions
4a8a9ee444 chore: Release
756cbe63d9 cli: fix cli Cargo.toml
796b03b42b test: rework tests to work with ShaderPresetPack
742a1c8658 dep: update glslang
7c03a7e3fe doc(pack): reword doc on `ShaderPresetPack`
4ba6effc2f rt(mtl): fix test
32c99d9f4a rt: implement filter chain loading in terms of pack
75b70cc0e6 cli: add `pack` command to create a preset pack
828464c351 rt(image): helper to load image from shaderpack `TextureBuffer`
c19593e289 pack: implement shader pack library to preload resources from disk
1bbbd784d8 preset: make ShaderPassConfig and TextureConfig type aliases to PathReference
4273a833e8 pack: add librashader-pack
f14f45b3b1 rt: separate out meta information for textures
859d16e64e rt: separate out meta information to its own struct
b395b94a40 doc(abi2): add migration details for `libra_source_image_d3d12_t` layout
90a9ee754d doc: fix migration api typo
34b50059ca doc(d3d12,vk): clear up language for barriers
7a13136f9a capi(d3d12): allow d3d12 to optionally use a resource handle with chain-managed descriptors
0cb3880d7f rt(d3d12): allow construction of D3D12InputImage without a descriptor handle
c526b7043a capi(d3d12): change the layout so that descriptor is first in libra_source_image_d3d12_t
33d95ac399 rt(d3d12): allow creating input view without a CPU handle
40691cc406 rt(d3d12): allow creating output view from a resource ref
3c20c83bc9 rt(d3d12): use InterfaceRef for D3D12InputImage to avoid refcount for input image
b123f63a6e doc(reflect): fix link
4285ad2bd1 rt(d3d12): allow a pipeline to be available for multiple formats without recompilation
c57e502b78 rt(d3d12): make images with OwnedFramebuffer provenance use ManuallyDrop
7edff0ae35 rt(d3d12): allow manually specifying the strategy to get a manuallydrop for luts
dedde05c83 rt(d3d12): move update subresources to lut
7d483f2e08 rt(d3d9): take viewport by reference to avoid AddRef/Release
7b7fd99b92 rt(d3d11): take viewport by reference to avoid AddRef/Release
28931ae50a rt(d3d12): update d3d12-descriptor-heap
2fe7702957 rt(d3d11): avoid QueryInterface in GetSize
a5c8fcf4f8 doc: missing period
629070ea2f ci: build cli
da53c3df59 cli(render): make frame inclusive to ensure correct feedback and history behaviour
84e78f4e48 cli(render): add ability to specify frame options
91f8089277 cli(render): add ability to specify params and passes enabled
3993f57271 doc(cli): document CLI
55ff7a93f2 cli: rename librashader-test -> librashader-cli
227757a221 cli: parse transpile versions
a72a593029 reflect: allow indexed and non-indexed semantics to be serialized and deserialized as strings
1537c1bcd7 cli: ensure shaders are validated before compile
5573f13227 test: add CLI with multiple functions
bac09ad2a3 reflect: allow validation of shaders without reflecting against semantics
31ece05246 reflect: allow more reflection objects to be serializable
5ede061975 preprocess: allow shadersource to be serializable with serde
1676150858 presets: allow presets to be serializable with serde
eeda0d02d0 test: make RenderTest trait object safe
f7a938a00d deps: update dependencies
6394b28d40 deps: update dependencies
443fa20d22 test: clean up imports and features
5c726efe21 test(d3d12): Add D3D12 render test
d33c2a84b2 test(d3d9): Add Direct3D 9 render test
5a35a2bd1e test(render): reenable cache
41034330a7 test(mtl): Add Metal render test
a7836923d7 test: split test harnesses into features
20039b9347 test: abstract test framework
799d409ddb test(gl): Add OpenGL render test
79513a301e test(vk): Add Vulkan render test
6de2de8d12 test(wgpu): Add WGPU render test
2904a2ac10 test(d3d11): Add D3D11 triangle test to image
341fbceb82 rt(d3d12): derive clone on D3D12InputImage
c54747d398 deps: update dependencies
3888b56c83 rt(d3d9): accept input image by reference
b0df631651 rt(d3d9): load LUTS as BGRA8
dbfa822f7c rt(d3d9): disable alpha blending
e7fe96520e rt(gl): ensure framebuffers are bound
97ad0d64bf rt(gl): make FilterChain::load_from argument order consistent with other runtimes
feaebc5f44 fmt(gl): cargo fmt
ef35e2a620 presets: rename to_hashmap -> into_hashmap
987e967269 doc(capi): add error doc for infallible error
c3469520f9 examples(d3d11): fix d3d11 examples
45b98a2bdd rt(gl): use array_init instead of manually doing it with MaybeUninit
2ee9eca854 ci: ignore .DS_Store
59937aced5 fmt: cargo fmt
e4eed34c10 capi: const qualify chain in `_filter_chain_get_param` and `_filter_chain_get_active_pass_count`"
7abd679bd7 doc: add migration docs
41353ac9c4 rt(gl): remove need for explicit external FBO object
4d790e7a7b doc: update ABI versions in version.rs and readme
aeb0a16cfb rt(vk): allow specifying the queue to use
16108838b1 rt(d3d11): remove unneeded transmutes
7cfbca7755 rt(wgpu): remove unnecessary output move
8467e5cd97 rt(mtl): remove unnecessary output move
1e33b4cc03 capi: make viewport optional, defaulting to a viewport that is the entire size of the render target
eb53699590 capi(include): update headers
0cbf8024a7 build: update soname
7e354a56a1 capi: upgrade cbindgen to fix syn bug
0a87bcc657 rt: respect viewport extents when rendering
f0a7970b44 rt: add viewport extent separate from output texture dimensions
e7e6ed8fb8 capi: remove _internal_alloc from libra_preset_param_list_t
61fdaeea14 capi(gl): update to take opengl context in constructor
2552d4321b capi(gl): set output in libra_output_image_gl_t, not viewport
eaf939c861 capi(vk): set output in libra_output_image_vk_t, not viewport
14abb0362b capi(d3d12): set output in libra_output_image_d3d12_t, not viewport
e064f8d0be capi(d3d12): remove ignored fields from libra_source_image_d3d12_t
f18c22a95a capi(d3d11): take ManuallyDrop<ID3D11ShaderResourceView> directly in libra_d3d11_filter_chain_t
763c05755d rt: mark error types as non_exhaustive
e6d23f1d8f doc: update opengl example
2f9df143cf deps: consolidate and update dependencies
1bdadaa449 rt(gl): port to OpenGL runtime to glow
77b957bf5e reflect: stop adding null bytes to uniform names
1e0727f89d chore: Release
60d2c3f177 chore: Release
72a98272f3 doc(capi): document all C API members
7d0b135710 rt(mtl): remove unused 'pass lifetime
4ba5aefafc capi: don't export wrap_ok macro
6edfaed91f test: update slang-shaders
cd877d7883 build: respect CARGO env in build script
d56d92ea0f test: update slang-shaders
f49e3b58d8 doc(capi): fix rotation integer docs
1f4f1b6c12 capi: Fix Metal error message, function header
57f2dabf0c reflect: fix `CurrentSubFrame` being parsed as `TotalSubFrames`
81840a9e9c reflect: insert alias semantics for shader name through pragma
87e0405675 presets: properly trim shader name if given through pragma
e0c97f77b4 build: nicer build script messages
6e60dd6fa0 test: update slang-shaders
d55d0e4839 rt(mtl): use f32::MAX for lodMaxClamp
6cdd9247de presets: trim aliases and shader parameter names
cbe6510f76 preprocess: the step argument is optional according to slang-shaders spec
d6f8950bdc rt: only draw last pass twice if the last pass is needed as feedback
0fe5bbd57b rt: do reflection to see if the final pass is needed as feedback
eace595ebb chore: Release
66561ad2ed doc: fix header image
05d48841ad doc: update docs for stable build instructions
927740433c build: remove unneeded image dependency
316e92dc09 build: allow building stable with --stable flag
e930f90a9c capi: allow building on stable by gating #[doc] comments
2f988d5b1d rt: add stable feature to build ShaderPassArtifacts via Box<dyn CompileReflectShader>
e0a5c90103 reflect: implement stable FromCompilation
4cc3c875bf reflect: allow compilation of boxed trait objects
805854b94b reflect: simplify FromCompilation output signature
c291d9d85f capi: remove dependency on `try_blocks`
8b2ff57ee8 reflect: replace `let_chains` with `Option::filter`
3d9139b4e0 rt(d3d11): replace `let_chains` with stable alternatives
b432a1e02d rt(d3d12): replace `let_chains` with stable alternatives
ab9ab6fe68 rt(gl): use `Option::filter` instead of `let_chains`
0ba4c482b3 rt(vk): use `Option::filter` instead of `let_chains`
57f9a13ee7 rt(wgpu): use `Option::filter` instead of `let_chains`
2661effab4 chore: Release
02288554b9 rt(mtl): fix history buffer off-by-one by buffering the history read.
98d8d91c66 capi: undo const qualification of chain in `_filter_chain_get_param` and `_filter_chain_get_active_pass_count`
6f5b342c1b build: fix dev-dependencies on non-apple
bec0482513 ci: trigger ci
e24beede0b rt(mtl): draw final pass to output targets
22b2118e97 rt(mtl): don't resize if the requested format differs from optimal format
856f69113d rt(mtl): don't make mipmapper for history if not needed
477d0ae67c rt(wgpu): draw final pass to output targets
e68da7b984 rt(gl): draw final pass to output targets
666588ef0d rt(vk): draw final pass to output targets
336f540ce9 rt(d3d9): draw final pass to output targets
894d19eb81 rt(d3d12): draw final pass to output targets
1bf5d7efca rt(d3d11): draw final pass to output targets
c9205bc922 rt(mtl): remove internal_frame_count
cebc7a939a rt(mtl): fix faulty history but only copying one mipmap slice of texture
fe48fd03a5 test(mtl): update the metal test
50580cfc3b chore: Release
075a2981e1 rt(gl): fix `glShaderSource` going past the buffer
a7dd40a79f ci: allow c_str_literals to maintain a lower MSRV
fd8d6f6ab8 chore: Release
c5fdffa6aa dep: update cargo.lock
090e268c4a doc: improve docs
8856a78eb8 api: expose `FastHashMap` and `ShortString`
ec3add1616 rt(params): rename get_parameter -> parameter_value
f83fd1e98f ci: debug is broken on windows 7
c3033cfbbf reflect: port to spirv-cross2
820fb69328 reflect: remove get_prefix from get_texture_semantic/get_unique_semantic
c9a6411394 deps: consolidate and update dependencies
40a56bf165 reflect/preprocess/presets: use SmallString where it makes sense
c447e40583 rt: redesign parameters to be thread-safe across FFI using arcswap and atomicusize
ae76bf9cc1 rt(d3d12): pull out descriptor heap implementation into its own crate
e934f175ef rt(d3d12): stop leaking transition barriers
2e7c3b3273 rt(d3d12): use gpu_allocator instead of CreateCommittedResource
e90c27ebbd rt(vk): use manuallydrop for VulkanBuffer rather than Option
8fb5d48a1e chore: Release
b7fd3bc507 runtime: fix image swizzle for remainder
af05cc5bd8 ci: build for windows 7
2f0a3356d9 rel([email protected]): doc fix
0efaf2b8a0 doc: fix docsrs build on macos
436d1fe3cd chore: Release
0ce11bac71 doc: promote d3d9 to secondary support
bc269c270c rt(d3d9): remove backtrace from Direct3DError
db3d5d05a6 presets: use crate for extract_if with fully qualified syntax to avoid name collision with unstable std
1957f576ca reflect: further improve link i/o pass by ensuring that the output SPIR-V is valid according to Vulkan 1.0 semantics
5560c1ed09 runtime: use a more efficient swizzling implementation without array_chunks_mut
f8c055524b doc(readme): document shader pre-linkage
fee92113f1 ci: remove PR full test
45c3c876f4 reflect: remove explicit compile function in favour of TryFrom
28f5674a80 presets: remove rustc 1.74 compatibility
bffad12ea2 doc(presets): add docstring for ContextItem::key
321e30a0f1 capi: extend config_struct macro to handle overrides
9b40c10466 reflect: improve inout link algorithm
cd14bca23a Revert "reflect: improve inout link algorithm"
1ac78695c6 reflect: improve inout link algorithm
d1e49b7eb4 rt(d3d9): search "global" namespace for register assignments
5ef0055e05 rt(d3d9): fix vertex assignments accidentally searching fragment
1c6581d737 rt(d3d9): set colorwritemask to all
ac66b1b6f6 style: fix warnings
977975f4c7 doc: document naga reflect
3b9514ad38 doc: allow wgpu doc on apple
899fb50da6 chore: Release
162226ce44 ci: don't build locally
039fdfd41a ci: update OBS build platform
e13bb88df2 doc: update MSRV due to naga
7b375658c5 dep: update cargo.lock
3fb6e3843e dep: update to windows 0.58
3cda5b706d rt(d3d12): get rid of size in inputview
4d6793d305 rt(d3d11): get rid of input/output view wrappers
35f499f5e1 wgpu: enable pipeline caching
6ce711db26 ci: don't build Arch
d4b4366836 dep: update ash to 0.38
c646086df4 dep: update wgpu
f6cf642e50 chore: Release
98958dfb5e dep: update rustc_hash
b5d523e9f3 rt(mtl): move icrate definitions to objc2-metal
5e9ce1207c Revert "ci: temporarily remove deny-deprecated on ctypes to unblock ares"
1b0574c140 presets: show file name and details on io error
b84e104212 capi: fix malformed vulkan include

git-subtree-dir: thirdparty/librashader
git-subtree-split: 4abd48eb244e15ca40307f28fce0061627ff0143
2024-10-05 12:31:45 +01:00
Luke Usher 81d5cdba32 Merge commit '5151003fa4331d3a4473c32e6c7b4ebcd59e29ba' 2024-10-05 12:31:42 +01:00
Luke Usher 5151003fa4 Squashed 'thirdparty/libchdr/' changes from aa34b14191..aaca599e18
aaca599e18 Merge pull request #130 from notaz/release_lzma_init_buffers
2099836c2b release lzma init buffers
9b6ff6c3c2 Merge pull request #128 from scribam/zstd-legacy
85dd19217a Merge pull request #129 from scribam/arm-neon
71e57f533d lzma: extends neon support when hwcap is not available
a6c474ddee zstd: disable legacy support

git-subtree-dir: thirdparty/libchdr
git-subtree-split: aaca599e18e43933fc193bd1b715c368c306208b
2024-10-05 12:31:42 +01:00
Luke Usher c6a838d6ee Merge commit '349f1d281c29a3af9f2814399028e0e7bc829c96' 2024-10-05 12:31:38 +01:00
Luke Usher 349f1d281c Squashed 'thirdparty/sljit/' changes from c2e49625e4..f6326087b3
f6326087b3 Improve op flags operation on x86
2a79c59737 Fix sign conversion warning on Windows arm64 (#270)
7a6ceb3e03 Fix examples (#269)
2c105e2461 Fixed a compare logical case on s390x
4c4802dd4c Emit brk #0xf000 for breakpoint opcode on arm64 (#267)
3b9c0c8925 Enable public usage of SLJIT_LOCALS_OFFSET (#264)
4dd388a736 loongarch: add memory fence & support tmp register for atomic (#263)
8c089bd584 Implement m,emory fence
d21a5448f6 Fix compiler warning
f9f512c080 Support tmp register for atomic
883118537c Add action (#259)
8099661b54 Add SLJIT_ATOMIC_USE_LS/CAS support to LoongArch (#261)
728db2007a riscv: only enable SIMD if supported by toolchain (#260)
8727735933 Bitmanipulation instructions on RISC-V (#258)
c85dd1dd97 Rework sljit_emit_enter argument passing
3e5568bfcc Add more tmp reg tests
bd056ab1df Add control flags to atomic operations
ee8861243f loongarch: Implement ll/sc support for atomic operations (#254)
d3a10d1986 x86: avoid SIGILL in CPUs with AVX but without AVX2 (#256)
460f02a1b8 x86: check for extended function availability before using (#255)
0a74f16bea x86: improve execute_cpuid() portability (#251)
7b0fd86bd5 test: avoid leaking compiler on early exit (#253)
31cfa2fef2 Add shuffle support for LoongArch (#252)
06a5bafd7c Initial implementation of riscv simd
5ad2541117 x86: improve execute_get_xcr0_low() portability (#250)
6128486813 Implement atomic store on ppc and mips
fcade55c27 Update atomic on s390x
8bcd711708 Rework atomic load/store
bd221f0e86 Support non-overlaping vector registers

git-subtree-dir: thirdparty/sljit
git-subtree-split: f6326087b3404efb07c6d3deed97b3c3b8098c0c
2024-10-05 12:31:38 +01:00
Cheesemeister3k cac4aab301 #1485 [SNES] NTT Data Keypad (#1660)
Test equipment used: NTT Data Keypad, Raphnet ADAP-2XSNES-USB_V2 and
test32.sfc, Analogue Super Nt.
2024-10-05 12:25:28 +01:00
Factor 5911338cac Add support for 256KB of SRAM for SNES (#1616)
256KB of SRAM is possible on hardware and many hacks use it (Super
Metroid Practice ROM, Super Mario Bros. Maker, etc.). It could be
implemented in Ares in many ways, but I limited the ramSize to 1-8 with
a wrap-around as values higher than 8 wrap-around when used (9 is the
same as 1, 10 is the same as 2, etc. verified by
[kandowontu](https://github.com/kandowontu)).
2024-10-05 12:25:16 +01:00
png183 c0af33d4ce gba: improve SIO register handling (#1662)
Implements the following changes:

- Bits 8-11 of SIOCNT should be read/write
- Mode specified by SIOCNT uses 2 bits rather than 1
- Lower 3 bits of JOYCNT are cleared when 1's are written
2024-10-05 12:22:31 +01:00
hackgrid ea77623238 desktop-ui: enable loading rom folders from the cmd line (#1656)
Needs to merge #1654 first.

Enables loading folders instead files from cmd line.
Removes loading firmware files from rom folders in sfc mia (firmware
data would be loaded twice, db hashes would not match anymore).
2024-10-01 16:10:54 +01:00
png183 033752679f gba: add 2-cycle IRQ synchronizer and improve halted CPU timings (#1651)
Adds a 2-cycle synchronizer delay to IRQs. Unhalting the CPU also
appears to be delayed by 2 cycles.

The GBA CPU core currently steps in increments of 16 cycles when halted
or stopped to reduce emulation overhead. Since this optimization can
cause unhalt timings to be off by a few cycles, this PR reduces the step
size for halt mode and removes the optimization for stop mode.
Performance impact should be minimal in cases where performance is a
concern.
2024-09-26 14:23:54 +01:00
jcm 025fc6450a hiro/cocoa: Add bounds checking for table view context menus (#1650)
Right-clicking in a table view beyond the bounds of the defined rows in
the table would crash previously.
2024-09-25 09:57:13 +01:00
jcm d6858ad821 ruby/metal: Emit librashader shader creation errors (#1647)
Displaying errors from librashader shader loads in Metal will make
librashader debugging easier.
2024-09-18 18:34:10 +01:00
png183 0b46609e95 gba: add latches to DISPCNT background enable, object enable, and force blank bits (#1643)
Clearing the force blank bit or setting the background/object enable
bits in DISPCNT should only take effect after 3 scanlines.
2024-09-17 14:32:18 +01:00
jcm b5a8edc5d0 Address possible use of uninitialized variables (#1646)
Minor code cleanup. clang reports that it's possible under some
circumstances these variables are uninitialized when used.

Overall motivation is to utilize some stricter compiler settings in the
future and error on `-Wuninitialized`.
2024-09-17 14:32:07 +01:00
invertego 757a7dd670 Merge commit '950573df14f9307b7eaff4600370d76057aae466' into update_parallel_rdp 2024-09-15 05:27:45 -07:00
invertego 950573df14 Squashed 'ares/n64/vulkan/parallel-rdp/' changes from 7e5ad30b8..d1cc912c5
d1cc912c5 Update to 1f69c762be68feb9fcd5276d75acc6e5a6160a19
1a8cbce2f Update to fe5becd13638873db90d46e7ba7d48255971f82a
353ab9d38 Update to a2b0e8503ba525d70c61bccbe59c87d14daf89a6

git-subtree-dir: ares/n64/vulkan/parallel-rdp
git-subtree-split: d1cc912c5c47abaf9b6b155d3cd15255df65cf5b
2024-09-15 05:27:26 -07:00
Giovanni Bajo f43da416fd n64: refactor devirtualize and memory ops, force JIT only with instruction cache (#1635)
This change contains a large refactoring with no functional changes,
plus a small functional change that will help speeding up the JIT.

The refactoring allows CPU::devirtualize to return all the information
regarding the memory access. This in turn allows to simplify all memory
ops (which contained lots of duplicated code) to use devirtualize,
reducing code duplication.

The functional change is that we now use JIT only when running code from
icache (which is 99.9999% of the times). Running code without icache is
extremely slow on real hardware and only happens in specific situations
(eg: during boot when RDRAM is not initialized). By limiting the JIT to
run from icache, we open the door to implement proper icache support in
the JIT and finish remove the instruction epilogue with its slow
"instruction cache stepper" (which in addition to being slow, is also
inaccurate and make us fail cache test ROMs).
2024-09-08 13:45:09 +01:00
18f45ddc85 n64: add support for multi-byte writes to gdb (#1620)
Currently, gdb hooks in n64 doesn't allow for large sizes of writes
because it wouldn't be obvious which data size use on the bus. While
this is true, it doesn't really matter for RDRAM where all data sizes
work in the obvious, correct way. So relax the constraint to allow
writing large portions of RDRAM with a single gdb command.

Co-authored-by: Chase Meadors <[email protected]>
Co-authored-by: Luke Usher <[email protected]>
2024-09-03 09:49:40 +01:00
Giovanni Bajo 7135646c44 Update parallelRDP and MoltenVK (#1634) 2024-09-03 09:49:25 +01:00
invertegoandGiovanni Bajo 4ab8472a53 n64: fix instruction tracing with recompiler (#1629)
Co-authored-by: Giovanni Bajo <[email protected]>
2024-09-02 14:08:56 +01:00
Screwtapello a47e793b57 Stage resources into the output directory on Linux. (#1627)
Originally, to run ares on Linux after building it, you'd `make install`
which copied everything into the right places in your home directory.
Windows and macOS don't have the same traditions about installing apps,
so on those platforms the makefile staged a "portable" installation in
the output directory.

Since then, `make install` was modified to install ares system-wide.
It's possible to use it to install ares into your home directory, but a
bit overkill. For consistency, let's also stage a "portable"
installation in the output directory on Linux and the BSDs, so it's
easier to test on those platforms.
2024-09-02 11:36:41 +01:00
invertego 1ffb4f6d5a n64: use devirtualize for code block lookup (#1630)
Essentially a revert of fbaf7d8118

This optimization was found to compromise correctness due to the lack of
alignment checks. If something like this proves truly beneficial for
performance, we can bring it back.
2024-09-02 11:35:45 +01:00
remutro aa350116d4 Add option to enable TMSS boot rom for Mega Drive (#1632)
If you like to slow down booting into your game and want to read the
same old text each time on the Mega Drive, the option is yours now! This
is available as a choice in Settings > Options now. By default, this
will not be enabled (same behavior as existed prior to this PR). You
don't need to specify anything in the firmware section, ares had the
plumbing in place all along, it was just hardcoded to not load the tmss
boot rom.
2024-09-02 11:35:07 +01:00
png183 e8a5579cb7 gba: latch IE and IF values for 1 cycle, and improve stop mode emulation (#1631)
Improved interrupt timings, and made sure stop mode exits only once the
requested keypad input is registered.
2024-09-01 09:00:28 +01:00
Screwtapello f091fcfacd Fix detection of the Sufami Turbo base cartridge. (#1628)
The Sufami Turbo base cartridge is listed in the Super Famicom verified
dump database, so it did not use the heuristics. It turns out the
heuristic detection wouldn't have worked anyway - although the game's
header does contain the serial A9PJ, the heuristic revision() method
does not report that serial at all.

Meanwhile, the serial A9PJ is *also* used by the game Bishoujo Senshi
Sailor Moon SuperS - Fuwafuwa Panic, so loading this game would prompt
for Sufami Turbo mini-cartridges even though the game could not make use
of them.

I have used mia to generated heuristic manifests for every SNES/SFC game
in the No-Intro set, and the only difference introduced by this change
is that the Sufami Turbo base cartridge is now detected as having Sufami
Turbo slots, and Fuwafuwa Panic is not.

It took me a little messing around to find a syntax for comparing an
`array_view<u8>` that the compiler would accept; if there's some more
concise or idiomatic way to do it, please let me know.

Of the other emulators I surveyed, bsnes works just like ares did,
MesenS doesn't seem to support the Sufami Turbo, and snes9x uses a
slightly stricter check than I implemented:


https://github.com/snes9xgit/snes9x/blob/85591435763dede7ef111aab8070cdf2d5a7da49/memmap.cpp#L1016-L1023
2024-08-29 12:22:43 +01:00
Luke Usher fa9d350a66 md: sync more regularly when emulating 32X
Fixes a hang in Knuckles Chaotix at the cost of performance.

Needs further investigation.
2024-08-28 16:16:41 +01:00
744 changed files with 94294 additions and 68389 deletions

No files matched your search

+3
View File
@@ -122,9 +122,11 @@ jobs:
run: |
if [ $ImageOS == "macos14" ]; then
sudo xcode-select -s /Applications/Xcode_15.2.app/Contents/Developer
export MACOSX_DEPLOYMENT_TARGET=10.13
echo "Set Xcode version to 15.2 for macos-latest build."
elif [ $ImageOS == "macos12" ]; then
sudo xcode-select -s /Applications/Xcode_13.4.app/Contents/Developer
export MACOSX_DEPLOYMENT_TARGET=10.9
echo "Set Xcode version to 13.4 for macos-compat build."
fi
brew install make ninja cmake
@@ -140,6 +142,7 @@ jobs:
pushd thirdparty/MoltenVK
./build-moltenvk.sh
popd
export MACOSX_DEPLOYMENT_TARGET=10.15
pushd thirdparty/librashader
./build-librashader.sh
popd
+1 -1
View File
@@ -10,7 +10,7 @@ Platform* platform = nullptr;
bool _runAhead = false;
const string Name = "ares";
const string Version = "140";
const string Version = "141";
const string Copyright = "ares team, Near";
const string License = "ISC";
const string LicenseURI = "https://opensource.org/licenses/ISC";
+2 -2
View File
@@ -75,8 +75,8 @@ struct GSU {
auto serialize(serializer&) -> void;
//disassembler.cpp
noinline auto disassembleInstruction() -> string;
noinline auto disassembleContext() -> string;
NALL_NOINLINE auto disassembleInstruction() -> string;
NALL_NOINLINE auto disassembleContext() -> string;
auto disassembleOpcode(char* output) -> void;
auto disassembleALT0(char* output) -> void;
+2 -2
View File
@@ -111,8 +111,8 @@ struct HG51B {
auto writeRegister(n7 address, n24 data) -> void;
//disassembler.cpp
noinline auto disassembleInstruction(maybe<n15> pb = {}, maybe<n8> pc = {}) -> string;
noinline auto disassembleContext() -> string;
NALL_NOINLINE auto disassembleInstruction(maybe<n15> pb = {}, maybe<n8> pc = {}) -> string;
NALL_NOINLINE auto disassembleContext() -> string;
protected:
struct Registers {
+2 -2
View File
@@ -124,8 +124,8 @@ struct I8080 {
auto serialize(serializer&) -> void;
//disassembler.cpp
noinline auto disassembleInstruction(maybe<n16> pc = {}) -> string;
noinline auto disassembleContext() -> string;
NALL_NOINLINE auto disassembleInstruction(maybe<n16> pc = {}) -> string;
NALL_NOINLINE auto disassembleContext() -> string;
auto disassemble(n16 pc, n8 prefix, n8 code) -> string;
+2 -2
View File
@@ -156,8 +156,8 @@ struct MOS6502 {
auto serialize(serializer&) -> void;
//disassembler.cpp
noinline auto disassembleInstruction(maybe<n16> pc = {}) -> string;
noinline auto disassembleContext() -> string;
NALL_NOINLINE auto disassembleInstruction(maybe<n16> pc = {}) -> string;
NALL_NOINLINE auto disassembleContext() -> string;
struct PR {
bool c; //carry
@@ -513,19 +513,19 @@ auto SH2::MOVI(u32 i, u32 n) -> void {
}
//MOV.W @(disp,PC),Rn
noinline auto SH2::MOVWI(u32 d, u32 n) -> void {
NALL_NOINLINE auto SH2::MOVWI(u32 d, u32 n) -> void {
u32 pc = inDelaySlot() ? PPC - 2 : PC;
R[n] = (s16)readWord(pc + d * 2);
}
//MOV.L @(disp,PC),Rn
noinline auto SH2::MOVLI(u32 d, u32 n) -> void {
NALL_NOINLINE auto SH2::MOVLI(u32 d, u32 n) -> void {
u32 pc = inDelaySlot() ? PPC - 2 : PC;
R[n] = readLong((pc & ~3) + d * 4);
}
//MOVA @(disp,PC),R0
noinline auto SH2::MOVA(u32 d) -> void {
NALL_NOINLINE auto SH2::MOVA(u32 d) -> void {
u32 pc = inDelaySlot() ? PPC - 2 : PC;
R[0] = (pc & ~3) + d * 4;
}
+2 -2
View File
@@ -29,8 +29,8 @@ struct SM83 {
//disassembler.cpp
virtual auto readDebugger(n16 address) -> n8 { return 0; }
noinline auto disassembleInstruction(maybe<n16> pc = {}) -> string;
noinline auto disassembleContext() -> string;
NALL_NOINLINE auto disassembleInstruction(maybe<n16> pc = {}) -> string;
NALL_NOINLINE auto disassembleContext() -> string;
//memory.cpp
auto operand() -> n8;
+3 -3
View File
@@ -117,9 +117,9 @@ struct SPC700 {
auto serialize(serializer&) -> void;
//disassembler.cpp
noinline auto disassembleInstruction(n16 address, n1 p) -> string;
noinline auto disassembleInstruction() -> string;
noinline auto disassembleContext() -> string;
NALL_NOINLINE auto disassembleInstruction(n16 address, n1 p) -> string;
NALL_NOINLINE auto disassembleInstruction() -> string;
NALL_NOINLINE auto disassembleContext() -> string;
struct Flags {
bool c; //carry
@@ -1,4 +1,4 @@
noinline auto TLCS900H::disassembleInstruction() -> string {
NALL_NOINLINE auto TLCS900H::disassembleInstruction() -> string {
string output;
auto pc = load(PC);
@@ -636,7 +636,7 @@ noinline auto TLCS900H::disassembleInstruction() -> string {
return pad(output, -48);
}
noinline auto TLCS900H::disassembleContext() -> string {
NALL_NOINLINE auto TLCS900H::disassembleContext() -> string {
string output;
output.append("XWA:", hex(r.xwa[RFP].l.l0, 8L), " ");
output.append("XBC:", hex(r.xbc[RFP].l.l0, 8L), " ");
@@ -25,8 +25,8 @@ struct uPD96050 {
auto writeDP(n12 address, n8 data) -> void;
//disassembler.cpp
noinline auto disassembleInstruction(maybe<n14> ip = {}) -> string;
noinline auto disassembleContext() -> string;
NALL_NOINLINE auto disassembleInstruction(maybe<n14> ip = {}) -> string;
NALL_NOINLINE auto disassembleContext() -> string;
enum class Revision : u32 { uPD7725, uPD96050 } revision;
n24 programROM[16384];
@@ -235,9 +235,9 @@ struct WDC65816 {
auto serialize(serializer&) -> void;
//disassembler.cpp
noinline auto disassembleInstruction(n24 address, bool e, bool m, bool x) -> string;
noinline auto disassembleInstruction() -> string;
noinline auto disassembleContext(maybe<bool> e = {}) -> string;
NALL_NOINLINE auto disassembleInstruction(n24 address, bool e, bool m, bool x) -> string;
NALL_NOINLINE auto disassembleInstruction() -> string;
NALL_NOINLINE auto disassembleContext(maybe<bool> e = {}) -> string;
struct f8 {
bool c = 0; //carry
+2 -2
View File
@@ -211,8 +211,8 @@ struct Z80 {
auto serialize(serializer&) -> void;
//disassembler.cpp
noinline auto disassembleInstruction(maybe<n16> pc = {}) -> string;
noinline auto disassembleContext() -> string;
NALL_NOINLINE auto disassembleInstruction(maybe<n16> pc = {}) -> string;
NALL_NOINLINE auto disassembleContext() -> string;
auto disassemble(n16 pc, n8 prefix, n8 code) -> string;
auto disassembleCB(n16 pc, n8 prefix, n8 code) -> string;
+4
View File
@@ -1,4 +1,6 @@
auto APU::readIO(n32 address) -> n8 {
cpu.synchronize(apu);
switch(address) {
//NR10
@@ -144,6 +146,8 @@ auto APU::readIO(n32 address) -> n8 {
}
auto APU::writeIO(n32 address, n8 data) -> void {
cpu.synchronize(apu);
switch(address) {
//NR10
+30 -14
View File
@@ -30,20 +30,25 @@ auto CPU::unload() -> void {
}
auto CPU::main() -> void {
ARM7TDMI::irq = irq.ime && (irq.enable & irq.flag);
ARM7TDMI::irq = irq.synchronizer[0];
if(stopped()) {
if(!(irq.enable & irq.flag & Interrupt::Keypad)) {
Thread::step(16);
if(!keypad.conditionMet) {
stepIRQ();
Thread::step(1);
Thread::synchronize();
return;
}
Thread::step(2);
Thread::synchronize();
context.stopped = false;
}
if(halted()) {
if(!(irq.enable & irq.flag)) {
return step(16);
if(!(irq.enable[0] & irq.flag[0])) {
return step(4);
}
step(2);
context.halted = false;
}
@@ -64,6 +69,17 @@ auto CPU::dmaRun() -> void {
}
}
auto CPU::setInterruptFlag(u32 source) -> void {
irq.flag[1] |= source;
}
inline auto CPU::stepIRQ() -> void {
irq.synchronizer[0] = irq.synchronizer[1];
irq.synchronizer[1] = irq.ime && (irq.enable[0] & irq.flag[0]);
irq.enable[0] = irq.enable[1];
irq.flag[0] = irq.flag[1];
}
auto CPU::step(u32 clocks) -> void {
if(!clocks) return;
@@ -75,6 +91,7 @@ auto CPU::step(u32 clocks) -> void {
dma[3].waiting = max(0, dma[3].waiting - (s32)clocks);
for(auto _ : range(clocks)) {
stepIRQ();
timer[0].run();
timer[1].run();
timer[2].run();
@@ -93,17 +110,16 @@ auto CPU::step(u32 clocks) -> void {
context.clock++;
}
#if defined(PROFILE_PERFORMANCE)
//10-20% speedup by only synchronizing other components every 16 clock cycles
Thread::step(clocks);
Thread::synchronize(ppu, player);
//occasionally synchronize with APU in case CPU has not recently interacted with it
static u32 counter = 0;
counter += clocks;
if(counter < 16) return;
clocks = counter;
counter = 0;
#endif
Thread::step(clocks);
Thread::synchronize();
if(counter >= 1024) {
Thread::synchronize(apu);
counter = 0;
}
}
auto CPU::power() -> void {
+9 -3
View File
@@ -47,6 +47,8 @@ struct CPU : ARM7TDMI, Thread, IO {
auto main() -> void;
auto dmaRun() -> void;
auto setInterruptFlag(u32 source) -> void;
auto stepIRQ() -> void;
auto step(u32 clocks) -> void override;
auto power() -> void;
@@ -158,7 +160,8 @@ struct CPU : ARM7TDMI, Thread, IO {
n1 transferEnableReceive;
n1 transferEnableSend;
n1 startBit;
n1 transferLength;
n4 uartFlags;
n2 mode;
n1 irqEnable;
n16 data[4];
@@ -172,6 +175,8 @@ struct CPU : ARM7TDMI, Thread, IO {
n1 enable;
n1 condition;
n1 flag[10];
n1 conditionMet;
} keypad;
struct Joybus {
@@ -201,8 +206,9 @@ struct CPU : ARM7TDMI, Thread, IO {
struct IRQ {
n1 ime;
n16 enable;
n16 flag;
n1 synchronizer[2];
n16 enable[2];
n16 flag[2];
} irq;
struct Wait {
+2 -2
View File
@@ -2,8 +2,8 @@ inline auto CPU::DMA::run() -> bool {
if(!active || waiting) return false;
transfer();
if(irq) cpu.irq.flag |= CPU::Interrupt::DMA0 << id;
if(drq && id == 3) cpu.irq.flag |= CPU::Interrupt::Cartridge;
if(irq) cpu.setInterruptFlag(CPU::Interrupt::DMA0 << id);
if(drq && id == 3) cpu.setInterruptFlag(CPU::Interrupt::Cartridge);
return true;
}
+18 -16
View File
@@ -55,8 +55,9 @@ auto CPU::readIO(n32 address) -> n8 {
| serial.startBit << 7
);
case 0x0400'0129: return (
serial.transferLength << 4
| serial.irqEnable << 6
serial.uartFlags << 0
| serial.mode << 4
| serial.irqEnable << 6
);
//SIOMLT_SEND (SIODATA8)
@@ -183,12 +184,12 @@ auto CPU::readIO(n32 address) -> n8 {
case 0x0400'015b: return 0;
//IE
case 0x0400'0200: return irq.enable.byte(0);
case 0x0400'0201: return irq.enable.byte(1);
case 0x0400'0200: return irq.enable[0].byte(0);
case 0x0400'0201: return irq.enable[0].byte(1);
//IF
case 0x0400'0202: return irq.flag.byte(0);
case 0x0400'0203: return irq.flag.byte(1);
case 0x0400'0202: return irq.flag[0].byte(0);
case 0x0400'0203: return irq.flag[0].byte(1);
//WAITCNT
case 0x0400'0204: return (
@@ -339,8 +340,9 @@ auto CPU::writeIO(n32 address, n8 data) -> void {
serial.startBit = data.bit(7);
return;
case 0x0400'0129:
serial.transferLength = data.bit(4);
serial.irqEnable = data.bit(6);
serial.uartFlags = data.bit(0,3);
serial.mode = data.bit(4,5);
serial.irqEnable = data.bit(6);
return;
//SIOMLT_SEND (SIODATA8)
@@ -383,10 +385,10 @@ auto CPU::writeIO(n32 address, n8 data) -> void {
//JOYCNT
case 0x0400'0140:
joybus.resetSignal = data.bit(0);
joybus.receiveComplete = data.bit(1);
joybus.sendComplete = data.bit(2);
joybus.resetIRQEnable = data.bit(6);
joybus.resetSignal &= ~data.bit(0);
joybus.receiveComplete &= ~data.bit(1);
joybus.sendComplete &= ~data.bit(2);
joybus.resetIRQEnable = data.bit(6);
return;
case 0x0400'0141: return;
case 0x0400'0142: return;
@@ -415,12 +417,12 @@ auto CPU::writeIO(n32 address, n8 data) -> void {
case 0x0400'0159: return;
//IE
case 0x0400'0200: irq.enable.byte(0) = data; return;
case 0x0400'0201: irq.enable.byte(1) = data; return;
case 0x0400'0200: irq.enable[1].byte(0) = data; return;
case 0x0400'0201: irq.enable[1].byte(1) = data; return;
//IF
case 0x0400'0202: irq.flag.byte(0) = irq.flag.byte(0) & ~data; return;
case 0x0400'0203: irq.flag.byte(1) = irq.flag.byte(1) & ~data; return;
case 0x0400'0202: irq.flag[1].byte(0) = irq.flag[1].byte(0) & ~data; return;
case 0x0400'0203: irq.flag[1].byte(1) = irq.flag[1].byte(1) & ~data; return;
//WAITCNT
case 0x0400'0204:
+6 -8
View File
@@ -1,7 +1,4 @@
auto CPU::Keypad::run() -> void {
if(!enable) return;
system.controls.poll();
const bool lookup[] = {
system.controls.a->value(),
system.controls.b->value(),
@@ -15,12 +12,13 @@ auto CPU::Keypad::run() -> void {
system.controls.l->value(),
};
bool test = condition; //0 = OR, 1 = AND
conditionMet = condition; //0 = OR, 1 = AND
for(u32 index : range(10)) {
if(!flag[index]) continue;
bool input = lookup[index];
if(condition == 0) test |= input;
if(condition == 1) test &= input;
n1 input = lookup[index];
if(condition == 0) conditionMet |= input;
if(condition == 1) conditionMet &= input;
}
if(test) cpu.irq.flag |= CPU::Interrupt::Keypad;
if(conditionMet && enable) cpu.setInterruptFlag(CPU::Interrupt::Keypad);
}
+6 -3
View File
@@ -52,7 +52,8 @@ auto CPU::serialize(serializer& s) -> void {
s(serial.transferEnableReceive);
s(serial.transferEnableSend);
s(serial.startBit);
s(serial.transferLength);
s(serial.uartFlags);
s(serial.mode);
s(serial.irqEnable);
for(auto& value : serial.data) s(value);
s(serial.data8);
@@ -60,6 +61,7 @@ auto CPU::serialize(serializer& s) -> void {
s(keypad.enable);
s(keypad.condition);
for(auto& flag : keypad.flag) s(flag);
s(keypad.conditionMet);
s(joybus.sc);
s(joybus.sd);
@@ -82,8 +84,9 @@ auto CPU::serialize(serializer& s) -> void {
s(joybus.generalFlag);
s(irq.ime);
s(irq.enable);
s(irq.flag);
for(auto& flag : irq.synchronizer) s(flag);
for(auto& flag : irq.enable) s(flag);
for(auto& flag : irq.flag) s(flag);
for(auto& flag : wait.nwait) s(flag);
for(auto& flag : wait.swait) s(flag);
+1 -1
View File
@@ -43,7 +43,7 @@ auto CPU::Timer::step() -> void {
if(++period == 0) {
period = reload;
if(irq) cpu.irq.flag |= CPU::Interrupt::Timer0 << id;
if(irq) cpu.setInterruptFlag(CPU::Interrupt::Timer0 << id);
if(apu.fifo[0].timer == id) cpu.runFIFO(0);
if(apu.fifo[1].timer == id) cpu.runFIFO(1);
+1
View File
@@ -9,6 +9,7 @@ namespace ares::GameBoyAdvance {
#include <ares/inline.hpp>
auto enumerate() -> vector<string>;
auto load(Node::System& node, string name) -> bool;
auto option(string name, string value) -> bool;
enum : u32 { //mode flags for bus read, write:
Nonsequential = 1, //N cycle
+2 -2
View File
@@ -63,7 +63,7 @@ auto Player::frame() -> void {
&& !cpu.serial.transferEnableReceive
&& cpu.serial.transferEnableSend
&& cpu.serial.startBit
&& cpu.serial.transferLength
&& cpu.serial.mode == 0x1
&& cpu.serial.irqEnable
) {
status.packet = (status.packet + 1) % 17;
@@ -86,7 +86,7 @@ auto Player::frame() -> void {
case 15: status.send = 0x30000003; break;
case 16: status.send = 0x30000003; break;
}
cpu.irq.flag |= CPU::Interrupt::Serial;
cpu.setInterruptFlag(CPU::Interrupt::Serial);
}
}
+10 -3
View File
@@ -4,13 +4,19 @@ n1 PPU::Background::IO::frame;
n5 PPU::Background::IO::mosaicWidth;
n5 PPU::Background::IO::mosaicHeight;
auto PPU::Background::setEnable(n1 status) -> void {
io.enable[3] = status;
for(auto& flag : io.enable) flag &= status;
}
auto PPU::Background::scanline(u32 y) -> void {
memory::move(io.enable, io.enable + 1, sizeof(io.enable) - 1);
mosaicOffset = 0;
}
auto PPU::Background::run(u32 x, u32 y) -> void {
output = {};
if(ppu.blank() || !io.enable) {
if(ppu.blank() || !io.enable[0]) {
mosaic = {};
return;
}
@@ -37,10 +43,11 @@ auto PPU::Background::run(u32 x, u32 y) -> void {
}
//horizontal mosaic
if(!io.mosaic || ++mosaicOffset >= 1 + io.mosaicWidth) {
mosaicOffset = 0;
if(!io.mosaic || !mosaicOffset) {
mosaicOffset = 1 + io.mosaicWidth;
mosaic = output;
}
mosaicOffset--;
}
auto PPU::Background::linear(u32 x, u32 y) -> void {
+16 -15
View File
@@ -8,17 +8,17 @@ auto PPU::readIO(n32 address) -> n8 {
| Background::IO::frame << 4
| objects.io.hblank << 5
| objects.io.mapping << 6
| io.forceBlank << 7
| io.forceBlank[3] << 7
);
case 0x0400'0001: return (
bg0.io.enable << 0
| bg1.io.enable << 1
| bg2.io.enable << 2
| bg3.io.enable << 3
| objects.io.enable << 4
| window0.io.enable << 5
| window1.io.enable << 6
| window2.io.enable << 7
bg0.io.enable[3] << 0
| bg1.io.enable[3] << 1
| bg2.io.enable[3] << 2
| bg3.io.enable[3] << 3
| objects.io.enable[3] << 4
| window0.io.enable << 5
| window1.io.enable << 6
| window2.io.enable << 7
);
//GRSWP
@@ -112,14 +112,15 @@ auto PPU::writeIO(n32 address, n8 data) -> void {
Background::IO::frame = data.bit(4);
objects.io.hblank = data.bit(5);
objects.io.mapping = data.bit(6);
io.forceBlank = data.bit(7);
io.forceBlank[3] = data.bit(7);
for(auto& flag : io.forceBlank) flag |= data.bit(7);
return;
case 0x0400'0001:
bg0.io.enable = data.bit(0);
bg1.io.enable = data.bit(1);
bg2.io.enable = data.bit(2);
bg3.io.enable = data.bit(3);
objects.io.enable = data.bit(4);
bg0.setEnable(data.bit(0));
bg1.setEnable(data.bit(1));
bg2.setEnable(data.bit(2));
bg3.setEnable(data.bit(3));
objects.setEnable(data.bit(4));
window0.io.enable = data.bit(5);
window1.io.enable = data.bit(6);
window2.io.enable = data.bit(7);
+20 -7
View File
@@ -1,7 +1,16 @@
auto PPU::Objects::setEnable(n1 status) -> void {
io.enable[3] = status;
for(auto& flag : io.enable) flag &= status;
}
auto PPU::Objects::scanline(u32 y) -> void {
memory::move(io.enable, io.enable + 1, sizeof(io.enable) - 1);
if(y >= 160) return;
mosaicOffset = 0;
auto& buffer = lineBuffers[y & 1];
for(auto& pixel : buffer) pixel = {};
if(ppu.blank() || !io.enable) return;
if(ppu.io.forceBlank[1] || cpu.stopped() || !io.enable[1]) return; //checks if display conditions will be met next scanline
for(auto& object : ppu.object) {
n8 py = y - object.y;
@@ -60,7 +69,7 @@ auto PPU::Objects::scanline(u32 y) -> void {
if(color) {
if(object.colors == 0) color = object.palette * 16 + color;
buffer[bx].enable = true;
buffer[bx].color = ppu.pram[256 + color];
buffer[bx].color = 256 + color;
buffer[bx].translucent = object.mode == 1;
}
}
@@ -74,26 +83,30 @@ auto PPU::Objects::scanline(u32 y) -> void {
auto PPU::Objects::run(u32 x, u32 y) -> void {
output = {};
if(ppu.blank() || !io.enable) {
if(ppu.blank() || !io.enable[0]) {
mosaic = {};
return;
}
auto& buffer = lineBuffers[y & 1];
output = buffer[x];
output.color = ppu.pram[output.color];
//horizontal mosaic
if(mosaicOffset >= 1 + io.mosaicWidth) {
mosaicOffset = 0;
if(!mosaicOffset) {
mosaicOffset = 1 + io.mosaicWidth;
mosaic = output;
} else if(!mosaic.mosaic || !output.mosaic) {
mosaic = output;
}
mosaicOffset++;
mosaicOffset--;
}
auto PPU::Objects::power() -> void {
io = {};
for(auto& pixel : buffer) pixel = {};
for(auto& buffer : lineBuffers) {
for(auto& pixel : buffer) pixel = {};
}
output = {};
mosaic = {};
mosaicOffset = 0;
+44 -21
View File
@@ -23,6 +23,10 @@ PPU ppu;
#include "debugger.cpp"
#include "serialization.cpp"
auto PPU::setAccurate(bool value) -> void {
accurate = value;
}
auto PPU::load(Node::Object parent) -> void {
vram.allocate(96_KiB);
pram.allocate(512);
@@ -73,7 +77,7 @@ auto PPU::unload() -> void {
}
inline auto PPU::blank() -> bool {
return io.forceBlank || cpu.stopped();
return io.forceBlank[0] || cpu.stopped();
}
auto PPU::step(u32 clocks) -> void {
@@ -85,7 +89,6 @@ auto PPU::main() -> void {
cpu.keypad.run();
io.vblank = io.vcounter >= 160 && io.vcounter <= 226;
io.vcoincidence = io.vcounter == io.vcompare;
if(io.vcounter == 0) {
frame();
@@ -97,24 +100,42 @@ auto PPU::main() -> void {
bg3.io.ly = bg3.io.y;
}
step(1);
io.vcoincidence = io.vcounter == io.vcompare;
if(io.vcounter == 160) {
if(io.irqvblank) cpu.setInterruptFlag(CPU::Interrupt::VBlank);
}
step(1);
if(io.irqvcoincidence) {
if(io.vcoincidence) cpu.setInterruptFlag(CPU::Interrupt::VCoincidence);
}
if(io.vcounter == 160) {
if(io.irqvblank) cpu.irq.flag |= CPU::Interrupt::VBlank;
cpu.dmaVblank();
}
if(io.irqvcoincidence) {
if(io.vcoincidence) cpu.irq.flag |= CPU::Interrupt::VCoincidence;
step(3);
if(io.vcounter == 162) {
if(videoCapture) cpu.dma[3].enable = 0;
videoCapture = !videoCapture && cpu.dma[3].timingMode == 3 && cpu.dma[3].enable;
}
if(io.vcounter >= 2 && io.vcounter < 162 && videoCapture) cpu.dmaHDMA();
step(46);
step(41);
if(io.vcounter < 160) {
u32 y = io.vcounter;
bg0.scanline(y);
bg1.scanline(y);
bg2.scanline(y);
bg3.scanline(y);
objects.scanline(y);
u32 y = io.vcounter;
memory::move(io.forceBlank, io.forceBlank + 1, sizeof(io.forceBlank) - 1);
bg0.scanline(y);
bg1.scanline(y);
bg2.scanline(y);
bg3.scanline(y);
objects.scanline((y + 1) % 228);
if(y < 160) {
auto line = screen->pixels().data() + y * 240;
for(u32 x : range(240)) {
bg0.run(x, y);
@@ -128,27 +149,29 @@ auto PPU::main() -> void {
window3.output = true;
n15 color = dac.run(x, y);
line[x] = color;
step(4);
if(accurate) step(4);
}
if(!accurate) step(960);
} else {
step(960);
}
step(1);
io.hblank = 1;
if(io.irqhblank) cpu.irq.flag |= CPU::Interrupt::HBlank;
step(1);
if(io.irqhblank) cpu.setInterruptFlag(CPU::Interrupt::HBlank);
step(1);
if(io.vcounter < 160) cpu.dmaHblank();
step(226);
step(223);
io.hblank = 0;
if(++io.vcounter == 228) io.vcounter = 0;
if(io.vcounter == 162) {
if(videoCapture) cpu.dma[3].enable = 0;
videoCapture = !videoCapture && cpu.dma[3].timingMode == 3 && cpu.dma[3].enable;
}
if(io.vcounter >= 2 && io.vcounter < 162 && videoCapture) cpu.dmaHDMA();
}
auto PPU::frame() -> void {
system.controls.poll();
screen->frame();
scheduler.exit(Event::Frame);
}
+10 -4
View File
@@ -7,6 +7,8 @@ struct PPU : Thread, IO {
Memory::Writable<n8 > vram; //96KB
Memory::Writable<n16> pram;
bool accurate;
struct Debugger {
//debugger.cpp
auto load(Node::Object) -> void;
@@ -27,6 +29,8 @@ struct PPU : Thread, IO {
} debugger;
//ppu.cpp
auto setAccurate(bool value) -> void;
auto load(Node::Object) -> void;
auto unload() -> void;
@@ -70,7 +74,7 @@ private:
struct IO {
n1 gameBoyColorMode;
n1 forceBlank;
n1 forceBlank[4];
n1 greenSwap;
n1 vblank;
@@ -97,6 +101,7 @@ private:
struct Background {
//background.cpp
auto setEnable(n1 status) -> void;
auto scanline(u32 y) -> void;
auto run(u32 x, u32 y) -> void;
auto linear(u32 x, u32 y) -> void;
@@ -115,7 +120,7 @@ private:
static n5 mosaicWidth;
static n5 mosaicHeight;
n1 enable;
n1 enable[4];
n2 priority;
n2 characterBase;
@@ -162,6 +167,7 @@ private:
struct Objects {
//object.cpp
auto setEnable(n1 status) -> void;
auto scanline(u32 y) -> void;
auto run(u32 x, u32 y) -> void;
auto power() -> void;
@@ -170,7 +176,7 @@ private:
auto serialize(serializer&) -> void;
struct IO {
n1 enable;
n1 enable[4];
n1 hblank; //1 = allow access to OAM during Hblank
n1 mapping; //0 = two-dimensional, 1 = one-dimensional
@@ -178,7 +184,7 @@ private:
n5 mosaicHeight;
} io;
Pixel buffer[240];
Pixel lineBuffers[2][240];
Pixel output;
Pixel mosaic;
u32 mosaicOffset;
+3 -3
View File
@@ -5,7 +5,7 @@ auto PPU::serialize(serializer& s) -> void {
s(pram);
s(io.gameBoyColorMode);
s(io.forceBlank);
for(auto& flag : io.forceBlank) s(flag);
s(io.greenSwap);
s(io.vblank);
s(io.hblank);
@@ -39,7 +39,7 @@ auto PPU::serialize(serializer& s) -> void {
auto PPU::Background::serialize(serializer& s) -> void {
s(id);
s(io.enable);
for(auto& flag : io.enable) s(flag);
s(io.priority);
s(io.characterBase);
s(io.unused);
@@ -67,7 +67,7 @@ auto PPU::Background::serialize(serializer& s) -> void {
}
auto PPU::Objects::serialize(serializer& s) -> void {
s(io.enable);
for(auto& flag : io.enable) s(flag);
s(io.hblank);
s(io.mapping);
s(io.mosaicWidth);
+1 -1
View File
@@ -1,4 +1,4 @@
static const string SerializerVersion = "v139.3";
static const string SerializerVersion = "v140.3";
auto System::serialize(bool synchronize) -> serializer {
if(synchronize) scheduler.enter(Scheduler::Mode::Synchronize);
+7
View File
@@ -14,6 +14,13 @@ auto load(Node::System& node, string name) -> bool {
return system.load(node, name);
}
auto option(string name, string value) -> bool {
if(name == "Pixel Accuracy") {
ppu.setAccurate(value.boolean());
}
return true;
}
Scheduler scheduler;
BIOS bios;
System system;
+1 -1
View File
@@ -93,7 +93,7 @@ auto CPU::power(bool reset) -> void {
M68000::power();
Thread::create(system.frequency() / 7.0, {&CPU::main, this});
tmssEnable = system.tmss->value();
tmssEnable = system.tmss;
if(!reset) ram.fill();
io = {};
+1 -1
View File
@@ -73,7 +73,7 @@ auto M32X::SH7604::step(u32 clocks) -> void {
auto M32X::SH7604::power(bool reset) -> void {
Thread::create((system.frequency() / 7.0) * 3.0, {&M32X::SH7604::main, this});
SH2::recompilerStepCycles = 20; // Minimum cycles for recompiler to run for each batch of instructions
minCyclesBetweenFullSyncs = 100; // Minimum cycles between full sync with the M68K/MD side
minCyclesBetweenFullSyncs = 50; // Minimum cycles between full sync with the M68K/MD side
minCyclesBetweenSh2Syncs = 5; // Minimum Cycles between sync with the other SH2 (syncOtherSh2)
minCyclesBetweenM68kSyncs = 10; // Minimum Cycles between sync with the M68K (syncM68k)
SH2::power(reset);
+3 -2
View File
@@ -35,6 +35,9 @@ auto option(string name, string value) -> bool {
m32x.shs.recompiler.enabled = value.boolean();
}
}
if(name == "TMSS") {
system.tmss = value.boolean();
}
return true;
}
@@ -115,8 +118,6 @@ auto System::load(Node::System& root, string name) -> bool {
root = node;
if(!node->setPak(pak = platform->pak(node))) return false;
tmss = node->append<Node::Setting::Boolean>("TMSS", false);
scheduler.reset();
controls.load(node);
bus.load(node);
+1 -1
View File
@@ -2,7 +2,7 @@ extern Random random;
struct System {
Node::System node;
Node::Setting::Boolean tmss;
bool tmss;
VFS::Pak pak;
struct Controls {
+17 -27
View File
@@ -108,31 +108,25 @@ auto CPU::instruction() -> void {
return;
}
if(Accuracy::CPU::Recompiler && recompiler.enabled) {
// Fast path: attempt to lookup previously compiled blocks with devirtualizeFast
// and fastFetchBlock, this skips exception handling, error checking, and
// code emitting pathways for maximum lookup performance.
// As memory writes cause recompiler block invalidation, this shouldn't be detectable.
if (auto address = devirtualizeFast(ipu.pc)) {
if(auto block = recompiler.fastFetchBlock(address)) {
block->execute(*this);
return;
}
}
auto access = devirtualize<Read, Word>(ipu.pc);
if(!access) return;
if (auto address = devirtualize(ipu.pc)) {
auto block = recompiler.block(ipu.pc, *address, GDB::server.hasBreakpoints());
if(Accuracy::CPU::Recompiler && recompiler.enabled && access.cache) {
if(vaddrAlignedError<Word>(access.vaddr, false)) return;
auto block = recompiler.block(ipu.pc, access.paddr, GDB::server.hasBreakpoints());
if(block) {
block->execute(*this);
}
} else {
auto data = fetch(ipu.pc);
if (!data) return;
pipeline.begin();
instructionPrologue(ipu.pc, *data);
decoderEXECUTE(*data);
instructionEpilogue<0>();
pipeline.end();
return;
}
}
auto data = fetch(access);
if (!data) return;
pipeline.begin();
instructionPrologue(ipu.pc, *data);
decoderEXECUTE(*data);
instructionEpilogue<0>();
pipeline.end();
}
auto CPU::instructionPrologue(u64 address, u32 instruction) -> void {
@@ -141,11 +135,7 @@ auto CPU::instructionPrologue(u64 address, u32 instruction) -> void {
template<bool Recompiled>
auto CPU::instructionEpilogue() -> void {
if constexpr(Recompiled) {
//simulates timings without performing actual icache loads
icache.step(ipu.pc, devirtualizeFast(ipu.pc));
assert(ipu.r[0].u64 == 0);
} else {
if constexpr(!Recompiled) {
ipu.r[0].u64 = 0;
}
}
+73 -44
View File
@@ -72,6 +72,17 @@ struct CPU : Thread {
}
} pipeline{*this};
struct PhysAccess {
enum Direction : u32 { Read, Write };
explicit operator bool() const { return found; }
bool found; //this is a valid physical access
bool cache; //access must go through cache
u32 paddr; //physical address on 32-bit MIPS bus
u64 vaddr; //virtual address used on the CPU (64-bit)
};
//context.cpp
struct Context {
CPU& self;
@@ -101,29 +112,46 @@ struct CPU : Thread {
struct InstructionCache {
CPU& self;
struct Line;
auto line(u32 vaddr) -> Line& { return lines[vaddr >> 5 & 0x1ff]; }
auto line(u64 vaddr) -> Line& { return lines[vaddr >> 5 & 0x1ff]; }
//used by the recompiler to simulate instruction cache fetch timing
auto step(u32 vaddr, u32 address) -> void {
auto step(u64 vaddr, u32 paddr) -> void {
auto& line = this->line(vaddr);
if(!line.hit(address)) {
if(!line.hit(paddr)) {
self.step(48 * 2);
line.valid = 1;
line.tag = address & ~0x0000'0fff;
line.tag = paddr & ~0x0000'0fff;
} else {
self.step(1 * 2);
}
}
//used by the interpreter to fully emulate the instruction cache
auto fetch(u32 vaddr, u32 address, CPU& cpu) -> u32 {
auto jitFetch(u64 vaddr, u32 paddr, CPU& cpu) -> void {
auto& line = this->line(vaddr);
if(!line.hit(address)) {
line.fill(address, cpu);
} else {
cpu.step(1 * 2);
if(!line.hit(paddr)) {
line.fill(paddr, cpu);
}
return line.read(address);
}
//used by the interpreter to fully emulate the instruction cache
auto fetch(u64 vaddr, u32 paddr, CPU& cpu) -> u32 {
auto& line = this->line(vaddr);
if(!line.hit(paddr)) {
line.fill(paddr, cpu);
}
return line.read(paddr);
}
auto coherent(u64 vaddr, u32 paddr) -> bool {
auto& line = this->line(vaddr);
if(!line.hit(paddr))
return true;
u32 ram[8];
self.busReadBurst<ICache>(paddr & ~0x0000'0fff | line.index, ram);
for (int i=0; i<8; i++)
if (ram[i] != line.words[i])
return false;
return true;
}
auto power(bool reset) -> void {
@@ -138,11 +166,11 @@ struct CPU : Thread {
//16KB
struct Line {
auto hit(u32 address) const -> bool { return valid && tag == (address & ~0x0000'0fff); }
auto fill(u32 address, CPU& cpu) -> void {
auto hit(u32 paddr) const -> bool { return valid && tag == (paddr & ~0x0000'0fff); }
auto fill(u32 paddr, CPU& cpu) -> void {
cpu.step(48 * 2);
valid = 1;
tag = address & ~0x0000'0fff;
tag = paddr & ~0x0000'0fff;
cpu.busReadBurst<ICache>(tag | index, words);
}
@@ -151,7 +179,7 @@ struct CPU : Thread {
cpu.busWriteBurst<ICache>(tag | index, words);
}
auto read(u32 address) const -> u32 { return words[address >> 2 & 7]; }
auto read(u32 paddr) const -> u32 { return words[paddr >> 2 & 7]; }
bool valid;
u32 tag;
@@ -163,20 +191,21 @@ struct CPU : Thread {
//dcache.cpp
struct DataCache {
struct Line;
auto line(u32 vaddr) -> Line&;
template<u32 Size> auto read(u32 vaddr, u32 address) -> u64;
template<u32 Size> auto write(u32 vaddr, u32 address, u64 data) -> void;
auto line(u64 vaddr) -> Line&;
template<u32 Size> auto read(u64 vaddr, u32 paddr) -> u64;
template<u32 Size> auto write(u64 vaddr, u32 paddr, u64 data) -> void;
auto power(bool reset) -> void;
auto readDebug(u32 vaddr, u32 address) -> u8;
auto readDebug(u64 vaddr, u32 paddr) -> u8;
auto writeDebug(u64 vaddr, u32 paddr, u8 value) -> void;
//8KB
struct Line {
auto hit(u32 address) const -> bool;
auto fill(u32 address) -> void;
auto hit(u32 paddr) const -> bool;
auto fill(u32 paddr) -> void;
auto writeBack() -> void;
template<u32 Size> auto read(u32 address) const -> u64;
template<u32 Size> auto write(u32 address, u64 data) -> void;
template<u32 Size> auto read(u32 paddr) const -> u64;
template<u32 Size> auto write(u32 paddr, u64 data) -> void;
bool valid;
u16 dirty;
@@ -198,14 +227,6 @@ struct CPU : Thread {
TLB(CPU& self) : self(self) {}
static constexpr u32 Entries = 32;
struct Match {
explicit operator bool() const { return found; }
bool found;
bool cache;
u32 address;
};
struct Entry {
//scc-tlb.cpp
auto synchronize() -> void;
@@ -227,12 +248,11 @@ struct CPU : Thread {
} entry[TLB::Entries];
//tlb.cpp
auto load(u64 vaddr, bool noExceptions = false) -> Match;
auto load(u64 vaddr, const Entry& entry, bool noExceptions = false) -> maybe<Match>;
auto load(u64 vaddr, bool noExceptions = false) -> PhysAccess;
auto load(u64 vaddr, const Entry& entry, bool noExceptions = false) -> maybe<PhysAccess>;
auto loadFast(u64 vaddr) -> Match;
auto store(u64 vaddr) -> Match;
auto store(u64 vaddr, const Entry& entry) -> maybe<Match>;
auto store(u64 vaddr, bool noExceptions = false) -> PhysAccess;
auto store(u64 vaddr, const Entry& entry, bool noExceptions = false) -> maybe<PhysAccess>;
struct TlbCache { ;
static constexpr int entries = 4;
@@ -276,21 +296,30 @@ struct CPU : Thread {
auto userSegment64(u64 vaddr) const -> Context::Segment;
auto segment(u64 vaddr) -> Context::Segment;
auto devirtualize(u64 vaddr) -> maybe<u64>;
alwaysinline auto devirtualizeFast(u64 vaddr) -> u64;
template<u32 Dir, u32 Size> auto devirtualize(u64 vaddr, bool raiseAlignedError = true, bool raiseExceptions = true) -> PhysAccess;
auto devirtualizeDebug(u64 vaddr) -> u64;
auto fetch(u64 vaddr) -> maybe<u32>;
auto fetch(PhysAccess access) -> maybe<u32>;
auto jitFetch(u64 vaddr, u32 addr) -> void {
icache.jitFetch(vaddr, addr, *this);
}
template<u32 Size> auto busWrite(u32 address, u64 data) -> void;
template<u32 Size> auto busRead(u32 address) -> u64;
template<u32 Size> auto busWriteBurst(u32 address, u32 *data) -> void;
template<u32 Size> auto busReadBurst(u32 address, u32 *data) -> void;
template<u32 Size> auto read(u64 vaddr) -> maybe<u64>;
template<u32 Size> auto write(u64 vaddr, u64 data, bool alignedError=true) -> bool;
template<u32 Size> auto read(PhysAccess access) -> maybe<u64>;
template<u32 Size> auto write(PhysAccess access, u64 data) -> bool;
template<u32 Size> auto read(u64 vaddr) -> maybe<u64> {
return read<Size>(devirtualize<Read, Size>(vaddr));
}
template<u32 Size> auto write(u64 vaddr, u64 data, bool alignedError = true) -> bool {
return write<Size>(devirtualize<Write, Size>(vaddr, alignedError), data);
}
template<u32 Size> auto vaddrAlignedError(u64 vaddr, bool write) -> bool;
auto addressException(u64 vaddr) -> void;
auto readDebug(u64 vaddr) -> u8;
template <u32 Size> auto writeDebug(u64 vaddr, u64 data) -> bool;
//serialization.cpp
auto serialize(serializer&) -> void;
@@ -301,6 +330,7 @@ struct CPU : Thread {
Exception(CPU& self) : self(self) {}
auto trigger(u32 code, u32 coprocessor = 0, bool tlbMiss = 0) -> void;
auto reportGDBException(int code, u64 pc) -> void;
auto interrupt() -> void;
auto tlbModification() -> void;
@@ -890,10 +920,9 @@ struct CPU : Thread {
}
auto pool(u32 address) -> Pool*;
auto block(u32 vaddr, u32 address, bool singleInstruction = false) -> Block*;
auto fastFetchBlock(u32 address) -> Block*;
auto block(u64 vaddr, u32 address, bool singleInstruction = false) -> Block*;
auto emit(u32 vaddr, u32 address, bool singleInstruction = false) -> Block*;
auto emit(u64 vaddr, u32 address, bool singleInstruction = false) -> Block*;
auto emitZeroClear(u32 n) -> void;
auto emitEXECUTE(u32 instruction) -> bool;
auto emitSPECIAL(u32 instruction) -> bool;
+40 -31
View File
@@ -1,12 +1,12 @@
auto CPU::DataCache::Line::hit(u32 address) const -> bool {
return valid && tag == (address & ~0x0000'0fff);
auto CPU::DataCache::Line::hit(u32 paddr) const -> bool {
return valid && tag == (paddr & ~0x0000'0fff);
}
auto CPU::DataCache::Line::fill(u32 address) -> void {
auto CPU::DataCache::Line::fill(u32 paddr) -> void {
cpu.step(40 * 2);
valid = 1;
dirty = 0;
tag = address & ~0x0000'0fff;
tag = paddr & ~0x0000'0fff;
fillPc = cpu.ipu.pc;
cpu.busReadBurst<DCache>(tag | index, words);
}
@@ -17,66 +17,75 @@ auto CPU::DataCache::Line::writeBack() -> void {
cpu.busWriteBurst<DCache>(tag | index, words);
}
auto CPU::DataCache::line(u32 vaddr) -> Line& {
auto CPU::DataCache::line(u64 vaddr) -> Line& {
return lines[vaddr >> 4 & 0x1ff];
}
template<u32 Size>
auto CPU::DataCache::Line::read(u32 address) const -> u64 {
if constexpr(Size == Byte) { return bytes[address >> 0 & 15 ^ 3]; }
if constexpr(Size == Half) { return halfs[address >> 1 & 7 ^ 1]; }
if constexpr(Size == Word) { return words[address >> 2 & 3 ^ 0]; }
auto CPU::DataCache::Line::read(u32 paddr) const -> u64 {
if constexpr(Size == Byte) { return bytes[paddr >> 0 & 15 ^ 3]; }
if constexpr(Size == Half) { return halfs[paddr >> 1 & 7 ^ 1]; }
if constexpr(Size == Word) { return words[paddr >> 2 & 3 ^ 0]; }
if constexpr(Size == Dual) {
u64 upper = words[address >> 2 & 2 | 0];
u64 lower = words[address >> 2 & 2 | 1];
u64 upper = words[paddr >> 2 & 2 | 0];
u64 lower = words[paddr >> 2 & 2 | 1];
return upper << 32 | lower << 0;
}
}
template<u32 Size>
auto CPU::DataCache::Line::write(u32 address, u64 data) -> void {
if constexpr(Size == Byte) { bytes[address >> 0 & 15 ^ 3] = data; }
if constexpr(Size == Half) { halfs[address >> 1 & 7 ^ 1] = data; }
if constexpr(Size == Word) { words[address >> 2 & 3 ^ 0] = data; }
auto CPU::DataCache::Line::write(u32 paddr, u64 data) -> void {
if constexpr(Size == Byte) { bytes[paddr >> 0 & 15 ^ 3] = data; }
if constexpr(Size == Half) { halfs[paddr >> 1 & 7 ^ 1] = data; }
if constexpr(Size == Word) { words[paddr >> 2 & 3 ^ 0] = data; }
if constexpr(Size == Dual) {
words[address >> 2 & 2 | 0] = data >> 32;
words[address >> 2 & 2 | 1] = data >> 0;
words[paddr >> 2 & 2 | 0] = data >> 32;
words[paddr >> 2 & 2 | 1] = data >> 0;
}
dirty |= ((1 << Size) - 1) << (address & 0xF);
dirty |= ((1 << Size) - 1) << (paddr & 0xF);
dirtyPc = cpu.ipu.pc;
}
template<u32 Size>
auto CPU::DataCache::read(u32 vaddr, u32 address) -> u64 {
auto CPU::DataCache::read(u64 vaddr, u32 paddr) -> u64 {
auto& line = this->line(vaddr);
if(!line.hit(address)) {
if(!line.hit(paddr)) {
if(line.valid && line.dirty) line.writeBack();
line.fill(address);
line.fill(paddr);
} else {
cpu.step(1 * 2);
}
return line.read<Size>(address);
return line.read<Size>(paddr);
}
auto CPU::DataCache::readDebug(u32 vaddr, u32 address) -> u8 {
auto CPU::DataCache::readDebug(u64 vaddr, u32 paddr) -> u8 {
auto& line = this->line(vaddr);
if(!line.hit(address)) {
if(!line.hit(paddr)) {
Thread dummyThread{};
return bus.read<Byte>(address, dummyThread, "Ares Debugger");
return bus.read<Byte>(paddr, dummyThread, "Ares Debugger");
}
return line.read<Byte>(address);
return line.read<Byte>(paddr);
}
template<u32 Size>
auto CPU::DataCache::write(u32 vaddr, u32 address, u64 data) -> void {
auto CPU::DataCache::write(u64 vaddr, u32 paddr, u64 data) -> void {
auto& line = this->line(vaddr);
if(!line.hit(address)) {
if(!line.hit(paddr)) {
if(line.valid && line.dirty) line.writeBack();
line.fill(address);
line.fill(paddr);
} else {
cpu.step(1 * 2);
}
line.write<Size>(address, data);
line.write<Size>(paddr, data);
}
auto CPU::DataCache::writeDebug(u64 vaddr, u32 paddr, u8 data) -> void {
auto& line = this->line(vaddr);
if(!line.hit(paddr)) {
Thread dummyThread{};
return bus.write<Byte>(paddr, data, dummyThread, "Ares Debugger");
}
line.write<Byte>(paddr, data);
}
auto CPU::DataCache::power(bool reset) -> void {
@@ -91,4 +100,4 @@ auto CPU::DataCache::power(bool reset) -> void {
}
template
auto CPU::DataCache::Line::write<Byte>(u32 address, u64 data) -> void;
auto CPU::DataCache::Line::write<Byte>(u32 paddr, u64 data) -> void;
+22 -5
View File
@@ -1,10 +1,6 @@
auto CPU::Exception::trigger(u32 code, u32 coprocessor, bool tlbMiss) -> void {
self.debugger.exception(code);
if(code != 0) {
auto sig = (code == 2 || code == 3) ? GDB::Signal::SEGV : GDB::Signal::TRAP;
GDB::server.reportSignal(sig, self.ipu.pc);
}
reportGDBException(code, self.ipu.pc);
u64 vectorBase = !self.scc.status.vectorLocation ? (s32)0x8000'0000 : (s32)0xbfc0'0200;
@@ -64,3 +60,24 @@ auto CPU::Exception::nmi() -> void {
self.scc.epcError = self.ipu.pc;
self.pipeline.setPc(0xffff'ffff'bfc0'0000);
}
auto CPU::Exception::reportGDBException(int code, u64 pc) -> void {
switch(code) {
case 0: GDB::server.reportSignal(GDB::Signal::WINCH, pc); break; // ignored by default
case 1: GDB::server.reportSignal(GDB::Signal::SEGV, pc); break;
case 2: GDB::server.reportSignal(GDB::Signal::SEGV, pc); break;
case 3: GDB::server.reportSignal(GDB::Signal::SEGV, pc); break;
case 4: GDB::server.reportSignal(GDB::Signal::BUS, pc); break;
case 5: GDB::server.reportSignal(GDB::Signal::BUS, pc); break;
case 6: GDB::server.reportSignal(GDB::Signal::ABORT, pc); break;
case 7: GDB::server.reportSignal(GDB::Signal::ABORT, pc); break;
case 8: GDB::server.reportSignal(GDB::Signal::SYS, pc); break;
case 9: GDB::server.reportSignal(GDB::Signal::STOP, pc); break;
case 10: GDB::server.reportSignal(GDB::Signal::ILL, pc); break;
case 11: GDB::server.reportSignal(GDB::Signal::URG, pc); break; // ignored by default
case 12: GDB::server.reportSignal(GDB::Signal::FPE, pc); break;
case 13: GDB::server.reportSignal(GDB::Signal::TRAP, pc); break;
case 15: GDB::server.reportSignal(GDB::Signal::FPE, pc); break;
case 23: GDB::server.reportSignal(GDB::Signal::LOST, pc); break;
}
}
+1 -1
View File
@@ -250,7 +250,7 @@ auto CPU::checkFPUExceptions() -> bool {
#define CHECK_FPE_IMPL(type, res, operation, convert) \
fenv.clearExcept(); \
volatile type v##res = [&]() noinline -> type { return operation; }(); \
volatile type v##res = [&]() NALL_NOINLINE -> type { return operation; }(); \
if (checkFPUExceptions<convert>()) return; \
type res = v##res;
+32 -30
View File
@@ -118,27 +118,29 @@ auto CPU::BREAK() -> void {
}
auto CPU::CACHE(u8 operation, cr64& rs, s16 imm) -> void {
u32 address;
if (auto phys = devirtualize(rs.u64 + imm)) address = *phys;
else return;
auto access = devirtualize<Read, Word>(rs.u64 + imm);
if (!access) return;
if(!access.cache) {
debug(unusual, "[CPU] CACHE access to non-cacheable address ", hex(access.vaddr, 8L), " at PC ", hex(PC, 16L));
}
switch(operation) {
case 0x00: { //icache index invalidate
auto& line = icache.line(address);
auto& line = icache.line(access.vaddr);
line.valid = 0;
break;
}
case 0x04: { //icache load tag
auto& line = icache.line(address);
auto& line = icache.line(access.vaddr);
scc.tagLo.primaryCacheState = line.valid << 1;
scc.tagLo.physicalAddress = line.tag;
break;
}
case 0x08: { //icache store tag
auto& line = icache.line(address);
auto& line = icache.line(access.vaddr);
line.valid = scc.tagLo.primaryCacheState.bit(1);
line.tag = scc.tagLo.physicalAddress;
if(scc.tagLo.primaryCacheState == 0b01) debug(unusual, "[CPU] CACHE CPCS=1");
@@ -147,39 +149,39 @@ auto CPU::CACHE(u8 operation, cr64& rs, s16 imm) -> void {
}
case 0x10: { //icache hit invalidate
auto& line = icache.line(address);
if(line.hit(address)) line.valid = 0;
auto& line = icache.line(access.vaddr);
if(line.hit(access.paddr)) line.valid = 0;
break;
}
case 0x14: { //icache fill
auto& line = icache.line(address);
line.fill(address, cpu);
auto& line = icache.line(access.vaddr);
line.fill(access.paddr, cpu);
break;
}
case 0x18: { //icache hit write back
auto& line = icache.line(address);
if(line.hit(address)) line.writeBack(cpu);
auto& line = icache.line(access.vaddr);
if(line.hit(access.paddr)) line.writeBack(cpu);
break;
}
case 0x01: { //dcache index write back invalidate
auto& line = dcache.line(address);
auto& line = dcache.line(access.vaddr);
if(line.valid && line.dirty) line.writeBack();
line.valid = 0;
break;
}
case 0x05: { //dcache index load tag
auto& line = dcache.line(address);
auto& line = dcache.line(access.vaddr);
scc.tagLo.primaryCacheState = line.valid << 1 | line.dirty << 0;
scc.tagLo.physicalAddress = line.tag;
break;
}
case 0x09: { //dcache index store tag
auto& line = dcache.line(address);
auto& line = dcache.line(access.vaddr);
line.valid = scc.tagLo.primaryCacheState.bit(1);
line.dirty = scc.tagLo.primaryCacheState.bit(0);
line.tag = scc.tagLo.physicalAddress;
@@ -189,17 +191,17 @@ auto CPU::CACHE(u8 operation, cr64& rs, s16 imm) -> void {
}
case 0x0d: { //dcache create dirty exclusive
auto& line = dcache.line(address);
if(!line.hit(address) && line.dirty) line.writeBack();
line.tag = address & ~0xfff;
auto& line = dcache.line(access.vaddr);
if(!line.hit(access.paddr) && line.dirty) line.writeBack();
line.tag = access.paddr & ~0xfff;
line.valid = 1;
line.dirty = 1;
break;
}
case 0x11: { //dcache hit invalidate
auto& line = dcache.line(address);
if(line.hit(address)) {
auto& line = dcache.line(access.vaddr);
if(line.hit(access.paddr)) {
line.valid = 0;
line.dirty = 0;
}
@@ -207,8 +209,8 @@ auto CPU::CACHE(u8 operation, cr64& rs, s16 imm) -> void {
}
case 0x15: { //dcache hit write back invalidate
auto& line = dcache.line(address);
if(line.hit(address)) {
auto& line = dcache.line(access.vaddr);
if(line.hit(access.paddr)) {
if(line.dirty) line.writeBack();
line.valid = 0;
}
@@ -216,8 +218,8 @@ auto CPU::CACHE(u8 operation, cr64& rs, s16 imm) -> void {
}
case 0x19: { //dcache hit write back
auto& line = dcache.line(address);
if(line.hit(address)) {
auto& line = dcache.line(access.vaddr);
if(line.hit(access.paddr)) {
if(line.dirty) line.writeBack();
}
break;
@@ -610,10 +612,10 @@ auto CPU::LHU(r64& rt, cr64& rs, s16 imm) -> void {
}
auto CPU::LL(r64& rt, cr64& rs, s16 imm) -> void {
if(auto address = devirtualize(rs.u64 + imm)) {
if (auto data = read<Word>(rs.u64 + imm)) {
if(auto access = devirtualize<Read, Word>(rs.u64 + imm)) {
if (auto data = read<Word>(access.vaddr)) {
rt.u64 = s32(*data);
scc.ll = *address >> 4;
scc.ll = access.paddr >> 4;
scc.llbit = 1;
}
}
@@ -621,10 +623,10 @@ auto CPU::LL(r64& rt, cr64& rs, s16 imm) -> void {
auto CPU::LLD(r64& rt, cr64& rs, s16 imm) -> void {
if(!context.kernelMode() && context.bits == 32) return exception.reservedInstruction();
if(auto address = devirtualize(rs.u64 + imm)) {
if (auto data = read<Dual>(rs.u64 + imm)) {
if(auto access = devirtualize<Read, Word>(rs.u64 + imm)) {
if (auto data = read<Dual>(access.vaddr)) {
rt.u64 = *data;
scc.ll = *address >> 4;
scc.ll = access.paddr >> 4;
scc.llbit = 1;
}
}
+56 -156
View File
@@ -88,60 +88,42 @@ auto CPU::segment(u64 vaddr) -> Context::Segment {
unreachable;
}
auto CPU::devirtualize(u64 vaddr) -> maybe<u64> {
if(vaddrAlignedError<Word>(vaddr, false)) return nothing;
template <u32 Dir, u32 Size>
auto CPU::devirtualize(u64 vaddr, bool raiseAlignedError, bool raiseExceptions) -> PhysAccess {
if (raiseAlignedError && vaddrAlignedError<Size>(vaddr, Dir == Write)) {
return PhysAccess{false};
}
//fast path for RDRAM, which is by far the most accessed memory region
if (vaddr >= 0xffff'ffff'8000'0000ull && vaddr <= 0xffff'ffff'83ef'ffffull) {
if constexpr(Dir == Read) return PhysAccess{true, true, (u32)vaddr & 0x3eff'ffff, vaddr};
if constexpr(Dir == Write) return PhysAccess{true, true, (u32)vaddr & 0x3eff'ffff, vaddr};
}
switch(segment(vaddr)) {
case Context::Segment::Unused:
addressException(vaddr);
exception.addressLoad();
return nothing;
if(raiseExceptions) {
addressException(vaddr);
if constexpr(Dir == Read) exception.addressLoad();
if constexpr(Dir == Write) exception.addressStore();
}
return PhysAccess{false};
case Context::Segment::Mapped:
if(auto match = tlb.load(vaddr)) return match.address & context.physMask;
addressException(vaddr);
return nothing;
if constexpr(Dir == Read) if(auto access = tlb.load (vaddr, !raiseExceptions)) return access;
if constexpr(Dir == Write) if(auto access = tlb.store(vaddr, !raiseExceptions)) return access;
return PhysAccess{false};
case Context::Segment::Cached:
return PhysAccess{true, true, (u32)(vaddr & 0x1fff'ffff), vaddr};
case Context::Segment::Direct:
return vaddr & 0x1fff'ffff;
return PhysAccess{true, false, (u32)(vaddr & 0x1fff'ffff), vaddr};
case Context::Segment::Cached32:
return PhysAccess{true, true, (u32)(vaddr & 0xffff'ffff), vaddr};
case Context::Segment::Direct32:
return vaddr & 0xffff'ffff;
return PhysAccess{true, false, (u32)(vaddr & 0xffff'ffff), vaddr};
}
unreachable;
}
// Fast(er) version of devirtualize for icache lookups
// avoids handling unmapped regions/exceptions as these should have already
// been handled by instruction fetch, also ignores tlb match failure
auto CPU::devirtualizeFast(u64 vaddr) -> u64 {
// Assume address space is mapped into pages that are 4kb in size
// If we have a cached physical address for this page, use it
// This cache is purged on any writes to the TLB so should never become stale
auto vbase = vaddr >> 12;
if(devirtualizeCache.vbase == vbase && devirtualizeCache.pbase) {
auto offset = vaddr & 0xfff;
return (devirtualizeCache.pbase & ~0xfff) + offset;
}
// Cache the physical address of this page for the next call
devirtualizeCache.vbase = vaddr >> 12;
switch(segment(vaddr)) {
case Context::Segment::Mapped: {
auto match = tlb.loadFast(vaddr);
return devirtualizeCache.pbase = match.address & context.physMask;
}
case Context::Segment::Cached:
case Context::Segment::Direct:
return devirtualizeCache.pbase = vaddr & 0x1fff'ffff;
case Context::Segment::Cached32:
case Context::Segment::Direct32:
return devirtualizeCache.pbase = vaddr & 0xffff'ffff;
}
return devirtualizeCache.pbase = 0;
}
auto CPU::devirtualizeDebug(u64 vaddr) -> u64 {
return devirtualizeFast(vaddr); // this wrapper preserves the inlining of 'devirtualizeFast'
return devirtualize<Read, Byte>(vaddr, false).paddr; // this wrapper preserves the inlining of 'devirtualizeFast'
}
template<u32 Size>
@@ -164,132 +146,45 @@ inline auto CPU::busReadBurst(u32 address, u32 *data) -> void {
return bus.readBurst<Size>(address, data, *this);
}
auto CPU::fetch(u64 vaddr) -> maybe<u32> {
if(vaddrAlignedError<Word>(vaddr, false)) return nothing;
switch(segment(vaddr)) {
case Context::Segment::Unused:
step(1 * 2);
addressException(vaddr);
exception.addressLoad();
return nothing;
case Context::Segment::Mapped:
if(auto match = tlb.load(vaddr)) {
if(match.cache) return icache.fetch(vaddr, match.address & context.physMask, cpu);
step(1 * 2);
return busRead<Word>(match.address & context.physMask);
}
step(1 * 2);
addressException(vaddr);
return nothing;
case Context::Segment::Cached:
return icache.fetch(vaddr, vaddr & 0x1fff'ffff, cpu);
case Context::Segment::Cached32:
return icache.fetch(vaddr, vaddr & 0xffff'ffff, cpu);
case Context::Segment::Direct:
step(1 * 2);
return busRead<Word>(vaddr & 0x1fff'ffff);
case Context::Segment::Direct32:
step(1 * 2);
return busRead<Word>(vaddr & 0xffff'ffff);
}
unreachable;
auto CPU::fetch(PhysAccess access) -> maybe<u32> {
step(1 * 2);
if(!access) return nothing;
if(access.cache) return icache.fetch(access.vaddr, access.paddr, cpu);
return busRead<Word>(access.paddr);
}
template<u32 Size>
auto CPU::read(u64 vaddr) -> maybe<u64> {
if(vaddrAlignedError<Size>(vaddr, false)) return nothing;
GDB::server.reportMemRead(vaddr, Size);
switch(segment(vaddr)) {
case Context::Segment::Unused:
step(1 * 2);
addressException(vaddr);
exception.addressLoad();
return nothing;
case Context::Segment::Mapped:
if(auto match = tlb.load(vaddr)) {
if(match.cache) return dcache.read<Size>(vaddr, match.address & context.physMask);
step(1 * 2);
return busRead<Size>(match.address & context.physMask);
}
step(1 * 2);
addressException(vaddr);
return nothing;
case Context::Segment::Cached:
return dcache.read<Size>(vaddr, vaddr & 0x1fff'ffff);
case Context::Segment::Cached32:
return dcache.read<Size>(vaddr, vaddr & 0xffff'ffff);
case Context::Segment::Direct:
step(1 * 2);
return busRead<Size>(vaddr & 0x1fff'ffff);
case Context::Segment::Direct32:
step(1 * 2);
return busRead<Size>(vaddr & 0xffff'ffff);
}
unreachable;
auto CPU::read(PhysAccess access) -> maybe<u64> {
if(!access) return nothing;
GDB::server.reportMemRead(access.vaddr, Size);
if(access.cache) return dcache.read<Size>(access.vaddr, access.paddr);
return busRead<Size>(access.paddr);
}
auto CPU::readDebug(u64 vaddr) -> u8 {
Thread dummyThread{};
auto access = devirtualize<Read, Byte>(vaddr, false, false);
if(!access) return 0;
if(access.cache) return dcache.readDebug(access.vaddr, access.paddr);
return bus.read<Byte>(access.paddr, dummyThread, "Ares Debugger");
}
switch(segment(vaddr)) {
case Context::Segment::Unused: return 0;
case Context::Segment::Mapped:
if(auto match = tlb.load(vaddr, true)) {
if(match.cache) return dcache.readDebug(vaddr, match.address & context.physMask);
return bus.read<Byte>(match.address & context.physMask, dummyThread, "Ares Debugger");
}
return 0;
case Context::Segment::Cached:
return dcache.readDebug(vaddr, vaddr & 0x1fff'ffff);
case Context::Segment::Cached32:
return dcache.readDebug(vaddr, vaddr & 0xffff'ffff);
case Context::Segment::Direct:
return bus.read<Byte>(vaddr & 0x1fff'ffff, dummyThread, "Ares Debugger");
case Context::Segment::Direct32:
return bus.read<Byte>(vaddr & 0xffff'ffff, dummyThread, "Ares Debugger");
}
unreachable;
template<u32 Size>
auto CPU::write(PhysAccess access, u64 data) -> bool {
if(!access) return false;
GDB::server.reportMemWrite(access.vaddr, Size);
if(access.cache) return dcache.write<Size>(access.vaddr, access.paddr, data), true;
return busWrite<Size>(access.paddr, data), true;
}
template<u32 Size>
auto CPU::write(u64 vaddr0, u64 data, bool alignedError) -> bool {
if(alignedError && vaddrAlignedError<Size>(vaddr0, true)) return false;
u64 vaddr = vaddr0 & ~((u64)Size - 1);
GDB::server.reportMemWrite(vaddr0, Size);
switch(segment(vaddr)) {
case Context::Segment::Unused:
step(1 * 2);
addressException(vaddr0);
exception.addressStore();
return false;
case Context::Segment::Mapped:
if(auto match = tlb.store(vaddr)) {
if(match.cache) return dcache.write<Size>(vaddr, match.address & context.physMask, data), true;
step(1 * 2);
return busWrite<Size>(match.address & context.physMask, data), true;
}
step(1 * 2);
addressException(vaddr0);
return false;
case Context::Segment::Cached:
return dcache.write<Size>(vaddr, vaddr & 0x1fff'ffff, data), true;
case Context::Segment::Cached32:
return dcache.write<Size>(vaddr, vaddr & 0xffff'ffff, data), true;
case Context::Segment::Direct:
step(1 * 2);
return busWrite<Size>(vaddr & 0x1fff'ffff, data), true;
case Context::Segment::Direct32:
step(1 * 2);
return busWrite<Size>(vaddr & 0xffff'ffff, data), true;
}
unreachable;
auto CPU::writeDebug(u64 vaddr, u64 data) -> bool {
auto access = devirtualize<Write, Size>(vaddr, false, false);
if(!access) return false;
GDB::server.reportMemWrite(access.vaddr, Size);
if(access.cache) return dcache.write<Size>(access.vaddr, access.paddr, data), true;
return busWrite<Size>(access.paddr, data), true;
}
template<u32 Size>
@@ -321,3 +216,8 @@ auto CPU::addressException(u64 vaddr) -> void {
scc.xcontext.badVirtualAddress = vaddr >> 13;
scc.xcontext.region = vaddr >> 62;
}
template auto CPU::writeDebug<Byte>(u64, u64) -> bool;
template auto CPU::writeDebug<Half>(u64, u64) -> bool;
template auto CPU::writeDebug<Word>(u64, u64) -> bool;
template auto CPU::writeDebug<Dual>(u64, u64) -> bool;
+34 -15
View File
@@ -9,36 +9,39 @@ auto CPU::Recompiler::pool(u32 address) -> Pool* {
return pool;
}
auto CPU::Recompiler::block(u32 vaddr, u32 address, bool singleInstruction) -> Block* {
auto CPU::Recompiler::block(u64 vaddr, u32 address, bool singleInstruction) -> Block* {
if(auto block = pool(address)->blocks[address >> 2 & 0x3f]) return block;
auto block = emit(vaddr, address, singleInstruction);
pool(address)->blocks[address >> 2 & 0x3f] = block;
memory::jitprotect(true);
if(block) {
pool(address)->blocks[address >> 2 & 0x3f] = block;
memory::jitprotect(true);
}
return block;
}
auto CPU::Recompiler::fastFetchBlock(u32 address) -> Block* {
auto& pool = pools[address >> 8 & 0x1fffff];
if(pool) return pool->blocks[address >> 2 & 0x3f];
return nullptr;
}
#define IpuBase offsetof(IPU, r[16])
#define IpuReg(r) sreg(1), offsetof(IPU, r) - IpuBase
#define PipelineReg(x) mem(sreg(0), offsetof(CPU, pipeline) + offsetof(Pipeline, x))
auto CPU::Recompiler::emit(u32 vaddr, u32 address, bool singleInstruction) -> Block* {
auto CPU::Recompiler::emit(u64 vaddr, u32 address, bool singleInstruction) -> Block* {
if(unlikely(allocator.available() < 1_MiB)) {
print("CPU allocator flush\n");
allocator.release();
reset();
}
auto block = (Block*)allocator.acquire(sizeof(Block));
// abort compilation of block asap if the instruction cache is not coherent
if(!self.icache.coherent(vaddr, address))
return nullptr;
bool abort = false;
beginFunction(3);
Thread thread;
bool hasBranched = 0;
int numInsn = 0;
constexpr u32 branchToSelf = 0x1000'ffff; //beq 0,0,<pc>
u32 jumpToSelf = 2 << 26 | vaddr >> 2 & 0x3ff'ffff; //j <pc>
while(true) {
u32 instruction = bus.read<Word>(address, thread, "Ares Recompiler");
mov32(PipelineReg(nstate), imm(0));
@@ -46,30 +49,46 @@ auto CPU::Recompiler::emit(u32 vaddr, u32 address, bool singleInstruction) -> Bl
mov64(PipelineReg(pc), reg(0));
add64(PipelineReg(nextpc), reg(0), imm(4));
if(callInstructionPrologue) {
mov32(reg(1), imm(instruction));
mov64(reg(1), imm(vaddr));
mov32(reg(2), imm(instruction));
call(&CPU::instructionPrologue);
}
if(numInsn == 0 || (vaddr&0x1f)==0){
//abort compilation of block if the instruction cache is not coherent
if(!self.icache.coherent(vaddr, address)) {
resetCompiler();
return nullptr;
}
mov64(reg(1), imm(vaddr));
mov32(reg(2), imm(address));
call(&CPU::jitFetch);
}
numInsn++;
bool branched = emitEXECUTE(instruction);
if(unlikely(instruction == 0x1000'ffff //beq 0,0,<pc>
|| instruction == (2 << 26 | vaddr >> 2 & 0x3ff'ffff))) { //j <pc>
if(unlikely(instruction == branchToSelf || instruction == jumpToSelf)) {
//accelerate idle loops
mov32(reg(1), imm(64 * 2));
call(&CPU::step);
} else {
mov32(reg(1), imm(1 * 2));
call(&CPU::step);
}
call(&CPU::instructionEpilogue<1>);
test32(PipelineReg(state), imm(Pipeline::EndBlock), set_z);
mov32(PipelineReg(state), PipelineReg(nstate));
mov64(mem(IpuReg(pc)), PipelineReg(pc));
vaddr += 4;
address += 4;
jumpToSelf += 4;
if(hasBranched || (address & 0xfc) == 0 || singleInstruction) break; //block boundary
hasBranched = branched;
jumpEpilog(flag_nz);
}
jumpEpilog();
memory::jitprotect(false);
auto block = (Block*)allocator.acquire(sizeof(Block));
block->code = endFunction();
//print(hex(PC, 8L), " ", instructions, " ", size(), "\n");
+26 -36
View File
@@ -1,23 +1,23 @@
auto CPU::TLB::load(u64 vaddr, const Entry& entry, bool noExceptions) -> maybe<Match> {
auto CPU::TLB::load(u64 vaddr, const Entry& entry, bool noExceptions) -> maybe<PhysAccess> {
if(!entry.globals && entry.addressSpaceID != self.scc.tlb.addressSpaceID) return nothing;
if((vaddr & entry.addressMaskHi) != entry.virtualAddress) return nothing;
if(vaddr >> 62 != entry.region) return nothing;
bool lo = vaddr & entry.addressSelect;
if(!entry.valid[lo]) {
if(noExceptions)return Match{false};
if(noExceptions)return PhysAccess{false};
self.addressException(vaddr);
self.debugger.tlbLoadInvalid(vaddr);
self.exception.tlbLoadInvalid();
return Match{false};
return PhysAccess{false};
}
physicalAddress = entry.physicalAddress[lo] + (vaddr & entry.addressMaskLo);
self.debugger.tlbLoad(vaddr, physicalAddress);
return Match{true, entry.cacheAlgorithm[lo] != 2, physicalAddress};
return PhysAccess{true, entry.cacheAlgorithm[lo] != 2, physicalAddress, vaddr};
}
auto CPU::TLB::load(u64 vaddr, bool noExceptions) -> Match {
auto CPU::TLB::load(u64 vaddr, bool noExceptions) -> PhysAccess {
for(auto& entry : this->tlbCache.entry) {
if(!entry.entry) continue;
if(auto match = load(vaddr, *entry.entry, noExceptions)) {
@@ -41,45 +41,33 @@ auto CPU::TLB::load(u64 vaddr, bool noExceptions) -> Match {
return {false};
}
// Fast(er) version of load for recompiler icache lookups
// avoids exceptions/debug checks
auto CPU::TLB::loadFast(u64 vaddr) -> Match {
for(auto& entry : this->entry) {
if(!entry.globals && entry.addressSpaceID != self.scc.tlb.addressSpaceID) continue;
if((vaddr & entry.addressMaskHi) != entry.virtualAddress) continue;
if(vaddr >> 62 != entry.region) continue;
bool lo = vaddr & entry.addressSelect;
if(!entry.valid[lo]) return { false, 0, 0 };
physicalAddress = entry.physicalAddress[lo] + (vaddr & entry.addressMaskLo);
return {true, entry.cacheAlgorithm[lo] != 2, physicalAddress};
}
return {false, 0, 0};
}
auto CPU::TLB::store(u64 vaddr, const Entry& entry) -> maybe<Match> {
auto CPU::TLB::store(u64 vaddr, const Entry& entry, bool noExceptions) -> maybe<PhysAccess> {
if(!entry.globals && entry.addressSpaceID != self.scc.tlb.addressSpaceID) return nothing;
if((vaddr & entry.addressMaskHi) != entry.virtualAddress) return nothing;
if(vaddr >> 62 != entry.region) return nothing;
bool lo = vaddr & entry.addressSelect;
if(!entry.valid[lo]) {
self.addressException(vaddr);
self.debugger.tlbStoreInvalid(vaddr);
self.exception.tlbStoreInvalid();
return Match{false};
if(!noExceptions) {
self.addressException(vaddr);
self.debugger.tlbStoreInvalid(vaddr);
self.exception.tlbStoreInvalid();
}
return PhysAccess{false};
}
if(!entry.dirty[lo]) {
self.addressException(vaddr);
self.debugger.tlbModification(vaddr);
self.exception.tlbModification();
return Match{false};
if(!noExceptions) {
self.addressException(vaddr);
self.debugger.tlbModification(vaddr);
self.exception.tlbModification();
}
return PhysAccess{false};
}
physicalAddress = entry.physicalAddress[lo] + (vaddr & entry.addressMaskLo);
self.debugger.tlbStore(vaddr, physicalAddress);
return Match{true, entry.cacheAlgorithm[lo] != 2, physicalAddress};
return PhysAccess{true, entry.cacheAlgorithm[lo] != 2, physicalAddress, vaddr};
}
auto CPU::TLB::store(u64 vaddr) -> Match {
auto CPU::TLB::store(u64 vaddr, bool noExceptions) -> PhysAccess {
for(auto& entry : this->tlbCache.entry) {
if(!entry.entry) continue;
if(auto match = store(vaddr, *entry.entry)) {
@@ -89,15 +77,17 @@ auto CPU::TLB::store(u64 vaddr) -> Match {
}
for(auto& entry : this->entry) {
if(auto match = store(vaddr, entry)) {
if(auto match = store(vaddr, entry, noExceptions)) {
this->tlbCache.insert(entry);
return *match;
}
}
self.addressException(vaddr);
self.debugger.tlbStoreMiss(vaddr);
self.exception.tlbStoreMiss();
if(!noExceptions) {
self.addressException(vaddr);
self.debugger.tlbStoreMiss(vaddr);
self.exception.tlbStoreMiss();
}
return {false};
}
+40 -8
View File
@@ -137,7 +137,7 @@ auto System::initDebugHooks() -> void {
};
GDB::server.hooks.read = [](u64 address, u32 byteCount) -> string {
address |= 0xFFFFFFFF'00000000ull;
address = (s32)address;
string res{};
res.resize(byteCount * 2);
@@ -152,16 +152,48 @@ auto System::initDebugHooks() -> void {
return res;
};
GDB::server.hooks.write = [](u64 address, u32 unitSize, u64 value) {
address |= 0xFFFFFFFF'00000000ull;
switch(unitSize) {
case Byte: cpu.write<Byte>(address, value, false); break;
case Half: cpu.write<Half>(address, value, false); break;
case Word: cpu.write<Word>(address, value, false); break;
case Dual: cpu.write<Dual>(address, value, false); break;
GDB::server.hooks.write = [](u64 address, vector<u8> data) {
address = (s32)address;
// Handle writes of different/unaligned sizes only within the RDRAM area,
// where we are sure that the write size does not really matter
if(address >= 0xffff'ffff'8000'0000ull && address <= 0xffff'ffff'83ef'ffffull) {
for(auto b : data) {
cpu.dcache.writeDebug(address, address & 0x1fff'ffff, b);
address++;
}
} else if (address >= 0xffff'ffff'a000'0000ull && address <= 0xffff'ffff'a3ef'ffffull) {
Thread dummyThread{};
for(auto b : data) {
bus.write<Byte>(address & 0x1fff'ffff, b, dummyThread, "Ares Debugger");
address++;
}
} else {
// Otherwise, the write is expected to be of a set size to an aligned address.
u64 value;
switch(data.size()) {
case Byte:
value = (u64)data[0];
cpu.writeDebug<Byte>(address, value);
break;
case Half:
value = ((u64)data[0]<<8) | ((u64)data[1]<<0);
cpu.writeDebug<Half>(address, value);
break;
case Word:
value = ((u64)data[0]<<24) | ((u64)data[1]<<16) | ((u64)data[2]<<8) | ((u64)data[3]<<0);
cpu.writeDebug<Word>(address, value);
break;
case Dual:
value = ((u64)data[0]<<56) | ((u64)data[1]<<48) | ((u64)data[2]<<40) | ((u64)data[3]<<32);
value |= ((u64)data[4]<<24) | ((u64)data[5]<<16) | ((u64)data[6]<< 8) | ((u64)data[7]<< 0);
cpu.writeDebug<Dual>(address, value);
break;
}
}
};
GDB::server.hooks.regRead = [](u32 regIdx) {
if(regIdx < 32) {
return hex(cpu.ipu.r[regIdx].u64, 16, '0');
+1 -1
View File
@@ -1 +1 @@
43bc31642cc70d04adb828a285e68cdbde7110a9
1f69c762be68feb9fcd5276d75acc6e5a6160a19
@@ -201,6 +201,11 @@ bool Renderer::init_caps()
LOGW("Current proprietary Intel Windows driver is tested to perform much better without 8/16-bit integer support.\n");
allow_small_types = false;
}
else if (features.driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY_KHR)
{
LOGW("Current proprietary Qcom driver is known to be buggy with 8/16-bit integer arithmetic, disabling support for time being.\n");
allow_small_types = false;
}
// Intel ANV *must* use small integer arithmetic, or it doesn't pass test suite.
}
@@ -716,7 +721,7 @@ static int normalize_dzpix(int dz)
else if (dz == 0)
return 1;
unsigned bit = 31 - leading_zeroes(dz);
unsigned bit = 31 - Util::leading_zeroes(dz);
return 1 << (bit + 1);
}
@@ -1680,7 +1685,7 @@ void Renderer::submit_span_setup_jobs(Vulkan::CommandBuffer &cmd, bool upscale)
cmd.set_buffer_view(1, 0, *instance.gpu.span_info_jobs_view);
cmd.set_specialization_constant_mask(3);
cmd.set_specialization_constant(0, (upscale ? caps.upscaling : 1) * ImplementationConstants::DefaultWorkgroupSize);
cmd.set_specialization_constant(1, upscale ? trailing_zeroes(caps.upscaling) : 0u);
cmd.set_specialization_constant(1, upscale ? Util::trailing_zeroes(caps.upscaling) : 0u);
Vulkan::QueryPoolHandle begin_ts, end_ts;
if (caps.timestamp >= 2)
@@ -1780,7 +1785,7 @@ void Renderer::submit_rasterization(Vulkan::CommandBuffer &cmd, Vulkan::Buffer &
if (caps.timestamp >= 2)
start_ts = cmd.write_timestamp(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);
uint32_t scale_log2_bit = (upscaling ? trailing_zeroes(caps.upscaling) : 0u) << RASTERIZATION_UPSCALING_LOG2_BIT_OFFSET;
uint32_t scale_log2_bit = (upscaling ? Util::trailing_zeroes(caps.upscaling) : 0u) << RASTERIZATION_UPSCALING_LOG2_BIT_OFFSET;
for (size_t i = 0; i < stream.static_raster_state_cache.size(); i++)
{
@@ -1891,7 +1896,7 @@ void Renderer::submit_tile_binning_combined(Vulkan::CommandBuffer &cmd, bool ups
if (supports_subgroup_size_control(32, subgroup_size))
{
cmd.enable_subgroup_size_control(true);
cmd.set_subgroup_size_log2(true, 5, trailing_zeroes(subgroup_size));
cmd.set_subgroup_size_log2(true, 5, Util::trailing_zeroes(subgroup_size));
}
}
else
@@ -2092,7 +2097,7 @@ void Renderer::submit_depth_blend(Vulkan::CommandBuffer &cmd, Vulkan::Buffer &tm
cmd.set_specialization_constant(5, Limits::MaxPrimitives);
cmd.set_specialization_constant(6, upscaled ? caps.max_width : Limits::MaxWidth);
cmd.set_specialization_constant(7, uint32_t(force_write_mask || (!is_host_coherent && !upscaled)) |
((upscaled ? trailing_zeroes(caps.upscaling) : 0u) << 1u));
((upscaled ? Util::trailing_zeroes(caps.upscaling) : 0u) << 1u));
if (upscaled)
cmd.set_storage_buffer(0, 0, *upscaling_multisampled_rdram);
File diff suppressed because it is too large. Load diff
@@ -484,7 +484,7 @@ Vulkan::ImageHandle VideoInterface::vram_fetch_stage(const Registers &regs, unsi
async_cmd->set_specialization_constant_mask(7);
async_cmd->set_specialization_constant(0, uint32_t(rdram_size));
async_cmd->set_specialization_constant(1, regs.status & (VI_CONTROL_TYPE_MASK | VI_CONTROL_META_AA_BIT));
async_cmd->set_specialization_constant(2, trailing_zeroes(scaling_factor));
async_cmd->set_specialization_constant(2, Util::trailing_zeroes(scaling_factor));
async_cmd->push_constants(&push, 0, sizeof(push));
async_cmd->dispatch((extract_width + 15) / 16,
@@ -40,14 +40,18 @@ struct AlignedAllocation
static void *operator new(size_t size)
{
void *ret = ::Util::memalign_alloc(alignof(T), size);
#ifdef __EXCEPTIONS
if (!ret) throw std::bad_alloc();
#endif
return ret;
}
static void *operator new[](size_t size)
{
void *ret = ::Util::memalign_alloc(alignof(T), size);
#ifdef __EXCEPTIONS
if (!ret) throw std::bad_alloc();
#endif
return ret;
}
+23 -14
View File
@@ -29,13 +29,22 @@
namespace Util
{
#ifdef __GNUC__
#define leading_zeroes(x) ((x) == 0 ? 32 : __builtin_clz(x))
#define trailing_zeroes(x) ((x) == 0 ? 32 : __builtin_ctz(x))
#define trailing_ones(x) __builtin_ctz(~uint32_t(x))
#define leading_zeroes64(x) ((x) == 0 ? 64 : __builtin_clzll(x))
#define trailing_zeroes64(x) ((x) == 0 ? 64 : __builtin_ctzll(x))
#define trailing_ones64(x) __builtin_ctzll(~uint64_t(x))
#define popcount32(x) __builtin_popcount(x)
#define leading_zeroes_(x) ((x) == 0 ? 32 : __builtin_clz(x))
#define trailing_zeroes_(x) ((x) == 0 ? 32 : __builtin_ctz(x))
#define trailing_ones_(x) __builtin_ctz(~uint32_t(x))
#define leading_zeroes64_(x) ((x) == 0 ? 64 : __builtin_clzll(x))
#define trailing_zeroes64_(x) ((x) == 0 ? 64 : __builtin_ctzll(x))
#define trailing_ones64_(x) __builtin_ctzll(~uint64_t(x))
#define popcount32_(x) __builtin_popcount(x)
static inline uint32_t leading_zeroes(uint32_t x) { return leading_zeroes_(x); }
static inline uint32_t trailing_zeroes(uint32_t x) { return trailing_zeroes_(x); }
static inline uint32_t trailing_ones(uint32_t x) { return trailing_ones_(x); }
static inline uint32_t leading_zeroes64(uint64_t x) { return leading_zeroes64_(x); }
static inline uint32_t trailing_zeroes64(uint64_t x) { return trailing_zeroes64_(x); }
static inline uint32_t trailing_ones64(uint64_t x) { return trailing_ones64_(x); }
static inline uint32_t popcount32(uint32_t x) { return popcount32_(x); }
#elif defined(_MSC_VER)
namespace Internal
{
@@ -81,13 +90,13 @@ static inline uint32_t ctz64(uint64_t x)
}
}
#define popcount32(x) ::Util::Internal::popcount32(x)
#define leading_zeroes(x) ::Util::Internal::clz(x)
#define trailing_zeroes(x) ::Util::Internal::ctz(x)
#define trailing_ones(x) ::Util::Internal::ctz(~uint32_t(x))
#define leading_zeroes64(x) ::Util::Internal::clz64(x)
#define trailing_zeroes64(x) ::Util::Internal::ctz64(x)
#define trailing_ones64(x) ::Util::Internal::ctz64(~uint64_t(x))
static inline uint32_t leading_zeroes(uint32_t x) { return Internal::clz(x); }
static inline uint32_t trailing_zeroes(uint32_t x) { return Internal::ctz(x); }
static inline uint32_t trailing_ones(uint32_t x) { return Internal::ctz(~x); }
static inline uint32_t leading_zeroes64(uint64_t x) { return Internal::clz64(x); }
static inline uint32_t trailing_zeroes64(uint64_t x) { return Internal::ctz64(x); }
static inline uint32_t trailing_ones64(uint64_t x) { return Internal::ctz64(~x); }
static inline uint32_t popcount32(uint32_t x) { return Internal::popcount32(x); }
#else
#error "Implement me."
#endif
+2 -2
View File
@@ -107,7 +107,7 @@ int64_t get_current_time_nsecs()
return int64_t(double(li.QuadPart) * static_qpc_freq.inv_freq);
#else
struct timespec ts = {};
#ifdef ANDROID
#if defined(ANDROID) || defined(__FreeBSD__)
constexpr auto timebase = CLOCK_MONOTONIC;
#else
constexpr auto timebase = CLOCK_MONOTONIC_RAW;
@@ -128,4 +128,4 @@ double Timer::end()
auto nt = get_current_time_nsecs();
return double(nt - t) * 1e-9;
}
}
}
@@ -767,7 +767,7 @@ void CommandBuffer::blit_image(const Image &dst, const Image &src,
void CommandBuffer::begin_context()
{
dirty = ~0u;
dirty_sets = ~0u;
dirty_sets_realloc = ~0u;
dirty_vbos = ~0u;
current_pipeline = {};
current_pipeline_layout = VK_NULL_HANDLE;
@@ -1291,9 +1291,9 @@ Pipeline CommandBuffer::build_graphics_pipeline(Device *device, const DeferredPi
// Depth state
VkPipelineDepthStencilStateCreateInfo ds = { VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO };
ds.stencilTestEnable = compile.compatible_render_pass->has_stencil(compile.subpass_index) && compile.static_state.state.stencil_test;
ds.depthTestEnable = compile.compatible_render_pass->has_depth(compile.subpass_index) && compile.static_state.state.depth_test;
ds.depthWriteEnable = compile.compatible_render_pass->has_depth(compile.subpass_index) && compile.static_state.state.depth_write;
ds.stencilTestEnable = compile.compatible_render_pass->has_stencil(compile.subpass_index) && compile.static_state.state.stencil_test != 0;
ds.depthTestEnable = compile.compatible_render_pass->has_depth(compile.subpass_index) && compile.static_state.state.depth_test != 0;
ds.depthWriteEnable = compile.compatible_render_pass->has_depth(compile.subpass_index) && compile.static_state.state.depth_write != 0;
if (ds.depthTestEnable)
ds.depthCompareOp = static_cast<VkCompareOp>(compile.static_state.state.depth_compare);
@@ -1391,7 +1391,7 @@ Pipeline CommandBuffer::build_graphics_pipeline(Device *device, const DeferredPi
VkPipelineShaderStageRequiredSubgroupSizeCreateInfo subgroup_size_info_task;
VkPipelineShaderStageRequiredSubgroupSizeCreateInfo subgroup_size_info_mesh;
for (unsigned i = 0; i < Util::ecast(ShaderStage::Count); i++)
for (int i = 0; i < Util::ecast(ShaderStage::Count); i++)
{
auto mask = compile.layout->get_resource_layout().spec_constant_mask[i] &
get_combined_spec_constant_mask(compile);
@@ -1414,7 +1414,7 @@ Pipeline CommandBuffer::build_graphics_pipeline(Device *device, const DeferredPi
}
}
for (unsigned i = 0; i < Util::ecast(ShaderStage::Count); i++)
for (int i = 0; i < Util::ecast(ShaderStage::Count); i++)
{
auto stage = static_cast<ShaderStage>(i);
if (compile.program->get_shader(stage))
@@ -2089,7 +2089,7 @@ void CommandBuffer::set_program_layout(const PipelineLayout *layout)
VK_ASSERT(layout);
if (!pipeline_state.layout)
{
dirty_sets = ~0u;
dirty_sets_realloc = ~0u;
set_dirty(COMMAND_BUFFER_DIRTY_PUSH_CONSTANTS_BIT);
}
else if (layout->get_hash() != pipeline_state.layout->get_hash())
@@ -2097,11 +2097,20 @@ void CommandBuffer::set_program_layout(const PipelineLayout *layout)
auto &new_layout = layout->get_resource_layout();
auto &old_layout = pipeline_state.layout->get_resource_layout();
uint32_t first_invalidated_set_index = VULKAN_NUM_DESCRIPTOR_SETS;
uint32_t new_push_set = layout->get_push_set_index();
uint32_t old_push_set = pipeline_state.layout->get_push_set_index();
if (new_push_set == old_push_set)
{
new_push_set = UINT32_MAX;
old_push_set = UINT32_MAX;
}
// If the push constant layout changes, all descriptor sets
// are invalidated.
if (new_layout.push_constant_layout_hash != old_layout.push_constant_layout_hash)
{
dirty_sets = ~0u;
first_invalidated_set_index = 0;
set_dirty(COMMAND_BUFFER_DIRTY_PUSH_CONSTANTS_BIT);
}
else
@@ -2109,13 +2118,28 @@ void CommandBuffer::set_program_layout(const PipelineLayout *layout)
// Find the first set whose descriptor set layout differs.
for (unsigned set = 0; set < VULKAN_NUM_DESCRIPTOR_SETS; set++)
{
if (layout->get_allocator(set) != pipeline_state.layout->get_allocator(set))
if (layout->get_allocator(set) != pipeline_state.layout->get_allocator(set) ||
set == new_push_set || set == old_push_set)
{
dirty_sets |= ~((1u << set) - 1);
first_invalidated_set_index = set;
break;
}
}
}
if (first_invalidated_set_index < VULKAN_NUM_DESCRIPTOR_SETS)
{
dirty_sets_rebind |= ~((1u << first_invalidated_set_index) - 1u);
for (unsigned set = first_invalidated_set_index; set < VULKAN_NUM_DESCRIPTOR_SETS; set++)
{
if (layout->get_allocator(set) != pipeline_state.layout->get_allocator(set) ||
set == new_push_set || set == old_push_set)
{
dirty_sets_realloc |= 1u << set;
}
}
}
}
pipeline_state.layout = layout;
@@ -2241,21 +2265,21 @@ void CommandBuffer::set_uniform_buffer(unsigned set, unsigned binding, const Buf
VK_ASSERT(buffer.get_create_info().usage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT);
auto &b = bindings.bindings[set][binding];
if (buffer.get_cookie() == bindings.cookies[set][binding] && b.buffer.range == range)
if (buffer.get_cookie() == bindings.cookies[set][binding] && b.buffer.dynamic.range == range)
{
if (b.dynamic_offset != offset)
if (b.buffer.push.offset != offset)
{
dirty_sets_dynamic |= 1u << set;
b.dynamic_offset = offset;
dirty_sets_rebind |= 1u << set;
b.buffer.push.offset = offset;
}
}
else
{
b.buffer = { buffer.get_buffer(), 0, range };
b.dynamic_offset = offset;
b.buffer.dynamic = { buffer.get_buffer(), 0, range };
b.buffer.push = { buffer.get_buffer(), offset, range };
bindings.cookies[set][binding] = buffer.get_cookie();
bindings.secondary_cookies[set][binding] = 0;
dirty_sets |= 1u << set;
dirty_sets_realloc |= 1u << set;
}
}
@@ -2267,14 +2291,14 @@ void CommandBuffer::set_storage_buffer(unsigned set, unsigned binding, const Buf
VK_ASSERT(buffer.get_create_info().usage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
auto &b = bindings.bindings[set][binding];
if (buffer.get_cookie() == bindings.cookies[set][binding] && b.buffer.offset == offset && b.buffer.range == range)
if (buffer.get_cookie() == bindings.cookies[set][binding] && b.buffer.dynamic.offset == offset && b.buffer.dynamic.range == range)
return;
b.buffer = { buffer.get_buffer(), offset, range };
b.dynamic_offset = 0;
b.buffer.dynamic = { buffer.get_buffer(), offset, range };
b.buffer.push = b.buffer.dynamic;
bindings.cookies[set][binding] = buffer.get_cookie();
bindings.secondary_cookies[set][binding] = 0;
dirty_sets |= 1u << set;
dirty_sets_realloc |= 1u << set;
}
void CommandBuffer::set_uniform_buffer(unsigned set, unsigned binding, const Buffer &buffer)
@@ -2297,7 +2321,7 @@ void CommandBuffer::set_sampler(unsigned set, unsigned binding, const Sampler &s
auto &b = bindings.bindings[set][binding];
b.image.fp.sampler = sampler.get_sampler();
b.image.integer.sampler = sampler.get_sampler();
dirty_sets |= 1u << set;
dirty_sets_realloc |= 1u << set;
bindings.secondary_cookies[set][binding] = sampler.get_cookie();
}
@@ -2311,7 +2335,7 @@ void CommandBuffer::set_buffer_view_common(unsigned set, unsigned binding, const
b.buffer_view = view.get_view();
bindings.cookies[set][binding] = view.get_cookie();
bindings.secondary_cookies[set][binding] = 0;
dirty_sets |= 1u << set;
dirty_sets_realloc |= 1u << set;
}
void CommandBuffer::set_buffer_view(unsigned set, unsigned binding, const BufferView &view)
@@ -2353,7 +2377,7 @@ void CommandBuffer::set_input_attachments(unsigned set, unsigned start_binding)
b.image.fp.imageView = view->get_float_view();
b.image.integer.imageView = view->get_integer_view();
bindings.cookies[set][start_binding + i] = view->get_cookie();
dirty_sets |= 1u << set;
dirty_sets_realloc |= 1u << set;
}
}
@@ -2374,14 +2398,14 @@ void CommandBuffer::set_texture(unsigned set, unsigned binding,
b.image.integer.imageLayout = layout;
b.image.integer.imageView = integer_view;
bindings.cookies[set][binding] = cookie;
dirty_sets |= 1u << set;
dirty_sets_realloc |= 1u << set;
}
void CommandBuffer::set_bindless(unsigned set, VkDescriptorSet desc_set)
{
VK_ASSERT(set < VULKAN_NUM_DESCRIPTOR_SETS);
bindless_sets[set] = desc_set;
dirty_sets |= 1u << set;
dirty_sets_realloc |= 1u << set;
}
void CommandBuffer::set_texture(unsigned set, unsigned binding, const ImageView &view)
@@ -2452,180 +2476,225 @@ void CommandBuffer::set_unorm_storage_texture(unsigned set, unsigned binding, co
view.get_image().get_layout(VK_IMAGE_LAYOUT_GENERAL), view.get_cookie() | COOKIE_BIT_UNORM);
}
void CommandBuffer::rebind_descriptor_set(uint32_t set)
void CommandBuffer::flush_descriptor_binds(const VkDescriptorSet *sets,
uint32_t &first_set, uint32_t &set_count,
uint32_t *dynamic_offsets, uint32_t &num_dynamic_offsets)
{
auto &layout = pipeline_state.layout->get_resource_layout();
if (layout.bindless_descriptor_set_mask & (1u << set))
{
VK_ASSERT(bindless_sets[set]);
table.vkCmdBindDescriptorSets(cmd, actual_render_pass ? VK_PIPELINE_BIND_POINT_GRAPHICS : VK_PIPELINE_BIND_POINT_COMPUTE,
current_pipeline_layout, set, 1, &bindless_sets[set], 0, nullptr);
if (!set_count)
return;
}
auto &set_layout = layout.sets[set];
uint32_t num_dynamic_offsets = 0;
uint32_t dynamic_offsets[VULKAN_NUM_BINDINGS];
table.vkCmdBindDescriptorSets(
cmd, actual_render_pass ? VK_PIPELINE_BIND_POINT_GRAPHICS : VK_PIPELINE_BIND_POINT_COMPUTE,
current_pipeline_layout, first_set, set_count, sets, num_dynamic_offsets, dynamic_offsets);
// UBOs
for_each_bit(set_layout.uniform_buffer_mask, [&](uint32_t binding) {
unsigned array_size = set_layout.array_size[binding];
for (unsigned i = 0; i < array_size; i++)
{
VK_ASSERT(num_dynamic_offsets < VULKAN_NUM_BINDINGS);
dynamic_offsets[num_dynamic_offsets++] = bindings.bindings[set][binding + i].dynamic_offset;
}
});
table.vkCmdBindDescriptorSets(cmd, actual_render_pass ? VK_PIPELINE_BIND_POINT_GRAPHICS : VK_PIPELINE_BIND_POINT_COMPUTE,
current_pipeline_layout, set, 1, &allocated_sets[set], num_dynamic_offsets, dynamic_offsets);
set_count = 0;
num_dynamic_offsets = 0;
}
void CommandBuffer::flush_descriptor_set(uint32_t set)
void CommandBuffer::rebind_descriptor_set(uint32_t set, VkDescriptorSet *sets, uint32_t &first_set, uint32_t &set_count,
uint32_t *dynamic_offsets, uint32_t &num_dynamic_offsets)
{
if (set_count == 0)
first_set = set;
else if (first_set + set_count != set)
{
flush_descriptor_binds(sets, first_set, set_count, dynamic_offsets, num_dynamic_offsets);
first_set = set;
}
auto &layout = pipeline_state.layout->get_resource_layout();
if (layout.bindless_descriptor_set_mask & (1u << set))
{
VK_ASSERT(bindless_sets[set]);
table.vkCmdBindDescriptorSets(cmd, actual_render_pass ? VK_PIPELINE_BIND_POINT_GRAPHICS : VK_PIPELINE_BIND_POINT_COMPUTE,
current_pipeline_layout, set, 1, &bindless_sets[set], 0, nullptr);
sets[set_count++] = bindless_sets[set];
return;
}
auto &set_layout = layout.sets[set];
uint32_t num_dynamic_offsets = 0;
uint32_t dynamic_offsets[VULKAN_NUM_BINDINGS];
Hasher h;
h.u32(set_layout.fp_mask);
// UBOs
for_each_bit(set_layout.uniform_buffer_mask, [&](uint32_t binding) {
unsigned array_size = set_layout.array_size[binding];
for (unsigned i = 0; i < array_size; i++)
{
h.u64(bindings.cookies[set][binding + i]);
h.u32(bindings.bindings[set][binding + i].buffer.range);
VK_ASSERT(bindings.bindings[set][binding + i].buffer.buffer != VK_NULL_HANDLE);
VK_ASSERT(num_dynamic_offsets < VULKAN_NUM_BINDINGS);
dynamic_offsets[num_dynamic_offsets++] = bindings.bindings[set][binding + i].dynamic_offset;
VK_ASSERT(num_dynamic_offsets < VULKAN_NUM_DYNAMIC_UBOS);
dynamic_offsets[num_dynamic_offsets++] = bindings.bindings[set][binding + i].buffer.push.offset;
}
});
sets[set_count++] = allocated_sets[set];
}
void CommandBuffer::validate_descriptor_binds(uint32_t set)
{
#ifdef VULKAN_DEBUG
auto &layout = pipeline_state.layout->get_resource_layout();
auto &set_layout = layout.sets[set];
for_each_bit(set_layout.uniform_buffer_mask,
[&](uint32_t binding)
{
unsigned array_size = set_layout.array_size[binding];
for (unsigned i = 0; i < array_size; i++)
VK_ASSERT(bindings.bindings[set][binding + i].buffer.dynamic.buffer != VK_NULL_HANDLE);
});
// SSBOs
for_each_bit(set_layout.storage_buffer_mask, [&](uint32_t binding) {
unsigned array_size = set_layout.array_size[binding];
for (unsigned i = 0; i < array_size; i++)
{
h.u64(bindings.cookies[set][binding + i]);
h.u32(bindings.bindings[set][binding + i].buffer.offset);
h.u32(bindings.bindings[set][binding + i].buffer.range);
VK_ASSERT(bindings.bindings[set][binding + i].buffer.buffer != VK_NULL_HANDLE);
}
});
for_each_bit(set_layout.storage_buffer_mask,
[&](uint32_t binding)
{
unsigned array_size = set_layout.array_size[binding];
for (unsigned i = 0; i < array_size; i++)
VK_ASSERT(bindings.bindings[set][binding + i].buffer.dynamic.buffer != VK_NULL_HANDLE);
});
// Texel buffers
for_each_bit(set_layout.sampled_texel_buffer_mask | set_layout.storage_texel_buffer_mask, [&](uint32_t binding) {
unsigned array_size = set_layout.array_size[binding];
for (unsigned i = 0; i < array_size; i++)
{
h.u64(bindings.cookies[set][binding + i]);
VK_ASSERT(bindings.bindings[set][binding + i].buffer_view != VK_NULL_HANDLE);
}
});
for_each_bit(set_layout.sampled_texel_buffer_mask | set_layout.storage_texel_buffer_mask,
[&](uint32_t binding)
{
unsigned array_size = set_layout.array_size[binding];
for (unsigned i = 0; i < array_size; i++)
VK_ASSERT(bindings.bindings[set][binding + i].buffer_view != VK_NULL_HANDLE);
});
// Sampled images
for_each_bit(set_layout.sampled_image_mask, [&](uint32_t binding) {
unsigned array_size = set_layout.array_size[binding];
for (unsigned i = 0; i < array_size; i++)
{
h.u64(bindings.cookies[set][binding + i]);
if ((set_layout.immutable_sampler_mask & (1u << (binding + i))) == 0)
{
h.u64(bindings.secondary_cookies[set][binding + i]);
VK_ASSERT(bindings.bindings[set][binding + i].image.fp.sampler != VK_NULL_HANDLE);
}
h.u32(bindings.bindings[set][binding + i].image.fp.imageLayout);
VK_ASSERT(bindings.bindings[set][binding + i].image.fp.imageView != VK_NULL_HANDLE);
}
});
for_each_bit(set_layout.sampled_image_mask,
[&](uint32_t binding)
{
unsigned array_size = set_layout.array_size[binding];
for (unsigned i = 0; i < array_size; i++)
{
if ((set_layout.immutable_sampler_mask & (1u << (binding + i))) == 0)
VK_ASSERT(bindings.bindings[set][binding + i].image.fp.sampler != VK_NULL_HANDLE);
VK_ASSERT(bindings.bindings[set][binding + i].image.fp.imageView != VK_NULL_HANDLE);
}
});
// Separate images
for_each_bit(set_layout.separate_image_mask, [&](uint32_t binding) {
unsigned array_size = set_layout.array_size[binding];
for (unsigned i = 0; i < array_size; i++)
{
h.u64(bindings.cookies[set][binding + i]);
h.u32(bindings.bindings[set][binding + i].image.fp.imageLayout);
VK_ASSERT(bindings.bindings[set][binding + i].image.fp.imageView != VK_NULL_HANDLE);
}
});
for_each_bit(set_layout.separate_image_mask,
[&](uint32_t binding)
{
unsigned array_size = set_layout.array_size[binding];
for (unsigned i = 0; i < array_size; i++)
VK_ASSERT(bindings.bindings[set][binding + i].image.fp.imageView != VK_NULL_HANDLE);
});
// Separate samplers
for_each_bit(set_layout.sampler_mask & ~set_layout.immutable_sampler_mask, [&](uint32_t binding) {
unsigned array_size = set_layout.array_size[binding];
for (unsigned i = 0; i < array_size; i++)
{
h.u64(bindings.secondary_cookies[set][binding + i]);
VK_ASSERT(bindings.bindings[set][binding + i].image.fp.sampler != VK_NULL_HANDLE);
}
});
for_each_bit(set_layout.sampler_mask & ~set_layout.immutable_sampler_mask,
[&](uint32_t binding)
{
unsigned array_size = set_layout.array_size[binding];
for (unsigned i = 0; i < array_size; i++)
VK_ASSERT(bindings.bindings[set][binding + i].image.fp.sampler != VK_NULL_HANDLE);
});
// Storage images
for_each_bit(set_layout.storage_image_mask, [&](uint32_t binding) {
unsigned array_size = set_layout.array_size[binding];
for (unsigned i = 0; i < array_size; i++)
{
h.u64(bindings.cookies[set][binding + i]);
h.u32(bindings.bindings[set][binding + i].image.fp.imageLayout);
VK_ASSERT(bindings.bindings[set][binding + i].image.fp.imageView != VK_NULL_HANDLE);
}
});
for_each_bit(set_layout.storage_image_mask,
[&](uint32_t binding)
{
unsigned array_size = set_layout.array_size[binding];
for (unsigned i = 0; i < array_size; i++)
VK_ASSERT(bindings.bindings[set][binding + i].image.fp.imageView != VK_NULL_HANDLE);
});
// Input attachments
for_each_bit(set_layout.input_attachment_mask, [&](uint32_t binding) {
unsigned array_size = set_layout.array_size[binding];
for (unsigned i = 0; i < array_size; i++)
{
h.u64(bindings.cookies[set][binding + i]);
h.u32(bindings.bindings[set][binding + i].image.fp.imageLayout);
VK_ASSERT(bindings.bindings[set][binding + i].image.fp.imageView != VK_NULL_HANDLE);
}
});
for_each_bit(set_layout.input_attachment_mask,
[&](uint32_t binding)
{
unsigned array_size = set_layout.array_size[binding];
for (unsigned i = 0; i < array_size; i++)
VK_ASSERT(bindings.bindings[set][binding + i].image.fp.imageView != VK_NULL_HANDLE);
});
#else
(void)set;
#endif
}
Hash hash = h.get();
auto allocated = pipeline_state.layout->get_allocator(set)->find(thread_index, hash);
void CommandBuffer::push_descriptor_set(uint32_t set)
{
#ifdef VULKAN_DEBUG
validate_descriptor_binds(set);
#endif
// The descriptor set was not successfully cached, rebuild.
if (!allocated.second)
VkDescriptorUpdateTemplate update_template = pipeline_state.layout->get_update_template(set);
VK_ASSERT(update_template);
table.vkCmdPushDescriptorSetWithTemplateKHR(
cmd, update_template,
pipeline_state.layout->get_layout(), set, bindings.bindings[set]);
}
void CommandBuffer::flush_descriptor_set(uint32_t set, VkDescriptorSet *sets,
uint32_t &first_set, uint32_t &set_count,
uint32_t *dynamic_offsets, uint32_t &num_dynamic_offsets)
{
if (set_count == 0)
first_set = set;
else if (first_set + set_count != set)
{
auto update_template = pipeline_state.layout->get_update_template(set);
VK_ASSERT(update_template);
table.vkUpdateDescriptorSetWithTemplate(device->get_device(), allocated.first,
update_template, bindings.bindings[set]);
flush_descriptor_binds(sets, first_set, set_count, dynamic_offsets, num_dynamic_offsets);
first_set = set;
}
table.vkCmdBindDescriptorSets(cmd, actual_render_pass ? VK_PIPELINE_BIND_POINT_GRAPHICS : VK_PIPELINE_BIND_POINT_COMPUTE,
current_pipeline_layout, set, 1, &allocated.first, num_dynamic_offsets, dynamic_offsets);
allocated_sets[set] = allocated.first;
auto &layout = pipeline_state.layout->get_resource_layout();
if (layout.bindless_descriptor_set_mask & (1u << set))
{
VK_ASSERT(bindless_sets[set]);
sets[set_count++] = bindless_sets[set];
return;
}
auto &set_layout = layout.sets[set];
#ifdef VULKAN_DEBUG
validate_descriptor_binds(set);
#endif
// UBOs
for_each_bit(set_layout.uniform_buffer_mask, [&](uint32_t binding) {
unsigned array_size = set_layout.array_size[binding];
for (unsigned i = 0; i < array_size; i++)
dynamic_offsets[num_dynamic_offsets++] = bindings.bindings[set][binding + i].buffer.push.offset;
});
auto vk_set = pipeline_state.layout->get_allocator(set)->request_descriptor_set(thread_index, device->frame_context_index);
VkDescriptorUpdateTemplate update_template = pipeline_state.layout->get_update_template(set);
VK_ASSERT(update_template);
table.vkUpdateDescriptorSetWithTemplate(device->get_device(), vk_set, update_template, bindings.bindings[set]);
sets[set_count++] = vk_set;
allocated_sets[set] = vk_set;
}
void CommandBuffer::flush_descriptor_sets()
{
auto &layout = pipeline_state.layout->get_resource_layout();
uint32_t set_update = layout.descriptor_set_mask & dirty_sets;
for_each_bit(set_update, [&](uint32_t set) { flush_descriptor_set(set); });
dirty_sets &= ~set_update;
uint32_t first_set = 0;
uint32_t set_count = 0;
VkDescriptorSet sets[VULKAN_NUM_DESCRIPTOR_SETS];
uint32_t dynamic_offsets[VULKAN_NUM_DYNAMIC_UBOS];
uint32_t num_dynamic_offsets = 0;
// If we update a set, we also bind dynamically.
dirty_sets_dynamic &= ~set_update;
dirty_sets_rebind |= dirty_sets_realloc;
uint32_t set_update_mask = layout.descriptor_set_mask & dirty_sets_rebind;
// If we only rebound UBOs, we might get away with just rebinding descriptor sets, no hashing and lookup required.
uint32_t dynamic_set_update = layout.descriptor_set_mask & dirty_sets_dynamic;
for_each_bit(dynamic_set_update, [&](uint32_t set) { rebind_descriptor_set(set); });
dirty_sets_dynamic &= ~dynamic_set_update;
uint32_t push_set_index = pipeline_state.layout->get_push_set_index();
if (push_set_index != UINT32_MAX && (dirty_sets_rebind & (1u << push_set_index)) != 0)
{
push_descriptor_set(push_set_index);
set_update_mask &= ~(1u << push_set_index);
}
for_each_bit(set_update_mask, [&](uint32_t set) {
if ((dirty_sets_realloc & (1u << set)) != 0)
flush_descriptor_set(set, sets, first_set, set_count, dynamic_offsets, num_dynamic_offsets);
else
rebind_descriptor_set(set, sets, first_set, set_count, dynamic_offsets, num_dynamic_offsets);
});
dirty_sets_realloc = 0;
dirty_sets_rebind = 0;
flush_descriptor_binds(sets, first_set, set_count, dynamic_offsets, num_dynamic_offsets);
}
void CommandBuffer::draw(uint32_t vertex_count, uint32_t instance_count, uint32_t first_vertex, uint32_t first_instance)
@@ -2967,7 +3036,7 @@ void CommandBuffer::restore_state(const CommandBufferSavedState &state)
memcpy(bindings.bindings[i], state.bindings.bindings[i], sizeof(bindings.bindings[i]));
memcpy(bindings.cookies[i], state.bindings.cookies[i], sizeof(bindings.cookies[i]));
memcpy(bindings.secondary_cookies[i], state.bindings.secondary_cookies[i], sizeof(bindings.secondary_cookies[i]));
dirty_sets |= 1u << i;
dirty_sets_realloc |= 1u << i;
}
}
}
@@ -846,8 +846,8 @@ private:
VkRect2D scissor = {};
CommandBufferDirtyFlags dirty = ~0u;
uint32_t dirty_sets = 0;
uint32_t dirty_sets_dynamic = 0;
uint32_t dirty_sets_realloc = 0;
uint32_t dirty_sets_rebind = 0;
uint32_t dirty_vbos = 0;
uint32_t active_vbos = 0;
VkPipelineStageFlags2 uses_swapchain_in_stages = 0;
@@ -883,8 +883,20 @@ private:
bool flush_compute_pipeline(bool synchronous);
void flush_descriptor_sets();
void begin_graphics();
void flush_descriptor_set(uint32_t set);
void rebind_descriptor_set(uint32_t set);
void flush_descriptor_set(
uint32_t set, VkDescriptorSet *sets,
uint32_t &first_set, uint32_t &set_count,
uint32_t *dynamic_offsets, uint32_t &num_dynamic_offsets);
void push_descriptor_set(uint32_t set);
void rebind_descriptor_set(
uint32_t set, VkDescriptorSet *sets,
uint32_t &first_set, uint32_t &set_count,
uint32_t *dynamic_offsets, uint32_t &num_dynamic_offsets);
void flush_descriptor_binds(const VkDescriptorSet *sets,
uint32_t &first_set, uint32_t &set_count,
uint32_t *dynamic_offsets, uint32_t &num_dynamic_offsets);
void validate_descriptor_binds(uint32_t set);
void begin_compute();
void begin_context();
+56 -15
View File
@@ -203,10 +203,10 @@ PFN_vkGetInstanceProcAddr Context::get_instance_proc_addr()
return instance_proc_addr;
}
bool Context::init_loader(PFN_vkGetInstanceProcAddr addr)
bool Context::init_loader(PFN_vkGetInstanceProcAddr addr, bool force_reload)
{
std::lock_guard<std::mutex> holder(loader_init_lock);
if (loader_init_once && !addr)
if (loader_init_once && !force_reload && !addr)
return true;
if (!addr)
@@ -608,12 +608,6 @@ bool Context::create_instance(const char * const *instance_ext, uint32_t instanc
ext.supports_surface_capabilities2 = true;
}
if (ext.supports_surface_capabilities2 && has_extension(VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME))
{
instance_exts.push_back(VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME);
ext.supports_surface_maintenance1 = true;
}
if ((flags & CONTEXT_CREATION_ENABLE_ADVANCED_WSI_BIT) != 0 &&
has_surface_extension &&
has_extension(VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME))
@@ -630,8 +624,6 @@ bool Context::create_instance(const char * const *instance_ext, uint32_t instanc
return layer_itr != end(queried_layers);
};
VkValidationFeaturesEXT validation_features = { VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT };
force_no_validation = Util::get_environment_bool("GRANITE_VULKAN_NO_VALIDATION", false);
if (!force_no_validation && has_layer("VK_LAYER_KHRONOS_validation"))
@@ -644,6 +636,10 @@ bool Context::create_instance(const char * const *instance_ext, uint32_t instanc
std::vector<VkExtensionProperties> layer_exts(layer_ext_count);
vkEnumerateInstanceExtensionProperties("VK_LAYER_KHRONOS_validation", &layer_ext_count, layer_exts.data());
#if 0
VkValidationFeaturesEXT validation_features = { VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT };
// Tons of false positives around timeline semaphores atm, so don't bother.
if (find_if(begin(layer_exts), end(layer_exts), [](const VkExtensionProperties &e) {
return strcmp(e.extensionName, VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME) == 0;
}) != end(layer_exts))
@@ -657,6 +653,7 @@ bool Context::create_instance(const char * const *instance_ext, uint32_t instanc
validation_features.pEnabledValidationFeatures = validation_sync_features;
info.pNext = &validation_features;
}
#endif
if (!ext.supports_debug_utils &&
find_if(begin(layer_exts), end(layer_exts), [](const VkExtensionProperties &e) {
@@ -669,6 +666,22 @@ bool Context::create_instance(const char * const *instance_ext, uint32_t instanc
}
#endif
if (ext.supports_surface_capabilities2 && has_extension(VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME))
{
#ifdef VULKAN_DEBUG
// It seems like there are some bugs with EXT_swapchain_maint1 in VVL atm.
const bool support_maint1 = force_no_validation;
#else
constexpr bool support_maint1 = true;
#endif
if (support_maint1)
{
instance_exts.push_back(VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME);
ext.supports_surface_maintenance1 = true;
}
}
info.enabledExtensionCount = instance_exts.size();
info.ppEnabledExtensionNames = instance_exts.empty() ? nullptr : instance_exts.data();
info.enabledLayerCount = instance_layers.size();
@@ -1302,7 +1315,14 @@ bool Context::create_device(VkPhysicalDevice gpu_, VkSurfaceKHR surface,
ADD_CHAIN(ext.storage_8bit_features, 8BIT_STORAGE_FEATURES_KHR);
enabled_extensions.push_back(VK_KHR_8BIT_STORAGE_EXTENSION_NAME);
}
}
if (ext.device_api_core_version >= VK_API_VERSION_1_3)
{
ADD_CHAIN(ext.vk13_features, VULKAN_1_3_FEATURES);
}
else
{
if (has_extension(VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME))
{
ADD_CHAIN(ext.subgroup_size_control_features, SUBGROUP_SIZE_CONTROL_FEATURES_EXT);
@@ -1310,9 +1330,6 @@ bool Context::create_device(VkPhysicalDevice gpu_, VkSurfaceKHR surface,
}
}
if (ext.device_api_core_version >= VK_API_VERSION_1_3)
ADD_CHAIN(ext.vk13_features, VULKAN_1_3_FEATURES);
if (has_extension(VK_NV_COMPUTE_SHADER_DERIVATIVES_EXTENSION_NAME))
{
enabled_extensions.push_back(VK_NV_COMPUTE_SHADER_DERIVATIVES_EXTENSION_NAME);
@@ -1362,6 +1379,12 @@ bool Context::create_device(VkPhysicalDevice gpu_, VkSurfaceKHR surface,
ADD_CHAIN(ext.device_generated_commands_compute_features, DEVICE_GENERATED_COMMANDS_COMPUTE_FEATURES_NV);
}
if (has_extension(VK_NV_DESCRIPTOR_POOL_OVERALLOCATION_EXTENSION_NAME))
{
enabled_extensions.push_back(VK_NV_DESCRIPTOR_POOL_OVERALLOCATION_EXTENSION_NAME);
ADD_CHAIN(ext.descriptor_pool_overallocation_features, DESCRIPTOR_POOL_OVERALLOCATION_FEATURES_NV);
}
if (has_extension(VK_EXT_MESH_SHADER_EXTENSION_NAME))
{
enabled_extensions.push_back(VK_EXT_MESH_SHADER_EXTENSION_NAME);
@@ -1374,6 +1397,12 @@ bool Context::create_device(VkPhysicalDevice gpu_, VkSurfaceKHR surface,
ADD_CHAIN(ext.index_type_uint8_features, INDEX_TYPE_UINT8_FEATURES_EXT);
}
if (has_extension(VK_EXT_RGBA10X6_FORMATS_EXTENSION_NAME))
{
enabled_extensions.push_back(VK_EXT_RGBA10X6_FORMATS_EXTENSION_NAME);
ADD_CHAIN(ext.rgba10x6_formats_features, RGBA10X6_FORMATS_FEATURES_EXT);
}
if (has_extension(VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME))
{
ext.supports_external_memory_host = true;
@@ -1398,6 +1427,18 @@ bool Context::create_device(VkPhysicalDevice gpu_, VkSurfaceKHR surface,
ext.supports_push_descriptor = true;
}
if (has_extension(VK_EXT_IMAGE_COMPRESSION_CONTROL_EXTENSION_NAME))
{
enabled_extensions.push_back(VK_EXT_IMAGE_COMPRESSION_CONTROL_EXTENSION_NAME);
ADD_CHAIN(ext.image_compression_control_features, IMAGE_COMPRESSION_CONTROL_FEATURES_EXT);
}
if (has_extension(VK_EXT_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_EXTENSION_NAME))
{
enabled_extensions.push_back(VK_EXT_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_EXTENSION_NAME);
ADD_CHAIN(ext.image_compression_control_swapchain_features, IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_FEATURES_EXT);
}
if (ext.device_api_core_version >= VK_API_VERSION_1_3)
{
ext.supports_store_op_none = true;
@@ -1581,14 +1622,14 @@ bool Context::create_device(VkPhysicalDevice gpu_, VkSurfaceKHR surface,
{
if (has_extension(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME))
ADD_CHAIN(driver_properties, DRIVER_PROPERTIES);
if (has_extension(VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME))
ADD_CHAIN(size_control_props, SUBGROUP_SIZE_CONTROL_PROPERTIES);
ADD_CHAIN(id_properties, ID_PROPERTIES);
ADD_CHAIN(subgroup_properties, SUBGROUP_PROPERTIES);
}
if (ext.device_api_core_version >= VK_API_VERSION_1_3)
ADD_CHAIN(ext.vk13_props, VULKAN_1_3_PROPERTIES);
else if (has_extension(VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME))
ADD_CHAIN(size_control_props, SUBGROUP_SIZE_CONTROL_PROPERTIES);
if (ext.supports_external_memory_host)
ADD_CHAIN(ext.host_memory_properties, EXTERNAL_MEMORY_HOST_PROPERTIES_EXT);
@@ -100,12 +100,16 @@ struct DeviceFeatures
VkPhysicalDeviceMeshShaderFeaturesEXT mesh_shader_features = {};
VkPhysicalDeviceMeshShaderPropertiesEXT mesh_shader_properties = {};
VkPhysicalDeviceIndexTypeUint8FeaturesEXT index_type_uint8_features = {};
VkPhysicalDeviceRGBA10X6FormatsFeaturesEXT rgba10x6_formats_features = {};
VkPhysicalDeviceImageCompressionControlFeaturesEXT image_compression_control_features = {};
VkPhysicalDeviceImageCompressionControlSwapchainFeaturesEXT image_compression_control_swapchain_features = {};
// Vendor
VkPhysicalDeviceComputeShaderDerivativesFeaturesNV compute_shader_derivative_features = {};
VkPhysicalDeviceDeviceGeneratedCommandsFeaturesNV device_generated_commands_features = {};
VkPhysicalDeviceDeviceGeneratedCommandsComputeFeaturesNV device_generated_commands_compute_features = {};
VkPhysicalDeviceDeviceGeneratedCommandsPropertiesNV device_generated_commands_properties = {};
VkPhysicalDeviceDescriptorPoolOverallocationFeaturesNV descriptor_pool_overallocation_features = {};
// Fallback feature structs (Vulkan 1.1)
VkPhysicalDeviceHostQueryResetFeatures host_query_reset_features = {};
@@ -239,7 +243,7 @@ public:
Context();
Context(const Context &) = delete;
void operator=(const Context &) = delete;
static bool init_loader(PFN_vkGetInstanceProcAddr addr);
static bool init_loader(PFN_vkGetInstanceProcAddr addr, bool force_reload = false);
static PFN_vkGetInstanceProcAddr get_instance_proc_addr();
~Context();
@@ -40,9 +40,8 @@ DescriptorSetAllocator::DescriptorSetAllocator(Hash hash, Device *device_, const
if (!bindless)
{
unsigned count = device_->num_thread_indices;
for (unsigned i = 0; i < count; i++)
per_thread.emplace_back(new PerThread);
unsigned count = device_->num_thread_indices * device_->per_frame.size();
per_thread_and_frame.resize(count);
}
if (bindless && !device->get_device_features().vk12_features.descriptorIndexing)
@@ -176,11 +175,27 @@ DescriptorSetAllocator::DescriptorSetAllocator(Hash hash, Device *device_, const
#ifdef VULKAN_DEBUG
LOGI("Creating descriptor set layout.\n");
#endif
if (table.vkCreateDescriptorSetLayout(device->get_device(), &info, nullptr, &set_layout) != VK_SUCCESS)
if (table.vkCreateDescriptorSetLayout(device->get_device(), &info, nullptr, &set_layout_pool) != VK_SUCCESS)
LOGE("Failed to create descriptor set layout.");
#ifdef GRANITE_VULKAN_FOSSILIZE
device->register_descriptor_set_layout(set_layout, get_hash(), info);
if (set_layout_pool)
device->register_descriptor_set_layout(set_layout_pool, get_hash(), info);
#endif
if (!bindless && device->get_device_features().supports_push_descriptor && !device->workarounds.broken_push_descriptors)
{
info.flags |= VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR;
for (auto &b : bindings)
if (b.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC)
b.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
if (table.vkCreateDescriptorSetLayout(device->get_device(), &info, nullptr, &set_layout_push) != VK_SUCCESS)
LOGE("Failed to create descriptor set layout.");
#ifdef GRANITE_VULKAN_FOSSILIZE
if (set_layout_push)
device->register_descriptor_set_layout(set_layout_push, get_hash(), info);
#endif
}
}
void DescriptorSetAllocator::reset_bindless_pool(VkDescriptorPool pool)
@@ -196,7 +211,7 @@ VkDescriptorSet DescriptorSetAllocator::allocate_bindless_set(VkDescriptorPool p
VkDescriptorSetAllocateInfo info = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO };
info.descriptorPool = pool;
info.descriptorSetCount = 1;
info.pSetLayouts = &set_layout;
info.pSetLayouts = &set_layout_pool;
VkDescriptorSetVariableDescriptorCountAllocateInfoEXT count_info =
{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO_EXT };
@@ -225,12 +240,6 @@ VkDescriptorPool DescriptorSetAllocator::allocate_bindless_pool(unsigned num_set
info.poolSizeCount = 1;
VkDescriptorPoolSize size = pool_size[0];
if (num_descriptors > size.descriptorCount)
{
LOGE("Trying to allocate more than max bindless descriptors for descriptor layout.\n");
return VK_NULL_HANDLE;
}
size.descriptorCount = num_descriptors;
info.pPoolSizes = &size;
@@ -247,82 +256,98 @@ void DescriptorSetAllocator::begin_frame()
{
if (!bindless)
{
for (auto &thr : per_thread)
thr->should_begin = true;
// This can only be called in a situation where no command buffers are alive,
// so we don't need to consider any locks here.
if (device->per_frame.size() * device->num_thread_indices != per_thread_and_frame.size())
per_thread_and_frame.resize(device->per_frame.size() * device->num_thread_indices);
// It would be safe to set all offsets to 0 here, but that's a little wasteful.
for (uint32_t i = 0; i < device->num_thread_indices; i++)
per_thread_and_frame[i * device->per_frame.size() + device->frame_context_index].offset = 0;
}
}
std::pair<VkDescriptorSet, bool> DescriptorSetAllocator::find(unsigned thread_index, Hash hash)
VkDescriptorSet DescriptorSetAllocator::request_descriptor_set(unsigned thread_index, unsigned frame_index)
{
VK_ASSERT(!bindless);
auto &state = *per_thread[thread_index];
if (state.should_begin)
size_t flattened_index = thread_index * device->per_frame.size() + frame_index;
auto &state = per_thread_and_frame[flattened_index];
unsigned pool_index = state.offset / VULKAN_NUM_SETS_PER_POOL;
unsigned pool_offset = state.offset % VULKAN_NUM_SETS_PER_POOL;
if (pool_index >= state.pools.size())
{
state.set_nodes.begin_frame();
state.should_begin = false;
Pool *pool = state.object_pool.allocate();
VkDescriptorPoolCreateInfo info = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO };
info.maxSets = VULKAN_NUM_SETS_PER_POOL;
if (!pool_size.empty())
{
info.poolSizeCount = pool_size.size();
info.pPoolSizes = pool_size.data();
}
bool overallocation =
device->get_device_features().descriptor_pool_overallocation_features.descriptorPoolOverallocation ==
VK_TRUE;
if (overallocation)
{
// No point in allocating new pools if we can keep using the existing one.
info.flags |= VK_DESCRIPTOR_POOL_CREATE_ALLOW_OVERALLOCATION_POOLS_BIT_NV |
VK_DESCRIPTOR_POOL_CREATE_ALLOW_OVERALLOCATION_SETS_BIT_NV;
}
bool need_alloc = !overallocation || state.pools.empty();
pool->pool = VK_NULL_HANDLE;
if (need_alloc && table.vkCreateDescriptorPool(device->get_device(), &info, nullptr, &pool->pool) != VK_SUCCESS)
{
LOGE("Failed to create descriptor pool.\n");
state.object_pool.free(pool);
return VK_NULL_HANDLE;
}
VkDescriptorSetLayout layouts[VULKAN_NUM_SETS_PER_POOL];
std::fill(std::begin(layouts), std::end(layouts), set_layout_pool);
VkDescriptorSetAllocateInfo alloc = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO };
alloc.descriptorPool = pool->pool != VK_NULL_HANDLE ? pool->pool : state.pools.front()->pool;
alloc.descriptorSetCount = VULKAN_NUM_SETS_PER_POOL;
alloc.pSetLayouts = layouts;
if (table.vkAllocateDescriptorSets(device->get_device(), &alloc, pool->sets) != VK_SUCCESS)
LOGE("Failed to allocate descriptor sets.\n");
state.pools.push_back(pool);
}
auto *node = state.set_nodes.request(hash);
if (node)
return { node->set, true };
node = state.set_nodes.request_vacant(hash);
if (node)
return { node->set, false };
VkDescriptorPool pool;
VkDescriptorPoolCreateInfo info = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO };
info.maxSets = VULKAN_NUM_SETS_PER_POOL;
if (!pool_size.empty())
{
info.poolSizeCount = pool_size.size();
info.pPoolSizes = pool_size.data();
}
if (table.vkCreateDescriptorPool(device->get_device(), &info, nullptr, &pool) != VK_SUCCESS)
{
LOGE("Failed to create descriptor pool.\n");
return { VK_NULL_HANDLE, false };
}
VkDescriptorSet sets[VULKAN_NUM_SETS_PER_POOL];
VkDescriptorSetLayout layouts[VULKAN_NUM_SETS_PER_POOL];
std::fill(std::begin(layouts), std::end(layouts), set_layout);
VkDescriptorSetAllocateInfo alloc = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO };
alloc.descriptorPool = pool;
alloc.descriptorSetCount = VULKAN_NUM_SETS_PER_POOL;
alloc.pSetLayouts = layouts;
if (table.vkAllocateDescriptorSets(device->get_device(), &alloc, sets) != VK_SUCCESS)
LOGE("Failed to allocate descriptor sets.\n");
state.pools.push_back(pool);
for (auto set : sets)
state.set_nodes.make_vacant(set);
return { state.set_nodes.request_vacant(hash)->set, false };
VkDescriptorSet vk_set = state.pools[pool_index]->sets[pool_offset];
state.offset++;
return vk_set;
}
void DescriptorSetAllocator::clear()
{
for (auto &thr : per_thread)
for (auto &state : per_thread_and_frame)
{
thr->set_nodes.clear();
for (auto &pool : thr->pools)
for (auto *obj : state.pools)
{
table.vkResetDescriptorPool(device->get_device(), pool, 0);
table.vkDestroyDescriptorPool(device->get_device(), pool, nullptr);
table.vkDestroyDescriptorPool(device->get_device(), obj->pool, nullptr);
state.object_pool.free(obj);
}
thr->pools.clear();
state.pools.clear();
state.offset = 0;
state.object_pool = {};
}
}
DescriptorSetAllocator::~DescriptorSetAllocator()
{
if (set_layout != VK_NULL_HANDLE)
table.vkDestroyDescriptorSetLayout(device->get_device(), set_layout, nullptr);
table.vkDestroyDescriptorSetLayout(device->get_device(), set_layout_pool, nullptr);
table.vkDestroyDescriptorSetLayout(device->get_device(), set_layout_push, nullptr);
clear();
}
@@ -56,8 +56,8 @@ struct DescriptorSetLayout
// Avoid -Wclass-memaccess warnings since we hash DescriptorSetLayout.
static const unsigned VULKAN_NUM_SETS_PER_POOL = 16;
static const unsigned VULKAN_DESCRIPTOR_RING_SIZE = 8;
static const unsigned VULKAN_NUM_SETS_PER_POOL = 64;
static const unsigned VULKAN_DESCRIPTOR_RING_SIZE = 16;
class DescriptorSetAllocator;
class BindlessDescriptorPool;
@@ -121,11 +121,16 @@ public:
DescriptorSetAllocator(const DescriptorSetAllocator &) = delete;
void begin_frame();
std::pair<VkDescriptorSet, bool> find(unsigned thread_index, Util::Hash hash);
VkDescriptorSet request_descriptor_set(unsigned thread_index, unsigned frame_context);
VkDescriptorSetLayout get_layout() const
VkDescriptorSetLayout get_layout_for_pool() const
{
return set_layout;
return set_layout_pool;
}
VkDescriptorSetLayout get_layout_for_push() const
{
return set_layout_push;
}
void clear();
@@ -140,27 +145,25 @@ public:
void reset_bindless_pool(VkDescriptorPool pool);
private:
struct DescriptorSetNode : Util::TemporaryHashmapEnabled<DescriptorSetNode>, Util::IntrusiveListEnabled<DescriptorSetNode>
{
explicit DescriptorSetNode(VkDescriptorSet set_)
: set(set_)
{
}
VkDescriptorSet set;
};
Device *device;
const VolkDeviceTable &table;
VkDescriptorSetLayout set_layout = VK_NULL_HANDLE;
VkDescriptorSetLayout set_layout_pool = VK_NULL_HANDLE;
VkDescriptorSetLayout set_layout_push = VK_NULL_HANDLE;
struct PerThread
struct Pool
{
Util::TemporaryHashmap<DescriptorSetNode, VULKAN_DESCRIPTOR_RING_SIZE, true> set_nodes;
std::vector<VkDescriptorPool> pools;
bool should_begin = true;
VkDescriptorPool pool;
VkDescriptorSet sets[VULKAN_NUM_SETS_PER_POOL];
};
std::vector<std::unique_ptr<PerThread>> per_thread;
struct PerThreadAndFrame
{
std::vector<Pool *> pools;
Util::ObjectPool<Pool> object_pool;
uint32_t offset = 0;
};
std::vector<PerThreadAndFrame> per_thread_and_frame;
std::vector<VkDescriptorPoolSize> pool_size;
bool bindless = false;
};
+23 -1
View File
@@ -502,6 +502,8 @@ const PipelineLayout *Device::request_pipeline_layout(const CombinedResourceLayo
h.data(layout.spec_constant_mask, sizeof(layout.spec_constant_mask));
h.u32(layout.attribute_mask);
h.u32(layout.render_target_mask);
// Drivers with and without push descriptor support need to observe different hashes for Fossilize.
h.s32(int(ext.supports_push_descriptor && !workarounds.broken_push_descriptors));
for (unsigned set = 0; set < VULKAN_NUM_DESCRIPTOR_SETS; set++)
{
Util::for_each_bit(layout.sets[set].immutable_sampler_mask, [&](unsigned bit) {
@@ -853,7 +855,11 @@ void Device::init_workarounds()
// Events are not supported in MoltenVK.
// TODO: Use VK_KHR_portability_subset to determine this.
workarounds.emulate_event_as_pipeline_barrier = true;
// MoltenVK is broken with push descriptor templates.
// KhronosGroup/MoltenVK issue 2323.
workarounds.broken_push_descriptors = true;
LOGW("Emulating events as pipeline barriers on Metal emulation.\n");
LOGW("Disabling push descriptors on Metal emulation.\n");
#else
bool sync2_workarounds = false;
const bool mesa_driver = ext.driver_id == VK_DRIVER_ID_MESA_RADV ||
@@ -874,11 +880,21 @@ void Device::init_workarounds()
workarounds.emulate_event_as_pipeline_barrier = true;
}
if (ext.driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY && gpu_props.driverVersion < VK_VERSION_MAJOR(535))
// For whatever ridiculous reason, pipeline cache control causes GPU hangs on Pascal cards in parallel-rdp.
// Use mesh shaders as the sentinel to check for that.
if (ext.driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY &&
(gpu_props.driverVersion < VK_VERSION_MAJOR(535) ||
!ext.mesh_shader_features.meshShader))
{
LOGW("Disabling pipeline cache control.\n");
workarounds.broken_pipeline_cache_control = true;
}
else if (ext.driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY_KHR)
{
// Seems broken on this driver too. Compilation stutter galore ...
LOGW("Disabling pipeline cache control.\n");
workarounds.broken_pipeline_cache_control = true;
}
if (sync2_workarounds)
{
@@ -890,6 +906,12 @@ void Device::init_workarounds()
// Avoids having to add workaround path to events as well, just fallback to plain barriers.
workarounds.emulate_event_as_pipeline_barrier = true;
}
// I cannot reproduce this myself, but there are several users experiencing GPU hangs with push descriptors
// on AMD drivers (not RADV), so :shrug:.
// https://github.com/simple64/simple64/issues/449
if (ext.driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE || ext.driver_id == VK_DRIVER_ID_AMD_PROPRIETARY)
workarounds.broken_push_descriptors = true;
#endif
if (ext.supports_tooling_info && vkGetPhysicalDeviceToolPropertiesEXT)
@@ -25,6 +25,7 @@
namespace Vulkan
{
constexpr unsigned VULKAN_NUM_DESCRIPTOR_SETS = 4;
constexpr unsigned VULKAN_NUM_DYNAMIC_UBOS = 8; // Vulkan min-spec
constexpr unsigned VULKAN_NUM_BINDINGS = 32;
constexpr unsigned VULKAN_NUM_BINDINGS_BINDLESS_VARYING = 16 * 1024;
constexpr unsigned VULKAN_NUM_ATTACHMENTS = 8;
@@ -46,5 +46,6 @@ struct ImplementationWorkarounds
bool broken_pipeline_cache_control = false;
bool force_host_cached = false;
bool force_sync1_access = false;
bool broken_push_descriptors = false;
};
}
@@ -210,7 +210,7 @@ private:
uint32_t height = 0;
};
static const unsigned VULKAN_FRAMEBUFFER_RING_SIZE = 8;
static const unsigned VULKAN_FRAMEBUFFER_RING_SIZE = 16;
class FramebufferAllocator
{
public:
+134 -84
View File
@@ -65,16 +65,24 @@ PipelineLayout::PipelineLayout(Hash hash, Device *device_, const CombinedResourc
{
set_allocators[i] = device->request_descriptor_set_allocator(layout.sets[i], layout.stages_for_bindings[i],
immutable_samplers ? immutable_samplers->samplers[i] : nullptr);
layouts[i] = set_allocators[i]->get_layout();
layouts[i] = set_allocators[i]->get_layout_for_pool();
if (layout.descriptor_set_mask & (1u << i))
{
num_sets = i + 1;
// Assume the last set index in layout is the highest frequency update one, make that push descriptor if possible.
// Only one descriptor set can be push descriptor.
bool has_push_layout = set_allocators[i]->get_layout_for_push() != VK_NULL_HANDLE;
if (has_push_layout)
push_set_index = i;
}
}
if (num_sets > device->get_gpu_properties().limits.maxBoundDescriptorSets)
{
LOGE("Number of sets %u exceeds device limit of %u.\n",
num_sets, device->get_gpu_properties().limits.maxBoundDescriptorSets);
}
if (push_set_index != UINT32_MAX)
layouts[push_set_index] = set_allocators[push_set_index]->get_layout_for_push();
if (num_sets > VULKAN_NUM_DESCRIPTOR_SETS)
LOGE("Number of sets %u exceeds limit of %u.\n", num_sets, VULKAN_NUM_DESCRIPTOR_SETS);
VkPipelineLayoutCreateInfo info = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
if (num_sets)
@@ -120,127 +128,169 @@ void PipelineLayout::create_update_templates()
for_each_bit(set_layout.uniform_buffer_mask, [&](uint32_t binding) {
unsigned array_size = set_layout.array_size[binding];
VK_ASSERT(update_count < VULKAN_NUM_BINDINGS);
auto &entry = update_entries[update_count++];
entry.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
entry.dstBinding = binding;
entry.dstArrayElement = 0;
entry.descriptorCount = array_size;
entry.offset = offsetof(ResourceBinding, buffer) + sizeof(ResourceBinding) * binding;
entry.stride = sizeof(ResourceBinding);
// Work around a RenderDoc capture bug where descriptorCount > 1 is not handled correctly.
for (unsigned i = 0; i < array_size; i++)
{
auto &entry = update_entries[update_count++];
entry.descriptorType = desc_set == push_set_index ?
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER : VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
entry.dstBinding = binding;
entry.dstArrayElement = i;
entry.descriptorCount = 1;
if (desc_set == push_set_index)
entry.offset = offsetof(ResourceBinding, buffer.push) + sizeof(ResourceBinding) * (binding + i);
else
entry.offset = offsetof(ResourceBinding, buffer.dynamic) + sizeof(ResourceBinding) * (binding + i);
entry.stride = sizeof(ResourceBinding);
}
});
for_each_bit(set_layout.storage_buffer_mask, [&](uint32_t binding) {
unsigned array_size = set_layout.array_size[binding];
VK_ASSERT(update_count < VULKAN_NUM_BINDINGS);
auto &entry = update_entries[update_count++];
entry.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
entry.dstBinding = binding;
entry.dstArrayElement = 0;
entry.descriptorCount = array_size;
entry.offset = offsetof(ResourceBinding, buffer) + sizeof(ResourceBinding) * binding;
entry.stride = sizeof(ResourceBinding);
for (unsigned i = 0; i < array_size; i++)
{
auto &entry = update_entries[update_count++];
entry.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
entry.dstBinding = binding;
entry.dstArrayElement = i;
entry.descriptorCount = 1;
entry.offset = offsetof(ResourceBinding, buffer.dynamic) + sizeof(ResourceBinding) * (binding + i);
entry.stride = sizeof(ResourceBinding);
}
});
for_each_bit(set_layout.sampled_texel_buffer_mask, [&](uint32_t binding) {
unsigned array_size = set_layout.array_size[binding];
VK_ASSERT(update_count < VULKAN_NUM_BINDINGS);
auto &entry = update_entries[update_count++];
entry.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
entry.dstBinding = binding;
entry.dstArrayElement = 0;
entry.descriptorCount = array_size;
entry.offset = offsetof(ResourceBinding, buffer_view) + sizeof(ResourceBinding) * binding;
entry.stride = sizeof(ResourceBinding);
for (unsigned i = 0; i < array_size; i++)
{
auto &entry = update_entries[update_count++];
entry.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
entry.dstBinding = binding;
entry.dstArrayElement = i;
entry.descriptorCount = 1;
entry.offset = offsetof(ResourceBinding, buffer_view) + sizeof(ResourceBinding) * (binding + i);
entry.stride = sizeof(ResourceBinding);
}
});
for_each_bit(set_layout.storage_texel_buffer_mask, [&](uint32_t binding) {
unsigned array_size = set_layout.array_size[binding];
VK_ASSERT(update_count < VULKAN_NUM_BINDINGS);
auto &entry = update_entries[update_count++];
entry.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;
entry.dstBinding = binding;
entry.dstArrayElement = 0;
entry.descriptorCount = array_size;
entry.offset = offsetof(ResourceBinding, buffer_view) + sizeof(ResourceBinding) * binding;
entry.stride = sizeof(ResourceBinding);
for (unsigned i = 0; i < array_size; i++)
{
auto &entry = update_entries[update_count++];
entry.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;
entry.dstBinding = binding;
entry.dstArrayElement = i;
entry.descriptorCount = 1;
entry.offset = offsetof(ResourceBinding, buffer_view) + sizeof(ResourceBinding) * (binding + i);
entry.stride = sizeof(ResourceBinding);
}
});
for_each_bit(set_layout.sampled_image_mask, [&](uint32_t binding) {
unsigned array_size = set_layout.array_size[binding];
VK_ASSERT(update_count < VULKAN_NUM_BINDINGS);
auto &entry = update_entries[update_count++];
entry.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
entry.dstBinding = binding;
entry.dstArrayElement = 0;
entry.descriptorCount = array_size;
if (set_layout.fp_mask & (1u << binding))
entry.offset = offsetof(ResourceBinding, image.fp) + sizeof(ResourceBinding) * binding;
else
entry.offset = offsetof(ResourceBinding, image.integer) + sizeof(ResourceBinding) * binding;
entry.stride = sizeof(ResourceBinding);
for (unsigned i = 0; i < array_size; i++)
{
auto &entry = update_entries[update_count++];
entry.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
entry.dstBinding = binding;
entry.dstArrayElement = i;
entry.descriptorCount = 1;
if (set_layout.fp_mask & (1u << binding))
entry.offset = offsetof(ResourceBinding, image.fp) + sizeof(ResourceBinding) * (binding + i);
else
entry.offset = offsetof(ResourceBinding, image.integer) + sizeof(ResourceBinding) * (binding + i);
entry.stride = sizeof(ResourceBinding);
}
});
for_each_bit(set_layout.separate_image_mask, [&](uint32_t binding) {
unsigned array_size = set_layout.array_size[binding];
VK_ASSERT(update_count < VULKAN_NUM_BINDINGS);
auto &entry = update_entries[update_count++];
entry.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
entry.dstBinding = binding;
entry.dstArrayElement = 0;
entry.descriptorCount = array_size;
if (set_layout.fp_mask & (1u << binding))
entry.offset = offsetof(ResourceBinding, image.fp) + sizeof(ResourceBinding) * binding;
else
entry.offset = offsetof(ResourceBinding, image.integer) + sizeof(ResourceBinding) * binding;
entry.stride = sizeof(ResourceBinding);
for (unsigned i = 0; i < array_size; i++)
{
auto &entry = update_entries[update_count++];
entry.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
entry.dstBinding = binding;
entry.dstArrayElement = i;
entry.descriptorCount = 1;
if (set_layout.fp_mask & (1u << binding))
entry.offset = offsetof(ResourceBinding, image.fp) + sizeof(ResourceBinding) * (binding + i);
else
entry.offset = offsetof(ResourceBinding, image.integer) + sizeof(ResourceBinding) * (binding + i);
entry.stride = sizeof(ResourceBinding);
}
});
for_each_bit(set_layout.sampler_mask & ~set_layout.immutable_sampler_mask, [&](uint32_t binding) {
unsigned array_size = set_layout.array_size[binding];
VK_ASSERT(update_count < VULKAN_NUM_BINDINGS);
auto &entry = update_entries[update_count++];
entry.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER;
entry.dstBinding = binding;
entry.dstArrayElement = 0;
entry.descriptorCount = array_size;
entry.offset = offsetof(ResourceBinding, image.fp) + sizeof(ResourceBinding) * binding;
entry.stride = sizeof(ResourceBinding);
for (unsigned i = 0; i < array_size; i++)
{
auto &entry = update_entries[update_count++];
entry.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER;
entry.dstBinding = binding;
entry.dstArrayElement = i;
entry.descriptorCount = 1;
entry.offset = offsetof(ResourceBinding, image.fp) + sizeof(ResourceBinding) * (binding + i);
entry.stride = sizeof(ResourceBinding);
}
});
for_each_bit(set_layout.storage_image_mask, [&](uint32_t binding) {
unsigned array_size = set_layout.array_size[binding];
VK_ASSERT(update_count < VULKAN_NUM_BINDINGS);
auto &entry = update_entries[update_count++];
entry.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
entry.dstBinding = binding;
entry.dstArrayElement = 0;
entry.descriptorCount = array_size;
if (set_layout.fp_mask & (1u << binding))
entry.offset = offsetof(ResourceBinding, image.fp) + sizeof(ResourceBinding) * binding;
else
entry.offset = offsetof(ResourceBinding, image.integer) + sizeof(ResourceBinding) * binding;
entry.stride = sizeof(ResourceBinding);
for (unsigned i = 0; i < array_size; i++)
{
auto &entry = update_entries[update_count++];
entry.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
entry.dstBinding = binding;
entry.dstArrayElement = i;
entry.descriptorCount = 1;
if (set_layout.fp_mask & (1u << binding))
entry.offset = offsetof(ResourceBinding, image.fp) + sizeof(ResourceBinding) * (binding + i);
else
entry.offset = offsetof(ResourceBinding, image.integer) + sizeof(ResourceBinding) * (binding + i);
entry.stride = sizeof(ResourceBinding);
}
});
for_each_bit(set_layout.input_attachment_mask, [&](uint32_t binding) {
unsigned array_size = set_layout.array_size[binding];
VK_ASSERT(update_count < VULKAN_NUM_BINDINGS);
auto &entry = update_entries[update_count++];
entry.descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT;
entry.dstBinding = binding;
entry.dstArrayElement = 0;
entry.descriptorCount = array_size;
if (set_layout.fp_mask & (1u << binding))
entry.offset = offsetof(ResourceBinding, image.fp) + sizeof(ResourceBinding) * binding;
else
entry.offset = offsetof(ResourceBinding, image.integer) + sizeof(ResourceBinding) * binding;
entry.stride = sizeof(ResourceBinding);
for (unsigned i = 0; i < array_size; i++)
{
auto &entry = update_entries[update_count++];
entry.descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT;
entry.dstBinding = binding;
entry.dstArrayElement = i;
entry.descriptorCount = 1;
if (set_layout.fp_mask & (1u << binding))
entry.offset = offsetof(ResourceBinding, image.fp) + sizeof(ResourceBinding) * (binding + i);
else
entry.offset = offsetof(ResourceBinding, image.integer) + sizeof(ResourceBinding) * (binding + i);
entry.stride = sizeof(ResourceBinding);
}
});
VkDescriptorUpdateTemplateCreateInfo info = {VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO };
VkDescriptorUpdateTemplateCreateInfo info = { VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO };
info.pipelineLayout = pipe_layout;
info.descriptorSetLayout = set_allocators[desc_set]->get_layout();
info.templateType = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET;
if (desc_set == push_set_index)
{
info.descriptorSetLayout = set_allocators[desc_set]->get_layout_for_push();
info.templateType = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_PUSH_DESCRIPTORS_KHR;
}
else
{
info.descriptorSetLayout = set_allocators[desc_set]->get_layout_for_pool();
info.templateType = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET;
}
info.set = desc_set;
info.descriptorUpdateEntryCount = update_count;
info.pDescriptorUpdateEntries = update_entries;
+20 -11
View File
@@ -83,18 +83,21 @@ struct CombinedResourceLayout
Util::Hash push_constant_layout_hash = 0;
};
struct ResourceBinding
union ResourceBinding
{
union {
VkDescriptorBufferInfo buffer;
struct
{
VkDescriptorImageInfo fp;
VkDescriptorImageInfo integer;
} image;
VkBufferView buffer_view;
};
VkDeviceSize dynamic_offset;
struct
{
VkDescriptorBufferInfo dynamic;
VkDescriptorBufferInfo push;
} buffer;
struct
{
VkDescriptorImageInfo fp;
VkDescriptorImageInfo integer;
} image;
VkBufferView buffer_view;
};
struct ResourceBindings
@@ -138,12 +141,18 @@ public:
return update_template[set];
}
uint32_t get_push_set_index() const
{
return push_set_index;
}
private:
Device *device;
VkPipelineLayout pipe_layout = VK_NULL_HANDLE;
CombinedResourceLayout layout;
DescriptorSetAllocator *set_allocators[VULKAN_NUM_DESCRIPTOR_SETS] = {};
VkDescriptorUpdateTemplate update_template[VULKAN_NUM_DESCRIPTOR_SETS] = {};
uint32_t push_set_index = UINT32_MAX;
void create_update_templates();
};
@@ -35,11 +35,16 @@
#include "logging.hpp"
#include <utility>
#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT
// Workaround silly Xlib headers that define macros for these globally :(
#ifdef None
#undef None
#endif
#ifdef Bool
#undef Bool
#endif
#ifdef Status
#undef Status
#endif
#ifdef VULKAN_DEBUG
#define VK_ASSERT(x) \
@@ -222,6 +222,9 @@ bool WSI::init_surface_swapchain_dxgi(unsigned width, unsigned height)
case BackbufferFormat::HDR10:
format = { VK_FORMAT_A2B10G10R10_UNORM_PACK32, VK_COLOR_SPACE_HDR10_ST2084_EXT };
break;
default:
return false;
}
constexpr unsigned num_images = 3;
@@ -411,11 +414,13 @@ void WSI::nonblock_delete_swapchains()
{
if (!swap.fence || swap.fence->wait_timeout(0))
{
platform->destroy_swapchain_resources(swap.swapchain);
table->vkDestroySwapchainKHR(device->get_device(), swap.swapchain, nullptr);
}
else if (pending >= 2)
{
swap.fence->wait();
platform->destroy_swapchain_resources(swap.swapchain);
table->vkDestroySwapchainKHR(device->get_device(), swap.swapchain, nullptr);
}
else
@@ -448,6 +453,7 @@ void WSI::drain_swapchain(bool in_tear_down)
{
if (old_swap.fence)
old_swap.fence->wait();
platform->destroy_swapchain_resources(old_swap.swapchain);
table->vkDestroySwapchainKHR(context->get_device(), old_swap.swapchain, nullptr);
}
@@ -475,6 +481,7 @@ void WSI::tear_down_swapchain()
drain_swapchain(true);
platform->event_swapchain_destroyed();
platform->destroy_swapchain_resources(swapchain);
table->vkDestroySwapchainKHR(context->get_device(), swapchain, nullptr);
swapchain = VK_NULL_HANDLE;
has_acquired_swapchain_index = false;
@@ -1664,6 +1671,7 @@ WSI::SwapchainError WSI::init_swapchain(unsigned width, unsigned height)
platform->event_swapchain_destroyed();
auto res = table->vkCreateSwapchainKHR(context->get_device(), &info, nullptr, &swapchain);
platform->destroy_swapchain_resources(old_swapchain);
table->vkDestroySwapchainKHR(context->get_device(), old_swapchain, nullptr);
has_acquired_swapchain_index = false;
present_id = 0;
@@ -1749,6 +1757,7 @@ void WSIPlatform::event_swapchain_created(Device *, VkSwapchainKHR, unsigned, un
VkFormat, VkColorSpaceKHR,
VkSurfaceTransformFlagBitsKHR) {}
void WSIPlatform::event_swapchain_destroyed() {}
void WSIPlatform::destroy_swapchain_resources(VkSwapchainKHR) {}
void WSIPlatform::event_frame_tick(double, double) {}
void WSIPlatform::event_swapchain_index(Device *, unsigned) {}
void WSIPlatform::begin_drop_event() {}
@@ -118,6 +118,7 @@ public:
float aspect_ratio, size_t num_swapchain_images,
VkFormat format, VkColorSpaceKHR color_space,
VkSurfaceTransformFlagBitsKHR pre_rotate);
virtual void destroy_swapchain_resources(VkSwapchainKHR swapchain);
virtual void event_swapchain_destroyed();
virtual void event_frame_tick(double frame, double elapsed);
virtual void event_swapchain_index(Device *device, unsigned index);
@@ -50,10 +50,10 @@ auto NTTDataKeypad::data() -> n2 {
case 10: return l->value();
case 11: return r->value();
case 12: return 1; //4-bit device signature
case 13: return 0;
case 14: return 1;
case 15: return 1;
case 12: return 0; //4-bit device signature
case 13: return 1;
case 14: return 0;
case 15: return 0;
case 16: return zero->value();
case 17: return one->value();
+1 -1
View File
@@ -8,7 +8,7 @@ struct DIP {
//serialization.cpp
auto serialize(serializer&) -> void;
n8 value = 0x00;
n8 value = 0x03; // default game time used for the competition is 6 minutes
};
extern DIP dip;
+1 -1
View File
@@ -3,7 +3,7 @@ auto SA1::dmaNormal() -> void {
while(io.dtc--) {
n8 data = r.mdr;
n24 source = io.dsa++;
n16 target = io.dda++;
n24 target = io.dda++;
if(io.sd == DMA::SourceROM && io.dd == DMA::DestBWRAM) {
step();
+1 -1
View File
@@ -152,7 +152,7 @@ auto SDD1::Decompressor::PEM::getBit(n8 context) -> n8 {
const State& s = SDD1::Decompressor::PEM::evolutionTable[currentStatus];
n8 bit;
bool endOfRun;
bool endOfRun = false;
switch(s.codeNumber) {
case 0: bit = self.bg0.getBit(endOfRun); break;
case 1: bit = self.bg1.getBit(endOfRun); break;
+2 -2
View File
@@ -49,7 +49,7 @@ auto SuperFX::rpix(n8 x, n8 y) -> n8 {
flushPixelCache(pixelcache[1]);
flushPixelCache(pixelcache[0]);
u32 cn; //character number
u32 cn = 0; //character number
switch(regs.por.obj ? 3 : regs.scmr.ht) {
case 0: cn = ((x & 0xf8) << 1) + ((y & 0xf8) >> 3); break;
case 1: cn = ((x & 0xf8) << 1) + ((x & 0xf8) >> 1) + ((y & 0xf8) >> 3); break;
@@ -76,7 +76,7 @@ auto SuperFX::flushPixelCache(PixelCache& cache) -> void {
n8 x = cache.offset << 3;
n8 y = cache.offset >> 5;
u32 cn; //character number
u32 cn = 0; //character number
switch(regs.por.obj ? 3 : regs.scmr.ht) {
case 0: cn = ((x & 0xf8) << 1) + ((y & 0xf8) >> 3); break;
case 1: cn = ((x & 0xf8) << 1) + ((x & 0xf8) >> 1) + ((y & 0xf8) >> 3); break;
+2 -1
View File
@@ -38,7 +38,8 @@ auto PPU::Window::render(Layer& layer, bool enable, bool output[448]) -> void {
}
auto PPU::Window::render(Color& color, u32 mask, bool output[448]) -> void {
bool set, clear;
bool set = 0;
bool clear = 0;
switch(mask) {
case 0: memory::fill<bool>(output, 256, 1); return; //always
case 1: set = 1, clear = 0; break; //inside
+6 -6
View File
@@ -90,12 +90,12 @@ struct PPU : PPUBase::Implementation, PPUcounter {
//main.cpp
auto main() -> void;
noinline auto cycleObjectEvaluate() -> void;
template<u32 Cycle> noinline auto cycleBackgroundFetch() -> void;
noinline auto cycleBackgroundBegin() -> void;
noinline auto cycleBackgroundBelow() -> void;
noinline auto cycleBackgroundAbove() -> void;
noinline auto cycleRenderPixel() -> void;
NALL_NOINLINE auto cycleObjectEvaluate() -> void;
template<u32 Cycle> NALL_NOINLINE auto cycleBackgroundFetch() -> void;
NALL_NOINLINE auto cycleBackgroundBegin() -> void;
NALL_NOINLINE auto cycleBackgroundBelow() -> void;
NALL_NOINLINE auto cycleBackgroundAbove() -> void;
NALL_NOINLINE auto cycleRenderPixel() -> void;
template<u32> auto cycle() -> void;
//io.cpp
+3 -1
View File
@@ -138,15 +138,17 @@ endif
$(call rcopy,$(mia.path)/Database/*,$(output.path)/$(name).app/Contents/Resources/Database/)
sips -s format icns resource/$(name).png --out $(output.path)/$(name).app/Contents/Resources/$(name).icns
codesign --force --deep --options runtime --entitlements resource/$(name).selfsigned.entitlements --sign - $(output.path)/$(name).app
else ifeq ($(platform),windows)
else ifneq ($(filter $(platform),windows linux bsd),)
$(call mkdir,$(output.path)/Shaders/)
$(call mkdir,$(output.path)/Database/)
$(call rcopy,$(thirdparty.path)/slang-shaders/*,$(output.path)/Shaders/)
$(call rcopy,$(mia.path)/Database/*,$(output.path)/Database/)
ifeq ($(librashader), true)
ifdef librashader.path
$(call copy,$(librashader.path),$(output.path)/)
endif
endif
endif
verbose: nall.verbose ruby.verbose hiro.verbose all;
+1 -1
View File
@@ -138,7 +138,7 @@ auto nall::main(Arguments arguments) -> void {
program.startGameLoad.reset();
for(auto argument : arguments) {
if(file::exists(argument)) program.startGameLoad.append(argument);
if(file::exists(argument) || directory::exists(argument)) program.startGameLoad.append(argument);
}
Instances::presentation.construct();
+2
View File
@@ -46,6 +46,8 @@ struct Emulator {
vector<Firmware> firmware;
shared_pointer<mia::Pak> system;
shared_pointer<mia::Pak> game;
shared_pointer<mia::Pak> gamepad;
shared_pointer<mia::Pak> gb;
vector<InputPort> ports;
vector<string> inputBlacklist;
vector<string> portBlacklist;
+1
View File
@@ -39,6 +39,7 @@ namespace ares::Atari2600 {
#ifdef CORE_GBA
namespace ares::GameBoyAdvance {
auto load(Node::System& node, string name) -> bool;
auto option(string name, string value) -> bool;
}
#include "game-boy-advance.cpp"
#endif
+2
View File
@@ -57,6 +57,8 @@ auto GameBoyAdvance::load() -> bool {
system = mia::System::create("Game Boy Advance");
if(!system->load(firmware[0].location)) return errorFirmware(firmware[0]), false;
ares::GameBoyAdvance::option("Pixel Accuracy", settings.video.pixelAccuracy);
if(!ares::GameBoyAdvance::load(root, "[Nintendo] Game Boy Advance")) return false;
if(auto port = root->find<ares::Node::Port>("Cartridge Slot")) {
+2
View File
@@ -84,6 +84,8 @@ auto MegaDrive::load() -> bool {
if(!system->load()) return false;
}
ares::MegaDrive::option("TMSS", settings.megadrive.tmss);
if(!ares::MegaDrive::load(root, {"[Sega] ", name, " (", region, ")"})) return false;
if(auto port = root->find<ares::Node::Port>("Cartridge Slot")) {
-2
View File
@@ -6,9 +6,7 @@ struct Nintendo64 : Emulator {
auto save() -> bool override;
auto pak(ares::Node::Object) -> shared_pointer<vfs::directory> override;
shared_pointer<mia::Pak> gamepad;
shared_pointer<mia::Pak> disk;
shared_pointer<mia::Pak> gb;
u32 regionID = 0;
sTimer diskInsertTimer;
};
+31 -9
View File
@@ -6,7 +6,6 @@ struct Nintendo64DD : Emulator {
auto save() -> bool override;
auto pak(ares::Node::Object) -> shared_pointer<vfs::directory> override;
shared_pointer<mia::Pak> gamepad;
u32 regionID = 0;
sTimer diskInsertTimer;
};
@@ -95,16 +94,39 @@ auto Nintendo64DD::load() -> bool {
if(auto port = root->find<ares::Node::Port>({"Controller Port ", 1 + id})) {
auto peripheral = port->allocate("Gamepad");
port->connect();
bool transferPakConnected = false;
if(auto port = peripheral->find<ares::Node::Port>("Pak")) {
if(id == 0 && game->pak->attribute("cpak").boolean()) {
gamepad = mia::Pak::create("Nintendo 64");
gamepad->pak->append("save.pak", 32_KiB);
gamepad->load("save.pak", ".pak", game->location);
port->allocate("Controller Pak");
port->connect();
} else if(game->pak->attribute("rpak").boolean()) {
port->allocate("Rumble Pak");
if(id == 0 && game->pak->attribute("tpak").boolean()) {
#if defined(CORE_GB)
auto transferPak = port->allocate("Transfer Pak");
port->connect();
if(auto slot = transferPak->find<ares::Node::Port>("Cartridge Slot")) {
gb = mia::Medium::create("Game Boy");
string tmpPath;
if(gb->load(Emulator::load(gb, tmpPath))) {
slot->allocate();
slot->connect();
transferPakConnected = true;
} else {
port->disconnect();
gb.reset();
}
}
#endif
}
if(!transferPakConnected) {
if(id == 0 && game->pak->attribute("cpak").boolean()) {
gamepad = mia::Pak::create("Nintendo 64");
gamepad->pak->append("save.pak", 32_KiB);
gamepad->load("save.pak", ".pak", game->location);
port->allocate("Controller Pak");
port->connect();
} else if(game->pak->attribute("rpak").boolean()) {
port->allocate("Rumble Pak");
port->connect();
}
}
}
}
+31 -1
View File
@@ -43,6 +43,36 @@ SuperFamicom::SuperFamicom() {
device.digital ("Right", virtualPorts[id].mouse.right);
port.append(device); }
{ InputDevice device{"NTT Data Keypad"};
device.digital("Up", virtualPorts[id].pad.up);
device.digital("Down", virtualPorts[id].pad.down);
device.digital("Left", virtualPorts[id].pad.left);
device.digital("Right", virtualPorts[id].pad.right);
device.digital("B", virtualPorts[id].pad.south);
device.digital("A", virtualPorts[id].pad.east);
device.digital("Y", virtualPorts[id].pad.west);
device.digital("X", virtualPorts[id].pad.north);
device.digital("L", virtualPorts[id].pad.l_bumper);
device.digital("R", virtualPorts[id].pad.r_bumper);
device.digital("Back", virtualPorts[id].pad.select);
device.digital("Next", virtualPorts[id].pad.start);
device.digital("1", virtualPorts[id].pad.one);
device.digital("2", virtualPorts[id].pad.two);
device.digital("3", virtualPorts[id].pad.three);
device.digital("4", virtualPorts[id].pad.four);
device.digital("5", virtualPorts[id].pad.five);
device.digital("6", virtualPorts[id].pad.six);
device.digital("7", virtualPorts[id].pad.seven);
device.digital("8", virtualPorts[id].pad.eight);
device.digital("9", virtualPorts[id].pad.nine);
device.digital("0", virtualPorts[id].pad.zero);
device.digital("*", virtualPorts[id].pad.star);
device.digital("C", virtualPorts[id].pad.clear);
device.digital("#", virtualPorts[id].pad.pound);
device.digital(".", virtualPorts[id].pad.point);
device.digital("End", virtualPorts[id].pad.end);
port.append(device); }
{ InputDevice device{"Super Scope"};
device.relative("X", virtualPorts[id].mouse.x);
device.relative("Y", virtualPorts[id].mouse.y);
@@ -60,7 +90,7 @@ SuperFamicom::SuperFamicom() {
ports.append(port);
}
inputBlacklist = {"Justifiers", "NTT Data Keypad", "Super Multitap"};
inputBlacklist = {"Justifiers", "Super Multitap"};
}
auto SuperFamicom::load() -> bool {
+15
View File
@@ -162,6 +162,21 @@ struct VirtualPad : InputDevice {
InputAnalog rstick_left;
InputAnalog rstick_right;
InputRumble rumble;
InputDigital one;
InputDigital two;
InputDigital three;
InputDigital four;
InputDigital five;
InputDigital six;
InputDigital seven;
InputDigital eight;
InputDigital nine;
InputDigital zero;
InputDigital star;
InputDigital clear;
InputDigital pound;
InputDigital point;
InputDigital end;
};
struct VirtualMouse : InputDevice {
+109
View File
@@ -532,6 +532,115 @@ auto Presentation::refreshSystemMenu() -> void {
if(peripheralItem.text() == connected->name()) peripheralItem.setChecked();
}
}
// Allow for additional paks for N64 gamepads
if((emulator->name == "Nintendo 64" || emulator->name == "Nintendo 64DD")
&& port->type() == "Controller") {
const string portNum = port->name()[port->name().length() - 1];
// remove this check to enable pak menu option for all 4 controllers
if(portNum == "1") {
if(portMenu.actionCount() > 0) portMenu.append(MenuSeparator());
Menu pakMenu{&portMenu};
pakMenu.setText("Pak");
Group pakGroup;
MenuRadioItem nothing{&pakMenu};;
nothing.setText("Nothing");
nothing.setAttribute<ares::Node::Port>("port", port);
nothing.onActivate([=] {
auto port = nothing.attribute<ares::Node::Port>("port");
const string portName = port->name();
if(auto port = emulator->root->find<ares::Node::Port>(portName)) {
port->disconnect();
auto peripheral = port->allocate("Gamepad");
port->connect();
}
});
pakGroup.append(nothing);
MenuRadioItem cpak{&pakMenu};
cpak.setAttribute<ares::Node::Port>("port", port);
cpak.setText("Controller Pak");
cpak.onActivate([=] {
auto port = cpak.attribute<ares::Node::Port>("port");
const string portName = port->name();
if(auto port = emulator->root->find<ares::Node::Port>(portName)) {
port->disconnect();
auto peripheral = port->allocate("Gamepad");
port->connect();
if(auto port = peripheral->find<ares::Node::Port>("Pak")) {
emulator->gamepad = mia::Pak::create("Nintendo 64");
emulator->gamepad->pak->append("save.pak", 32_KiB);
string pakExt = ".pak";
if(portNum != "1") { pakExt = string(".", portNum, ".pak");}
emulator->gamepad->load("save.pak", pakExt, emulator->game->location);
port->allocate("Controller Pak");
port->connect();
}
}
});
pakGroup.append(cpak);
MenuRadioItem rpak{&pakMenu};
rpak.setAttribute<ares::Node::Port>("port", port);
rpak.setText("Rumble Pak");
rpak.onActivate([=] {
auto port = rpak.attribute<ares::Node::Port>("port");
const string portName = port->name();
if(auto port = emulator->root->find<ares::Node::Port>(portName)) {
port->disconnect();
auto peripheral = port->allocate("Gamepad");
port->connect();
if(auto port = peripheral->find<ares::Node::Port>("Pak")) {
port->allocate("Rumble Pak");
port->connect();
}
}
});
pakGroup.append(rpak);
MenuRadioItem tpak{&pakMenu};
tpak.setAttribute<ares::Node::Port>("port", port);
tpak.setText("Transfer Pak");
tpak.onActivate([=] {
auto port = tpak.attribute<ares::Node::Port>("port");
const string portName = port->name();
if(auto port = emulator->root->find<ares::Node::Port>(portName)) {
port->disconnect();
auto peripheral = port->allocate("Gamepad");
port->connect();
if(auto port = peripheral->find<ares::Node::Port>("Pak")) {
#if defined(CORE_GB)
emulator->gb.reset();
auto transferPak = port->allocate("Transfer Pak");
port->connect();
if(auto slot = transferPak->find<ares::Node::Port>("Cartridge Slot")) {
emulator->gb = mia::Medium::create("Game Boy");
string tmpPath;
if(emulator->gb->load(emulator->load(emulator->gb, tmpPath))) {
slot->allocate();
slot->connect();
} else {
port->disconnect();
emulator->gb.reset();
}
}
#endif
}
}
});
pakGroup.append(tpak);
// set currently enabled pak
// Note: based on initialization routine in desktop-ui/emulator/nintendo-64.cpp & nintedo-64dd.cpp
if(emulator->game->pak->attribute("tpak").boolean() && portNum == "1") tpak.setChecked();
else if(emulator->game->pak->attribute("cpak").boolean() && portNum == "1") cpak.setChecked();
else if(emulator->game->pak->attribute("rpak").boolean()) rpak.setChecked();
else nothing.setChecked();
}
}
}
if(portsFound > 0) systemMenu.append(MenuSeparator());
+9 -1
View File
@@ -43,5 +43,13 @@ auto OptionSettings::construct() -> void {
});
nintendo64ExpansionPakLayout.setAlignment(1).setPadding(12_sx, 0);
nintendo64ExpansionPakHint.setText("Enable/Disable the 4MB Expansion Pak").setFont(Font().setSize(7.0)).setForegroundColor(SystemColor::Sublabel);
megaDriveSettingsLabel.setText("Mega Drive Settings").setFont(Font().setBold());
megaDriveTmssOption.setText("TMSS Boot Rom").setChecked(settings.megadrive.tmss).onToggle([&] {
settings.megadrive.tmss = megaDriveTmssOption.checked();
});
megaDriveTmssLayout.setAlignment(1).setPadding(12_sx, 0);
megaDriveTmssHint.setText("Enable/Disable the TMSS Boot Rom at system initialization").setFont(Font().setSize(7.0)).setForegroundColor(SystemColor::Sublabel);
}
+2
View File
@@ -124,6 +124,8 @@ auto Settings::process(bool load) -> void {
bind(boolean, "Nintendo64/ExpansionPak", nintendo64.expansionPak);
bind(boolean, "MegaDrive/TMSS", megadrive.tmss);
for(u32 index : range(9)) {
string name = {"Recent/Game-", 1 + index};
bind(string, name, recent.game[index]);
+8
View File
@@ -104,6 +104,10 @@ struct Settings : Markup::Node {
struct Nintendo64 {
bool expansionPak = true;
} nintendo64;
struct MegaDrive {
bool tmss = false;
} megadrive;
};
struct VideoSettings : VerticalLayout {
@@ -258,6 +262,10 @@ struct OptionSettings : VerticalLayout {
HorizontalLayout nintendo64ExpansionPakLayout{this, Size{~0, 0}, 5};
CheckLabel nintendo64ExpansionPakOption{&nintendo64ExpansionPakLayout, Size{0, 0}, 5};
Label nintendo64ExpansionPakHint{&nintendo64ExpansionPakLayout, Size{0, 0}};
Label megaDriveSettingsLabel{this, Size{~0, 0}, 5};
HorizontalLayout megaDriveTmssLayout{this, Size{~0, 0}, 5};
CheckLabel megaDriveTmssOption{&megaDriveTmssLayout, Size{0, 0}, 5};
Label megaDriveTmssHint{&megaDriveTmssLayout, Size{0, 0}};
};
struct FirmwareSettings : VerticalLayout {
+4 -1
View File
@@ -162,7 +162,10 @@
NSPoint localPoint = [self convertPoint:event.locationInWindow fromView:nil];
NSInteger row = [self rowAtPoint:localPoint];
NSInteger column = [self columnAtPoint:localPoint];
if (row < 0 || row >= tableView->state.items.size()) {
return nil;
}
if (row >= 0 && ![self isRowSelected:row]) {
[self selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:NO];
+1 -1
View File
@@ -676,7 +676,7 @@ board: EVENT-CC92
memory type=ROM content=Level-3
dip
processor manufacturer=NEC architecture=uPD7725
map address=20-3f,a0-bf:8000-ffff mask=0x7fff
map address=20-3f,a0-bf:8000-ffff mask=0x3fff
memory type=ROM content=Program architecture=uPD7725
memory type=ROM content=Data architecture=uPD7725
memory type=RAM content=Data architecture=uPD7725
+108
View File
@@ -287,6 +287,60 @@ game
size: 0x80000
content: Download
//sha256 is without firmware
game
sha256: 67c1a4917f4cd0fd704b83330a7e91763736c2d2a10a7e12318fdac54cd9c6c6
title: Campus Challenge '92
name: Super Nintendo Campus Challenge 1992
region: Japan
revision: 1.0
board: EVENT-CC92
memory
type: ROM
size: 0x40000
content: Program
memory
type: ROM
size: 0x80000
content: Level-1
memory
type: ROM
size: 0x80000
content: Level-2
memory
type: ROM
size: 0x80000
content: Level-3
memory
type: RAM
size: 0x2000
content: Save
volatile
memory
type: ROM
size: 0x1800
content: Program
manufacturer: NEC
architecture: uPD7725
identifier: DSP1
memory
type: ROM
size: 0x800
content: Data
manufacturer: NEC
architecture: uPD7725
identifier: DSP1
memory
type: RAM
size: 0x200
content: Data
manufacturer: NEC
architecture: uPD7725
identifier: DSP1
volatile
oscillator
frequency: 8000000
game
sha256: 6e7dcbb4df32903d6ff5da1e308342c0a72f5af3f11479cf49391dc3a17d5d7b
title: キャプテンコマンドー
@@ -12126,6 +12180,60 @@ game
size: 0x200000
content: Program
//sha256 is without firmware
game
sha256: 5443a97e9c40e25821a8fb8c91b63c7bd8c3060d9ff888ee6c573b87b53935f4
title: PowerFest '94
name: PowerFest '94
region: USA
revision: 1.0
board: EVENT-PF94
memory
type: ROM
size: 0x40000
content: Program
memory
type: ROM
size: 0x80000
content: Level-1
memory
type: ROM
size: 0x80000
content: Level-2
memory
type: ROM
size: 0x100000
content: Level-3
memory
type: RAM
size: 0x2000
content: Save
volatile
memory
type: ROM
size: 0x1800
content: Program
manufacturer: NEC
architecture: uPD7725
identifier: DSP1
memory
type: ROM
size: 0x800
content: Data
manufacturer: NEC
architecture: uPD7725
identifier: DSP1
memory
type: RAM
size: 0x200
content: Data
manufacturer: NEC
architecture: uPD7725
identifier: DSP1
volatile
oscillator
frequency: 8000000
game
sha256: 06c8fc466805f97c9147711b2d8370d4f4d05d9fa3a916f17aa1682f73c9a63b
title: Power Instinct
+9
View File
@@ -182,18 +182,25 @@ auto Nintendo64::analyze(vector<u8>& data) -> string {
string region = "NTSC";
switch(data[0x3e]) {
case 'A': region = "NTSC"; break; //North America + Japan
case 'B': region = "NTSC"; break; //Brazil
case 'C': region = "NTSC"; break; //China
case 'D': region = "PAL"; break; //Germany
case 'E': region = "NTSC"; break; //North America
case 'F': region = "PAL"; break; //France
case 'G': region = "NTSC"; break; //Gateway 64 (NTSC)
case 'H': region = "PAL"; break; //Netherlands
case 'I': region = "PAL"; break; //Italy
case 'J': region = "NTSC"; break; //Japan
case 'K': region = "NTSC"; break; //Korea
case 'L': region = "PAL"; break; //Gateway 64 (PAL)
case 'N': region = "NTSC"; break; //Canada
case 'P': region = "PAL"; break; //Europe
case 'S': region = "PAL"; break; //Spain
case 'U': region = "PAL"; break; //Australia
case 'W': region = "PAL"; break; //Scandinavia
case 'X': region = "PAL"; break; //Europe
case 'Y': region = "PAL"; break; //Europe
case 'Z': region = "PAL"; break; //Europe
}
string id;
@@ -431,6 +438,7 @@ auto Nintendo64::analyze(vector<u8>& data) -> string {
if(id == "NDP") {flash = 128_KiB;} //Dinosaur Planet (Unlicensed)
//Controller Pak
if(id == "N4W") {cpak = true; rpak = true;} //40 Winks (Aftermarket)
if(id == "NO7") {cpak = true; rpak = true;} //The World Is Not Enough
if(id == "NAY") {cpak = true;} //Aidyn Chronicles - The First Mage
if(id == "NBS") {cpak = true; rpak = true;} //All-Star Baseball '99
@@ -642,6 +650,7 @@ auto Nintendo64::analyze(vector<u8>& data) -> string {
if(id == "N8W") {cpak = true;} //World Cup '98
if(id == "NWO") {cpak = true; rpak = true;} //World Driver Championship
if(id == "NXF") {cpak = true; rpak = true;} //Xena Warrior Princess - The Talisman of Fate
if(id == "NXC") {cpak = true; rpak = true;} //Xeno Crisis (Aftermarket)
//Rumble Pak
if(id == "NJQ") {rpak = true;} //Batman Beyond - Return of the Joker [Batman of the Future - Return of the Joker (E)]
+52 -23
View File
@@ -36,21 +36,13 @@ protected:
auto SuperFamicom::load(string location) -> bool {
vector<u8> rom;
string directory = location;
if(directory::exists(location)) {
auto files = directory::files(location, "*.rom");
append(rom, {location, "program.rom" });
append(rom, {location, "data.rom" });
append(rom, {location, "expansion.rom"});
for(auto& file : files.match("*.program.rom")) append(rom, {location, file});
for(auto& file : files.match("*.data.rom" )) append(rom, {location, file});
for(auto& file : files.match("*.boot.rom" )) append(rom, {location, file});
} else if(rom = Medium::read(location)) {
directory = Location::dir(location);
//append firmware to the ROM if it is missing
auto manifest = analyze(rom);
auto document = BML::unserialize(manifest);
bool local_firmware = false;
bool folder = false;
auto append_missing_firmware = [&](auto document)
{
if(auto identifier = document["game/board/memory/identifier"]) {
if(!firmwareRomSize()) {
if(!firmwareRomSize() && !local_firmware) {
auto id = identifier.string();
array_view<u8> view;
if(id == "Cx4" ) view = Resource::SuperFamicom::Cx4;
@@ -66,15 +58,48 @@ auto SuperFamicom::load(string location) -> bool {
if(id == "ST018") view = Resource::SuperFamicom::ST018;
while(view) rom.append(*view++);
}
}
}
};
if(directory::exists(location)) {
auto files = directory::files(location, "*.rom");
append(rom, {location, "program.rom" });
append(rom, {location, "data.rom" });
append(rom, {location, "expansion.rom"});
for(auto& file : files.match("slot-*.rom" )) { append(rom, {location, file}); }
for(auto& file : files.match("*.program.rom")) { append(rom, {location, file}); local_firmware = true; }
for(auto& file : files.match("*.data.rom" )) { append(rom, {location, file}); local_firmware = true; }
for(auto& file : files.match("*.boot.rom" )) { append(rom, {location, file}); }
folder = true;
} else if(rom = Medium::read(location)) {
directory = Location::dir(location);
}
if(!rom) return false;
//append firmware to the ROM if it is missing
auto tmp_manifest = analyze(rom);
auto document = BML::unserialize(tmp_manifest);
append_missing_firmware(document);
this->sha256 = Hash::SHA256(rom).digest();
this->location = location;
this->manifest = Medium::manifestDatabase(sha256);
if(!manifest) {
auto local_manifest = location.replace({".", location.split(".").last()}, ".bml");
if (folder)
local_manifest = directory.append("manifest.bml");
if(file::exists(local_manifest)) {
manifest = file::read(local_manifest);
}
}
document = BML::unserialize(manifest);
append_missing_firmware(document); // if auto-detect failed, use chips from the manifest
if(!manifest) manifest = analyze(rom);
auto document = BML::unserialize(manifest);
document = BML::unserialize(manifest);
if(!document) return false;
pak = new vfs::directory;
@@ -529,6 +554,13 @@ auto SuperFamicom::board() const -> string {
if(headerAddress == 0x40ffb0) mode = "EXHIROM-";
}
//the Sufami Turbo has the non-descriptive label "ADD-ON BASE CASSETE"
//(yes, missing a T), and its serial "A9PJ" is shared with
//Bishoujo Senshi Sailor Moon SuperS - Fuwafuwa Panic (Japan)
//so we identify it with this embedded string
string sufamiSignature = "BANDAI SFC-ADX";
if(string(data.view(0, sufamiSignature.length())) == sufamiSignature) board.append("ST-", mode);
//this game's title overwrite the map mode with '!' (0x21), but is a LOROM game
if(label() == "YUYU NO QUIZ DE GO!GO") mode = "LOROM-";
@@ -538,10 +570,7 @@ auto SuperFamicom::board() const -> string {
bool epsonRTC = false;
bool sharpRTC = false;
if(serial() == "A9PJ") {
//Sufami Turbo (JPN)
board.append("ST-", mode);
} else if(serial() == "ZBSJ") {
if(serial() == "ZBSJ") {
//BS-X: Sore wa Namae o Nusumareta Machi no Monogatari (JPN)
board.append("BS-MCC-");
} else if(serial() == "042J") {
@@ -752,8 +781,8 @@ auto SuperFamicom::firmwareRomSize() const -> u32 {
}
auto SuperFamicom::ramSize() const -> u32 {
auto ramSize = data[headerAddress + 0x28] & 7;
if(ramSize) return 1024 << ramSize;
auto ramSize = data[headerAddress + 0x28];
if (ramSize) return (1 << (((ramSize - 1) & 7) + 1)) << 10;
return 0;
}
+2 -2
View File
@@ -84,8 +84,8 @@ inline auto CHD::load(const string& location) -> bool {
int track_no;
int frames;
int pregap_frames;
int postgap_frames;
int pregap_frames = 0;
int postgap_frames = 0;
// First, attempt to fetch CDROMv2 metadata
err = chd_get_metadata(chd, CDROM_TRACK_METADATA2_TAG, tracks.size(), metadata, sizeof(metadata), &metadata_size, nullptr, nullptr);
+9 -4
View File
@@ -192,10 +192,15 @@ namespace nall::GDB {
u32 sepIdx = sepIdxMaybe ? sepIdxMaybe.get() : 1;
u64 address = cmdName.slice(1, sepIdx-1).hex();
u64 unitSize = cmdName.slice(sepIdx+1, 1).hex();
u64 value = cmdParts.size() > 1 ? cmdParts[1].hex() : 0;
hooks.write(address, unitSize, value);
u64 byteSize = cmdName.slice(sepIdx+1, 1).hex();
string hexvalue = cmdParts.size() > 1 ? cmdParts[1] : string{};
vector<u8> value;
string hexbyte;
for (int i=0; i<hexvalue.size(); i+=2) {
hexbyte = hexvalue.slice(i, 2);
value.append((u8)toHex(hexbyte.data()));
}
hooks.write(address, value);
return "OK";
}
+21 -2
View File
@@ -9,10 +9,29 @@ enum class Signal : u8 {
HANGUP = 1,
INT = 2,
QUIT = 3,
ILLEGAL = 4,
ILL = 4,
TRAP = 5,
ABORT = 6,
EMT = 7,
FPE = 8,
KILL = 9,
BUS = 10,
SEGV = 11,
SYS = 12,
PIPE = 13,
ALRM = 14,
TERM = 15,
URG = 16,
STOP = 17,
TSTP = 18,
CONT = 19,
CHLD = 20,
TTIN = 21,
TTOU = 22,
IO = 23,
PROF = 27,
WINCH = 28,
LOST = 29,
};
/**
@@ -48,7 +67,7 @@ class Server : public nall::TCPText::Server {
struct {
// Memory
function<string(u64 address, u32 byteCount)> read{};
function<void(u64 address, u32 unitSize, u64 value)> write{};
function<void(u64 address, vector<u8> value)> write{};
function<u64(u64 address)> normalizeAddress{};
// Registers
+3 -3
View File
@@ -161,15 +161,15 @@ namespace nall {
#if defined(COMPILER_CLANG) || defined(COMPILER_GCC)
#define no_optimize __attribute__((optnone))
#define noinline __attribute__((noinline))
#define NALL_NOINLINE __attribute__((noinline))
#define alwaysinline inline __attribute__((always_inline))
#elif defined(COMPILER_MICROSOFT)
#define no_optimize
#define noinline __declspec(noinline)
#define NALL_NOINLINE __declspec(noinline)
#define alwaysinline inline __forceinline
#else
#define no_optimize
#define noinline
#define NALL_NOINLINE
#define alwaysinline inline
#endif
+1 -1
View File
@@ -21,7 +21,7 @@ namespace nall::recompiler {
if(args >= 1) options |= SLJIT_ARG_VALUE(SLJIT_ARG_TYPE_W, 1);
if(args >= 2) options |= SLJIT_ARG_VALUE(SLJIT_ARG_TYPE_W, 2);
if(args >= 3) options |= SLJIT_ARG_VALUE(SLJIT_ARG_TYPE_W, 3);
sljit_emit_enter(compiler, 0, options, 4, 3, 0, 0, 0);
sljit_emit_enter(compiler, 0, options, 4, 3, 0);
sljit_jump* entry = sljit_emit_jump(compiler, SLJIT_JUMP);
epilogue = sljit_emit_label(compiler);
sljit_emit_return_void(compiler);
+13 -10
View File
@@ -122,10 +122,14 @@ struct VideoMetal : VideoDriver, Metal {
}
auto isVRRSupported() -> bool {
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 120000
if (@available(macOS 12.0, *)) {
NSTimeInterval minInterval = view.window.screen.minimumRefreshInterval;
NSTimeInterval maxInterval = view.window.screen.maximumRefreshInterval;
_vrrIsSupported = minInterval != maxInterval;
#else
_vrrIsSupported = false;
#endif
return _vrrIsSupported;
} else {
return false;
@@ -139,6 +143,7 @@ struct VideoMetal : VideoDriver, Metal {
CFTimeInterval refreshRate = CGDisplayModeGetRefreshRate(displayMode);
_presentInterval = (1.0 / refreshRate);
} else {
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 120000
if (@available(macOS 12.0, *)) {
CFTimeInterval minimumInterval = view.window.screen.minimumRefreshInterval;
if (_refreshRateHint != 0) {
@@ -149,6 +154,7 @@ struct VideoMetal : VideoDriver, Metal {
_presentInterval = minimumInterval;
}
}
#endif
}
}
@@ -162,13 +168,15 @@ struct VideoMetal : VideoDriver, Metal {
}
if(file::exists(pathname)) {
if (_libra.preset_create(pathname.data(), &_preset) != NULL) {
if (auto error = _libra.preset_create(pathname.data(), &_preset)) {
print(string{"Metal: Failed to load shader: ", pathname, "\n"});
_libra.error_print(error);
return false;
}
if (_libra.mtl_filter_chain_create(&_preset, _commandQueue, nil, &_filterChain) != NULL) {
if (auto error = _libra.mtl_filter_chain_create(&_preset, _commandQueue, nil, &_filterChain)) {
print(string{"Metal: Failed to create filter chain for: ", pathname, "\n"});
_libra.error_print(error);
return false;
};
} else {
@@ -280,11 +288,6 @@ struct VideoMetal : VideoDriver, Metal {
_viewportSize.x = width;
_viewportSize.y = height;
_libraViewport.width = (uint32_t) width;
_libraViewport.height = (uint32_t) height;
_libraViewport.x = 0;
_libraViewport.y = 0;
_outputX = (_viewWidth - width) / 2;
_outputY = (_viewHeight - height) / 2;
@@ -341,8 +344,8 @@ private:
/// consisting of the pixel buffer from the emulator, onto a texture the same size as our eventual output,
/// `_renderTargetTexture`. Then it calls into librashader, which performs postprocessing onto the same
/// output texture. Then for the second render pass here, we composite the output texture within ares's viewport.
/// We need this last pass because librashader expects the viewport to be the same size as the output texture,
/// which is not the case for ares.
/// We defer this second pass because it is performed directly onto the drawable texture, which we want to delay
/// acquiring for as long as possible.
dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER);
@@ -382,7 +385,7 @@ private:
[renderEncoder endEncoding];
if (_filterChain) {
_libra.mtl_filter_chain_frame(&_filterChain, commandBuffer, frameCount, sourceTexture, _libraViewport, _renderTargetTexture, nil, nil);
_libra.mtl_filter_chain_frame(&_filterChain, commandBuffer, frameCount, sourceTexture, _renderTargetTexture, nil, nil, nil);
}
//this call will block the current thread/queue if a drawable is not yet available
-1
View File
@@ -99,6 +99,5 @@ struct Metal {
libra_instance_t _libra;
libra_shader_preset_t _preset;
libra_mtl_filter_chain_t _filterChain;
libra_viewport_t _libraViewport;
bool initialized = false;
};
Loaded 100 of 744 files, more files were not shown because too many files have changed in this diff. Show more