116 Commits
Author SHA1 Message Date
Flyinghead 8108e63907 rcheevos: race condition in event handlers and async tasks
The data passed to event handlers (achievements in particular) must be
copied if used by an async task. It might be deleted before the async
task runs.
Fixes MINIDUMP-3ZR MINIDUMP-4XJ MINIDUMP-4TB MINIDUMP-4CA MINIDUMP-38H
2024-10-21 16:13:00 +02:00
Flyinghead b0403ed103 imgui: ignore invalid fonts when merging
Avoid crashing in release mode if a merged font is invalid or FontNo
can't be found. The font is simply ignored and the glyph range isn't
loaded.
Fixes MINIDUMP-4TD
2024-10-21 16:03:28 +02:00
Flyinghead c86ed291cf picotcp: use dns.flyca.st as default dns
Use dns.flyca.st as default dns.
Resolve name when pico network starts.
2024-10-19 17:59:24 +02:00
Flyinghead d981c48a75 libretro: fix translations 2024-10-19 12:47:26 +02:00
github-actions[bot] 82925c4e81 Fetch translations & Recreate libretro_core_options_intl.h 2024-10-18 22:20:39 +00:00
Flyinghead 54e8589f63 android: disable import/export home when SAF is disabled. Code clean up
Disable import/export home folder buttons when not using SAF file
picker.
Get rid of unused home migration feature when onboarding.
Android TV screenshots and banner.
2024-10-18 19:23:03 +02:00
Flyinghead 4750ddb646 android: new option to use legacy file picker instead of new SAF one
Android 9 and earlier can use the legacy file browser. This fixes issues
with broken SAF file picker on Android TV 9.
Catch exception when no file picker is installed on the device (Android
TV 12) and instruct the user to install one.
Issue #1657
2024-10-17 16:21:06 +02:00
Flyinghead 894ac54b80 imgui: fix dpad navigation issue on shield tv
Pressing dpad up or down moves the selection 2 lines up or down resp.
Regression due to ca613db70d.
Happens on 1920x1080 screen with 213 dpi.
Relax vertically touching items test.
2024-10-17 16:04:27 +02:00
Flyinghead d4a223656f enable RTT Copy to VRAM for Silent Scope
Fixes VMU display
Issue #1703
2024-10-17 16:00:46 +02:00
Flyinghead b698cc3d10 dx11: reset device context state to fix libretro shaders
Reset device context before each render. Rebind palette and fog textures
every frame.
Fixes corruption with libretro shaders.
Issue #1612
2024-10-15 17:28:54 +02:00
Flyinghead 75af269bb8 rend: ignore inside clipping if rect is default value
Fixes invisible race start countdown in f355 (arcade).
Issue #1698
2024-10-15 15:55:34 +02:00
Flyinghead fd475bdd8e vk: some pipelines don't need the provoking vertex extension
ModVol and OIT Final and Clear pipelines don't need it.
2024-10-14 17:19:41 +02:00
Flyinghead f71dd6d4d8 cdi: better detection of invalid CDI media 2024-10-14 16:45:22 +02:00
Flyinghead 5aec6d05b0 vk: (lr) use one vk::DeviceQueueCreateInfo when possible
Using 2 with the same queue index causes a validation error and crash.
2024-10-14 16:39:33 +02:00
Flyinghead b4937ef271 f355: pass full BIOS path to slaves
if the bios file is located relative to the rom location, slaves won't
be able to find it. Pass the full bios path to avoid the issue.
2024-10-14 16:31:23 +02:00
Flyinghead a50164a91b vk: missing some changes to use VK_FORMAT_R8G8B8A8_UNORM
Fixes Naomi 2 games and android on-screen gamepad.
2024-10-13 14:22:50 +02:00
Wunkolo 595842f606 vk: Remove naomi2 vertex attributes when not needed
Disables the naomi2 vertex input attribute when emitting non-naomi2 pipelines.
This addresses some validation messages involving unused vertex inputs and optimizes the bandwidth of the input assembler a little bit for non-naomi2 games.
2024-10-13 13:26:42 +02:00
Wunkolo 8f74f7883a vk: Use VK_FORMAT_R8G8B8A8_UNORM for vertex colors
Rather than using `VK_FORMAT_R8G8B8A8_UINT` for these vertex attributes and then dividing by `255.0` in each of the shaders, the `VK_FORMAT_R8G8B8A8_UNORM` format will automatically remap byte components into the `0.0-1.0` range and removes the need to do the extra divisions or castings within the shader.
2024-10-13 13:25:40 +02:00
scribam 71dc4c3e39 ci: disable android signing on pull requests 2024-10-13 13:25:02 +02:00
scribam 6edad8eaba android: update gradle and agp 2024-10-13 13:25:02 +02:00
Wunkolo ea1cd9d31b vk: Fix undefined push-constant data
The full push-constant region is 24 bytes(6 floats), but some of these push-constant writes only wrote 20 bytes of data(5 floats).
Causing 4 bytes at the end to be left undefined.
Resolved by pushing an extra zero.
2024-10-13 13:24:27 +02:00
Flyinghead bb7ad07701 extra depth scale for sturmwind
Issue #1691
2024-10-12 17:35:59 +02:00
Flyinghead 9793e54646 avoid deadlock when sh4 cpu is restarted while emu is being stopped
The sh4 cpu is stopped/restarted during a soft reset, and dynarec code
reset (arm64, arm, x86). If the emulator is stopped concurrently, the
call may hang. Use a mutex-protected method to restart the cpu only if
the emu is still running.
2024-10-12 17:31:58 +02:00
Flyinghead 6d2ed417be lr: fix core options to work around translation issue
Adjacent string literals aren't supported.
Fix syntax errors in libretro_core_options_intl.h
Issue #1688
2024-10-12 11:49:12 +02:00
github-actions[bot] d3916f13d7 Fetch translations & Recreate libretro_core_options_intl.h 2024-10-11 22:20:30 +00:00
Flyinghead b2c6159b10 Merge remote-tracking branch 'origin/dev' 2024-10-11 15:28:19 +02:00
Wunk 4d73cc8e13 vk: Add VK_EXT_provoking_vertex optimization (#1681)
* vk: Add `VK_EXT_provoking_vertex` optimization

The dreamcast uses the last vertex as the provoking vertex, while vulkan uses the first vertex.
This requires an additional call to `setFirstProvokingVertex` to reorder the vertices for all incoming geometry.
With `VK_EXT_provoking_vertex`, the pipeline can designate that the provoking vertex is to be the last vertex, which removes the need to re-order incoming geometry on the CPU.

* vk: Propagate physical device API version to VMA

Allows VMA to make assumptions such as using the `*KHR` or non-`KHR` versions of certain function names.

* vk: Refactor libretro device initialization for `VK_EXT_provoking_vertex`

* vk: Top out at vulkan API version to VMA to 1.1

Despite the physical device possibly being 1.2 or 1.3, we only want up to 1.1. Otherwise we will be responsible for other API functions being resolved and loaded when passing to VMA.

* vk: Enable `VK_EXT_provoking_vertex` usage for ModVol and Final(OIT) pipeline

* vk: Enable `VK_EXT_provoking_vertex` for ModVol(OIT) pipeline

Pretty much anything handling dreamcast-geometry should use this extension when available

* vk: Additional `VK_EXT_provoking_vertex` pipeline fixes
2024-10-11 10:23:48 +02:00
Wunkolo 5b343562b9 vk: Add CommandBufferDebugScope utility-type
Adds a new RAII-based utility type for adding diagnostic information to command buffers.
Enabled only when `VK_DEBUG` is set to `1`.
2024-10-09 19:47:14 +02:00
Wunkandflyinghead b20db6a8bb vk: Disable LOD clamping in samplers (#1674)
This addresses the `BestPractices-Arm-vkCreateSampler-lod-clamping` message from ARM:
https://github.com/KhronosGroup/Vulkan-ValidationLayers/blob/65b79bac615ec1c47ab61a02d55a3bba871b56b9/layers/best_practices/bp_descriptor.cpp#L103-L110

Rather than clamping the LOD in the samplers, instead rely on the Image-View's `vk::ImageSubresourceRange` to limit the number of sampled LODs.

Currently, only game-textures actually have MipMaps, so this does not introduce any additional mip-map sampling or filtering anywhere. If any code want's to actually limit the number of LODs sampled, then they would allocate an additional ImageView for the range of MipMaps to be sampled.

Co-authored-by: flyinghead <[email protected]>
2024-10-09 19:46:36 +02:00
Wunkolo a8bc1c12f4 vk: Optimize redundant QuadBuffer uploads
Hashes the contents of the `QuadBuffer` vertex-data such that data is only uploaded when the vertices actually change rather than every frame.
2024-10-09 19:40:59 +02:00
Wunkolo b94acb70a6 vk: Use matching u/v/w sampler address-mode when possible
This addresses the `BestPractices-Arm-vkCreateSampler-different-wrapping-modes` message from ARM:
https://github.com/KhronosGroup/Vulkan-ValidationLayers/blob/65b79bac615ec1c47ab61a02d55a3bba871b56b9/layers/best_practices/bp_descriptor.cpp#L95-L100

The `W`-axis for these samplers is always unused, it's never the case that these samplers are going to be used for 3D textures.
ARM suggests trying to keep all of the wrapping-modes the same if possible for performance.

`wRepeat` will be set to the same value as `vRepeat` to try and encourage all three wrapping-modes to be the same.
2024-10-09 19:40:20 +02:00
Wunkolo 0c3f5c7a76 vk: Refactor device/instance extension enablement
* Uses a utility-lambda for repeated extension-adding logic
* Uses an `std::set` for the list of available extensions for quick queries
* `VK_EXT_DEBUG_REPORT` and `VK_EXT_DEBUG_UTILS` aren't device extensions and don't need to be here. They are instance extensions
* Each extension that is tested to be added has a corresponding log message for if it was enabled or if it was unavailable

```
00:00:162 rend\vulkan\vulkan_context.cpp:427 N[RENDERER]: Device extension enabled: VK_KHR_swapchain
00:00:162 rend\vulkan\vulkan_context.cpp:427 N[RENDERER]: Device extension enabled: VK_KHR_get_memory_requirements2
00:00:162 rend\vulkan\vulkan_context.cpp:427 N[RENDERER]: Device extension enabled: VK_KHR_dedicated_allocation
00:00:162 rend\vulkan\vulkan_context.cpp:430 N[RENDERER]: Device extension unavailable: VK_KHR_portability_subset
00:00:162 rend\vulkan\vulkan_context.cpp:430 N[RENDERER]: Device extension unavailable: VK_EXT_debug_marker
```
2024-10-09 19:39:49 +02:00
Flyinghead 03d20f9d5a bug report: ask for flycast version instead of github hash 2024-10-09 19:39:04 +02:00
Flyinghead 01e5cd882c pvr: fix default clipping rectangle
Set default clipping rectangle to max (1280x480).
Fixes Irides: Master of blocks bogus clipping to tile 0,0.
2024-10-09 19:37:42 +02:00
Wunk 556e2ead56 vk: Refactor physical device selection (#1671)
Rather than electing the first physical device it finds, and falling back on the first-listed GPU: a series of stable-partitions are done so that the "least compromising" GPU is selected based on a series of criteria.

It will now maximally try to find a GPU that(in order of priority):
* Is a discrete GPU
* Supports `fragmentStoresAndAtomics`
* Supports `R5G5B5`/`R5G6B5A1`/`R4G4B4A4`

In the case that a system has two dGPUs and one of them supports optimal-formats, the optimal-format one is selected

In the case that a system has an iGPU and the dGPU and they both support optimal formats, the dGPU is selected.

In the case that a system has an iGPU and the dGPU and the dGPU doesn't support optimal formats, the dGPU is still selected.
2024-10-08 17:07:09 +02:00
Wunkolo ff6a3119b0 vk: Determine object type enum when setting object name
Uses compile-time type-information provided by vulkan-hpp to automatically determine a vulkan-object's `objectType` when calling `setObjectName`.
2024-10-08 17:05:32 +02:00
Wunkolo 86657c0c12 Fix {Push,Pop}StyleVar assert
When entering an error or contentpath warning popup, there are two `ImguiStyleVar` on the stack.
Within the pop-up frame, an additional `ImguiStyleVar` is added.
When the popup ends, there is technically one more ImguiStyleVar on the internal stack than when we entered the popup, causing an assert to hit:

```
Program: G:\flycast\out\build\x64-Debug\flycast.exe
File: G:\flycast\core\deps\imgui\imgui.cpp
Line: 10104

Expression: SizeOfStyleVarStack >= g.StyleVarStack.Size && "PushStyleVar/PopStyleVar Mismatch!"
```

These nested imgui RAII objects need to be scoped such that the stack is preserved both upon entry and exit of these popups to avoid these asserts.
2024-10-08 17:05:03 +02:00
Flyinghead d7c168f406 naomi: copy cart dma data in chunks
Copy cart dma data in 1K chunks to avoid transferring all in one shot.
Fixes Soul Surfer and Beach Spiker audio glitches when playing a sample
before it's completely transferred.
Issue #1513
2024-10-08 17:03:00 +02:00
Flyinghead f37e43b26f vk: don't set scissoring in final OIT subpass
If scissoring is enabled, previous framebuffer background isn't drawn
outside of scissor region.
Fixes blinking in Soul Calibur battle loading screen.
Issue #1668
2024-10-08 11:49:34 +02:00
Flyinghead e427c4d5c4 lr: don't set IntOption if overridden
Fixes OIT Max Layers overrides being ignored.
Issue #1610
2024-10-08 11:39:49 +02:00
Wunkolo 56fe3b9c9d Fix arm64_unwind.h include-order
`sh4_core.h` has defines for symbols like `r` and `pr` which conflict with the variable names in `arm64_unwind.h` and cause even deeper errors over in `wingdi.h(4954)` and `wincrypt.h(4741)`.

Swapping the include order avoids this collision.
2024-10-07 18:48:26 +02:00
Flyinghead 30bb864b37 vulkan: check image usage flags if image already exists during RTT
Recreate image if usage flags are incorrect.
Fixes vk validation error in Virtua Tennis player list selection screen.
Issue #1664
2024-10-07 18:47:52 +02:00
nbFlyinghead d689c50e21 naomi: increase dma xfer rate except for force five
Revert 54ac9c6361 but make an exception
for force five. Issue #141
Fixes loud music in beach spikers and soul surfers. Issue #1513
Fixes some menu textures not being loaded in F355.
2024-10-05 13:30:36 +02:00
Flyinghead efd71b3a44 Storage::getParentPath can throw 2024-10-04 16:51:08 +02:00
Flyinghead 5450646b5c android: add virtual gamepad transparency setting
Issue #384
2024-10-04 16:45:28 +02:00
Flyinghead 55e0211a57 android: use ASS to select custom adreno driver 2024-10-04 16:36:56 +02:00
Flyinghead 5d719e0b49 rend: unexpected conversion to unsigned for VO_STARTX/Y with msvc
MSVC converts VO_STARTX and VO_STARTY bit-field values to unsigned int,
leading to an overflow when the result is negative.
Issue #1606
2024-10-03 18:03:55 +02:00
Flyinghead e10a9929da Set OIT max layers for more games
Issue #1610
2024-10-03 17:56:30 +02:00
scribam 3d41e4ea3e deps: update sdl to version 2.30.8 2024-10-02 19:50:37 +02:00
Flyinghead 2dd4cbe33d vk oit: draw transparent polys in the first frame after (re)init
Fixes missing background in progress window in Fighting Force 2
Issue #1652
2024-09-29 16:42:38 +02:00
Flyinghead 2653c5ed73 naomi2: fix modifier volumes clipping
Don't clip modifier volumes but tesselate triangles intersecting the
near plane. Then project clipped vertices onto it in the vertex shader.
Issue #1651
2024-09-29 16:42:38 +02:00
github-actions[bot] 343021a8c5 Fetch translations & Recreate libretro_core_options_intl.h 2024-09-27 22:20:38 +00:00
flyinghead 7907100b9c dx11: wrong texture returned to libretro in some cases
If a render to texture happens before presenting the current frame,
another texture will be bound to shader slot #0 and will be returned to
the libretro frontend.
Issue #1616 and #1619
2024-09-25 21:57:15 +02:00
Flyinghead f179c00bdd gl: fix Delay Frame Swapping pacing issue on android
Partial revert of c83b0e325f.
Fix root issue in OpenGLRenderer::DrawOSD.
2024-09-24 14:50:41 +02:00
Flyinghead c83b0e325f android: fix Delay Frame Swapping sync issue. ANR in single-threaded
Framebuffer swap should only be called when a new frame has been
rendered.
ANR when stopping the emulator in single-threaded mode: race condition
causes stopRequested to not be handled, and Pause event is never sent.
2024-09-24 11:54:15 +02:00
Flyinghead 210e8ca6e3 android: get rid of ouya sdk 2024-09-23 20:46:33 +02:00
Flyinghead a46dc71156 android: target android 14 / sdk 34 2024-09-23 18:01:21 +02:00
Flyinghead 4baf529ff7 race condition when stopping emu thread right after start
In threaded rendering, CpuRunning is set after the emu thread is
started. If calling Emulator::stop() before it is set, the call will
hang.
2024-09-23 17:43:23 +02:00
Flyinghead 63ef3aaab4 android: avoid crash when app is run in restricted mode
App may be run in restricted mode, where the Application object is a
generic android.app.Application. Disallow backup to avoid this. If
Application object isn't of the expected type, restart the app.
2024-09-23 17:21:02 +02:00
Flyinghead abe71409b5 android: race condition with render thread. ANR with auto save state
Race condition when starting then stopping the render thread:
mainui_enabled isn't set until after the thread has started. If stopped
during that time, it will hang.
Auto save state in a background thread to avoid ANR.
2024-09-23 17:17:46 +02:00
Flyinghead f8c12d2835 android: fix getFileInfo for document uri. cheats: limit file size
android: Fix AndroidStorage.getFileInfo for document uri (cheat file)
cheats: limit cheat file size to 1MB to avoid parsing garbage forever. Save file path only if valid.
2024-09-23 17:08:57 +02:00
github-actions[bot] 92555c68c6 Fetch translations & Recreate libretro_core_options_intl.h 2024-09-20 22:20:53 +00:00
Flyinghead 915532f946 vk: don't depend on current clamping values when compiling shader
Fog clamping values may change so the shader shouldn't depend on the
current value when compiling.
Fixes Red Dog Superior Firepower glitch.
Issue #1642
2024-09-18 11:34:35 +02:00
Flyinghead 079180faab Force PAL for Snow Surfer. Increase Max Layers for some games.
Force PAL broadcast for Snow Surfer.
Issue #1643
Increase max layers for Soldier of Fortune, BANG! Gunship Elite and MDK
2.
Issue #1610
2024-09-18 11:30:46 +02:00
Flyinghead 38de2868a0 sgdrvsim: axis 0 is master audio volume, not wheel
Use (unused) axis 4 instead. Wheel position is read with the ffb drive
board, not JVS.
Map clutch to left trigger 2.
Issue #1636
2024-09-16 18:26:13 +02:00
Flyinghead 0e97f97bb7 android: virtual gamepad rumble power wasn't set at app startup
Make sure emu.cfg is loaded before setting rumble power.
Issue #1640
2024-09-16 11:28:25 +02:00
Flyinghead 64d7199107 Merge remote-tracking branch 'origin/master' into dev 2024-09-16 11:16:04 +02:00
scribam 0077b8ecec deps: update sdl to version 2.30.7 2024-09-08 16:24:37 +02:00
Flyinghead 5393235c40 aica: skip remaining step functions when channel is disabled
CA is reset to 0 when channel is disabled due to AEG and this value must
not be changed.
Issue #472 and #1180
2024-09-07 14:55:45 +02:00
Flyinghead a81a02f14b maple: save rfid card after decrementing counter
Issue #1626
2024-09-07 14:50:29 +02:00
github-actions[bot] 480f93e50d Fetch translations & Recreate libretro_core_options_intl.h 2024-09-06 22:21:05 +00:00
Flyinghead 7174ef1194 naomi: full rfid chip emulation for mushiking series
Fixes hang after playing 3 games.
Issue #1626
2024-09-04 09:48:34 +02:00
Flyinghead f039a809da naomi: limit max analog values to avoid overflow
Fixes overflow in wild riders when going full right or full accel/brake.
Follow up to Issue #1627
2024-09-02 17:04:17 +02:00
Flyinghead 1223154665 jvs: fix analog axes values encoding
The least significant byte of analog axis values seems to be handled as
signed.
Fixes crazy taxi going right when turning full left.
Issue #1627
2024-09-01 18:41:37 +02:00
Flyinghead 8cb847940b vk oit: attachment 0 must be loaded too
Issue #1614
2024-09-01 18:41:37 +02:00
github-actions[bot] 9691fe9622 Fetch translations & Recreate libretro_core_options_intl.h 2024-08-30 22:20:34 +00:00
Flyinghead c575eac89b naomi: add mushik4t rom 2024-08-30 17:49:07 +02:00
Flyinghead b468c9bdae aica: reset CA to 0 when disabling channel due to AEG
CA was reset to 0 for non-looping sound when ending, but not for looping
ones when they reach full attenuation. This confuses some games.
Disable the channel when it reaches full attenuation and reset CA to 0.
Revert a7702c622a
Issue #1602
Better fix for
Issues #472, #483, #1180, #1249, #1030 (partial)
2024-08-29 13:09:39 +02:00
Flyinghead 368bd17ae0 vk: use swap_chain_size + 1 semaphores
Fixes validation error VUID-vkAcquireNextImageKHR-semaphore-01779
introduced in vulkan sdk 1.3.275
Issue #1620
2024-08-29 13:01:11 +02:00
Flyinghead 757732d0f8 vk oit: load correct attachment. Only reset pipelines when needed
Fixes partial renders: ECCO initial sequence.
Issue #1614
Don't reset pipelines when resizing framebuffer.
2024-08-29 12:57:10 +02:00
Flyinghead 0ff9caf656 ci: make breakpad optional on all platforms
Issue #739
2024-08-29 12:06:29 +02:00
Flyinghead a93bd9e64e Merge remote-tracking branch 'origin/playstore' into dev 2024-08-24 18:08:59 +02:00
Flyinghead 308d9fc1ac Increase max transparent layers for some games
Issue #1604
Add 96 layers option to libretro core.
Issue #1610
2024-08-24 16:49:16 +02:00
Flyinghead 7a27a43ba4 vulkan: Fix multipass RTT in OIT mode
Use correct temp attachment depending on pass count. Keep attachment
index to bind the correct one before final subpass.
Use correct initial layout for OP+PT attachment 1 (was eUndefined) when
creating the RTT render pass.
Fixes Alone in the Dark cutscenes.
Issue #1609

Recreate RTT attachments if switching CopyToVRAM on/off.
2024-08-24 16:37:52 +02:00
Flyinghead 244bb724c9 naomi: add FFB support for Tokyo Bus Guide
Only rumble seems to be used.
Issue #1601

Add info about awave BIOS
2024-08-24 16:29:29 +02:00
github-actions[bot] 349083030d Fetch translations & Recreate libretro_core_options_intl.h 2024-08-23 22:20:21 +00:00
Flyinghead b99b3450c8 sh4: compute fsrra as 1/sqrt(x) instead of sqrt(1/x)
Fixes invisible chaos emerald in Sonic Adventure cutscene after Emerald
Coast.
Partial revert of ec3ad9b.
Issue #1600
2024-08-20 21:12:05 +02:00
Flyinghead 6061d402bd build fix 2024-08-17 18:08:53 +02:00
Flyinghead 4d06743d9a gdrom: don't crash on unsupported sector size
Fixes MINIDUMP-284
2024-08-17 17:56:35 +02:00
Flyinghead af7353225b pvr: rgb888 needs to be correctly aligned when xclip.min != 0
Fixes glitchy combat opening animation in soul calibur (with full
framebuffer emulation). Use slower but correct method instead.
Issue #1591
2024-08-17 17:29:32 +02:00
Flyinghead cb0b667129 maple: don't assert dma interrupt if using hardware dma trigger
Fixes Virtua Cop 2 pause when shooting. Regression due to ecce915
Issue #474
2024-08-17 17:23:34 +02:00
Flyinghead 00f60a3d62 win32: don't compile clock.c with clang
Issue #1588
2024-08-17 17:08:40 +02:00
github-actions[bot] 5565b79b9e Fetch translations & Recreate libretro_core_options_intl.h 2024-08-16 22:20:19 +00:00
Flyinghead ecb468a723 msvc build fix 2024-08-15 13:05:02 +02:00
Flyinghead fbe7cc8fe3 input: crash when setting the keyboard to port None
Issue #1593
2024-08-15 12:28:22 +02:00
scribam 45bf218df4 ci: bump actions/setup-python to v5 2024-07-29 16:45:12 +02:00
scribam 6b3e86619e deps: fix libzip compilation error with gcc 14.1 2024-07-28 20:02:34 +02:00
scribam 56b8bb1245 vk: cleanup
* Use constants for extension names
* Prefer VK_USE_PLATFORM_* constants
* Remove IOS_MVK/MACOS_MVK deprecated code
2024-07-28 20:02:34 +02:00
scribam cea80560db deps: update submodules
* discord-rpc: use upstream repository
* googletest: v1.15.0
* libchdr: master
* oboe: v1.9.0
* rcheevos: v11.5.0
2024-07-28 20:02:34 +02:00
scribam 088c1e6b0d unit: build fix for apple platform when ctest is enabled 2024-07-28 20:02:34 +02:00
scribam bb048dea8f ci: use default xcode version
Currently, it is Xcode 15.4
2024-07-28 20:02:34 +02:00
scribam 588dca2bfd cmake: cleanup
* Set min macos deployment version to 10.13. This is the lowest version supported by Xcode 14.3.1 and 15+
* Prefer target_compile_options to add compilation flags
* Simplify macos custom commands with TARGET_FILE and TARGET_FILE_DIR
* Do not install discord-rpc and libzip files
2024-07-28 20:02:34 +02:00
github-actions[bot] 3afa4322f1 Fetch translations & Recreate libretro_core_options_intl.h 2024-07-26 22:20:39 +00:00
github-actions[bot] 0b6b8b3753 Fetch translations & Recreate libretro_core_options_intl.h 2024-07-19 22:21:06 +00:00
Flyinghead 53d092c326 naomi: set SB_GDSTARD and SB_GDLEND when starting dma
Fixes toukon4 fmv freeze
Issue #1582
2024-07-17 10:05:58 +02:00
flyinghead bfba850ee7 Google Play install link 2024-07-16 09:49:29 +02:00
scribam db5a4d1247 ci: add lua54 for freebsd and netbsd 2024-07-14 10:32:33 +02:00
scribam 3ad00596a1 ci: use freebsd 14.1 2024-07-14 10:32:33 +02:00
Flyinghead 39e04b07b8 Merge remote-tracking branch 'origin/dev' 2024-07-11 18:27:44 +02:00
Flyinghead b9cbf3e47f lr: get rid of trash 2024-07-11 18:27:31 +02:00
scribam 2a4356e72c deps: update sdl to version 2.30.5 2024-07-06 14:04:24 +02:00
Flyinghead 136ce267dc aica: apply 6dB attenuation after adding channels in mono
Less saturation with sega bass fishing challenge's default audio
settings.
Issue #1564
2024-07-06 14:03:48 +02:00
github-actions[bot] c6f50fad9d Fetch translations & Recreate libretro_core_options_intl.h 2024-07-05 22:20:32 +00:00
Flyinghead 916afeb5ef Fix Cool Boarders Burrrn game id
Issue #1073
Issue #1265
2024-07-04 09:00:30 +02:00
Flyinghead 81842ccc23 Cool Boarders Burrn and Castle Fantasia fixes
Force HLE Bios for Cool Boarders Burrn.
Issue #1073
Force Copy to VRAM for Cool Boarders Burrn and Castle Fantasia.
Issue #1265
2024-07-03 17:38:23 +02:00
Flyinghead 4fdd28fab7 rend: intersect poly clipping with base clipping
Fixes glitch in FluffMatch split-screen (Fur Fighters)
Issue #145
2024-07-03 11:13:51 +02:00
131 changed files with 4784 additions and 1557 deletions

No files matched your search

+1 -1
View File
@@ -12,7 +12,7 @@ assignees: ''
Platform / OS / Hardware:
Github hash:
Flycast version:
Hardware:
+24 -9
View File
@@ -16,7 +16,7 @@ jobs:
with:
fetch-depth: 0
submodules: recursive
- uses: actions/setup-java@v4
with:
java-version: '17'
@@ -28,6 +28,10 @@ jobs:
path: ~/.ccache
key: android-ccache-${{ github.sha }}
restore-keys: android-ccache-
- name: Disable release signing
run: sed -i 's/signingConfig signingConfigs.release/signingConfig signingConfigs.debug/g' shell/android-studio/flycast/build.gradle
if: github.repository != 'flyinghead/flycast' || github.event_name != 'push'
- name: Bump version code
uses: chkfung/[email protected]
@@ -37,19 +41,15 @@ jobs:
- name: Gradle
working-directory: shell/android-studio
run: ./gradlew bundleRelease --parallel
run: ./gradlew assembleRelease bundleRelease --parallel
env:
SENTRY_UPLOAD_URL: ${{ secrets.SENTRY_UPLOAD_URL }}
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
- name: Upload to Google Play
uses: r0adkll/[email protected]
- uses: actions/upload-artifact@v4
with:
serviceAccountJsonPlainText: ${{ secrets.PLAY_SERVICE_ACCOUNT_JSON }}
packageName: com.flycast.emulator
releaseFiles: shell/android-studio/flycast/build/outputs/bundle/release/flycast-release.aab
track: internal
status: draft
name: flycast-release.apk
path: shell/android-studio/flycast/build/outputs/apk/release/flycast-release.apk
- name: Build dump_syms
working-directory: core/deps/breakpad
@@ -69,6 +69,21 @@ jobs:
mv libflycast.so.sym symbols/libflycast.so/$BUILD_ID
done
- name: Configure AWS Credentials
id: aws-credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: AKIAJOZQS4H2PHQWYFCA
aws-secret-access-key: ${{ secrets.S3_SECRET_KEY }}
aws-region: us-east-2
if: github.repository == 'flyinghead/flycast' && github.event_name == 'push'
- name: Upload to S3
run: |
cp shell/android-studio/flycast/build/outputs/bundle/release/*.aab shell/android-studio/flycast/build/outputs/apk/release/
aws s3 sync shell/android-studio/flycast/build/outputs/apk/release s3://flycast-builds/android/${GITHUB_REF#refs/}-$GITHUB_SHA --acl public-read --exclude='*.json' --follow-symlinks
if: ${{ steps.aws-credentials.outputs.aws-account-id != '' }}
- name: Setup Sentry CLI
uses: mathieu-bour/setup-sentry-cli@v2
env:
+48
View File
@@ -0,0 +1,48 @@
name: BSD CI
on: [push, pull_request]
jobs:
build:
env:
CCACHE_DIR: ${{ github.workspace }}/.ccache
runs-on: ubuntu-latest
strategy:
matrix:
operating_system: [ freebsd, netbsd, openbsd ]
architecture: [ arm64, x86-64 ]
include:
- operating_system: freebsd
version: '14.1'
pkginstall: sudo pkg install -y alsa-lib ccache cmake evdev-proto git libao libevdev libudev-devd libzip lua54 miniupnpc ninja pkgconf pulseaudio sdl2
- operating_system: netbsd
version: '10.0'
pkginstall: sudo pkgin update && sudo pkgin -y install alsa-lib ccache cmake gcc12 git libao libzip lua54 miniupnpc ninja-build pkgconf pulseaudio SDL2 && export PATH=/usr/pkg/gcc12/bin:$PATH
- operating_system: openbsd
version: '7.5'
pkginstall: sudo pkg_add ccache cmake git libao libzip miniupnpc ninja pkgconf pulseaudio sdl2
exclude:
- architecture: arm64
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: true
- uses: actions/cache@v4
with:
path: ${{ env.CCACHE_DIR }}
key: ccache-${{ matrix.operating_system }}-${{ matrix.architecture }}-${{ github.sha }}
restore-keys: ccache-${{ matrix.operating_system }}-${{ matrix.architecture }}-
- uses: cross-platform-actions/[email protected]
with:
operating_system: ${{ matrix.operating_system }}
architecture: ${{ matrix.architecture }}
version: ${{ matrix.version }}
environment_variables: CCACHE_DIR
run: |
${{ matrix.pkginstall }}
cmake -B build -DCMAKE_BUILD_TYPE=Release -G Ninja
cmake --build build --config Release
+182
View File
@@ -0,0 +1,182 @@
name: C/C++ CI
on: [push, pull_request]
jobs:
build:
name: ${{ matrix.config.name }}
runs-on: ${{ matrix.config.os }}
defaults:
run:
shell: ${{ matrix.config.shell }}
env:
CCACHE_DIR: ${{ github.workspace }}/.ccache
strategy:
matrix:
config:
- {name: i686-pc-windows-msvc, os: windows-latest, shell: cmd, arch: x86, cmakeArgs: -G Ninja, buildType: Release}
- {name: apple-darwin, os: macos-latest, shell: sh, cmakeArgs: -G Xcode -DUSE_DISCORD=ON, destDir: osx, buildType: RelWithDebInfo}
- {name: apple-ios, os: macos-latest, shell: sh, cmakeArgs: -DCMAKE_SYSTEM_NAME=iOS -G Xcode, destDir: ios, buildType: Release}
- {name: x86_64-pc-linux-gnu, os: ubuntu-20.04, shell: sh, cmakeArgs: -G Ninja -DUSE_DISCORD=ON, destDir: linux, buildType: RelWithDebInfo}
- {name: x86_64-pc-windows-msvc, os: windows-latest, shell: cmd, arch: x64, cmakeArgs: -G Ninja -DUSE_DISCORD=ON, buildType: Release}
- {name: x86_64-w64-mingw32, os: windows-latest, shell: 'msys2 {0}', cmakeArgs: -G Ninja -DUSE_DISCORD=ON, destDir: win, buildType: RelWithDebInfo}
- {name: libretro-x86_64-pc-linux-gnu, os: ubuntu-latest, shell: sh, cmakeArgs: -DLIBRETRO=ON -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE -G Ninja, buildType: Release}
- {name: libretro-x86_64-w64-mingw32, os: windows-latest, shell: 'msys2 {0}', cmakeArgs: -DLIBRETRO=ON -G Ninja, buildType: Release}
steps:
- name: Set up build environment (macOS)
run: |
# Unlink and re-link to prevent errors when github mac runner images install python outside of brew. See https://github.com/actions/setup-python/issues/577
brew list -1 | grep python | while read formula; do brew unlink $formula; brew link --overwrite $formula; done
brew update || :
brew install libao ldid ninja pulseaudio
brew uninstall --ignore-dependencies zstd
VULKAN_VER=1.3.261.1 && aria2c https://sdk.lunarg.com/sdk/download/$VULKAN_VER/mac/vulkansdk-macos-$VULKAN_VER.dmg
hdiutil attach ./vulkansdk-macos-*.dmg -mountpoint /Volumes/VulkanSDK
sudo /Volumes/VulkanSDK/InstallVulkan.app/Contents/MacOS/InstallVulkan --root $HOME/VulkanSDK --accept-licenses --default-answer --confirm-command install
hdiutil detach /Volumes/VulkanSDK
echo "VULKAN_SDK=$HOME/VulkanSDK/macOS" >> $GITHUB_ENV
if: runner.os == 'macOS'
- name: Set up build environment (Linux)
run: |
sudo add-apt-repository ppa:christianrauch/libdecoration
sudo apt-get update
sudo apt-get -y install ccache libao-dev libasound2-dev libevdev-dev libgl1-mesa-dev liblua5.3-dev libminiupnpc-dev libpulse-dev libsdl2-dev libudev-dev libzip-dev ninja-build libcurl4-openssl-dev
sudo apt-get -y install libwayland-dev libdecor-0-dev libaudio-dev libjack-dev libsndio-dev libsamplerate0-dev libx11-dev libxext-dev libxrandr-dev libxcursor-dev libxfixes-dev libxi-dev libxss-dev libxkbcommon-dev libdrm-dev libgbm-dev libgles2-mesa-dev libegl1-mesa-dev libdbus-1-dev libibus-1.0-dev libudev-dev fcitx-libs-dev
if: runner.os == 'Linux'
- name: Set up build environment (Windows, MinGW)
uses: msys2/setup-msys2@v2
with:
msystem: MINGW64
install: git make mingw-w64-x86_64-ccache mingw-w64-x86_64-cmake mingw-w64-x86_64-lua mingw-w64-x86_64-ninja mingw-w64-x86_64-SDL2 mingw-w64-x86_64-toolchain
if: matrix.config.shell == 'msys2 {0}'
- name: Set up build environment (Windows, Visual Studio)
run: |
choco install ccache directx-sdk ninja
echo DXSDK_DIR=C:\Program Files (x86)\Microsoft DirectX SDK (June 2010)>> %GITHUB_ENV%
if: matrix.config.shell == 'cmd'
- uses: ilammy/msvc-dev-cmd@v1
with:
arch: ${{ matrix.config.arch }}
if: matrix.config.shell == 'cmd'
- uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: recursive
- name: Compile a universal OpenMP (macOS)
run: HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 brew reinstall --build-from-source --formula ./shell/apple/libomp.rb
if: matrix.config.name == 'apple-darwin'
- name: Compile OpenMP.xcframework (iOS)
run: ./shell/apple/emulator-ios/OpenMP/build_ios_openmp.sh --disableSimulator
if: matrix.config.name == 'apple-ios'
- uses: actions/cache@v4
with:
path: ${{ env.CCACHE_DIR }}
key: ccache-${{ matrix.config.name }}-${{ github.sha }}
restore-keys: ccache-${{ matrix.config.name }}-
if: runner.os != 'macOS'
- name: CMake
run: |
cmake -B build -DCMAKE_BUILD_TYPE=${{ matrix.config.buildType }} -DCMAKE_INSTALL_PREFIX=artifact ${{ matrix.config.cmakeArgs }} -DSENTRY_UPLOAD_URL=${{ env.SENTRY_UPLOAD_URL }}
cmake --build build --config ${{ matrix.config.buildType }} --target install
env:
SENTRY_UPLOAD_URL: ${{ secrets.SENTRY_UPLOAD_URL }}
- name: Unit Tests
run: |
cmake -B build -DCMAKE_BUILD_TYPE=${{ matrix.config.buildType }} -DCMAKE_INSTALL_PREFIX=artifact -DENABLE_CTEST=ON ${{ matrix.config.cmakeArgs }}
cmake --build build --config ${{ matrix.config.buildType }}
./build/flycast
if: matrix.config.name == 'x86_64-pc-linux-gnu'
- name: Dump symbols
run: |
mkdir -p symbols
core/deps/breakpad/bin/dump_syms artifact/bin/flycast.exe > symbols/flycast.sym 2>/dev/null
strip artifact/bin/flycast.exe
if: matrix.config.name == 'x86_64-w64-mingw32'
- name: Build breakpad (linux)
working-directory: core/deps/breakpad
run: |
./configure
make
sudo make install
if: matrix.config.name == 'x86_64-pc-linux-gnu'
- name: Dump symbols and make AppImage (linux)
run: |
mkdir -p symbols
dump_syms artifact/bin/flycast > symbols/flycast.sym 2>/dev/null
strip artifact/bin/flycast
shell/linux/make-appimage.sh . artifact/bin appimage
mv flycast-x86_64.AppImage artifact/bin
rm artifact/bin/flycast
if: matrix.config.name == 'x86_64-pc-linux-gnu'
- uses: actions/upload-artifact@v4
with:
name: flycast-${{ matrix.config.name }}
path: artifact/bin
- name: Package app (macos)
run: |
cd artifact/bin
zip -rm flycast.app.zip Flycast.app
if: matrix.config.name == 'apple-darwin'
- name: Package app (windows)
run: |
powershell Compress-Archive artifact/bin/flycast.exe artifact/bin/flycast.zip
rm artifact/bin/flycast.exe
if: matrix.config.name == 'x86_64-w64-mingw32'
- name: Configure AWS Credentials
id: aws-credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: AKIAJOZQS4H2PHQWYFCA
aws-secret-access-key: ${{ secrets.S3_SECRET_KEY }}
aws-region: us-east-2
if: github.repository == 'flyinghead/flycast' && github.event_name == 'push' && matrix.config.destDir != ''
- name: Upload to S3
run: aws s3 sync artifact/bin s3://flycast-builds/${{ matrix.config.destDir }}/${GITHUB_REF#refs/}-$GITHUB_SHA --acl public-read --follow-symlinks
shell: bash
if: ${{ steps.aws-credentials.outputs.aws-account-id != '' }}
- name: Setup Sentry CLI
uses: mathieu-bour/setup-sentry-cli@v2
env:
SENTRY_TOKEN: ${{ secrets.SENTRY_TOKEN }}
with:
url: https://sentry.io
token: ${{ env.SENTRY_TOKEN }}
organization: flycast
project: minidump
version: 2.21.2
if: ${{ env.SENTRY_TOKEN != '' }}
- name: Upload symbols to Sentry (Windows, macOS, Linux)
run: |
VERSION=$(git describe --tags --always)
sentry-cli releases new "$VERSION"
sentry-cli releases set-commits "$VERSION" --auto
sentry-cli debug-files upload symbols
shell: bash
env:
SENTRY_TOKEN: ${{ secrets.SENTRY_TOKEN }}
if: ${{ env.SENTRY_TOKEN != '' && (matrix.config.name == 'x86_64-w64-mingw32' || matrix.config.name == 'apple-darwin' || matrix.config.name == 'x86_64-pc-linux-gnu') }}
+36
View File
@@ -0,0 +1,36 @@
# Prepare source texts & upload them to Crowdin
name: Crowdin Source Texts Upload
# on change to the English texts
on:
push:
branches:
- master
paths:
- 'shell/libretro/libretro_core_options.h'
jobs:
upload_source_file:
runs-on: ubuntu-latest
steps:
- name: Setup Java JDK
uses: actions/setup-java@v4
with:
java-version: 18
distribution: zulu
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Checkout
uses: actions/checkout@v4
- name: Upload Source
shell: bash
env:
CROWDIN_API_KEY: ${{ secrets.CROWDIN_API_KEY }}
run: |
python3 intl/upload_workflow.py $CROWDIN_API_KEY "flyinghead-flycast" "shell/libretro/libretro_core_options.h"
+49
View File
@@ -0,0 +1,49 @@
# Download translations form Crowdin & Recreate libretro_core_options_intl.h
name: Crowdin Translation Integration
on:
schedule:
# please choose a random time & weekday to avoid all repos synching at the same time
- cron: '15 22 * * 5' # Fridays at 10:15 PM, UTC
jobs:
create_intl_file:
runs-on: ubuntu-latest
steps:
- name: Setup Java JDK
uses: actions/setup-java@v4
with:
java-version: 18
distribution: zulu
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Checkout
uses: actions/checkout@v4
with:
persist-credentials: false # otherwise, the token used is the GITHUB_TOKEN, instead of your personal access token.
fetch-depth: 0 # otherwise, there would be errors pushing refs to the destination repository.
- name: Create intl file
shell: bash
env:
CROWDIN_API_KEY: ${{ secrets.CROWDIN_API_KEY }}
run: |
python3 intl/download_workflow.py $CROWDIN_API_KEY "flyinghead-flycast" "shell/libretro/libretro_core_options_intl.h"
- name: Commit files
run: |
git config --local user.email "[email protected]"
git config --local user.name "github-actions[bot]"
git add intl/*_workflow.py "shell/libretro/libretro_core_options_intl.h"
git commit -m "Fetch translations & Recreate libretro_core_options_intl.h"
- name: GitHub Push
uses: ad-m/[email protected]
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
branch: ${{ github.ref }}
+62
View File
@@ -0,0 +1,62 @@
name: Nintendo Switch CI
on: [push, pull_request]
jobs:
build:
name: ${{ matrix.config.name }}
runs-on: ubuntu-latest
container: devkitpro/devkita64:latest
strategy:
matrix:
config:
- {name: standalone, destDir: switch}
- {name: libretro, cmakeArgs: -DLIBRETRO=ON}
steps:
- name: Set up build environment
run: |
sudo apt-get update
sudo apt-get -y install awscli ccache
- uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: true
- name: Mark git repository as safe
run: git config --global --add safe.directory $PWD
- uses: actions/cache@v4
with:
path: ~/.ccache
key: ccache-switch-${{ matrix.config.name }}-${{ github.sha }}
restore-keys: ccache-switch-${{ matrix.config.name }}-
- name: CMake
run: |
$DEVKITPRO/portlibs/switch/bin/aarch64-none-elf-cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=artifact -G Ninja ${{ matrix.config.cmakeArgs }}
cmake --build build --config Release --target install
- uses: actions/upload-artifact@v4
with:
name: flycast-switch-${{ matrix.config.name }}
path: artifact/bin
- name: Package app
run: zip -m artifact/bin/flycast.nro.zip artifact/bin/flycast.nro
if: hashFiles('artifact/bin/flycast.nro') != ''
- name: Configure AWS Credentials
id: aws-credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: AKIAJOZQS4H2PHQWYFCA
aws-secret-access-key: ${{ secrets.S3_SECRET_KEY }}
aws-region: us-east-2
if: github.repository == 'flyinghead/flycast' && github.event_name == 'push' && matrix.config.destDir != ''
- name: Upload to S3
run: aws s3 sync artifact/bin s3://flycast-builds/${{ matrix.config.destDir }}/${GITHUB_REF#refs/}-$GITHUB_SHA --acl public-read --follow-symlinks
if: ${{ steps.aws-credentials.outputs.aws-account-id != '' }}
+54
View File
@@ -0,0 +1,54 @@
name: Windows UWP CI
on: [push, pull_request]
jobs:
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: true
- name: CMake
run: |
cmake -B build -DCMAKE_BUILD_TYPE=Release -G "Visual Studio 17 2022" -A x64 -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION=10.0.22000.0
cmake --build build --config Release -- /m
shell: cmd
- uses: ilammy/msvc-dev-cmd@v1
- name: Package app
run: |
mkdir build\artifact
cd build\AppPackages\flycast\flycast_*_x64_Test
mkdir tmp
ren *.msix flycast.msix
makeappx.exe unpack /p .\flycast.msix /d tmp
copy ..\..\..\Release\*.dll tmp
makeappx pack /d tmp /p ..\..\..\artifact\flycast.appx
- name: Sign app
run: signtool sign /f shell\uwp\sign_cert.pfx /p '${{ secrets.SIGN_CERT_PWD }}' /v /fd SHA256 build\artifact\flycast.appx
if: github.repository == 'flyinghead/flycast' && github.event_name == 'push'
- uses: actions/upload-artifact@v4
with:
name: flycast-uwp
path: build/artifact
- name: Configure AWS Credentials
id: aws-credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: AKIAJOZQS4H2PHQWYFCA
aws-secret-access-key: ${{ secrets.S3_SECRET_KEY }}
aws-region: us-east-2
if: github.repository == 'flyinghead/flycast' && github.event_name == 'push'
- name: Upload to S3
run: aws s3 sync build/artifact s3://flycast-builds/xbox/${GITHUB_REF#refs/}-$GITHUB_SHA --acl public-read --follow-symlinks
shell: bash
if: ${{ steps.aws-credentials.outputs.aws-account-id != '' }}
+1 -1
View File
@@ -31,7 +31,7 @@
url = https://github.com/vkedwardli/Spout2.git
[submodule "core/deps/discord-rpc"]
path = core/deps/discord-rpc
url = https://github.com/flyinghead/discord-rpc
url = https://github.com/harmonytf/discord-rpc
[submodule "core/deps/libadrenotools"]
path = core/deps/libadrenotools
url = https://github.com/bylaws/libadrenotools
+21 -35
View File
@@ -18,7 +18,7 @@ if(APPLE)
set(CMAKE_OSX_DEPLOYMENT_TARGET "12.0" CACHE STRING "Minimum tvOS deployment version")
set(CMAKE_OSX_ARCHITECTURES "arm64" CACHE STRING "")
else()
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.11" CACHE STRING "Minimum macOS deployment version")
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13" CACHE STRING "Minimum macOS deployment version")
set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64" CACHE STRING "")
endif()
endif()
@@ -65,7 +65,7 @@ option(USE_DX11 "Build with Direct3D 11 support" ON)
option(LIBRETRO "Build libretro core" OFF)
option(USE_OPENGL "Use OpenGL API" ON)
option(USE_VIDEOCORE "RPI: use the legacy Broadcom GLES libraries" OFF)
option(APPLE_BREAKPAD "macOS: Build breakpad client and dump symbols" OFF)
option(USE_BREAKPAD "Build and link with breakpad library" ON)
option(ENABLE_GDB_SERVER "Build with GDB debugging support" OFF)
option(ENABLE_DC_PROFILER "Build with support for target machine (SH4) profiler" OFF)
option(ENABLE_FC_PROFILER "Build with support for host app (Flycast) profiler" OFF)
@@ -266,7 +266,7 @@ if(NINTENDO_SWITCH)
target_include_directories(${PROJECT_NAME} PRIVATE shell/switch)
endif()
if(NOT LIBRETRO)
if(USE_BREAKPAD AND NOT LIBRETRO)
find_program(SH_EXECUTABLE sh)
if((ANDROID AND NOT CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") OR (MINGW AND SH_EXECUTABLE) OR (CMAKE_SYSTEM_NAME STREQUAL "Linux"))
add_subdirectory(core/deps/breakpad)
@@ -275,7 +275,7 @@ if(NOT LIBRETRO)
endif()
target_link_libraries(${PROJECT_NAME} PUBLIC breakpad_client)
target_compile_definitions(${PROJECT_NAME} PRIVATE USE_BREAKPAD)
elseif(APPLE AND NOT IOS AND APPLE_BREAKPAD)
elseif(APPLE AND NOT IOS)
target_sources(${PROJECT_NAME} PRIVATE
core/deps/breakpad/src/client/minidump_file_writer-inl.h
core/deps/breakpad/src/client/minidump_file_writer.cc
@@ -327,26 +327,26 @@ if(NOT LIBRETRO)
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/build/breakpad/dump_syms
-a x86_64
-g ${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/Flycast.app.dSYM
${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/Flycast.app/Contents/MacOS/Flycast > ../symbols/Flycast-x64.sym
$<TARGET_FILE:flycast> > ../symbols/Flycast-x64.sym
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/build/breakpad/dump_syms
-a arm64
-g ${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/Flycast.app.dSYM
${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/Flycast.app/Contents/MacOS/Flycast > ../symbols/Flycast-arm64.sym
$<TARGET_FILE:flycast> > ../symbols/Flycast-arm64.sym
)
else()
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND sleep 20
COMMAND mkdir -p ../symbols
COMMAND dsymutil ${CMAKE_CURRENT_BINARY_DIR}/Flycast.app/Contents/MacOS/Flycast
COMMAND dsymutil $<TARGET_FILE:flycast>
-o ${CMAKE_CURRENT_BINARY_DIR}/Flycast.app.dSYM
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/build/breakpad/dump_syms
-a x86_64
-g ${CMAKE_CURRENT_BINARY_DIR}/Flycast.app.dSYM
${CMAKE_CURRENT_BINARY_DIR}/Flycast.app/Contents/MacOS/Flycast > ../symbols/Flycast-x64.sym
$<TARGET_FILE:flycast> > ../symbols/Flycast-x64.sym
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/build/breakpad/dump_syms
-a arm64
-g ${CMAKE_CURRENT_BINARY_DIR}/Flycast.app.dSYM
${CMAKE_CURRENT_BINARY_DIR}/Flycast.app/Contents/MacOS/Flycast > ../symbols/Flycast-arm64.sym
$<TARGET_FILE:flycast> > ../symbols/Flycast-arm64.sym
)
endif()
endif()
@@ -376,7 +376,7 @@ if(USE_OPENMP)
message("-- Found OpenMP.xcframework: ${OPENMP_FRAMEWORK} (Linking static library before CMake support linking xcframework)")
target_link_libraries(${PROJECT_NAME} PRIVATE ${OPENMP_FRAMEWORK})
target_include_directories(${PROJECT_NAME} PRIVATE ${OPENMP_XCFRAMEWORK}/ios-arm64_arm64e/OpenMP.framework/Versions/A/Headers)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Xclang -fopenmp")
target_compile_options(${PROJECT_NAME} PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-Xclang -fopenmp>)
endif()
else()
message("-- Could NOT find OpenMP.xcframework, compile it using shell/apple/emulator-ios/OpenMP/build_ios_openmp.sh")
@@ -645,7 +645,7 @@ if(NOT LIBZIP_FOUND OR NINTENDO_SWITCH)
option(BUILD_DOC "Build documentation" OFF)
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
option(LIBZIP_DO_INSTALL "Install libzip and the related files" OFF)
add_subdirectory(core/deps/libzip)
add_subdirectory(core/deps/libzip EXCLUDE_FROM_ALL)
target_link_libraries(${PROJECT_NAME} PRIVATE libzip::zip)
endif()
@@ -742,7 +742,7 @@ target_sources(${PROJECT_NAME} PRIVATE core/deps/md5/md5.cpp)
if(USE_DISCORD AND NOT LIBRETRO)
option(BUILD_EXAMPLES "Build example apps" OFF)
add_subdirectory(core/deps/discord-rpc)
add_subdirectory(core/deps/discord-rpc EXCLUDE_FROM_ALL)
target_include_directories(${PROJECT_NAME} PRIVATE core/deps/discord-rpc/include)
target_link_libraries(${PROJECT_NAME} PRIVATE discord-rpc)
target_compile_definitions(${PROJECT_NAME} PRIVATE USE_DISCORD)
@@ -1327,7 +1327,7 @@ if(NOT LIBRETRO)
endif()
if(USE_VULKAN)
target_compile_definitions(${PROJECT_NAME} PUBLIC VK_NO_PROTOTYPES)
target_compile_definitions(${PROJECT_NAME} PUBLIC VK_ENABLE_BETA_EXTENSIONS VK_NO_PROTOTYPES)
if(ANDROID)
target_compile_definitions(${PROJECT_NAME} PUBLIC VK_USE_PLATFORM_ANDROID_KHR)
elseif(X11_FOUND)
@@ -1335,7 +1335,6 @@ if(USE_VULKAN)
elseif(WIN32)
target_compile_definitions(${PROJECT_NAME} PUBLIC VK_USE_PLATFORM_WIN32_KHR)
elseif(APPLE)
target_compile_definitions(${PROJECT_NAME} PUBLIC VK_USE_PLATFORM_MACOS_MVK)
target_compile_definitions(${PROJECT_NAME} PUBLIC VK_USE_PLATFORM_METAL_EXT)
endif()
@@ -1343,7 +1342,6 @@ if(USE_VULKAN)
target_link_libraries(${PROJECT_NAME} PRIVATE Vulkan::Headers)
add_subdirectory(core/deps/VulkanMemoryAllocator)
target_compile_definitions(VulkanMemoryAllocator INTERFACE $<$<BOOL:${APPLE}>:VMA_USE_STL_SHARED_MUTEX=0>) # Only available on macOS 10.12+
target_compile_options(VulkanMemoryAllocator INTERFACE $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CXX_COMPILER_ID:AppleClang,Clang>>:-Wno-nullability-completeness>)
target_link_libraries(${PROJECT_NAME} PRIVATE GPUOpen::VulkanMemoryAllocator)
@@ -1649,7 +1647,7 @@ if(NOT LIBRETRO)
shell/apple/emulator-ios/emulator/iCade-iOS/iCadeReaderView.h
shell/apple/emulator-ios/emulator/iCade-iOS/iCadeReaderView.m
shell/apple/emulator-ios/emulator/iCade-iOS/iCadeState.h)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fobjc-arc -fmodules")
target_compile_options(${PROJECT_NAME} PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-fobjc-arc -fmodules>)
set_source_files_properties(shell/apple/emulator-ios/emulator/FlycastViewController.mm PROPERTIES COMPILE_OPTIONS -fcxx-modules)
set_source_files_properties(shell/apple/emulator-ios/emulator/iCade-iOS/iCadeReaderView.m PROPERTIES COMPILE_OPTIONS -fno-objc-arc)
@@ -1715,15 +1713,9 @@ if(NOT LIBRETRO)
add_subdirectory(core/deps/Syphon)
target_link_libraries(${PROJECT_NAME} PRIVATE Syphon)
if(${CMAKE_GENERATOR} MATCHES "^Xcode.*")
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy ${SYPHON_METALLIB}
${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/Flycast.app/Contents/Resources/)
else()
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy ${SYPHON_METALLIB}
${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/Flycast.app/Contents/Resources/)
endif()
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy ${SYPHON_METALLIB}
$<TARGET_FILE_DIR:flycast>/../Resources/)
target_compile_definitions(${PROJECT_NAME} PRIVATE VIDEO_ROUTING)
target_sources(${PROJECT_NAME} PRIVATE
@@ -1774,15 +1766,9 @@ if(NOT LIBRETRO)
${IOSURFACE_LIBRARY}
${MULTITOUCH_SUPPORT_LIBRARY})
if(${CMAKE_GENERATOR} MATCHES "^Xcode.*")
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy "$ENV{VULKAN_SDK}/lib/libMoltenVK.dylib"
${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/Flycast.app/Contents/Frameworks/libvulkan.dylib)
else()
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy "$ENV{VULKAN_SDK}/lib/libMoltenVK.dylib"
${CMAKE_CURRENT_BINARY_DIR}/Flycast.app/Contents/Frameworks/libvulkan.dylib)
endif()
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy "$ENV{VULKAN_SDK}/lib/libMoltenVK.dylib"
$<TARGET_FILE_DIR:flycast>/../Frameworks/libvulkan.dylib)
endif()
elseif(UNIX)
if(NOT BUILD_TESTING)
@@ -1843,7 +1829,7 @@ if(BUILD_TESTING)
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
endif()
add_subdirectory(core/deps/googletest EXCLUDE_FROM_ALL)
target_link_libraries(${PROJECT_NAME} PRIVATE gtest_main)
target_link_libraries(${PROJECT_NAME} PRIVATE GTest::gtest_main)
target_sources(${PROJECT_NAME} PRIVATE
tests/src/CheatManagerTest.cpp
+2
View File
@@ -16,6 +16,8 @@ Join us on our [**Discord server**](https://discord.gg/X8YWP8w) for a chat.
## Install
### Android ![android](https://flyinghead.github.io/flycast-builds/android.jpg)
Install Flycast from [**Google Play**](https://play.google.com/store/apps/details?id=com.flycast.emulator).
### Flatpak (Linux ![ubuntu logo](https://flyinghead.github.io/flycast-builds/ubuntu.png))
1. [Set up Flatpak](https://www.flatpak.org/setup/).
+75 -64
View File
@@ -365,16 +365,16 @@ void Achievements::term()
void Achievements::authenticationSuccess(const rc_client_user_t *user)
{
NOTICE_LOG(COMMON, "RA Login successful");
asyncTask([this, user]() {
char url[512];
int rc = rc_client_user_get_image_url(user, url, sizeof(url));
if (rc == RC_OK)
{
std::string image = getOrDownloadImage(url);
std::string url(512, '\0');
int rc = rc_client_user_get_image_url(user, url.data(), url.size());
if (rc == RC_OK)
{
asyncTask([this, url]() {
std::string image = getOrDownloadImage(url.c_str());
std::string text = "User " + config::AchievementsUserName.get() + " authenticated";
notifier.notify(Notification::Login, image, text);
}
});
});
}
loggedOn = true;
if (!settings.content.fileName.empty()) // TODO better test?
loadGame();
@@ -600,46 +600,48 @@ void Achievements::handleUnlockEvent(const rc_client_event_t *event)
assert(cheevo != nullptr);
INFO_LOG(COMMON, "RA: Achievement %s (%u) for game %s unlocked", cheevo->title, cheevo->id, settings.content.title.c_str());
asyncTask([this, cheevo]() {
char url[512];
int rc = rc_client_achievement_get_image_url(cheevo, cheevo->state, url, sizeof(url));
if (rc == RC_OK)
{
std::string image = getOrDownloadImage(url);
std::string text = "Achievement " + std::string(cheevo->title) + " unlocked!";
notifier.notify(Notification::Login, image, text, cheevo->description);
}
});
std::string title(cheevo->title);
std::string description(cheevo->description);
std::string url(512, '\0');
int rc = rc_client_achievement_get_image_url(cheevo, cheevo->state, url.data(), url.size());
if (rc == RC_OK)
{
asyncTask([this, url, title, description]() {
std::string image = getOrDownloadImage(url.c_str());
std::string text = "Achievement " + title + " unlocked!";
notifier.notify(Notification::Login, image, text, description);
});
}
}
void Achievements::handleAchievementChallengeIndicatorShowEvent(const rc_client_event_t *event)
{
const rc_client_achievement_t* cheevo = event->achievement;
INFO_LOG(COMMON, "RA: Challenge: %s", cheevo->title);
asyncTask([this, cheevo]() {
char url[128];
int rc = rc_client_achievement_get_image_url(cheevo, RC_CLIENT_ACHIEVEMENT_STATE_UNLOCKED, url, sizeof(url));
if (rc == RC_OK)
{
std::string image = getOrDownloadImage(url);
std::string url(512, '\0');
int rc = rc_client_achievement_get_image_url(cheevo, RC_CLIENT_ACHIEVEMENT_STATE_UNLOCKED, url.data(), url.size());
if (rc == RC_OK)
{
asyncTask([this, url]() {
std::string image = getOrDownloadImage(url.c_str());
notifier.showChallenge(image);
}
});
});
}
}
void Achievements::handleAchievementChallengeIndicatorHideEvent(const rc_client_event_t *event)
{
const rc_client_achievement_t* cheevo = event->achievement;
INFO_LOG(COMMON, "RA: Challenge hidden: %s", cheevo->title);
asyncTask([this, cheevo]() {
char url[128];
int rc = rc_client_achievement_get_image_url(cheevo, RC_CLIENT_ACHIEVEMENT_STATE_UNLOCKED, url, sizeof(url));
if (rc == RC_OK)
{
std::string image = getOrDownloadImage(url);
std::string url(512, '\0');
int rc = rc_client_achievement_get_image_url(cheevo, RC_CLIENT_ACHIEVEMENT_STATE_UNLOCKED, url.data(), url.size());
if (rc == RC_OK)
{
asyncTask([this, url]() {
std::string image = getOrDownloadImage(url.c_str());
notifier.hideChallenge(image);
}
});
});
}
}
void Achievements::handleLeaderboardStarted(const rc_client_event_t *event)
@@ -685,18 +687,21 @@ void Achievements::handleUpdateLeaderboardTracker(const rc_client_event_t *event
void Achievements::handleGameCompleted(const rc_client_event_t *event)
{
const rc_client_game_t* game = rc_client_get_game_info(rc_client);
asyncTask([this, game]() {
std::string text1 = (rc_client_get_hardcore_enabled(rc_client) ? "Mastered " : "Completed ") + std::string(game->title);
rc_client_user_game_summary_t summary;
rc_client_get_user_game_summary(rc_client, &summary);
std::stringstream ss;
ss << summary.num_unlocked_achievements << " achievements, " << summary.points_unlocked << " points";
std::string text2(ss.str());
std::string text3 = rc_client_get_user_info(rc_client)->display_name;
std::string url(512, '\0');
if (rc_client_game_get_image_url(game, url.data(), url.size()) != RC_OK)
url.clear();
asyncTask([this, url, text1, text2, text3]() {
std::string image;
char url[128];
if (rc_client_game_get_image_url(game, url, sizeof(url)) == RC_OK)
image = getOrDownloadImage(url);
std::string text1 = (rc_client_get_hardcore_enabled(rc_client) ? "Mastered " : "Completed ") + std::string(game->title);
rc_client_user_game_summary_t summary;
rc_client_get_user_game_summary(rc_client, &summary);
std::stringstream ss;
ss << summary.num_unlocked_achievements << " achievements, " << summary.points_unlocked << " points";
std::string text3 = rc_client_get_user_info(rc_client)->display_name;
notifier.notify(Notification::Mastery, image, text1, ss.str(), text3);
if (!url.empty())
image = getOrDownloadImage(url.c_str());
notifier.notify(Notification::Mastery, image, text1, text2, text3);
});
}
@@ -711,12 +716,15 @@ void Achievements::handleHideAchievementProgress(const rc_client_event_t *event)
void Achievements::handleUpdateAchievementProgress(const rc_client_event_t *event)
{
const rc_client_achievement_t* cheevo = event->achievement;
asyncTask([this, cheevo]() {
char url[128];
std::string url(512, '\0');
if (rc_client_achievement_get_image_url(cheevo, RC_CLIENT_ACHIEVEMENT_STATE_ACTIVE, url.data(), url.size()) != RC_OK)
url.clear();
std::string progress(cheevo->measured_progress);
asyncTask([this, url, progress]() {
std::string image;
if (rc_client_achievement_get_image_url(cheevo, RC_CLIENT_ACHIEVEMENT_STATE_ACTIVE, url, sizeof(url)) == RC_OK)
image = getOrDownloadImage(url);
notifier.notify(Notification::Progress, image, cheevo->measured_progress);
if (!url.empty())
image = getOrDownloadImage(url.c_str());
notifier.notify(Notification::Progress, image, progress);
});
}
@@ -919,21 +927,24 @@ void Achievements::gameLoaded(int result, const char *errorMessage)
settings.raHardcoreMode = false;
else
settings.raHardcoreMode = (bool)rc_client_get_hardcore_enabled(rc_client);
asyncTask([this, info]() {
std::string url(512, '\0');
if (rc_client_game_get_image_url(info, url.data(), url.size()) != RC_OK)
url.clear();
std::string text1(info->title);
rc_client_user_game_summary_t summary;
rc_client_get_user_game_summary(rc_client, &summary);
std::string text2;
if (summary.num_core_achievements > 0)
text2 = "You have " + std::to_string(summary.num_unlocked_achievements)
+ " of " + std::to_string(summary.num_core_achievements) + " achievements unlocked.";
else
text2 = "This game has no achievements.";
asyncTask([this, url, text1, text2]() {
std::string image;
char url[512];
if (rc_client_game_get_image_url(info, url, sizeof(url)) == RC_OK)
image = getOrDownloadImage(url);
rc_client_user_game_summary_t summary;
rc_client_get_user_game_summary(rc_client, &summary);
std::string text;
if (summary.num_core_achievements > 0)
text = "You have " + std::to_string(summary.num_unlocked_achievements)
+ " of " + std::to_string(summary.num_core_achievements) + " achievements unlocked.";
else
text = "This game has no achievements.";
std::string text2 = settings.raHardcoreMode ? "Hardcore Mode" : "";
notifier.notify(Notification::Login, image, info->title, text, text2);
if (!url.empty())
image = getOrDownloadImage(url.c_str());
std::string text3 = settings.raHardcoreMode ? "Hardcore Mode" : "";
notifier.notify(Notification::Login, image, text1, text2, text3);
});
}
+5 -1
View File
@@ -132,6 +132,9 @@ Option<std::vector<std::string>, false> ContentPath("Dreamcast.ContentPath");
Option<bool, false> HideLegacyNaomiRoms("Dreamcast.HideLegacyNaomiRoms", true);
Option<bool, false> UploadCrashLogs("UploadCrashLogs", true);
Option<bool, false> DiscordPresence("DiscordPresence", true);
#if defined(__ANDROID__) && !defined(LIBRETRO)
Option<bool, false> UseSafFilePicker("UseSafFilePicker", true);
#endif
// Profiler
Option<bool> ProfilerEnabled("Profiler.Enabled");
@@ -143,7 +146,7 @@ Option<float> ProfilerFrameWarningTime("Profiler.FrameWarningTime", 1.0f / 55.0f
Option<bool> NetworkEnable("Enable", false, "network");
Option<bool> ActAsServer("ActAsServer", false, "network");
OptionString DNS("DNS", "46.101.91.123", "network");
OptionString DNS("DNS", "dns.flyca.st", "network");
OptionString NetworkServer("server", "", "network");
Option<int> LocalPort("LocalPort", NaomiNetwork::SERVER_PORT, "network");
Option<bool> EmulateBBA("EmulateBBA", false, "network");
@@ -168,6 +171,7 @@ Option<bool> OmxAudioHdmi("audio_hdmi", true, "omx");
Option<int> MouseSensitivity("MouseSensitivity", 100, "input");
Option<int> VirtualGamepadVibration("VirtualGamepadVibration", 20, "input");
Option<int> VirtualGamepadTransparency("VirtualGamepadTransparency", 37, "input");
std::array<Option<MapleDeviceType>, 4> MapleMainDevices {
Option<MapleDeviceType>("device1", MDT_SegaController, "input"),
+4
View File
@@ -498,6 +498,9 @@ extern Option<std::vector<std::string>, false> ContentPath;
extern Option<bool, false> HideLegacyNaomiRoms;
extern Option<bool, false> UploadCrashLogs;
extern Option<bool, false> DiscordPresence;
#if defined(__ANDROID__) && !defined(LIBRETRO)
extern Option<bool, false> UseSafFilePicker;
#endif
// Profiling
extern Option<bool> ProfilerEnabled;
@@ -534,6 +537,7 @@ extern Option<bool> OmxAudioHdmi;
extern Option<int> MouseSensitivity;
extern Option<int> VirtualGamepadVibration;
extern Option<int> VirtualGamepadTransparency;
extern std::array<Option<MapleDeviceType>, 4> MapleMainDevices;
extern std::array<std::array<Option<MapleDeviceType>, 2>, 4> MapleExpansionDevices;
extern Option<bool> PerGameVmu;
+13 -26
View File
@@ -377,6 +377,17 @@ void CheatManager::setActive(bool active)
void CheatManager::loadCheatFile(const std::string& filename)
{
#ifndef LIBRETRO
try {
hostfs::FileInfo fileInfo = hostfs::storage().getFileInfo(filename);
if (fileInfo.size >= 1_MB) {
WARN_LOG(COMMON, "Cheat file '%s' is too big", filename.c_str());
return;
}
} catch (const hostfs::StorageException& e) {
WARN_LOG(COMMON, "Cannot find cheat file '%s': %s", filename.c_str(), e.what());
return;
}
FILE* cheatfile = hostfs::storage().openFile(filename, "r");
if (cheatfile == nullptr)
{
@@ -421,7 +432,8 @@ void CheatManager::loadCheatFile(const std::string& filename)
}
setActive(!cheats.empty());
INFO_LOG(COMMON, "%d cheats loaded", (int)cheats.size());
cfgSaveStr("cheats", gameId, filename);
if (!cheats.empty())
cfgSaveStr("cheats", gameId, filename);
#endif
}
@@ -454,31 +466,6 @@ void CheatManager::reset(const std::string& gameId)
else if (gameId == "THE KING OF ROUTE66") {
cheats.emplace_back(Cheat::Type::setValue, "ignore drive error", true, 32, 0x00023ee0, 0x0009000B, true); // rts, nop
}
else if (gameId.substr(0, 8) == "MKG TKOB")
{
const auto& setMushikingCheats = [this](u32 addr) {
cheats.emplace_back(Cheat::Type::setValue, "ignore rfid1 error", true, 32, addr, 0, true); // rfid[0].error = 0
cheats.emplace_back(Cheat::Type::setValue, "ignore rfid2 error", true, 32, addr + 0x48, 0, true); // rfid[1].error = 0
cheats.emplace_back(Cheat::Type::setValue, "ignore rfid1 status", true, 32, addr + 8, 0, true); // rfid[0].data18 = 0
cheats.emplace_back(Cheat::Type::setValue, "ignore rfid2 status", true, 32, addr + 0x50, 0, true); // rfid[1].data18 = 0
};
if (gameId == "MKG TKOB 2 EXP VER1.001-") // mushi2eo
setMushikingCheats(0x6fe1bc);
else if (gameId == "MKG TKOB 2 JPN VER2.001-") // mushik2e
setMushikingCheats(0x6ffe54);
else if (gameId == "MKG TKOB 2K3 2ND VER1.003-")// mushike
setMushikingCheats(0x4ad7ec);
else if (gameId == "MKG TKOB 4 JPN VER2.000-") // mushik4e
setMushikingCheats(0xb0e538);
else if (gameId == "MKG TKOB 2K3 2ND VER1.002-")// mushikeo
setMushikingCheats(0x4ad56c);
else if (gameId == "MKG TKOB 2K3 2ND KOR VER1.000-") // mushikk
setMushikingCheats(0x4ac9b8);
else if (gameId == "MKG TKOB 2K3 2ND CHN VER1.000-") // mushikc
setMushikingCheats(0x4aa9b8);
else if (gameId == "MKG TKOB 2 KOR VER1.000-") // mushik2k
setMushikingCheats(0x706084);
}
else if (gameId == "T-8120N") { // Dave Mirra BMX (US)
cheats.emplace_back(Cheat::Type::setValue, "fix main loop time", true, 32, 0x0030b8cc, 0x42040000, true); // 33.0 ms
}
+26 -14
View File
@@ -38,7 +38,7 @@ unsigned long track_mode;
/////////////////////////////////////////////////////////////////////////////
void CDI_read_track (FILE *fsource, image_s *image, track_s *track)
bool CDI_read_track (FILE *fsource, image_s *image, track_s *track)
{
unsigned char TRACK_START_MARK[10] = { 0, 0, 0x01, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF };
@@ -49,10 +49,16 @@ void CDI_read_track (FILE *fsource, image_s *image, track_s *track)
fseek(fsource, 8, SEEK_CUR); // extra data (DJ 3.00.780 and up)
fread(&current_start_mark, 10, 1, fsource);
if (memcmp(TRACK_START_MARK, current_start_mark, 10)) printf( "Unsupported format: Could not find the track start mark");
if (memcmp(TRACK_START_MARK, current_start_mark, 10)) {
printf("CDI_read_track: Unsupported format: Could not find the track start mark\n");
return false;
}
fread(&current_start_mark, 10, 1, fsource);
if (memcmp(TRACK_START_MARK, current_start_mark, 10)) printf( "Unsupported format: Could not find the track start mark");
if (memcmp(TRACK_START_MARK, current_start_mark, 10)) {
printf("CDI_read_track: Unsupported format: Could not find the track start mark\n");
return false;
}
fseek(fsource, 4, SEEK_CUR);
fread(&track->filename_length, 1, 1, fsource);
@@ -80,20 +86,25 @@ void CDI_read_track (FILE *fsource, image_s *image, track_s *track)
case 1 : track->sector_size = 2336; break;
case 2 : track->sector_size = 2352; break;
case 4 : track->sector_size = 2448; break;
default: printf("Unsupported sector size. value %ld\n", track->sector_size_value);
break;
default:
printf("CDI_read_track: Unsupported sector size. value %ld\n", track->sector_size_value);
return false;
}
if (track->mode > 2) printf( "Unsupported format: Track mode not supported");
if (track->mode > 2) {
printf("CDI_read_track: Unsupported format: Track mode not supported\n");
return false;
}
fseek(fsource, 29, SEEK_CUR);
if (image->version != CDI_V2)
{
{
fseek(fsource, 5, SEEK_CUR);
fread(&temp_value, 4, 1, fsource);
if (temp_value == 0xffffffff)
fseek(fsource, 78, SEEK_CUR); // extra data (DJ 3.00.780 and up)
}
}
return true;
}
@@ -104,9 +115,9 @@ void CDI_skip_next_session (FILE *fsource, image_s *image)
if (image->version != CDI_V2) fseek(fsource, 1, SEEK_CUR);
}
void CDI_get_tracks (FILE *fsource, image_s *image)
bool CDI_get_tracks (FILE *fsource, image_s *image)
{
fread(&image->tracks, 2, 1, fsource);
return fread(&image->tracks, 2, 1, fsource) == 1;
}
bool CDI_init (FILE *fsource, image_s *image, const char *fsourcename)
@@ -121,8 +132,9 @@ bool CDI_init (FILE *fsource, image_s *image, const char *fsourcename)
}
fseek(fsource, image->length-8, SEEK_SET);
fread(&image->version, 4, 1, fsource);
fread(&image->header_offset, 4, 1, fsource);
if (fread(&image->version, 4, 1, fsource) != 1
|| fread(&image->header_offset, 4, 1, fsource) != 1)
return false;
if ((image->version != CDI_V2 && image->version != CDI_V3 && image->version != CDI_V35)
|| image->header_offset == 0)
@@ -133,7 +145,7 @@ bool CDI_init (FILE *fsource, image_s *image, const char *fsourcename)
return true;
}
void CDI_get_sessions (FILE *fsource, image_s *image)
bool CDI_get_sessions (FILE *fsource, image_s *image)
{
#ifndef DEBUG_CDI
if (image->version == CDI_V35)
@@ -144,6 +156,6 @@ void CDI_get_sessions (FILE *fsource, image_s *image)
#else
fseek(fsource, 0L, SEEK_SET);
#endif
fread(&image->sessions, 2, 1, fsource);
return fread(&image->sessions, 2, 1, fsource) == 1;
}
+3 -3
View File
@@ -38,9 +38,9 @@ typedef struct track_s
unsigned long ask_type(FILE *fsource, long header_position);
bool CDI_init(FILE *fsource, image_s *image, const char *fsourcename);
void CDI_get_sessions(FILE *fsource, image_s *image);
void CDI_get_tracks(FILE *fsource, image_s *image);
void CDI_read_track(FILE *fsource, image_s *image, track_s *track);
bool CDI_get_sessions(FILE *fsource, image_s *image);
bool CDI_get_tracks(FILE *fsource, image_s *image);
bool CDI_read_track(FILE *fsource, image_s *image, track_s *track);
void CDI_skip_next_session(FILE *fsource, image_s *image);
#endif
+1 -1
View File
@@ -11661,7 +11661,7 @@ static bool ImGui::NavScoreItem(ImGuiNavItemData* result)
// Compute distance between boxes
// FIXME-NAV: Introducing biases for vertical navigation, needs to be removed.
float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x);
float dby = cand.Min.y == curr.Max.y || cand.Max.y == curr.Min.y
float dby = ImFabs(cand.Min.y - curr.Max.y) < 1 || ImFabs(cand.Max.y - curr.Min.y) < 1
// Scale down on Y to keep using box-distance for vertically touching items
? NavScoreItemDistInterval(ImLerp(cand.Min.y, cand.Max.y, 0.2f), ImLerp(cand.Min.y, cand.Max.y, 0.8f), ImLerp(curr.Min.y, curr.Max.y, 0.2f), ImLerp(curr.Min.y, curr.Max.y, 0.8f))
: NavScoreItemDistInterval(cand.Min.y, cand.Max.y, curr.Min.y, curr.Max.y);
+11 -1
View File
@@ -2773,6 +2773,9 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
dst_tmp_array.resize(atlas->Fonts.Size);
memset(src_tmp_array.Data, 0, (size_t)src_tmp_array.size_in_bytes());
memset(dst_tmp_array.Data, 0, (size_t)dst_tmp_array.size_in_bytes());
ImVector<bool> src_tmp_array_skip;
src_tmp_array_skip.resize(atlas->ConfigData.Size);
memset(src_tmp_array_skip.Data, 0, (size_t)src_tmp_array_skip.size_in_bytes());
// 1. Initialize font loading structure, check font data validity
for (int src_i = 0; src_i < atlas->ConfigData.Size; src_i++)
@@ -2797,7 +2800,8 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
if (!stbtt_InitFont(&src_tmp.FontInfo, (unsigned char*)cfg.FontData, font_offset))
{
IM_ASSERT(0 && "stbtt_InitFont(): failed to parse FontData. It is correct and complete? Check FontDataSize.");
return false;
src_tmp_array_skip[src_i] = true;
continue;
}
// Measure highest codepoints
@@ -2818,6 +2822,8 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
int total_glyphs_count = 0;
for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
{
if (src_tmp_array_skip[src_i])
continue;
ImFontBuildSrcData& src_tmp = src_tmp_array[src_i];
ImFontBuildDstData& dst_tmp = dst_tmp_array[src_tmp.DstIndex];
src_tmp.GlyphsSet.Create(src_tmp.GlyphsHighest + 1);
@@ -2844,6 +2850,8 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
// 3. Unpack our bit map into a flat list (we now have all the Unicode points that we know are requested _and_ available _and_ not overlapping another)
for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
{
if (src_tmp_array_skip[src_i])
continue;
ImFontBuildSrcData& src_tmp = src_tmp_array[src_i];
src_tmp.GlyphsList.reserve(src_tmp.GlyphsCount);
UnpackBitVectorToFlatIndexList(&src_tmp.GlyphsSet, &src_tmp.GlyphsList);
@@ -2974,6 +2982,8 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
// 9. Setup ImFont and glyphs for runtime
for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
{
if (src_tmp_array_skip[src_i])
continue;
// When merging fonts with MergeMode=true:
// - We can have multiple input fonts writing into a same destination font.
// - dst_font->ConfigData is != from cfg which is our source configuration.
@@ -76,6 +76,9 @@ int _zip_win32_error_to_errno(DWORD win32err);
#ifdef __clang__
#define DONT_WARN_INCOMPATIBLE_FN_PTR_BEGIN _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wincompatible-function-pointer-types\"")
#define DONT_WARN_INCOMPATIBLE_FN_PTR_END _Pragma("GCC diagnostic pop")
#elif defined(__GNUC__) && __GNUC__ > 13
#define DONT_WARN_INCOMPATIBLE_FN_PTR_BEGIN _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wincompatible-pointer-types\"")
#define DONT_WARN_INCOMPATIBLE_FN_PTR_END _Pragma("GCC diagnostic pop")
#else
#define DONT_WARN_INCOMPATIBLE_FN_PTR_BEGIN
#define DONT_WARN_INCOMPATIBLE_FN_PTR_END
+106 -20
View File
@@ -136,43 +136,52 @@ static void loadSpecialSettings()
// Aikagi
|| prod_id == "T20130M"
// AIR
|| prod_id == "T20112M")
|| prod_id == "T20112M"
// Cool Boarders Burrrn (JP)
|| prod_id == "T36901M"
// Castle Fantasia - Seima Taisen (JP)
|| prod_id == "T46901M"
// Silent Scope (US)
|| prod_id == "T9507N"
// Silent Scope (EU)
|| prod_id == "T9505D")
{
INFO_LOG(BOOT, "Enabling RTT Copy to VRAM for game %s", prod_id.c_str());
config::RenderToTextureBuffer.override(true);
}
// Cosmic Smash
if (prod_id == "HDR-0176" || prod_id == "RDC-0057")
{
INFO_LOG(BOOT, "Enabling translucent depth multipass for game %s", prod_id.c_str());
// Cosmic Smash
config::TranslucentPolygonDepthMask.override(true);
}
// NHL 2K2
if (prod_id == "MK-51182")
// Extra Depth Scaling
if (prod_id == "MK-51182") // NHL 2K2
{
INFO_LOG(BOOT, "Enabling Extra depth scaling for game %s", prod_id.c_str());
config::ExtraDepthScale.override(1e8f);
}
// Re-Volt (US, EU, JP)
else if (prod_id == "T-8109N" || prod_id == "T8107D 50" || prod_id == "T-8101M")
else if (prod_id == "T-8109N" // Re-Volt (US, EU, JP)
|| prod_id == "T8107D 50"
|| prod_id == "T-8101M"
|| prod_id == "DR001") // Sturmwind
{
INFO_LOG(BOOT, "Enabling Extra depth scaling for game %s", prod_id.c_str());
config::ExtraDepthScale.override(100.f);
}
// Test Drive V-Rally
else if (prod_id == "T15110N" || prod_id == "T15105D 50")
else if (prod_id == "T15110N" // Test Drive V-Rally
|| prod_id == "T15105D 50")
{
INFO_LOG(BOOT, "Enabling Extra depth scaling for game %s", prod_id.c_str());
config::ExtraDepthScale.override(0.1f);
}
// South Park Rally
else if (prod_id == "T-8116N" || prod_id == "T-8112D-50")
else if (prod_id == "T-8116N" // South Park Rally
|| prod_id == "T-8112D-50")
{
INFO_LOG(BOOT, "Enabling Extra depth scaling for game %s", prod_id.c_str());
config::ExtraDepthScale.override(1000.f);
}
// Capcom vs. SNK - Millennium Fight 2000 Pro
else if (prod_id == "T1247M")
else if (prod_id == "T1247M") // Capcom vs. SNK - Millennium Fight 2000 Pro
{
INFO_LOG(BOOT, "Enabling Extra depth scaling for game %s", prod_id.c_str());
config::ExtraDepthScale.override(10000.f);
@@ -255,7 +264,8 @@ static void loadSpecialSettings()
}
else if (prod_id == "T17708N" // Stupid Invaders (US)
|| prod_id == "T17711D" // Stupid Invaders (EU)
|| prod_id == "T46509M") // Suika (JP)
|| prod_id == "T46509M" // Suika (JP)
|| prod_id == "T36901M") // Cool Boarders Burrrn (JP)
{
NOTICE_LOG(BOOT, "Forcing HLE BIOS");
config::UseReios.override(true);
@@ -273,7 +283,8 @@ static void loadSpecialSettings()
else if (prod_id == "T-9709D-50" // San Francisco Rush 2049 (EU)
|| prod_id == "T-8112D-50" // South Park Rally (EU)
|| prod_id == "T7014D 50" // Super Runabout (EU)
|| prod_id == "T10001D 50") // MTV Sport - Skateboarding (PAL)
|| prod_id == "T10001D 50" // MTV Sport - Skateboarding (PAL)
|| prod_id == "MK-5101050") // Snow Surfers
{
NOTICE_LOG(BOOT, "Forcing PAL broadcasting");
config::Broadcast.override(1);
@@ -299,6 +310,63 @@ static void loadSpecialSettings()
NOTICE_LOG(BOOT, "Disabling Native Depth Interpolation");
config::NativeDepthInterpolation.override(false);
}
// Per-pixel transparent layers
int layers = 0;
if (prod_id == "MK-51011" // Time Stalkers (US)
|| prod_id == "MK-5101153") // Time Stalkers (EU)
layers = 72;
else if (prod_id == "T13001N" // Blue Stinger (US)
|| prod_id == "HDR-0003" // Blue Stinger (JP)
|| prod_id == "T13001D-05" // Blue Stinger (EU)
|| prod_id == "T13001D 18") // Blue Stinger (DE)
layers = 80;
else if (prod_id == "T2102M" // Panzer Front
|| prod_id == "T-8118N" // Spirit of Speed (US)
|| prod_id == "T-8117D-50" // Spirit of Speed (EU)
|| prod_id == "T13002N" // Vigilante 8 (US)
|| prod_id == "T13002D") // Vigilante 8 (EU)
layers = 64;
else if (prod_id == "T2106M") // L.O.L. Lack of Love
layers = 48;
else if (prod_id == "T1212M") // Gaiamaster - Kessen! Seikioh Densetsu
layers = 96;
else if (prod_id == "T-9707N" // San Francisco Rush 2049 (US)
|| prod_id == "T-9709D-50" // San Francisco Rush 2049 (EU)
|| prod_id == "T17721N" // Conflict Zone (US)
|| prod_id == "T46604D") // Conflict Zone (EU)
layers = 152;
else if (prod_id == "MK-51033" // ECCO the Dolphin (US)
|| prod_id == "MK-5103350" // ECCO the Dolphin (EU)
|| prod_id == "HDR-0103") // ECCO the Dolphin (JP)
layers = 96;
else if (prod_id == "T40203N") // Draconus: Cult of the Wyrm
layers = 80;
else if (prod_id == "T40212N" // Soldier of Fortune (US)
|| prod_id == "T17726D 50") // Soldier of Fortune (EU)
layers = 86;
else if (prod_id == "T44102N") // BANG! Gunship Elite
layers = 100;
else if (prod_id == "T12502N" // MDK 2 (US)
|| prod_id == "T12501D 50") // MDK 2 (EU)
layers = 200;
else if (prod_id == "T9708D 50") // Army Men
layers = 173;
else if (prod_id == "MK-51038" // Zombie Revenge (US)
|| prod_id == "MK-5103850" // Zombie Revenge (EU)
|| prod_id == "HDR-0026" // Zombie Revenge (JP)
|| prod_id == "36801N" // Fighting Force 2 (US)
|| prod_id == "36802D 80" // Fighting Force 2 (PAL, en-fr)
|| prod_id == "36802D 18") // Fighting Force 2 (PAL, de)
layers = 116;
else if (prod_id == "T15112N") // Demolition Racer (US)
layers = 44;
else if (prod_id == "T1208N" // Tech Romancer (US)
|| prod_id == "T7009D50") // Tech Romancer (EU)
layers = 56;
if (layers != 0) {
NOTICE_LOG(BOOT, "Forcing %d transparent layers", layers);
config::PerPixelLayers.override(layers);
}
}
else if (settings.platform.isArcade())
{
@@ -625,6 +693,8 @@ void Emulator::runInternal()
{
nvmem::saveFiles();
dc_reset(false);
if (!restartCpu())
resetRequested = false;
}
} while (resetRequested);
}
@@ -686,9 +756,12 @@ void Emulator::stop()
// Avoid race condition with GGPO restarting the sh4 for a new frame
if (config::GGPOEnable)
NetworkHandshake::term();
// must be updated after GGPO is stopped since it may run some rollback frames
state = Loaded;
sh4_cpu.Stop();
{
const std::lock_guard<std::mutex> _(mutex);
// must be updated after GGPO is stopped since it may run some rollback frames
state = Loaded;
sh4_cpu.Stop();
}
if (config::ThreadedRendering)
{
rend_cancel_emu_wait();
@@ -832,6 +905,8 @@ void Emulator::run()
verify(state == Running);
startTime = sh4_sched_now64();
renderTimeout = false;
if (!singleStep && stepRangeTo == 0)
sh4_cpu.Start();
try {
runInternal();
if (ggpo::active())
@@ -874,6 +949,7 @@ void Emulator::start()
if (config::ThreadedRendering)
{
const std::lock_guard<std::mutex> lock(mutex);
sh4_cpu.Start();
threadResult = std::async(std::launch::async, [this] {
ThreadName _("Flycast-emu");
InitAudio();
@@ -938,16 +1014,17 @@ bool Emulator::render()
if (!config::ThreadedRendering)
{
if (state != Running)
return false;
run();
if (stopRequested)
{
stopRequested = false;
TermAudio();
nvmem::saveFiles();
EventManager::event(Event::Pause);
return false;
}
if (state != Running)
return false;
run();
// TODO if stopping due to a user request, no frame has been rendered
return !renderTimeout;
}
@@ -971,4 +1048,13 @@ void Emulator::vblank()
sh4_cpu.Stop();
}
bool Emulator::restartCpu()
{
const std::lock_guard<std::mutex> _(mutex);
if (state != Running)
return false;
sh4_cpu.Start();
return true;
}
Emulator emu;
+5
View File
@@ -165,6 +165,11 @@ public:
* Called internally on vblank.
*/
void vblank();
/**
* Restart the cpu iff the emu is still running
* Returns true if the cpu was started
*/
bool restartCpu();
private:
bool checkStatus(bool wait = false);
+16 -16
View File
@@ -528,6 +528,7 @@ struct ChannelEx
enabled=false;
SetAegState(EG_Release);
AEG.SetValue(0x3FF);
CA = 0;
}
void enable()
@@ -611,9 +612,12 @@ struct ChannelEx
clip_verify((s64)sample*oDsp>=0);
StepAEG(this);
StepFEG(this);
StepStream(this);
lfo.Step(this);
if (enabled)
{
StepFEG(this);
StepStream(this);
lfo.Step(this);
}
return true;
}
}
@@ -854,8 +858,8 @@ struct ChannelEx
{
switch (offset)
{
case 0x00:
case 0x01:
case 0x00: // PCMS, SA
case 0x01: // KYONEX, KYONB, SSCTL, LPCTL, PCMS
UpdateStreamStep();
if (offset == 0 || size == 2)
UpdateSA();
@@ -872,8 +876,8 @@ struct ChannelEx
}
break;
case 0x04:
case 0x05:
case 0x04: // SA
case 0x05: // SA
UpdateSA();
break;
@@ -1219,7 +1223,7 @@ void AegStep(ChannelEx* ch)
if (ch->AEG.GetValue() >= 0x3FF)
{
aeg_printf("[%d]AEG_step : EG_Release End @ %x", ch->ChannelNumber, ch->AEG.GetValue());
ch->AEG.SetValue(0x3FF);
ch->disable();
}
break;
}
@@ -1440,8 +1444,8 @@ void ReadCommonReg(u32 reg,bool byte)
CommonData->EG = aeg; //AEG is only 10 bits, FEG is 13 bits
CommonData->SGC=Chans[chan].AEG.state;
if (! (byte && reg==0x2810))
Chans[chan].loop.looped=0;
if (!byte || reg == 0x2811)
Chans[chan].loop.looped = 0;
}
break;
case 0x2814: //CA
@@ -1513,13 +1517,9 @@ void AICA_Sample()
mixr += b;
}
//Mono !
// Mono
if (CommonData->Mono)
{
//Yay for mono =P
mixl+=mixr;
mixr=mixl;
}
mixl = mixr = FPs(mixl + mixr, 1);
//MVOL !
//we want to make sure mix* is *At least* 23 bits wide here, so 64 bit mul !
+4
View File
@@ -241,6 +241,10 @@ static void createNaomiDevices()
{
mcfg_Create(MDT_RFIDReaderWriter, 1, 5, 0);
mcfg_Create(MDT_RFIDReaderWriter, 2, 5, 1);
if (gameId.substr(0, 8) == "MKG TKOB") {
insertRfidCard(0);
insertRfidCard(1);
}
}
else
{
+88 -21
View File
@@ -1729,21 +1729,22 @@ struct RFIDReaderWriter : maple_base
DEBUG_LOG(MAPLE, "RFID card read (data? %d)", d4Seen);
w32(getStatus());
if (!d4Seen)
w32(0x12345678); // arbitrary value (unknown)
// serial0 and serial1 only
wptr(cardData, 8);
else
wptr(cardData, sizeof(cardData));
return (MapleDeviceRV)0xfe;
case 0xD9: // lock card
w32(getStatus());
cardLocked = true;
w32(getStatus());
INFO_LOG(MAPLE, "RFID card %d locked", player_num);
return (MapleDeviceRV)0xfe;
case 0xDA: // unlock card
w32(getStatus());
cardLocked = false;
cardInserted = false;
w32(getStatus());
NOTICE_LOG(MAPLE, "RFID card %d unlocked", player_num);
os_notify("Card ejected", 2000);
return (MapleDeviceRV)0xfe;
@@ -1760,6 +1761,34 @@ struct RFIDReaderWriter : maple_base
return (MapleDeviceRV)0xfe;
}
case 0xD1: // decrement counter
{
int counter = r8();
switch (counter) {
case 0x03:
counter = 0;
break;
case 0x0c:
counter = 1;
break;
case 0x30:
counter = 2;
break;
case 0xc0:
counter = 3;
break;
default:
WARN_LOG(MAPLE, "Unknown counter selector %x", counter);
counter = 0;
break;
}
DEBUG_LOG(MAPLE, "RFID decrement %d", counter);
cardData[19 - counter]--;
saveCard();
w32(getStatus());
return (MapleDeviceRV)0xfe;
}
default:
INFO_LOG(MAPLE, "RFIDReaderWriter: unknown MAPLE COMMAND %d", cmd);
return MDRE_UnknownCmd;
@@ -1795,24 +1824,62 @@ struct RFIDReaderWriter : maple_base
FILE *fp = nowide::fopen(path.c_str(), "rb");
if (fp == nullptr)
{
static u8 blankCard[128] = {
0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,0x6c, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0xff
};
// Generate random bytes used by vf4 vanilla to make the card id
srand(time(0));
blankCard[2] = rand() & 0xff;
blankCard[4] = rand() & 0xff;
blankCard[5] = rand() & 0xff;
blankCard[6] = rand() & 0xff;
blankCard[7] = rand() & 0xff;
memcpy(cardData, blankCard, sizeof(blankCard));
if (settings.content.gameId.substr(0, 8) == "MKG TKOB")
{
constexpr u8 MUSHIKING_CHIP_DATA[128] = {
0x12, 0x34, 0x56, 0x78, // Serial No.0
0x31, 0x00, 0x86, 0x07, // Serial No.1
0x00, 0x00, 0x00, 0x00, // Key
0x04, 0xf6, 0x00, 0xAA, // Extend Extend Access Mode
0x23, 0xFF, 0xFF, 0xFF, // Counter4 Counter3 Counter2 Counter1
0x00, 0x00, 0x00, 0x00, // User Data (first set date: day bits 0-4, month bits 5-8, year bits 9-... + 2000)
0x00, 0x00, 0x00, 0x00, // User Data
0x00, 0x00, 0x00, 0x00, // User Data
0x00, 0x00, 0x00, 0x00, // User Data
0x00, 0x00, 0x00, 0x00, // User Data
0x00, 0x00, 0x00, 0x00, // User Data
0x23, 0xFF, 0xFF, 0xFF, // User Data (max counters)
};
memcpy(cardData, MUSHIKING_CHIP_DATA, sizeof(MUSHIKING_CHIP_DATA));
for (int i = 0; i < 8; i++)
cardData[i] = rand() & 0xff;
u32 mask = 0;
if (settings.content.gameId == "MKG TKOB 2 JPN VER2.001-" // mushik2e
|| settings.content.gameId == "MKG TKOB 4 JPN VER2.000-") // mushik4e
mask = 0x40;
cardData[4] &= ~0xc0;
cardData[4] |= mask;
u32 serial1 = (cardData[4] << 24) | (cardData[5] << 16) | (cardData[6] << 8) | cardData[7];
u32 key = ~serial1;
key = ((key >> 4) & 0x0f0f0f0f)
| ((key << 4) & 0xf0f0f0f0);
cardData[8] = key >> 24;
cardData[9] = key >> 16;
cardData[10] = key >> 8;
cardData[11] = key;
}
else
{
constexpr u8 VF4_CARD_DATA[128] = {
0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,0x6c, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0xff
};
memcpy(cardData, VF4_CARD_DATA, sizeof(VF4_CARD_DATA));
// Generate random bytes used by vf4 vanilla to make the card id
srand(time(0));
cardData[2] = rand() & 0xff;
cardData[4] = rand() & 0xff;
cardData[5] = rand() & 0xff;
cardData[6] = rand() & 0xff;
cardData[7] = rand() & 0xff;
}
INFO_LOG(NAOMI, "Card P%d initialized", player_num + 1);
}
else
+17 -22
View File
@@ -47,34 +47,29 @@ void maple_vblank()
{
if (SB_MDEN & 1)
{
if (SDCKBOccupied)
if (SB_MDTSEL == 1)
{
maple_schd(0, 0, 0, nullptr);
SDCKBOccupied = false;
}
else
{
if (SB_MDTSEL == 1)
{
if (maple_ddt_pending_reset)
{
DEBUG_LOG(MAPLE, "DDT vblank ; reset pending");
}
else
{
DEBUG_LOG(MAPLE, "DDT vblank");
SB_MDST = 1;
maple_DoDma();
// if trigger reset is manual, mark it as pending
if ((SB_MSYS >> 12) & 1)
maple_ddt_pending_reset = true;
}
// Hardware trigger on vblank
if (maple_ddt_pending_reset) {
DEBUG_LOG(MAPLE, "DDT vblank ; reset pending");
}
else
{
maple_ddt_pending_reset = false;
DEBUG_LOG(MAPLE, "DDT vblank");
SB_MDST = 1;
maple_DoDma();
// if trigger reset is manual, mark it as pending
if ((SB_MSYS >> 12) & 1)
maple_ddt_pending_reset = true;
}
}
else
{
maple_ddt_pending_reset = false;
if (SDCKBOccupied)
maple_schd(0, 0, 0, nullptr);
}
SDCKBOccupied = false;
}
if (settings.platform.isConsole())
maple_handle_reconnect();
+41 -17
View File
@@ -725,6 +725,10 @@ public:
}
}
void setTokyoBusMode(bool enable) {
tokyoBus = enable;
}
protected:
u8 process(u8 in) override
{
@@ -758,7 +762,7 @@ protected:
break;
case 0xf1:
out = 0x10; // needed by f355
out = 0x10; // needed by f355 and tokyobus
break;
case 0xfa:
@@ -779,20 +783,31 @@ protected:
{
if (in >= 0x40 && in <= 0x7f)
{
// Spring
// bits 0-3 sets the strength of the spring effect
// bits 4-5 selects a table scaling the effect given the deflection:
// (from the f355 rom EPR-21867)
// 0: large deadzone then abrupt scaling (0 (* 129), 10, 20, 30, 40, 50, 60, 70, 7F)
// used by 18wheeler in game but with a different rom (TODO reveng)
// other tables scale linearly:
// 1: light speed (96 steps from 0 to 7f)
// 2: medium speed (48 steps, default)
// 3: high speed (32 steps)
springSat = (in & 0xf) / 15.f * motorPower;
const int speedSel = (in >> 4) & 3;
springSpeed = speedSel == 3 ? 1.f : speedSel == 2 ? 0.67f : speedSel == 1 ? 0.33f : 0.67f;
haptic::setSpring(0, springSat, springSpeed);
if (tokyoBus)
{
if (in <= 0x4a)
// heavy: 4a
// normal: 47
// light: 44
motorPower = (in & 0xf) / 10.f;
}
else
{
// Spring
// bits 0-3 sets the strength of the spring effect
// bits 4-5 selects a table scaling the effect given the deflection:
// (from the f355 rom EPR-21867)
// 0: large deadzone then abrupt scaling (0 (* 129), 10, 20, 30, 40, 50, 60, 70, 7F)
// used by 18wheeler in game but with a different rom (TODO reveng)
// other tables scale linearly:
// 1: light speed (96 steps from 0 to 7f)
// 2: medium speed (48 steps, default)
// 3: high speed (32 steps)
springSat = (in & 0xf) / 15.f * motorPower;
const int speedSel = (in >> 4) & 3;
springSpeed = speedSel == 3 ? 1.f : speedSel == 2 ? 0.67f : speedSel == 1 ? 0.33f : 0.67f;
haptic::setSpring(0, springSat, springSpeed);
}
}
else if (in >= 0x80 && in <= 0xbf)
{
@@ -841,6 +856,7 @@ private:
float springSpeed = 0.f;
float torque = 0.f;
float damper = 0.f;
bool tokyoBus = false;
};
// 18 Wheeler: fake the drive board and limit the wheel analog value
@@ -1283,10 +1299,12 @@ maple_naomi_jamma::maple_naomi_jamma()
INFO_LOG(MAPLE, "Enabling specific JVS setup for game %s", gameId.c_str());
io_boards.push_back(std::make_unique<jvs_837_13844_18wheeler>(1, this));
}
else if (gameId == "F355 CHALLENGE JAPAN")
else if (gameId == "F355 CHALLENGE JAPAN" || gameId == "TOKYO BUS GUIDE")
{
INFO_LOG(MAPLE, "Enabling specific JVS setup for game %s", gameId.c_str());
io_boards.push_back(std::make_unique<jvs_837_13844_racing>(1, this));
if (gameId == "TOKYO BUS GUIDE")
static_cast<jvs_837_13844_racing *>(io_boards.back().get())->setTokyoBusMode(true);
}
else if (gameId.substr(0, 8) == "MKG TKOB"
|| gameId.substr(0, 9) == "MUSHIKING"
@@ -2249,7 +2267,7 @@ u32 jvs_io_board::handle_jvs_message(u8 *buffer_in, u32 length_in, u8 *buffer_ou
}
else
{
axis_value = read_analog_axis(player_num, axisDesc.axis, axisDesc.inverted);
axis_value = read_analog_axis(player_num, axisDesc.axis, axisDesc.inverted);
}
}
else
@@ -2262,6 +2280,12 @@ u32 jvs_io_board::handle_jvs_message(u8 *buffer_in, u32 length_in, u8 *buffer_ou
axis_value = read_analog_axis(player_num, player_axis, false);
}
LOGJVS("%d:%4x ", axis, axis_value);
// Strangely, the least significant byte appears to be handled as signed,
// so we compensate when it's negative.
// Avoid overflow (wild riders)
axis_value = std::min<u16>(0xff7f, axis_value);
if (axis_value & 0x80)
axis_value += 0x100;
JVS_OUT(axis_value >> 8);
JVS_OUT(axis_value);
}
-7
View File
@@ -631,13 +631,6 @@ void *GDCartridge::GetDmaPtr(u32 &size)
return dimm_data + dimm_cur_address;
}
void GDCartridge::AdvancePtr(u32 size)
{
dimm_cur_address += size;
if(dimm_cur_address >= dimm_data_size)
dimm_cur_address %= dimm_data_size;
}
bool GDCartridge::Read(u32 offset, u32 size, void *dst)
{
if (dimm_data == NULL)
-1
View File
@@ -31,7 +31,6 @@ public:
device_reset();
}
void* GetDmaPtr(u32 &size) override;
void AdvancePtr(u32 size) override;
bool Read(u32 offset, u32 size, void* dst) override;
void SetGDRomName(const char *name, const char *parentName) { this->gdrom_name = name; this->gdrom_parent_name = parentName; }
+9 -9
View File
@@ -165,9 +165,9 @@ void *M4Cartridge::GetDmaPtr(u32 &size)
if (cfi_mode) {
u32 fpr_num = m4id & 0x7f;
if (((rom_cur_address >> 26) & 0x07) < fpr_num) {
if (((DmaOffset >> 26) & 0x07) < fpr_num) {
size = std::min(size, 2u);
return &cfidata[rom_cur_address & 0xffff];
return &cfidata[DmaOffset & 0xffff];
}
}
@@ -184,9 +184,8 @@ void *M4Cartridge::GetDmaPtr(u32 &size)
}
if (encryption)
{
size = std::min(size, (u32)sizeof(buffer));
size = std::min(size, buffer_actual_size);
return buffer;
}
else
{
@@ -207,17 +206,18 @@ void M4Cartridge::AdvancePtr(u32 size)
{
if (encryption)
{
if (size < buffer_actual_size)
{
if (size < buffer_actual_size) {
memmove(buffer, buffer + size, buffer_actual_size - size);
buffer_actual_size -= size;
}
else
else {
buffer_actual_size = 0;
}
enc_fill();
}
else
rom_cur_address += size;
else {
NaomiCartridge::AdvancePtr(size);
}
}
void M4Cartridge::enc_reset()
+6 -1
View File
@@ -127,6 +127,11 @@ void Multiboard::startSlave()
int x = cfgLoadInt("window", "left", (1920 - 640) / 2);
int y = cfgLoadInt("window", "top", (1080 - 480) / 2);
int width = cfgLoadInt("window", "width", 640);
std::string biosFile = CurrentCartridge->game->bios == nullptr ? "naomi" : CurrentCartridge->game->bios;
std::string biosPath = hostfs::findNaomiBios(biosFile + ".zip");
if (biosPath.empty())
biosPath = hostfs::findNaomiBios(biosFile + ".7z");
for (int i = 0; i < slaves; i++)
{
std::string region = "config:Dreamcast.Region=" + std::to_string(config::Region);
@@ -143,7 +148,7 @@ void Multiboard::startSlave()
"-config", region.c_str(),
"-config", left.c_str(),
"-config", top.c_str(),
CurrentCartridge->game->bios == nullptr ? "naomi" : CurrentCartridge->game->bios
biosPath.c_str()
};
os_RunInstance(std::size(args), args);
}
+26 -12
View File
@@ -44,6 +44,7 @@ static X76F100SerialFlash mainSerialId;
static X76F100SerialFlash romSerialId;
static int dmaSchedId = -1;
static int dmaXferDelay = 10; // cart dma xfer speed, in cycles/byte (default 20 MB/s)
void NaomiBoardIDWrite(const u16 data)
{
@@ -111,9 +112,9 @@ void WriteMem_naomi(u32 address, u32 data, u32 size)
static int naomiDmaSched(int tag, int sch_cycl, int jitter, void *arg)
{
u32 start = SB_GDSTAR & 0x1FFFFFE0;
u32 len = (SB_GDLEN + 31) & ~31;
SB_GDLEND = 0;
u32 start = SB_GDSTARD;
u32 len = std::min<int>(((SB_GDLEN + 31) & ~31) - SB_GDLEND, 1024);
SB_GDLEND += len;
while (len > 0)
{
u32 block_len = len;
@@ -123,20 +124,23 @@ static int naomiDmaSched(int tag, int sch_cycl, int jitter, void *arg)
INFO_LOG(NAOMI, "Aborted DMA transfer. Read past end of cart?");
for (u32 i = 0; i < len; i += 8, start += 8)
addrspace::write64(start, 0);
SB_GDLEND += len;
break;
}
WriteMemBlock_nommu_ptr(start, (u32*)ptr, block_len);
CurrentCartridge->AdvancePtr(block_len);
len -= block_len;
start += block_len;
SB_GDLEND += block_len;
}
SB_GDSTARD = start;
SB_GDST = 0;
asic_RaiseInterrupt(holly_GDROM_DMA);
return 0;
if (SB_GDLEN <= SB_GDLEND)
{
SB_GDST = 0;
asic_RaiseInterrupt(holly_GDROM_DMA);
return 0;
}
else {
return std::min<int>(SB_GDLEN - SB_GDLEND, 1024) * dmaXferDelay;
}
}
//Dma Start
@@ -155,13 +159,15 @@ static void Naomi_DmaStart(u32 addr, u32 data)
}
else if (!m3comm.DmaStart(addr, data) && CurrentCartridge != nullptr)
{
DEBUG_LOG(NAOMI, "NAOMI-DMA start addr %08X len %d", SB_GDSTAR, SB_GDLEN);
DEBUG_LOG(NAOMI, "NAOMI-DMA start addr %08X len %x", SB_GDSTAR, SB_GDLEN);
verify(1 == SB_GDDIR);
SB_GDST = 1;
SB_GDSTARD = SB_GDSTAR & 0x1FFFFFE0;
SB_GDLEND = 0;
// Max G1 bus rate: 50 MHz x 16 bits
// SH4_access990312_e.xls: 14.4 MB/s from GD-ROM to system RAM
// Here: 7 MB/s
sh4_sched_request(dmaSchedId, std::min<int>(SB_GDLEN * 27, SH4_MAIN_CLOCK));
// Here: 20 MB/s
sh4_sched_request(dmaSchedId, std::min<int>(SB_GDLEN, 1024) * dmaXferDelay);
return;
}
else
@@ -172,6 +178,14 @@ static void Naomi_DmaStart(u32 addr, u32 data)
asic_RaiseInterrupt(holly_GDROM_DMA);
}
void Naomi_setDmaDelay()
{
if (settings.content.gameId == "FORCE FIVE")
// 7 MB/s
dmaXferDelay = 27;
else
dmaXferDelay = 10;
}
static void Naomi_DmaEnable(u32 addr, u32 data)
{
+1
View File
@@ -21,6 +21,7 @@ void NaomiGameIDWrite(u16 data);
void setGameSerialId(const u8 *data);
void initDriveSimSerialPipe();
void Naomi_setDmaDelay();
namespace midiffb {
+26 -7
View File
@@ -56,22 +56,40 @@ bool atomiswaveForceFeedback;
static bool loadBios(const char *filename, Archive *child_archive, Archive *parent_archive, int region)
{
std::string path;
std::string biosName;
if (settings.naomi.slave)
{
// extract basename of bios
biosName = get_file_basename(filename);
size_t idx = get_last_slash_pos(biosName);
if (idx != std::string::npos)
biosName = biosName.substr(idx + 1);
path = filename;
}
else
{
biosName = filename;
}
int biosid = 0;
for (; BIOS[biosid].name != nullptr; biosid++)
if (!stricmp(BIOS[biosid].name, filename))
if (!stricmp(BIOS[biosid].name, biosName.c_str()))
break;
if (BIOS[biosid].name == nullptr)
{
WARN_LOG(NAOMI, "Unknown BIOS %s", filename);
WARN_LOG(NAOMI, "Unknown BIOS %s", biosName.c_str());
return false;
}
const BIOS_t *bios = &BIOS[biosid];
std::string arch_name(bios->filename != nullptr ? bios->filename : filename);
std::string path = hostfs::findNaomiBios(arch_name + ".zip");
if (path.empty())
path = hostfs::findNaomiBios(arch_name + ".7z");
{
std::string arch_name(bios->filename != nullptr ? bios->filename : filename);
path = hostfs::findNaomiBios(arch_name + ".zip");
if (path.empty())
path = hostfs::findNaomiBios(arch_name + ".7z");
}
DEBUG_LOG(NAOMI, "Loading BIOS from %s", path.c_str());
std::unique_ptr<Archive> bios_archive(OpenArchive(path));
@@ -221,8 +239,8 @@ static void loadMameRom(const std::string& path, const std::string& fileName, Lo
std::unique_ptr<Archive> parent_archive;
if (game->parent_name != nullptr)
{
std::string parentPath = hostfs::storage().getParentPath(path);
try {
std::string parentPath = hostfs::storage().getParentPath(path);
parentPath = hostfs::storage().getSubPath(parentPath, game->parent_name);
parent_archive.reset(OpenArchive(parentPath));
} catch (const FlycastException& e) {
@@ -230,7 +248,7 @@ static void loadMameRom(const std::string& path, const std::string& fileName, Lo
if (parent_archive != nullptr)
INFO_LOG(NAOMI, "Opened %s", game->parent_name);
else
WARN_LOG(NAOMI, "Parent not found: %s", parentPath.c_str());
WARN_LOG(NAOMI, "Parent not found: %s", game->parent_name);
}
@@ -704,6 +722,7 @@ void naomi_cart_LoadRom(const std::string& path, const std::string& fileName, Lo
{
hopper::init();
}
Naomi_setDmaDelay();
#ifdef NAOMI_MULTIBOARD
// Not a multiboard game but needs the same desktop environment
+3 -1
View File
@@ -86,7 +86,9 @@ public:
u32 ReadMem(u32 address, u32 size) override;
void WriteMem(u32 address, u32 data, u32 size) override;
void* GetDmaPtr(u32 &size) override;
void AdvancePtr(u32 size) override {}
void AdvancePtr(u32 size) override {
DmaOffset += size;
}
void Serialize(Serializer& ser) const override;
void Deserialize(Deserializer& deser) override;
bool GetBootId(RomBootID *bootId) override;
+29 -8
View File
@@ -38,9 +38,12 @@ const BIOS_t BIOS[] =
{
"awbios",
{
// Sammy version
{ 0, "bios0.ic23", 0x000000, 0x020000 },
//or
//{ "bios1.ic23", 0x000000, 0x020000 },
//{ 0, "bios1.ic23", 0x000000, 0x020000 },
// Sega version
//{ 0, "fpr-24363.ic48", 0x000000, 0x020000 },
}
},
{
@@ -2489,7 +2492,7 @@ const Game Games[] =
nullptr,
// no free play with eeprom
},
// Mushiking The King Of Beetles - Mushiking IV / V / VI (World)
// Mushiking The King Of Beetles 2004 Second (Japan)
{
"mushi2k4",
NULL,
@@ -4722,7 +4725,7 @@ const Game Games[] =
{ NULL, 0, 0 },
}
},
// MushiKing II - The King Of Beetle II ENG (Ver. 1.001)
// Mushiking The King Of Beetles - Mushiking II / III / III+ (World, Ver. 2.001)
{
"mushik2e",
NULL,
@@ -4746,6 +4749,7 @@ const Game Games[] =
},
// Mushiking The King Of Beetles - Mushiking IV / V / VI (World)
// change game version (4/5/6): in BACKUP DATA CLEAR menu hold P1 and P2 buttons 1 for 3 seconds, then change version number in appeared menu and select YES(CLEAR)
// ~equivalent of Japanese 2K6 versions
{
"mushik4e",
NULL,
@@ -4756,14 +4760,31 @@ const Game Games[] =
M4,
ROT0,
{
{ "fpr-24417.ic8", 0x0000000, 0x4000000 },
{ "fpr-24418.ic9", 0x4000000, 0x4000000 },
{ "fpr-24417.ic8", 0x0000000, 0x4000000, 0x01278cbb },
{ "fpr-24418.ic9", 0x4000000, 0x4000000, 0x7c8d7687 },
//ROM_REGION( 0x800, "pic_readout", 0 )
//ROM_LOAD( "317-0437-com.ic3", 0, 0x800, CRC(3b6fcee8) SHA1(65fbdd3b8c61a4b5ccb6389b25483a7ecdc0794d) )
{ "317-0437-com.ic3", 0, 0x800, 0x0000000, Key },
{ "317-0437-com.ic3", 0, 0x800, 0x3b6fcee8, Key },
}
},
// Mushiking The King Of Beetles - Mushiking IV / V / VI (Taiwan)
// change game version (4/5/6): in BACKUP DATA CLEAR menu hold P1 and P2 buttons 1 for 3 seconds, then change version number in appeared menu and select YES(CLEAR)
// ~equivalent of Japanese 2K6 versions
{
"mushik4t",
"mushik4e",
"Mushiking The King Of Beetles - Mushiking IV / V / VI (Taiwan)",
0x8000000,
0x5502,
"naomi",
M4,
ROT0,
{
{ "fpr-24420.ic8", 0x0000000, 0x4000000, 0x4a44a604 },
{ "fpr-24421.ic9", 0x4000000, 0x4000000, 0xaf137021 }, // BAD DUMP
{ NULL, 0, 0 },
//ROM_REGION( 0x800, "pic_readout", 0 )
{ "317-0437-com.ic3", 0, 0x800, 0x3b6fcee8, Key },
}
},
{
+3 -2
View File
@@ -716,10 +716,11 @@ static InputDescriptors drvsim_inputs = {
// WIPER-HI
},
{
{ "Master", Full, 0, true },
{ "", Full, ~0u, true }, // Master audio volume
{ "Accelerator", Half, 4 },
{ "Brake", Half, 5 },
{ "Clutch", Full, 1 },
{ "Clutch", Half, 6 },
{ "Wheel", Full, 0, true }, // only read via ffb board, not jvs
},
};
+52 -59
View File
@@ -983,7 +983,7 @@ public:
tri.x2 * curMatrix[0][2] + tri.y2 * curMatrix[1][2] + tri.z2 * curMatrix[2][2] + curMatrix[3][2]
};
dist = -dist - nearPlane;
ModTriangle newTri;
ModTriangle newTri[2];
int n = sutherlandHodgmanClip(dist, tri, newTri);
switch (n)
{
@@ -993,9 +993,10 @@ public:
case 3:
ta_add_triangle(tri);
break;
case 4:
case 5:
ta_add_triangle(tri);
ta_add_triangle(newTri);
ta_add_triangle(newTri[0]);
ta_add_triangle(newTri[1]);
break;
}
}
@@ -1016,86 +1017,78 @@ private:
}
// Clip the triangle 'trig' with respect to the provided distances to the clipping plane.
int sutherlandHodgmanClip(glm::vec3& dist, ModTriangle& trig, ModTriangle& newTrig)
int sutherlandHodgmanClip(glm::vec3& dist, ModTriangle& trig, ModTriangle *newTrig)
{
constexpr float clipEpsilon = 0.f; //0.00001;
constexpr float clipEpsilon2 = 0.f; //0.01;
if (!glm::any(glm::greaterThanEqual(dist , glm::vec3(clipEpsilon2))))
// all clipped
return 0;
if (!glm::any(glm::greaterThanEqual(dist , glm::vec3(clipEpsilon2)))) {
// all clipped: leave it alone as it will be projected onto the near plane in the shader
return 3;
}
if (glm::all(glm::greaterThanEqual(dist , glm::vec3(-clipEpsilon))))
// none clipped
return 3;
// There are either 1 or 2 vertices above the clipping plane.
// There are either 1 or 2 vertices above the clipping plane. Tesselate into 3 triangles along the plane.
glm::bvec3 above = glm::greaterThanEqual(dist, glm::vec3(0.f));
bool nextIsAbove;
glm::vec3 v0(trig.x0, trig.y0, trig.z0);
glm::vec3 v1(trig.x1, trig.y1, trig.z1);
glm::vec3 v2(trig.x2, trig.y2, trig.z2);
glm::vec3 v3;
// Find the CCW-most vertex above the plane.
if (above[1] && !above[0])
glm::vec3 v3, v4;
// Find the lonely vertex on one side of the plane
if (above[0] == above[2])
{
// Cycle once CCW. Use v3 as a temp
nextIsAbove = above[2];
// this is vertex 1 so cycle CCW
v3 = v0;
v0 = v1;
v1 = v2;
v2 = v3;
dist = glm::vec3(dist.y, dist.z, dist.x);
}
else if (above[2] && !above[1])
else if (above[0] == above[1])
{
// Cycle once CW. Use v3 as a temp.
nextIsAbove = above[0];
// this is vertex 2 so cycle CW
v3 = v2;
v2 = v1;
v1 = v0;
v0 = v3;
dist = glm::vec3(dist.z, dist.x, dist.y);
}
else
nextIsAbove = above[1];
v3 = intersect(v0, dist[0], v1, dist[1]);
v4 = intersect(v0, dist[0], v2, dist[2]);
// v0 v3 v4
trig.x0 = v0.x;
trig.y0 = v0.y;
trig.z0 = v0.z;
// We always need to clip v2-v0.
v3 = intersect(v0, dist[0], v2, dist[2]);
if (nextIsAbove)
{
v2 = intersect(v1, dist[1], v2, dist[2]);
trig.x1 = v1.x;
trig.y1 = v1.y;
trig.z1 = v1.z;
trig.x2 = v2.x;
trig.y2 = v2.y;
trig.z2 = v2.z;
newTrig.x0 = v0.x;
newTrig.y0 = v0.y;
newTrig.z0 = v0.z;
newTrig.x1 = v2.x;
newTrig.y1 = v2.y;
newTrig.z1 = v2.z;
newTrig.x2 = v3.x;
newTrig.y2 = v3.y;
newTrig.z2 = v3.z;
trig.x1 = v3.x;
trig.y1 = v3.y;
trig.z1 = v3.z;
trig.x2 = v4.x;
trig.y2 = v4.y;
trig.z2 = v4.z;
// v3 v1 v4
newTrig[0].x0 = v3.x;
newTrig[0].y0 = v3.y;
newTrig[0].z0 = v3.z;
newTrig[0].x1 = v1.x;
newTrig[0].y1 = v1.y;
newTrig[0].z1 = v1.z;
newTrig[0].x2 = v4.x;
newTrig[0].y2 = v4.y;
newTrig[0].z2 = v4.z;
// v2 v4 v1
newTrig[1].x0 = v2.x;
newTrig[1].y0 = v2.y;
newTrig[1].z0 = v2.z;
newTrig[1].x1 = v4.x;
newTrig[1].y1 = v4.y;
newTrig[1].z1 = v4.z;
newTrig[1].x2 = v1.x;
newTrig[1].y2 = v1.y;
newTrig[1].z2 = v1.z;
return 4;
}
else
{
v1 = intersect(v0, dist[0], v1, dist[1]);
trig.x1 = v1.x;
trig.y1 = v1.y;
trig.z1 = v1.z;
trig.x2 = v3.x;
trig.y2 = v3.y;
trig.z2 = v3.z;
return 3;
}
return 5;
}
bool enabled;
@@ -1327,15 +1320,15 @@ static void sendPolygon(ICHList *list)
case ICHList::VTX_TYPE_V:
{
N2_VERTEX *vtx = (N2_VERTEX *)((u8 *)list + sizeof(ICHList));
if (!isBetweenNearAndFar(vtx, list->vtxCount, needClipping))
break;
int listType = ta_get_list_type();
if (listType == -1)
listType = list->pcw.listType;
if (listType & 1)
sendMVPolygon(list, vtx, needClipping);
sendMVPolygon(list, vtx, true);
else
{
if (!isBetweenNearAndFar(vtx, list->vtxCount, needClipping))
break;
PolyParam pp{};
pp.pcw.Shadow = list->pcw.shadow;
pp.pcw.Texture = 0;
@@ -1356,15 +1349,15 @@ static void sendPolygon(ICHList *list)
case ICHList::VTX_TYPE_VU:
{
N2_VERTEX_VU *vtx = (N2_VERTEX_VU *)((u8 *)list + sizeof(ICHList));
if (!isBetweenNearAndFar(vtx, list->vtxCount, needClipping))
break;
int listType = ta_get_list_type();
if (listType == -1)
listType = list->pcw.listType;
if (listType & 1)
sendMVPolygon(list, vtx, needClipping);
sendMVPolygon(list, vtx, true);
else
{
if (!isBetweenNearAndFar(vtx, list->vtxCount, needClipping))
break;
PolyParam pp{};
pp.pcw.Shadow = list->pcw.shadow;
pp.pcw.Texture = list->pcw.texture;
+8 -5
View File
@@ -219,10 +219,10 @@ template u32 pvr_read32p<u32>(u32 addr);
template float pvr_read32p<float>(u32 addr);
//write
template<typename T>
template<typename T, bool Internal>
void DYNACALL pvr_write32p(u32 addr, T data)
{
if constexpr (sizeof(T) == 1)
if constexpr (!Internal && sizeof(T) == 1)
{
INFO_LOG(MEMORY, "%08x: 8-bit VRAM writes are not possible", addr);
return;
@@ -234,9 +234,12 @@ void DYNACALL pvr_write32p(u32 addr, T data)
*(T *)&vram[pvr_map32(addr)] = data;
}
template void pvr_write32p<u8>(u32 addr, u8 data);
template void pvr_write32p<u16>(u32 addr, u16 data);
template void pvr_write32p<u32>(u32 addr, u32 data);
template void pvr_write32p<u8, false>(u32 addr, u8 data);
template void pvr_write32p<u8, true>(u32 addr, u8 data);
template void pvr_write32p<u16, false>(u32 addr, u16 data);
template void pvr_write32p<u16, true>(u32 addr, u16 data);
template void pvr_write32p<u32, false>(u32 addr, u32 data);
template void pvr_write32p<u32, true>(u32 addr, u32 data);
void DYNACALL TAWrite(u32 address, const SQBuffer *data, u32 count)
{
+1 -1
View File
@@ -20,7 +20,7 @@ void YUV_reset();
// 32-bit vram path handlers
template<typename T> T DYNACALL pvr_read32p(u32 addr);
template<typename T> void DYNACALL pvr_write32p(u32 addr, T data);
template<typename T, bool Internal = false> void DYNACALL pvr_write32p(u32 addr, T data);
// Area 4 handlers
template<typename T, bool upper> T DYNACALL pvr_read_area4(u32 addr);
template<typename T, bool upper> void DYNACALL pvr_write_area4(u32 addr, T data);
+14 -13
View File
@@ -125,7 +125,6 @@ protected:
static void reset()
{
tileclip_val = 0;
memset(FaceBaseColor, 0xff, sizeof(FaceBaseColor));
memset(FaceOffsColor, 0xff, sizeof(FaceOffsColor));
memset(FaceBaseColor1, 0xff, sizeof(FaceBaseColor1));
@@ -398,7 +397,7 @@ strip_end:
//32B
case ParamType_User_Tile_Clip:
SetTileClip(data->data_32[3] & 63, data->data_32[4] & 31, data->data_32[5] & 63, data->data_32[6] & 31);
setClipRect(data->data_32[3] & 63, data->data_32[4] & 31, data->data_32[5] & 63, data->data_32[6] & 31);
data += SZ32;
break;
@@ -414,7 +413,7 @@ strip_end:
//PolyType :32B/64B
case ParamType_Polygon_or_Modifier_Volume:
{
TileClipMode(data->pcw.User_Clip);
setClipMode(data->pcw.User_Clip);
//Yep , C++ IS lame & limited
#include "ta_const_df.h"
if (CurrentList == ListType_None && !startList(data->pcw.ListType))
@@ -466,7 +465,7 @@ strip_end:
//32B
//Sets Sprite info , and switches to ta_sprite_data function
case ParamType_Sprite:
TileClipMode(data->pcw.User_Clip);
setClipMode(data->pcw.User_Clip);
if (CurrentList != ListType_None || startList(data->pcw.ListType))
{
VertexDataFP = ta_sprite_data;
@@ -502,23 +501,25 @@ public:
{
TaCmd = ta_main;
BaseTAParser::reset();
setClipRect(0, 0, 39, 14);
setClipMode(0);
}
private:
static void SetTileClip(u32 xmin,u32 ymin,u32 xmax,u32 ymax)
static void setClipRect(u32 xmin, u32 ymin, u32 xmax, u32 ymax)
{
u32 rv=tileclip_val & 0xF0000000;
rv|=xmin; //6 bits
rv|=xmax<<6; //6 bits
rv|=ymin<<12; //5 bits
rv|=ymax<<17; //5 bits
tileclip_val=rv;
u32 rv = tileclip_val & 0xF0000000;
rv |= xmin; // 6 bits
rv |= xmax << 6; // 6 bits
rv |= ymin << 12; // 5 bits
rv |= ymax << 17; // 5 bits
tileclip_val = rv;
}
static void TileClipMode(u32 mode)
static void setClipMode(u32 mode)
{
//Group_En bit seems ignored, thanks p1pkin
tileclip_val=(tileclip_val&(~0xF0000000)) | (mode<<28);
tileclip_val = (tileclip_val & ~0xF0000000) | (mode << 28);
}
//Polys -- update code on sprites if that gets updated too --
+6 -1
View File
@@ -101,7 +101,6 @@ static void recSh4_ClearCache()
static void recSh4_Run()
{
sh4_int_bCpuRun = true;
RestoreHostRoundingMode();
u8 *sh4_dyna_rcb = (u8 *)&Sh4cntx + sizeof(Sh4cntx);
@@ -341,6 +340,11 @@ void* DYNACALL rdv_LinkBlock(u8* code,u32 dpc)
return (void*)rv;
}
static void recSh4_Start()
{
sh4Interp.Start();
}
static void recSh4_Stop()
{
sh4Interp.Stop();
@@ -409,6 +413,7 @@ static bool recSh4_IsCpuRunning()
void Get_Sh4Recompiler(sh4_if* cpu)
{
cpu->Run = recSh4_Run;
cpu->Start = recSh4_Start;
cpu->Stop = recSh4_Stop;
cpu->Step = recSh4_Step;
cpu->Reset = recSh4_Reset;
+1 -12
View File
@@ -973,18 +973,7 @@ shil_opc_end()
//shop_fsrra
shil_opc(fsrra)
shil_canonical
(
f32,f1,(float fn),
return std::sqrt(1.f / fn);
)
shil_compile
(
shil_cf_arg_f32(rs1);
shil_cf(f1);
shil_cf_rv_f32(rd);
)
UN_OP_F(1/sqrtf)
shil_opc_end()
+1 -1
View File
@@ -362,7 +362,7 @@ sh4op(i1111_nnnn_0111_1101)
u32 n = GetN(op);
if (fpscr.PR==0)
{
fr[n] = sqrtf(1.f / fr[n]);
fr[n] = 1.f / sqrtf(fr[n]);
CHECK_FPU_32(fr[n]);
}
else
+6 -1
View File
@@ -47,7 +47,6 @@ static u16 ReadNexOp()
static void Sh4_int_Run()
{
sh4_int_bCpuRun = true;
RestoreHostRoundingMode();
try {
@@ -74,6 +73,11 @@ static void Sh4_int_Run()
sh4_int_bCpuRun = false;
}
static void Sh4_int_Start()
{
sh4_int_bCpuRun = true;
}
static void Sh4_int_Stop()
{
sh4_int_bCpuRun = false;
@@ -206,6 +210,7 @@ static void Sh4_int_Term()
void Get_Sh4Interpreter(sh4_if* cpu)
{
cpu->Start = Sh4_int_Start;
cpu->Run = Sh4_int_Run;
cpu->Stop = Sh4_int_Stop;
cpu->Step = Sh4_int_Step;
+34 -37
View File
@@ -122,59 +122,60 @@ enum Sh4RegType
NoReg=-1
};
//Varius sh4 registers
// SR (status register)
union sr_status_t
{
struct
{
u32 T_h : 1;//<<0
u32 S : 1;//<<1
u32 rsvd0 : 2;//<<2
u32 IMASK : 4;//<<4
u32 Q : 1;//<<8
u32 M : 1;//<<9
u32 rsvd1 : 5;//<<10
u32 FD : 1;//<<15
u32 rsvd2 : 12;//<<16
u32 BL : 1;//<<28
u32 RB : 1;//<<29
u32 MD : 1;//<<20
u32 rsvd3 : 1;//<<31
u32 T_h : 1;
u32 S : 1;
u32 : 2;
u32 IMASK : 4;
u32 Q : 1;
u32 M : 1;
u32 : 5;
u32 FD : 1;
u32 : 12;
u32 BL : 1;
u32 RB : 1;
u32 MD : 1;
u32 : 1;
};
u32 status;
};
#define STATUS_MASK 0x700083F2
//Status register bitfield
// Status register with isolated T bit.
// Used in place of the normal SR bitfield so that the T bit can be
// handled as a regular register. This simplifies dynarec implementations.
struct sr_t
{
union
{
struct
{
u32 T_h : 1;//<<0
u32 S : 1;//<<1
u32 rsvd0 : 2;//<<2
u32 IMASK : 4;//<<4
u32 Q : 1;//<<8
u32 M : 1;//<<9
u32 rsvd1 : 5;//<<10
u32 FD : 1;//<<15
u32 rsvd2 : 12;//<<16
u32 BL : 1;//<<28
u32 RB : 1;//<<29
u32 MD : 1;//<<20
u32 rsvd3 : 1;//<<31
u32 T_h : 1;
u32 S : 1;
u32 : 2;
u32 IMASK : 4;
u32 Q : 1;
u32 M : 1;
u32 : 5;
u32 FD : 1;
u32 : 12;
u32 BL : 1;
u32 RB : 1;
u32 MD : 1;
u32 : 1;
};
u32 status;
};
u32 T;
};
//FPSCR (fpu status and control register) bitfield
// FPSCR (fpu status and control register)
struct fpscr_t
{
union
@@ -203,13 +204,7 @@ struct fpscr_t
u32 PR : 1;
u32 SZ : 1;
u32 FR : 1;
u32 pad : 10;
};
struct
{
u32 _nil : 2+1+1+1+1+4+8+1;
u32 PR_SZ : 2;
u32 nilz : 11;
u32 : 10;
};
};
};
@@ -217,6 +212,7 @@ struct fpscr_t
//sh4 interface
struct sh4_if
{
void (*Start)();
void (*Run)();
void (*Stop)();
void (*Step)();
@@ -272,6 +268,7 @@ struct alignas(64) Sh4Context
u64 raw[64-8];
};
};
static_assert(sizeof(Sh4Context) == 448, "Invalid Sh4Context size");
struct alignas(32) SQBuffer {
u8 data[32];
+150 -139
View File
@@ -16,149 +16,160 @@ Disc* cdi_parse(const char* file, std::vector<u8> *digest)
WARN_LOG(GDROM, "Cannot open file '%s' errno %d", file, errno);
throw FlycastException(std::string("Cannot open CDI file ") + file);
}
image_s image = { 0 };
track_s track = { 0 };
if (!CDI_init(fsource, &image, file))
{
std::fclose(fsource);
throw FlycastException(std::string("Invalid CDI file ") + file);
}
CDI_get_sessions(fsource,&image);
Disc* rv= new Disc();
image.remaining_sessions = image.sessions;
/////////////////////////////////////////////////////////////// Loop sessions
try {
image_s image = { 0 };
track_s track = { 0 };
if (!CDI_init(fsource, &image, file))
throw FlycastException(std::string("Invalid CDI file ") + file);
bool ft=true, CD_M2=false,CD_M1=false,CD_DA=false;
while(image.remaining_sessions > 0)
{
ft=true;
image.global_current_session++;
CDI_get_tracks (fsource, &image);
image.header_position = std::ftell(fsource);
if (image.tracks == 0)
INFO_LOG(GDROM, "Open session");
else
{
// Clear cuesheet
image.remaining_tracks = image.tracks;
///////////////////////////////////////////////////////////////// Loop tracks
while(image.remaining_tracks > 0)
{
track.global_current_track++;
track.number = image.tracks - image.remaining_tracks + 1;
CDI_read_track (fsource, &image, &track);
if (track.sector_size != 2048 && track.sector_size != 2336 && track.sector_size != 2352 && track.sector_size != 2448)
{
delete rv;
std::fclose(fsource);
WARN_LOG(GDROM, "Invalid sector size: %lu", track.sector_size);
throw FlycastException("Invalid CDI sector size");
}
image.header_position = std::ftell(fsource);
// Show info
#if 0
printf("Saving ");
printf("Track: %2d ",track.global_current_track);
printf("Type: ");
switch(track.mode)
{
case 0 : printf("Audio/"); break;
case 1 : printf("Mode1/"); break;
case 2 :
default: printf("Mode2/"); break;
}
printf("%lu ",track.sector_size);
printf("Pregap: %-3ld ",track.pregap_length);
printf("Size: %-6ld ",track.length);
printf("LBA: %-6ld ",track.start_lba);
#endif
if (ft)
{
ft = false;
Session s;
s.StartFAD = track.pregap_length + track.start_lba;
s.FirstTrack = (u8)track.global_current_track;
rv->sessions.push_back(s);
}
Track t;
if (track.mode==2)
CD_M2=true;
if (track.mode==1)
CD_M1=true;
if (track.mode==0)
CD_DA=true;
t.ADR=1;//hmm is that ok ?
t.CTRL=track.mode==0?0:4;
t.StartFAD=track.start_lba+track.pregap_length;
t.EndFAD=t.StartFAD+track.length-1;
FILE *trackFile = hostfs::storage().openFile(file, "rb");
if (trackFile == nullptr)
{
delete rv;
std::fclose(fsource);
WARN_LOG(GDROM, "Cannot re-open file '%s' errno %d", file, errno);
throw FlycastException("Cannot re-open CDI file");
}
t.file = new RawTrackFile(trackFile, track.position + track.pregap_length * track.sector_size, t.StartFAD, track.sector_size);
rv->tracks.push_back(t);
if (track.length < 0)
WARN_LOG(GDROM, "Negative track size found. You must extract image with /pregap option");
std::fseek(fsource, track.position, SEEK_SET);
if (track.total_length < track.length + track.pregap_length)
{
WARN_LOG(GDROM, "This track seems truncated. Skipping...");
// FIXME that can't be right
std::fseek(fsource, track.total_length, SEEK_CUR);
}
else
{
std::fseek(fsource, track.total_length * track.sector_size, SEEK_CUR);
rv->EndFAD = track.start_lba + track.total_length - 1;
}
track.position = std::ftell(fsource);
std::fseek(fsource, image.header_position, SEEK_SET);
image.remaining_tracks--;
}
if (!CDI_get_sessions(fsource, &image)) {
WARN_LOG(GDROM, "Invalid CDI file '%s': can't get sessions", file);
throw FlycastException("Invalid CDI image");
}
if (image.sessions > 255) {
WARN_LOG(GDROM, "Invalid CDI file '%s': too many sessions (%d)", file, image.sessions);
throw FlycastException("Invalid CDI image");
}
CDI_skip_next_session(fsource, &image);
std::unique_ptr<Disc> rv = std::make_unique<Disc>();
image.remaining_sessions--;
image.remaining_sessions = image.sessions;
/////////////////////////////////////////////////////////////// Loop sessions
bool ft=true, CD_M2=false,CD_M1=false,CD_DA=false;
while(image.remaining_sessions > 0)
{
ft=true;
image.global_current_session++;
if (!CDI_get_tracks(fsource, &image)) {
WARN_LOG(GDROM, "Invalid CDI file '%s': can't get tracks", file);
throw FlycastException("Invalid CDI image");
}
image.header_position = std::ftell(fsource);
if (image.tracks == 0)
INFO_LOG(GDROM, "Open session");
else
{
if (image.tracks > 100) {
WARN_LOG(GDROM, "Invalid CDI file '%s': too many tracks (%d)", file, image.tracks);
throw FlycastException("Invalid CDI image");
}
// Clear cuesheet
image.remaining_tracks = image.tracks;
///////////////////////////////////////////////////////////////// Loop tracks
while(image.remaining_tracks > 0)
{
track.global_current_track++;
track.number = image.tracks - image.remaining_tracks + 1;
if (!CDI_read_track (fsource, &image, &track))
throw FlycastException("Invalid CDI image");
if (track.sector_size != 2048 && track.sector_size != 2336 && track.sector_size != 2352 && track.sector_size != 2448)
{
WARN_LOG(GDROM, "Invalid sector size: %lu", track.sector_size);
throw FlycastException("Invalid CDI sector size");
}
image.header_position = std::ftell(fsource);
// Show info
#if 0
printf("Saving ");
printf("Track: %2d ",track.global_current_track);
printf("Type: ");
switch(track.mode)
{
case 0 : printf("Audio/"); break;
case 1 : printf("Mode1/"); break;
case 2 :
default: printf("Mode2/"); break;
}
printf("%lu ",track.sector_size);
printf("Pregap: %-3ld ",track.pregap_length);
printf("Size: %-6ld ",track.length);
printf("LBA: %-6ld ",track.start_lba);
#endif
if (ft)
{
ft = false;
Session s;
s.StartFAD = track.pregap_length + track.start_lba;
s.FirstTrack = (u8)track.global_current_track;
rv->sessions.push_back(s);
}
Track t;
if (track.mode==2)
CD_M2=true;
if (track.mode==1)
CD_M1=true;
if (track.mode==0)
CD_DA=true;
t.ADR=1;//hmm is that ok ?
t.CTRL=track.mode==0?0:4;
t.StartFAD=track.start_lba+track.pregap_length;
t.EndFAD=t.StartFAD+track.length-1;
FILE *trackFile = hostfs::storage().openFile(file, "rb");
if (trackFile == nullptr) {
WARN_LOG(GDROM, "Cannot re-open file '%s' errno %d", file, errno);
throw FlycastException("Cannot re-open CDI file");
}
t.file = new RawTrackFile(trackFile, track.position + track.pregap_length * track.sector_size, t.StartFAD, track.sector_size);
rv->tracks.push_back(t);
if (track.length < 0)
WARN_LOG(GDROM, "Negative track size found. You must extract image with /pregap option");
std::fseek(fsource, track.position, SEEK_SET);
if (track.total_length < track.length + track.pregap_length)
{
WARN_LOG(GDROM, "This track seems truncated. Skipping...");
// FIXME that can't be right
std::fseek(fsource, track.total_length, SEEK_CUR);
}
else
{
std::fseek(fsource, track.total_length * track.sector_size, SEEK_CUR);
rv->EndFAD = track.start_lba + track.total_length - 1;
}
track.position = std::ftell(fsource);
std::fseek(fsource, image.header_position, SEEK_SET);
image.remaining_tracks--;
}
}
CDI_skip_next_session(fsource, &image);
image.remaining_sessions--;
}
if (digest != nullptr)
*digest = MD5Sum().add(fsource).getDigest();
std::fclose(fsource);
rv->type=GuessDiscType(CD_M1,CD_M2,CD_DA);
rv->LeadOut.StartFAD = rv->EndFAD;
rv->LeadOut.ADR = 1;
rv->LeadOut.CTRL = 4;
return rv.release();
} catch (...) {
std::fclose(fsource);
throw;
}
if (digest != nullptr)
*digest = MD5Sum().add(fsource).getDigest();
std::fclose(fsource);
rv->type=GuessDiscType(CD_M1,CD_M2,CD_DA);
rv->LeadOut.StartFAD = rv->EndFAD;
rv->LeadOut.ADR = 1;
rv->LeadOut.CTRL = 4;
return rv;
}
+2 -1
View File
@@ -233,7 +233,8 @@ struct RawTrackFile : TrackFile
*sector_type=SECFMT_2448_MODE2;
else
{
verify(false);
WARN_LOG(GDROM, "Unsupported sector size %d", fmt);
return false;
}
std::fseek(file, offset + FAD * fmt, SEEK_SET);
+6 -4
View File
@@ -21,6 +21,7 @@
#include "cfg/option.h"
#include "gamepad_device.h"
#include "ui/gui.h"
#include "hw/maple/maple_devs.h"
#include <memory>
extern u8 kb_key[4][6]; // normal keys pressed
@@ -82,11 +83,12 @@ protected:
void input(u8 keycode, bool pressed, int modifier_keys)
{
const int port = maple_port();
const bool portIsValid = port >= 0 && port < MAPLE_PORTS;
// Checking if we're in-game with an emulated keyboard first,
// then check if the "Bypass Emulated Keyboard" hotkey is held and we're not in GUI,
// if so: send emu hotkeys only and return earlier.
if ((settings.platform.isConsole() && config::MapleMainDevices[port] == MDT_Keyboard)
if ((settings.platform.isConsole() && portIsValid && config::MapleMainDevices[port] == MDT_Keyboard)
|| (settings.platform.isArcade() && settings.input.keyboardGame))
{
if (keycode == input_mapper->get_button_code(0, EMU_BTN_BYPASS_KB))
@@ -126,13 +128,13 @@ protected:
default:
break;
}
if (port >= 0 && port < (int)std::size(kb_shift))
if (portIsValid)
kb_shift[port] = _modifier_keys;
if (keycode != 0)
{
gui_keyboard_key(keycode, pressed);
if (keycode < 0xE0 && port >= 0 && port < (int)std::size(kb_key))
if (keycode < 0xE0 && portIsValid)
{
if (pressed)
{
@@ -176,7 +178,7 @@ protected:
// or the corresponding maple device (if any) isn't a keyboard
else if (gui_is_open()
|| port == (int)std::size(kb_key)
|| (settings.platform.isConsole() && config::MapleMainDevices[port] != MDT_Keyboard)
|| (settings.platform.isConsole() && (!portIsValid || config::MapleMainDevices[port] != MDT_Keyboard))
|| (settings.platform.isArcade() && !settings.input.keyboardGame))
gamepad_btn_input(keycode, pressed);
}
+39 -7
View File
@@ -756,11 +756,19 @@ static int modem_set_speed(pico_device *dev, uint32_t speed)
return 0;
}
static uint32_t dns_query_start;
static uint32_t dns_query_attempts;
static void reset_dns_entries()
{
dns_query_attempts = 0;
dns_query_start = 0;
public_ip.addr = 0;
afo_ip.addr = 0;
}
static void check_dns_entries()
{
static uint32_t dns_query_start = 0;
static uint32_t dns_query_attempts = 0;
if (public_ip.addr == 0)
{
u32 ip;
@@ -787,6 +795,8 @@ static void check_dns_entries()
{
public_ip.addr = 0xffffffff; // Bogus but not null
dns_query_attempts = 0;
dns_query_start = 0;
WARN_LOG(MODEM, "Can't resolve my IP");
}
else
// Retry
@@ -796,7 +806,7 @@ static void check_dns_entries()
}
else if (afo_ip.addr == 0)
{
if (!dns_query_start)
if (dns_query_start == 0)
{
dns_query_start = PICO_TIME_MS();
get_host_by_name("auriga.segasoft.com", dnsaddr); // Alien Front Online server
@@ -821,6 +831,7 @@ static void check_dns_entries()
pico_string_to_ipv4("146.185.135.179", &addr); // Default address
memcpy(&afo_ip.addr, &addr, sizeof(addr));
dns_query_attempts = 0;
WARN_LOG(MODEM, "Can't resolve auriga.segasoft.com. Using default 146.185.135.179");
}
else
// Retry
@@ -995,12 +1006,33 @@ static void *pico_thread_func(void *)
out_buffer_lock.unlock();
}
u32 addr;
pico_string_to_ipv4(config::DNS.get().c_str(), &addr);
memcpy(&dnsaddr.addr, &addr, sizeof(addr));
// Find DNS ip address
{
std::string dnsName = config::DNS;
if (dnsName == "46.101.91.123")
// override legacy default with current one
dnsName = "dns.flyca.st";
hostent *hp = gethostbyname(dnsName.c_str());
if (hp != nullptr && hp->h_length > 0)
{
memcpy(&dnsaddr.addr, hp->h_addr_list[0], sizeof(dnsaddr.addr));
char s[17];
pico_ipv4_to_string(s, dnsaddr.addr);
NOTICE_LOG(MODEM, "%s IP is %s", dnsName.c_str(), s);
}
else
{
u32 addr;
pico_string_to_ipv4("46.101.91.123", &addr);
dnsaddr.addr = addr;
WARN_LOG(MODEM, "Can't resolve dns.flyca.st. Using default 46.101.91.123");
}
}
reset_dns_entries();
// Create ppp/eth device
const bool usingPPP = !config::EmulateBBA;
u32 addr;
if (usingPPP)
{
// PPP
+5 -3
View File
@@ -41,7 +41,8 @@ CustomStorage& customStorage()
std::string getSubPath(const std::string& reference, const std::string& relative) override { die("Not implemented"); }
FileInfo getFileInfo(const std::string& path) override { die("Not implemented"); }
bool exists(const std::string& path) override { die("Not implemented"); }
bool addStorage(bool isDirectory, bool writeAccess, void (*callback)(bool cancelled, std::string selectedPath)) override {
bool addStorage(bool isDirectory, bool writeAccess, const std::string& description,
void (*callback)(bool cancelled, std::string selectedPath)) override {
die("Not implemented");
}
};
@@ -392,9 +393,10 @@ AllStorage& storage()
return storage;
}
bool addStorage(bool isDirectory, bool writeAccess, void (*callback)(bool cancelled, std::string selectedPath))
bool addStorage(bool isDirectory, bool writeAccess, const std::string& description,
void (*callback)(bool cancelled, std::string selectedPath))
{
return customStorage().addStorage(isDirectory, writeAccess, callback);
return customStorage().addStorage(isDirectory, writeAccess, description, callback);
}
}
+4 -2
View File
@@ -64,7 +64,8 @@ public:
class CustomStorage : public Storage
{
public:
virtual bool addStorage(bool isDirectory, bool writeAccess, void (*callback)(bool cancelled, std::string selectedPath)) = 0;
virtual bool addStorage(bool isDirectory, bool writeAccess, const std::string& description,
void (*callback)(bool cancelled, std::string selectedPath)) = 0;
};
class AllStorage : public Storage
@@ -82,7 +83,8 @@ public:
};
AllStorage& storage();
bool addStorage(bool isDirectory, bool writeAccess, void (*callback)(bool cancelled, std::string selectedPath));
bool addStorage(bool isDirectory, bool writeAccess, const std::string& description,
void (*callback)(bool cancelled, std::string selectedPath));
// iterate depth-first over the files contained in a folder hierarchy
class DirectoryTree
+5 -4
View File
@@ -50,6 +50,7 @@ using namespace vixl::aarch32;
#include "cfg/option.h"
#include "arm_unwind.h"
#include "oslib/virtmem.h"
#include "emulator.h"
//#define CANONICALTEST
@@ -428,8 +429,8 @@ public:
generate_mainloop();
::mainloop(context);
if (restarting)
p_sh4rcb->cntx.CpuRunning = 1;
if (restarting && !emu.restartCpu())
restarting = false;
} while (restarting);
}
@@ -1933,8 +1934,8 @@ void Arm32Assembler::compileOp(RuntimeBlockInfo* block, shil_opcode* op, bool op
case shop_fsrra:
Vmov(s1, 1.f);
Vdiv(s0, s1, reg.mapFReg(op->rs1));
Vsqrt(reg.mapFReg(op->rd), s0);
Vsqrt(s0, reg.mapFReg(op->rs1));
Vdiv(reg.mapFReg(op->rd), s1, s0);
break;
case shop_fsetgt:
+6 -5
View File
@@ -35,14 +35,15 @@ using namespace vixl::aarch64;
#include "hw/sh4/sh4_mmr.h"
#include "hw/sh4/sh4_interrupts.h"
#include "arm64_unwind.h"
#include "hw/sh4/sh4_core.h"
#include "hw/sh4/dyna/ngen.h"
#include "hw/sh4/sh4_mem.h"
#include "hw/sh4/sh4_rom.h"
#include "arm64_regalloc.h"
#include "hw/mem/addrspace.h"
#include "arm64_unwind.h"
#include "oslib/virtmem.h"
#include "emulator.h"
#undef do_sqw_nommu
@@ -879,9 +880,9 @@ public:
break;
case shop_fsrra:
Fsqrt(s0, regalloc.MapVRegister(op.rs1));
Fmov(s1, 1.f);
Fdiv(s0, s1, regalloc.MapVRegister(op.rs1));
Fsqrt(regalloc.MapVRegister(op.rd), s0);
Fdiv(regalloc.MapVRegister(op.rd), s1, s0);
break;
case shop_fsetgt:
@@ -2213,8 +2214,8 @@ public:
generate_mainloop();
::mainloop(v_cntx);
if (restarting)
p_sh4rcb->cntx.CpuRunning = 1;
if (restarting && !emu.restartCpu())
restarting = false;
} while (restarting);
} catch (const SH4ThrownException&) {
ERROR_LOG(DYNAREC, "SH4ThrownException in mainloop");
+4 -4
View File
@@ -537,18 +537,18 @@ protected:
break;
case shop_fsrra:
sqrtss(xmm0, mapXRegister(op.rs1));
if (ArchX64)
{
mov(eax, 0x3f800000); // 1.0
movd(xmm0, eax);
movd(mapXRegister(op.rd), eax);
}
else
{
static float one = 1.f;
movss(xmm0, dword[&one]);
movss(mapXRegister(op.rd), dword[&one]);
}
divss(xmm0, mapXRegister(op.rs1));
sqrtss(mapXRegister(op.rd), xmm0);
divss(mapXRegister(op.rd), xmm0);
break;
case shop_fsetgt:
+3 -2
View File
@@ -27,6 +27,7 @@
#include "hw/sh4/sh4_mem.h"
#include "hw/mem/addrspace.h"
#include "oslib/unwind_info.h"
#include "emulator.h"
static void (*mainloop)();
static void (*ngen_FailedToFindBlock_)();
@@ -900,8 +901,8 @@ public:
generate_mainloop();
::mainloop();
if (restarting)
p_sh4rcb->cntx.CpuRunning = 1;
if (restarting && !emu.restartCpu())
restarting = false;
} while (restarting);
} catch (const SH4ThrownException& e) {
ERROR_LOG(DYNAREC, "SH4ThrownException in mainloop %x pc %x", e.expEvn, e.epc);
+5 -7
View File
@@ -985,7 +985,7 @@ public:
template<typename T>
void write(T pixel) {
pvr_write32p(dstAddr, pixel);
pvr_write32p<T, true>(dstAddr, pixel);
dstAddr += sizeof(T);
}
@@ -1173,14 +1173,12 @@ public:
void write(int xmin, int xmax, const u8 *& pixel, int y)
{
for (int c = xmin; c < xmax - 3; c += 4)
for (int c = xmin; c < xmax; c++)
{
pixWriter.write((u32)((pixel[Blue + 4] << 24) | (pixel[Red] << 16) | (pixel[Green] << 8) | pixel[Blue]));
pixWriter.write(pixel[Blue]);
pixWriter.write(pixel[Green]);
pixWriter.write(pixel[Red]);
pixel += 4;
pixWriter.write((u32)((pixel[Green + 4] << 24) | (pixel[Blue + 4] << 16) | (pixel[Red] << 8) | pixel[Green]));
pixel += 4;
pixWriter.write((u32)((pixel[Red + 4] << 24) | (pixel[Green + 4] << 16) | (pixel[Blue + 4] << 8) | pixel[Red]));
pixel += 8;
}
}
+3 -1
View File
@@ -131,7 +131,9 @@ VertexOut main(in VertexIn vin)
#endif
vo.index = uint(polyNumber) + vin.vertexId;
#endif
#if MODIFIER_VOLUME == 1
vo.pos.z = min(vo.pos.z, -0.001f);
#endif
vo.pos = mul(projMat, vo.pos);
vo.pos = float4(vo.pos.xy / vo.pos.w, 1.f / vo.pos.w, 1.f);
+35 -19
View File
@@ -326,6 +326,27 @@ void DX11Renderer::Process(TA_context* ctx)
ta_parse(ctx, true);
}
void DX11Renderer::resetContextState()
{
// Reset device context state. Very much needed for libretro where current state is unknown.
deviceContext->OMSetRenderTargetsAndUnorderedAccessViews(0, nullptr, nullptr, 0, 0, nullptr, nullptr);
deviceContext->PSSetShader(nullptr, nullptr, 0);
deviceContext->GSSetShader(nullptr, nullptr, 0);
deviceContext->HSSetShader(nullptr, nullptr, 0);
deviceContext->DSSetShader(nullptr, nullptr, 0);
deviceContext->CSSetShader(nullptr, nullptr, 0);
deviceContext->VSSetShader(nullptr, nullptr, 0);
ID3D11ShaderResourceView *nullview[D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT] {};
deviceContext->CSSetShaderResources(0, D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT, nullview);
deviceContext->DSSetShaderResources(0, D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT, nullview);
deviceContext->GSSetShaderResources(0, D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT, nullview);
deviceContext->HSSetShaderResources(0, D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT, nullview);
deviceContext->PSSetShaderResources(0, D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT, nullview);
deviceContext->VSSetShaderResources(0, D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT, nullview);
deviceContext->SetPredication(nullptr, false);
deviceContext->SOSetTargets(0, nullptr, nullptr);
}
void DX11Renderer::configVertexShader()
{
matrices.CalcMatrices(&pvrrc, width, height);
@@ -359,10 +380,6 @@ void DX11Renderer::configVertexShader()
memcpy(mappedSubres.pData, &constant, sizeof(constant));
deviceContext->Unmap(vtxConstants, 0);
deviceContext->VSSetConstantBuffers(0, 1, &vtxConstants.get());
deviceContext->GSSetShader(nullptr, nullptr, 0);
deviceContext->HSSetShader(nullptr, nullptr, 0);
deviceContext->DSSetShader(nullptr, nullptr, 0);
deviceContext->CSSetShader(nullptr, nullptr, 0);
}
void DX11Renderer::uploadGeometryBuffers()
@@ -454,9 +471,7 @@ void DX11Renderer::setupPixelShaderConstants()
bool DX11Renderer::Render()
{
// make sure to unbind the framebuffer view before setting it as render target
ID3D11ShaderResourceView *nullView = nullptr;
deviceContext->PSSetShaderResources(0, 1, &nullView);
resetContextState();
bool is_rtt = pvrrc.isRTT;
if (!is_rtt)
{
@@ -1201,26 +1216,27 @@ void DX11Renderer::readRttRenderTarget(u32 texAddress)
void DX11Renderer::updatePaletteTexture()
{
if (!palette_updated)
return;
palette_updated = false;
deviceContext->UpdateSubresource(paletteTexture, 0, nullptr, palette32_ram, 32 * sizeof(u32), 32 * sizeof(u32) * 32);
if (palette_updated)
{
palette_updated = false;
deviceContext->UpdateSubresource(paletteTexture, 0, nullptr, palette32_ram, 32 * sizeof(u32), 32 * sizeof(u32) * 32);
}
deviceContext->PSSetShaderResources(1, 1, &paletteTextureView.get());
deviceContext->PSSetSamplers(1, 1, &samplers->getSampler(false).get());
}
void DX11Renderer::updateFogTexture()
{
if (!fog_needs_update || !config::Fog)
if (!config::Fog)
return;
fog_needs_update = false;
u8 temp_tex_buffer[256];
MakeFogTexture(temp_tex_buffer);
deviceContext->UpdateSubresource(fogTexture, 0, nullptr, temp_tex_buffer, 128, 128 * 2);
if (fog_needs_update)
{
fog_needs_update = false;
u8 temp_tex_buffer[256];
MakeFogTexture(temp_tex_buffer);
deviceContext->UpdateSubresource(fogTexture, 0, nullptr, temp_tex_buffer, 128, 128 * 2);
}
deviceContext->PSSetShaderResources(2, 1, &fogTextureView.get());
deviceContext->PSSetSamplers(2, 1, &samplers->getSampler(true).get());
}
+3
View File
@@ -46,6 +46,8 @@ struct DX11Renderer : public Renderer
frameRendered = false;
#ifndef LIBRETRO
imguiDriver->setFrameRendered();
#else
theDX11Context.present();
#endif
return true;
}
@@ -99,6 +101,7 @@ protected:
virtual void setRTTSize(int width, int height) {}
void writeFramebufferToVRAM();
void renderVideoRouting();
void resetContextState();
ComPtr<ID3D11Device> device;
ComPtr<ID3D11DeviceContext> deviceContext;
+6
View File
@@ -460,6 +460,7 @@ enum VertexMacroEnum {
MacroPositionOnly,
MacroTwoVolumes,
MacroLightOn,
MacroModifierVolume,
};
static D3D_SHADER_MACRO VertexMacros[]
@@ -469,6 +470,7 @@ static D3D_SHADER_MACRO VertexMacros[]
{ "POSITION_ONLY", "0" },
{ "pp_TwoVolumes", "0" },
{ "LIGHT_ON", "1" },
{ "MODIFIER_VOLUME", "0" },
{ nullptr, nullptr }
};
@@ -573,6 +575,7 @@ const ComPtr<ID3D11VertexShader>& DX11Shaders::getVertexShader(bool gouraud, boo
VertexMacros[MacroPositionOnly].Definition = MacroValues[false];
VertexMacros[MacroTwoVolumes].Definition = MacroValues[false];
VertexMacros[MacroLightOn].Definition = MacroValues[true];
VertexMacros[MacroModifierVolume].Definition = MacroValues[false];
std::string source(DX11N2VertexShader);
source += std::string("\n") + DX11N2ColorShader;
vertexShader = compileVS(source.c_str(), "main", VertexMacros);
@@ -599,6 +602,7 @@ const ComPtr<ID3D11VertexShader>& DX11Shaders::getMVVertexShader(bool naomi2)
VertexMacros[MacroPositionOnly].Definition = MacroValues[true];
VertexMacros[MacroTwoVolumes].Definition = MacroValues[false];
VertexMacros[MacroLightOn].Definition = MacroValues[false];
VertexMacros[MacroModifierVolume].Definition = MacroValues[true];
modVolVertexShaders[index] = compileVS(DX11N2VertexShader, "main", VertexMacros);
}
}
@@ -689,6 +693,7 @@ ComPtr<ID3DBlob> DX11Shaders::getVertexShaderBlob()
// FIXME code dup
VertexMacros[MacroPositionOnly].Definition = MacroValues[false];
VertexMacros[MacroTwoVolumes].Definition = MacroValues[false];
VertexMacros[MacroModifierVolume].Definition = MacroValues[false];
std::string source(DX11N2VertexShader);
source += std::string("\n") + DX11N2ColorShader;
return compileShader(source.c_str(), "main", "vs_4_0", VertexMacros);
@@ -700,6 +705,7 @@ ComPtr<ID3DBlob> DX11Shaders::getMVVertexShaderBlob()
VertexMacros[MacroGouraud].Definition = MacroValues[false];
VertexMacros[MacroPositionOnly].Definition = MacroValues[true];
VertexMacros[MacroTwoVolumes].Definition = MacroValues[false];
VertexMacros[MacroModifierVolume].Definition = MacroValues[true];
return compileShader(DX11N2VertexShader, "main", "vs_4_0", VertexMacros);
}
+7 -1
View File
@@ -132,6 +132,8 @@ ComPtr<ID3D11RenderTargetView>& DX11Context::getRenderTarget(int width, int heig
void DX11Context::presentFrame(ComPtr<ID3D11ShaderResourceView>& textureView, int width, int height)
{
ID3D11ShaderResourceView *nullSRView = nullptr;
pDeviceContext->PSSetShaderResources(0, 1, &nullSRView);
ComPtr<ID3D11RenderTargetView> renderTarget = getRenderTarget(width, height);
pDeviceContext->OMSetRenderTargets(1, &renderTarget.get(), nullptr);
@@ -160,7 +162,11 @@ void DX11Context::presentFrame(ComPtr<ID3D11ShaderResourceView>& textureView, in
ID3D11RenderTargetView *nullView = nullptr;
pDeviceContext->OMSetRenderTargets(1, &nullView, nullptr);
pDeviceContext->PSSetShaderResources(0, 1, &this->textureView.get());
}
void DX11Context::present()
{
pDeviceContext->PSSetShaderResources(0, 1, &textureView.get());
}
#endif
+1
View File
@@ -35,6 +35,7 @@ class DX11Context : public GraphicsContext
public:
bool init(ID3D11Device *device, ID3D11DeviceContext *deviceContext, pD3DCompile D3DCompile, D3D_FEATURE_LEVEL featureLevel);
void term() override;
void present();
const ComPtr<ID3D11Device>& getDevice() const { return pDevice; }
const ComPtr<ID3D11DeviceContext>& getDeviceContext() const { return pDeviceContext; }
+1
View File
@@ -649,6 +649,7 @@ struct DX11OITRenderer : public DX11Renderer
bool Render() override
{
resetContextState();
bool is_rtt = pvrrc.isRTT;
if (!is_rtt)
+6
View File
@@ -781,6 +781,7 @@ enum VertexMacroEnum {
MacroDivPosZ,
MacroPositionOnly,
MacroLightOn,
MacroModifierVolume,
};
static D3D_SHADER_MACRO VertexMacros[]
@@ -790,6 +791,7 @@ static D3D_SHADER_MACRO VertexMacros[]
{ "DIV_POS_Z", "0" },
{ "POSITION_ONLY", "0" },
{ "LIGHT_ON", "1" },
{ "MODIFIER_VOLUME", "0" },
{ nullptr, nullptr }
};
@@ -900,6 +902,7 @@ const ComPtr<ID3D11VertexShader>& DX11OITShaders::getVertexShader(bool gouraud,
VertexMacros[MacroPositionOnly].Definition = MacroValues[positionOnly];
VertexMacros[MacroTwoVolumes].Definition = MacroValues[twoVolumes];
VertexMacros[MacroLightOn].Definition = MacroValues[lightOn];
VertexMacros[MacroModifierVolume].Definition = MacroValues[false];
std::string source(DX11N2VertexShader);
if (!positionOnly && lightOn)
source += std::string("\n") + DX11N2ColorShader;
@@ -927,6 +930,7 @@ const ComPtr<ID3D11VertexShader>& DX11OITShaders::getMVVertexShader(bool naomi2)
VertexMacros[MacroPositionOnly].Definition = MacroValues[true];
VertexMacros[MacroTwoVolumes].Definition = MacroValues[false];
VertexMacros[MacroLightOn].Definition = MacroValues[false];
VertexMacros[MacroModifierVolume].Definition = MacroValues[true];
mvVertexShader = compileVS(DX11N2VertexShader, "main", VertexMacros);
}
}
@@ -1058,6 +1062,7 @@ ComPtr<ID3DBlob> DX11OITShaders::getVertexShaderBlob()
// FIXME code dup
VertexMacros[MacroPositionOnly].Definition = MacroValues[false];
VertexMacros[MacroTwoVolumes].Definition = MacroValues[true];
VertexMacros[MacroModifierVolume].Definition = MacroValues[false];
std::string source(DX11N2VertexShader);
source += std::string("\n") + DX11N2ColorShader;
return compileShader(source.c_str(), "main", "vs_5_0", VertexMacros);
@@ -1069,6 +1074,7 @@ ComPtr<ID3DBlob> DX11OITShaders::getMVVertexShaderBlob()
VertexMacros[MacroGouraud].Definition = MacroValues[false];
VertexMacros[MacroPositionOnly].Definition = MacroValues[true];
VertexMacros[MacroTwoVolumes].Definition = MacroValues[false];
VertexMacros[MacroModifierVolume].Definition = MacroValues[true];
return compileShader(DX11N2VertexShader, "main", "vs_5_0", VertexMacros);
}
+1
View File
@@ -34,6 +34,7 @@ N2Vertex4Source::N2Vertex4Source(const gl4PipelineShader* shader) : OpenGl4Sourc
{
addConstant("OIT_RENDER");
addConstant("DIV_POS_Z", false);
addConstant("MODIFIER_VOLUME", 0);
if (shader == nullptr)
{
addConstant("POSITION_ONLY", 1);
+1
View File
@@ -642,6 +642,7 @@ static void create_modvol_shader()
gl4.modvol_shader.ndcMat = glGetUniformLocation(gl4.modvol_shader.program, "ndcMat");
N2Vertex4Source n2VertexShader;
n2VertexShader.setConstant("MODIFIER_VOLUME", true);
fragmentShader.setConstant("DIV_POS_Z", false);
gl4.n2ModVolShader.program = gl_CompileAndLink(n2VertexShader.generate().c_str(), fragmentShader.generate().c_str());
gl4.n2ModVolShader.ndcMat = glGetUniformLocation(gl4.n2ModVolShader.program, "ndcMat");
+2 -1
View File
@@ -1175,7 +1175,8 @@ void OpenGLRenderer::DrawOSD(bool clear_screen)
glDrawArrays(GL_TRIANGLE_STRIP, i * 4, 4);
glCheck();
imguiDriver->setFrameRendered();
if (clear_screen)
imguiDriver->setFrameRendered();
}
#endif
#endif
+8 -4
View File
@@ -121,6 +121,9 @@ void main()
computeEnvMap(vtx_uv.xy, vpos.xyz, vnorm);
#endif
#endif
#if MODIFIER_VOLUME == 1
vpos.z = min(vpos.z, -0.001);
#endif
vpos = projMat * vpos;
wDivide(vpos);
@@ -348,17 +351,18 @@ void computeBumpMap(inout vec4 color0, vec4 color1, vec3 position, vec3 normal,
)";
N2VertexSource::N2VertexSource(bool gouraud, bool geometryOnly, bool texture) : OpenGlSource()
N2VertexSource::N2VertexSource(bool gouraud, bool modifierVolume, bool texture) : OpenGlSource()
{
addConstant("pp_Gouraud", gouraud);
addConstant("POSITION_ONLY", geometryOnly);
addConstant("pp_Gouraud", (int)gouraud);
addConstant("POSITION_ONLY", (int)modifierVolume);
addConstant("pp_TwoVolumes", 0);
addConstant("pp_Texture", (int)texture);
addConstant("LIGHT_ON", 1);
addConstant("MODIFIER_VOLUME", (int)modifierVolume);
addSource(VertexCompatShader);
addSource(GouraudSource);
if (!geometryOnly)
if (!modifierVolume)
addSource(N2ColorShader);
addSource(N2VertexShader);
}
+1 -1
View File
@@ -22,7 +22,7 @@
class N2VertexSource : public OpenGlSource
{
public:
N2VertexSource(bool gouraud, bool geometryOnly, bool texture);
N2VertexSource(bool gouraud, bool modifierVolume, bool texture);
};
template<typename ShaderType>
+5 -2
View File
@@ -26,10 +26,12 @@
#include <stb_image.h>
#ifndef LIBRETRO
#define OSD_TEX_W 512
#define OSD_TEX_H 256
#if defined(__ANDROID__) && !defined(LIBRETRO)
#ifdef __ANDROID__
extern float vjoy_pos[15][8];
#else
@@ -74,7 +76,7 @@ static void DrawButton(const float xy[8], u32 state)
OSDVertex vtx;
vtx.r = vtx.g = vtx.b = (0x7F - 0x40 * state / 255) * vjoy_pos[VJOY_VISIBLE][0];
vtx.a = 0xA0 * vjoy_pos[VJOY_VISIBLE][4];
vtx.a = (100 - config::VirtualGamepadTransparency) * 0xff / 100 * vjoy_pos[VJOY_VISIBLE][4];
vjoy_pos[VJOY_VISIBLE][4] += (vjoy_pos[VJOY_VISIBLE][0] - vjoy_pos[VJOY_VISIBLE][4]) / 2;
vtx.x = xy[0]; vtx.y = xy[1];
@@ -172,6 +174,7 @@ u8 *loadOSDButtons(int &width, int &height)
}
return image_data;
}
#endif // LIBRETRO
u32 vmu_lcd_data[8][48 * 32];
bool vmu_lcd_status[8];
+9 -1
View File
@@ -54,11 +54,19 @@ static inline TileClipping GetTileClip(u32 val, const glm::mat4& viewport, int *
csy = csy * 32;
cey = (cey + 1) * 32;
if (csx <= 0 && csy <= 0 && cex >= 640 && cey >= 480 && tileClippingMode == TileClipping::Outside)
if (csx == 0 && csy == 0 && cex >= 640 && cey >= 480)
return TileClipping::Off;
if (!pvrrc.isRTT)
{
if (tileClippingMode == TileClipping::Outside && !config::EmulateFramebuffer)
{
// Intersect with framebuffer clipping
csx = std::max<float>(csx, pvrrc.fb_X_CLIP.min);
csy = std::max<float>(csy, pvrrc.fb_Y_CLIP.min);
cex = std::min<float>(cex, pvrrc.fb_X_CLIP.max + 1);
cey = std::min<float>(cey, pvrrc.fb_Y_CLIP.max + 1);
}
glm::vec4 clip_start(csx, csy, 0, 1);
glm::vec4 clip_end(cex, cey, 0, 1);
clip_start = viewport * clip_start;
+9 -9
View File
@@ -290,14 +290,14 @@ inline static void getVideoShift(float& x, float& y)
switch (SPG_LOAD.hcount)
{
case 857: // NTSC, VGA
x = VO_STARTX.HStart - (vga ? 0xa8 : 0xa4);
x = (int)VO_STARTX.HStart - (vga ? 0xa8 : 0xa4);
break;
case 863: // PAL
x = VO_STARTX.HStart - 0xae;
x = (int)VO_STARTX.HStart - 0xae;
break;
case 851: // Naomi
case 850: // meltyb
x = VO_STARTX.HStart - 0xa5; // a0 for 15 kHz
x = (int)VO_STARTX.HStart - 0xa5; // a0 for 15 kHz
break;
default:
x = 0;
@@ -307,24 +307,24 @@ inline static void getVideoShift(float& x, float& y)
switch (SPG_LOAD.vcount)
{
case 524: // NTSC, VGA
y = VO_STARTY.VStart_field1 - (vga ? 0x28 : 0x12);
y = (int)VO_STARTY.VStart_field1 - (vga ? 0x28 : 0x12);
break;
case 262: // NTSC 240p
y = VO_STARTY.VStart_field1 - 0x11;
y = (int)VO_STARTY.VStart_field1 - 0x11;
break;
case 624: // PAL
y = VO_STARTY.VStart_field1 - 0x2d;
y = (int)VO_STARTY.VStart_field1 - 0x2d;
break;
case 312: // PAL 240p
y = VO_STARTY.VStart_field1 - 0x2e;
y = (int)VO_STARTY.VStart_field1 - 0x2e;
break;
case 529: // Naomi 31 kHz
case 528: // meltyb
y = VO_STARTY.VStart_field1 - 0x24;
y = (int)VO_STARTY.VStart_field1 - 0x24;
break;
case 536: // Naomi 15 kHz 480i
case 268: // Naomi 15 kHz 240p
y = VO_STARTY.VStart_field1 - 0x17; // 16 for 240p
y = (int)VO_STARTY.VStart_field1 - 0x17; // 16 for 240p
break;
default:
y = 0;
+2 -1
View File
@@ -26,6 +26,7 @@
using namespace nlohmann;
#include "archive/ZipArchive.h"
#include "oslib/directory.h"
#include "oslib/storage.h"
#include "stdclass.h"
std::string getNativeLibraryPath();
@@ -147,7 +148,7 @@ bool getCustomGpuDriverInfo(std::string& name, std::string& description, std::st
void uploadCustomGpuDriver(const std::string& zipPath)
{
FILE *zipf = nowide::fopen(zipPath.c_str(), "rb");
FILE *zipf = hostfs::storage().openFile(zipPath, "r");
if (zipf == nullptr)
throw FlycastException("Can't open zip file");
ZipArchive archive;
+36 -6
View File
@@ -82,6 +82,9 @@ void BaseDrawer::SetBaseScissor(const vk::Extent2D& viewport)
void BaseDrawer::scaleAndWriteFramebuffer(vk::CommandBuffer commandBuffer, FramebufferAttachment *finalFB)
{
static const float scopeColor[4] = { 0.25f, 0.25f, 0.25f, 0.25f };
CommandBufferDebugScope _(commandBuffer, "scaleAndWriteFramebuffer", scopeColor);
u32 width = (pvrrc.ta_GLOB_TILE_CLIP.tile_x_num + 1) * 32;
u32 height = (pvrrc.ta_GLOB_TILE_CLIP.tile_y_num + 1) * 32;
@@ -162,6 +165,9 @@ void BaseDrawer::scaleAndWriteFramebuffer(vk::CommandBuffer commandBuffer, Frame
void Drawer::DrawPoly(const vk::CommandBuffer& cmdBuffer, u32 listType, bool sortTriangles, const PolyParam& poly, u32 first, u32 count)
{
static const float scopeColor[4] = { 0.25f, 0.50f, 0.25f, 1.0f };
CommandBufferDebugScope _(cmdBuffer, "DrawPoly", scopeColor);
vk::Rect2D scissorRect;
TileClipping tileClip = SetTileClip(poly.tileclip, scissorRect);
if (tileClip == TileClipping::Outside)
@@ -194,7 +200,7 @@ void Drawer::DrawPoly(const vk::CommandBuffer& cmdBuffer, u32 listType, bool sor
if (tileClip == TileClipping::Inside || trilinearAlpha != 1.f || gpuPalette != 0)
{
std::array<float, 6> pushConstants = {
const std::array<float, 6> pushConstants = {
(float)scissorRect.offset.x,
(float)scissorRect.offset.y,
(float)scissorRect.offset.x + (float)scissorRect.extent.width,
@@ -239,6 +245,10 @@ void Drawer::DrawSorted(const vk::CommandBuffer& cmdBuffer, const std::vector<So
{
if (first == last)
return;
static const float scopeColor[4] = { 0.25f, 0.50f, 0.50f, 1.0f };
CommandBufferDebugScope _(cmdBuffer, "DrawSorted", scopeColor);
for (u32 idx = first; idx < last; idx++)
DrawPoly(cmdBuffer, ListType_Translucent, true, pvrrc.global_param_tr[polys[idx].polyIndex], polys[idx].first, polys[idx].count);
if (multipass && config::TranslucentPolygonDepthMask)
@@ -267,6 +277,10 @@ void Drawer::DrawList(const vk::CommandBuffer& cmdBuffer, u32 listType, bool sor
{
if (first == last)
return;
static const float scopeColor[4] = { 0.50f, 0.25f, 0.50f, 1.0f };
CommandBufferDebugScope _(cmdBuffer, "DrawList", scopeColor);
const PolyParam *pp_end = polys.data() + last;
for (const PolyParam *pp = &polys[first]; pp != pp_end; pp++)
if (pp->count > 2)
@@ -278,6 +292,9 @@ void Drawer::DrawModVols(const vk::CommandBuffer& cmdBuffer, int first, int coun
if (count == 0 || pvrrc.modtrig.empty() || !config::ModifierVolumes)
return;
static const float scopeColor[4] = { 0.75f, 0.25f, 0.25f, 1.0f };
CommandBufferDebugScope _(cmdBuffer, "DrawModVols", scopeColor);
cmdBuffer.bindVertexBuffers(0, curMainBuffer, offsets.modVolOffset);
SetScissor(cmdBuffer, baseScissor);
@@ -319,7 +336,7 @@ void Drawer::DrawModVols(const vk::CommandBuffer& cmdBuffer, int first, int coun
}
cmdBuffer.bindVertexBuffers(0, curMainBuffer, {0});
std::array<float, 5> pushConstants = { 1 - FPU_SHAD_SCALE.scale_factor / 256.f, 0, 0, 0, 0 };
const std::array<float, 6> pushConstants = { 1 - FPU_SHAD_SCALE.scale_factor / 256.f, 0, 0, 0, 0, 0 };
cmdBuffer.pushConstants<float>(pipelineManager->GetPipelineLayout(), vk::ShaderStageFlagBits::eFragment, 0, pushConstants);
pipeline = pipelineManager->GetModifierVolumePipeline(ModVolMode::Final, 0, false);
@@ -384,7 +401,18 @@ bool Drawer::Draw(const Texture *fogTexture, const Texture *paletteTexture)
vk::CommandBuffer cmdBuffer = BeginRenderPass();
setFirstProvokingVertex(pvrrc);
static const float scopeColor[4] = { 0.75f, 0.75f, 0.75f, 1.0f };
CommandBufferDebugScope _(cmdBuffer, "Draw", scopeColor);
if (VulkanContext::Instance()->hasProvokingVertex())
{
// Pipelines are using VK_EXT_provoking_vertex, no need to
// re-order vertices
}
else
{
setFirstProvokingVertex(pvrrc);
}
// Upload vertex and index buffers
VertexShaderUniforms vtxUniforms;
@@ -402,7 +430,7 @@ bool Drawer::Draw(const Texture *fogTexture, const Texture *paletteTexture)
cmdBuffer.bindIndexBuffer(curMainBuffer, offsets.indexOffset, vk::IndexType::eUint32);
// Make sure to push constants even if not used
std::array<float, 5> pushConstants = { 0, 0, 0, 0, 0 };
const std::array<float, 6> pushConstants = { 0, 0, 0, 0, 0, 0 };
cmdBuffer.pushConstants<float>(pipelineManager->GetPipelineLayout(), vk::ShaderStageFlagBits::eFragment, 0, pushConstants);
RenderPass previous_pass{};
@@ -491,13 +519,15 @@ vk::CommandBuffer TextureDrawer::BeginRenderPass()
}
textureCache->SetInFlight(texture);
constexpr vk::ImageUsageFlags imageUsage = vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eSampled;
if (!texture->image || texture->format != vk::Format::eR8G8B8A8Unorm
|| texture->extent.width != widthPow2 || texture->extent.height != heightPow2)
|| texture->extent.width != widthPow2 || texture->extent.height != heightPow2
|| (texture->usageFlags & imageUsage) != imageUsage)
{
texture->extent = vk::Extent2D(widthPow2, heightPow2);
texture->format = vk::Format::eR8G8B8A8Unorm;
texture->needsStaging = true;
texture->CreateImage(vk::ImageTiling::eOptimal, vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eSampled,
texture->CreateImage(vk::ImageTiling::eOptimal, imageUsage,
vk::ImageLayout::eUndefined, vk::ImageAspectFlagBits::eColor);
colorImageCurrentLayout = vk::ImageLayout::eUndefined;
}
+49 -23
View File
@@ -28,6 +28,9 @@
void OITDrawer::DrawPoly(const vk::CommandBuffer& cmdBuffer, u32 listType, bool autosort, Pass pass,
const PolyParam& poly, u32 first, u32 count)
{
static const float scopeColor[4] = { 0.25f, 0.50f, 0.25f, 1.0f };
CommandBufferDebugScope _(cmdBuffer, "DrawPoly(OIT)", scopeColor);
vk::Rect2D scissorRect;
TileClipping tileClip = SetTileClip(poly.tileclip, scissorRect);
if (tileClip == TileClipping::Outside)
@@ -133,6 +136,10 @@ void OITDrawer::DrawList(const vk::CommandBuffer& cmdBuffer, u32 listType, bool
{
if (first == last)
return;
static const float scopeColor[4] = { 0.50f, 0.25f, 0.50f, 1.0f };
CommandBufferDebugScope _(cmdBuffer, "DrawList(OIT)", scopeColor);
const PolyParam *pp_end = polys.data() + last;
for (const PolyParam *pp = &polys[first]; pp != pp_end; pp++)
if (pp->count > 2)
@@ -145,6 +152,9 @@ void OITDrawer::DrawModifierVolumes(const vk::CommandBuffer& cmdBuffer, int firs
if (count == 0 || pvrrc.modtrig.empty() || !config::ModifierVolumes)
return;
static const float scopeColor[4] = { 0.75f, 0.25f, 0.25f, 1.0f };
CommandBufferDebugScope _(cmdBuffer, "DrawModVols(OIT)", scopeColor);
cmdBuffer.bindVertexBuffers(0, curMainBuffer, offsets.modVolOffset);
SetScissor(cmdBuffer, baseScissor);
@@ -264,15 +274,23 @@ void OITDrawer::UploadMainBuffer(const OITDescriptorSets::VertexShaderUniforms&
vk::Framebuffer OITTextureDrawer::getFramebuffer(int renderPass, int renderPassCount)
{
if (renderPass < renderPassCount - 1)
return *tempFramebuffers[(renderPassCount - 1 - renderPass) % 2];
else
{
framebufferIndex = (renderPassCount - renderPass) % 2;
return *tempFramebuffers[framebufferIndex];
}
else {
framebufferIndex = 0;
return *framebuffer;
}
}
bool OITDrawer::Draw(const Texture *fogTexture, const Texture *paletteTexture)
{
vk::CommandBuffer cmdBuffer = NewFrame();
static const float scopeColor[4] = { 0.75f, 0.75f, 0.75f, 1.0f };
CommandBufferDebugScope _(cmdBuffer, "Draw(OIT)", scopeColor);
if (needAttachmentTransition)
{
needAttachmentTransition = false;
@@ -323,7 +341,15 @@ bool OITDrawer::Draw(const Texture *fogTexture, const Texture *paletteTexture)
bool firstFrameAfterInit = oitBuffers->isFirstFrameAfterInit();
oitBuffers->OnNewFrame(cmdBuffer);
setFirstProvokingVertex(pvrrc);
if (VulkanContext::Instance()->hasProvokingVertex())
{
// Pipelines are using VK_EXT_provoking_vertex, no need to
// re-order vertices
}
else
{
setFirstProvokingVertex(pvrrc);
}
// Upload vertex and index buffers
UploadMainBuffer(vtxUniforms, fragUniforms);
@@ -396,11 +422,21 @@ bool OITDrawer::Draw(const Texture *fogTexture, const Texture *paletteTexture)
DrawList(cmdBuffer, ListType_Punch_Through, false, Pass::Color, pvrrc.global_param_pt, previous_pass.pt_count, current_pass.pt_count);
// TR
if (current_pass.autosort)
if (firstFrameAfterInit)
{
if (!firstFrameAfterInit)
DrawList(cmdBuffer, ListType_Translucent, true, Pass::OIT, pvrrc.global_param_tr, previous_pass.tr_count, current_pass.tr_count);
// Clear abuffers
cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipelineManager->GetClearPipeline());
quadBuffer->Bind(cmdBuffer);
quadBuffer->Draw(cmdBuffer);
vk::MemoryBarrier memoryBarrier(vk::AccessFlagBits::eShaderWrite, vk::AccessFlagBits::eShaderRead);
cmdBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eFragmentShader, vk::PipelineStageFlagBits::eFragmentShader,
vk::DependencyFlagBits::eByRegion, memoryBarrier, nullptr, nullptr);
cmdBuffer.bindVertexBuffers(0, curMainBuffer, {0});
firstFrameAfterInit = false;
}
if (current_pass.autosort)
DrawList(cmdBuffer, ListType_Translucent, true, Pass::OIT, pvrrc.global_param_tr, previous_pass.tr_count, current_pass.tr_count);
else
DrawList(cmdBuffer, ListType_Translucent, false, Pass::Color, pvrrc.global_param_tr, previous_pass.tr_count, current_pass.tr_count);
@@ -416,21 +452,7 @@ bool OITDrawer::Draw(const Texture *fogTexture, const Texture *paletteTexture)
cmdBuffer.clearAttachments(vk::ClearAttachment(vk::ImageAspectFlagBits::eColor, 0, clear_colors[0]),
vk::ClearRect(viewport, 0, 1));
}
SetScissor(cmdBuffer, baseScissor);
if (firstFrameAfterInit)
{
// missing the transparent stuff on the first frame cuz I'm lazy
// Clear
cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipelineManager->GetClearPipeline());
quadBuffer->Bind(cmdBuffer);
quadBuffer->Draw(cmdBuffer);
vk::MemoryBarrier memoryBarrier(vk::AccessFlagBits::eShaderWrite, vk::AccessFlagBits::eShaderRead);
cmdBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eFragmentShader, vk::PipelineStageFlagBits::eFragmentShader,
vk::DependencyFlagBits::eByRegion, memoryBarrier, nullptr, nullptr);
firstFrameAfterInit = false;
}
// Tr modifier volumes
if (GetContext()->GetVendorID() != VulkanContext::VENDOR_QUALCOMM) // Adreno bug
{
@@ -440,6 +462,7 @@ bool OITDrawer::Draw(const Texture *fogTexture, const Texture *paletteTexture)
DrawModifierVolumes<true>(cmdBuffer, previous_pass.mvo_tr_count, current_pass.mvo_tr_count - previous_pass.mvo_tr_count, pvrrc.global_param_mvo_tr.data());
}
SetScissor(cmdBuffer, viewport);
vk::Pipeline pipeline = pipelineManager->GetFinalPipeline(dithering && finalPass);
cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline);
quadBuffer->Bind(cmdBuffer);
@@ -467,10 +490,11 @@ void OITDrawer::MakeBuffers(int width, int height, vk::ImageUsageFlags colorUsag
{
oitBuffers->Init(width, height);
if (width <= maxWidth && height <= maxHeight)
if (width <= maxWidth && height <= maxHeight && colorUsage == currentBufferUsage)
return;
maxWidth = std::max(maxWidth, width);
maxHeight = std::max(maxHeight, height);
currentBufferUsage = colorUsage;
for (auto& framebuffer : tempFramebuffers) {
if (framebuffer)
@@ -576,13 +600,15 @@ vk::CommandBuffer OITTextureDrawer::NewFrame()
}
textureCache->SetInFlight(texture);
constexpr vk::ImageUsageFlags imageUsage = vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eSampled;
if (!texture->image || texture->format != vk::Format::eR8G8B8A8Unorm
|| texture->extent.width != widthPow2 || texture->extent.height != heightPow2)
|| texture->extent.width != widthPow2 || texture->extent.height != heightPow2
|| (texture->usageFlags & imageUsage) != imageUsage)
{
texture->extent = vk::Extent2D(widthPow2, heightPow2);
texture->format = vk::Format::eR8G8B8A8Unorm;
texture->needsStaging = true;
texture->CreateImage(vk::ImageTiling::eOptimal, vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eSampled,
texture->CreateImage(vk::ImageTiling::eOptimal, imageUsage,
vk::ImageLayout::eUndefined, vk::ImageAspectFlagBits::eColor);
colorImageCurrentLayout = vk::ImageLayout::eUndefined;
}
+6 -5
View File
@@ -82,7 +82,7 @@ protected:
void MakeBuffers(int width, int height, vk::ImageUsageFlags colorUsage = {});
virtual vk::Framebuffer getFramebuffer(int renderPass, int renderPassCount) = 0;
virtual int getFramebufferIndex() { return 0; }
int getFramebufferIndex() { return framebufferIndex; }
vk::Rect2D viewport;
std::array<std::unique_ptr<FramebufferAttachment>, 2> colorAttachments;
@@ -92,6 +92,7 @@ protected:
std::vector<bool> clearNeeded;
int maxWidth = 0;
int maxHeight = 0;
int framebufferIndex = 0;
private:
void DrawPoly(const vk::CommandBuffer& cmdBuffer, u32 listType, bool autosort, Pass pass,
@@ -128,6 +129,7 @@ private:
OITDescriptorSets descriptorSets;
vk::Buffer curMainBuffer;
bool dithering = false;
vk::ImageUsageFlags currentBufferUsage {};
};
class OITScreenDrawer : public OITDrawer
@@ -136,9 +138,10 @@ public:
void Init(SamplerManager *samplerManager, OITShaderManager *shaderManager, OITBuffers *oitBuffers,
const vk::Extent2D& viewport)
{
if (!screenPipelineManager)
if (!screenPipelineManager) {
screenPipelineManager = std::make_unique<OITPipelineManager>();
screenPipelineManager->Init(shaderManager, oitBuffers);
screenPipelineManager->Init(shaderManager, oitBuffers);
}
OITDrawer::Init(samplerManager, screenPipelineManager.get(), oitBuffers);
MakeFramebuffers(viewport);
@@ -186,7 +189,6 @@ public:
protected:
vk::Framebuffer getFramebuffer(int renderPass, int renderPassCount) override;
int getFramebufferIndex() override { return framebufferIndex; }
private:
void MakeFramebuffers(const vk::Extent2D& viewport);
@@ -195,7 +197,6 @@ private:
bool frameRendered = false;
float aspectRatio = 0.f;
bool frameStarted = false;
int framebufferIndex = 0;
};
class OITTextureDrawer : public OITDrawer
+14 -3
View File
@@ -23,7 +23,7 @@
void OITPipelineManager::CreatePipeline(u32 listType, bool autosort, const PolyParam& pp, Pass pass, int gpuPalette)
{
vk::PipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo = GetMainVertexInputStateCreateInfo();
vk::PipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo = GetMainVertexInputStateCreateInfo(true, pp.isNaomi2());
// Input assembly state
vk::PipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateCreateInfo(vk::PipelineInputAssemblyStateCreateFlags(),
@@ -49,6 +49,17 @@ void OITPipelineManager::CreatePipeline(u32 listType, bool autosort, const PolyP
0.0f, // depthBiasSlopeFactor
1.0f // lineWidth
);
// Dreamcast uses the last vertex as the provoking vertex, but Vulkan uses the first.
// Utilize VK_EXT_provoking_vertex when available to set the provoking vertex to be the
// last vertex
vk::PipelineRasterizationProvokingVertexStateCreateInfoEXT provokingVertexInfo{};
if (GetContext()->hasProvokingVertex())
{
provokingVertexInfo.provokingVertexMode = vk::ProvokingVertexModeEXT::eLastVertex;
pipelineRasterizationStateCreateInfo.pNext = &provokingVertexInfo;
}
vk::PipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo;
// Depth and stencil
@@ -144,7 +155,7 @@ void OITPipelineManager::CreatePipeline(u32 listType, bool autosort, const PolyP
OITShaderManager::FragmentShaderParams params = {};
params.alphaTest = listType == ListType_Punch_Through;
params.bumpmap = pp.tcw.PixelFmt == PixelBumpMap;
params.clamping = pp.tsp.ColorClamp && (pvrrc.fog_clamp_min.full != 0 || pvrrc.fog_clamp_max.full != 0xffffffff);
params.clamping = pp.tsp.ColorClamp;
params.insideClipTest = (pp.tileclip >> 28) == 3;
params.fog = config::Fog ? pp.tsp.FogCtrl : 2;
params.gouraud = pp.pcw.Gouraud;
@@ -336,7 +347,7 @@ void OITPipelineManager::CreateClearPipeline()
&pipelineDynamicStateCreateInfo, // pDynamicState
*pipelineLayout, // layout
renderPasses->GetRenderPass(true, true), // renderPass
2 // subpass
1 // subpass
);
clearPipeline = GetContext()->GetDevice().createGraphicsPipelineUnique(GetContext()->GetPipelineCache(), graphicsPipelineCreateInfo).value;
+27 -10
View File
@@ -417,7 +417,7 @@ private:
return ((int)mode << 2) | cullMode | ((u32)naomi2 << 5) | ((u32)(!settings.platform.isNaomi2() && config::NativeDepthInterpolation) << 6);
}
vk::PipelineVertexInputStateCreateInfo GetMainVertexInputStateCreateInfo(bool full = true) const
vk::PipelineVertexInputStateCreateInfo GetMainVertexInputStateCreateInfo(bool full = true, bool naomi2 = false) const
{
// Vertex input state
static const vk::VertexInputBindingDescription vertexBindingDescriptions[] =
@@ -427,11 +427,11 @@ private:
static const vk::VertexInputAttributeDescription vertexInputAttributeDescriptions[] =
{
vk::VertexInputAttributeDescription(0, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, x)), // pos
vk::VertexInputAttributeDescription(1, 0, vk::Format::eR8G8B8A8Uint, offsetof(Vertex, col)), // base color
vk::VertexInputAttributeDescription(2, 0, vk::Format::eR8G8B8A8Uint, offsetof(Vertex, spc)), // offset color
vk::VertexInputAttributeDescription(1, 0, vk::Format::eR8G8B8A8Unorm, offsetof(Vertex, col)), // base color
vk::VertexInputAttributeDescription(2, 0, vk::Format::eR8G8B8A8Unorm, offsetof(Vertex, spc)), // offset color
vk::VertexInputAttributeDescription(3, 0, vk::Format::eR32G32Sfloat, offsetof(Vertex, u)), // tex coord
vk::VertexInputAttributeDescription(4, 0, vk::Format::eR8G8B8A8Uint, offsetof(Vertex, col1)), // base1 color
vk::VertexInputAttributeDescription(5, 0, vk::Format::eR8G8B8A8Uint, offsetof(Vertex, spc1)), // offset1 color
vk::VertexInputAttributeDescription(4, 0, vk::Format::eR8G8B8A8Unorm, offsetof(Vertex, col1)), // base1 color
vk::VertexInputAttributeDescription(5, 0, vk::Format::eR8G8B8A8Unorm, offsetof(Vertex, spc1)), // offset1 color
vk::VertexInputAttributeDescription(6, 0, vk::Format::eR32G32Sfloat, offsetof(Vertex, u1)), // tex1 coord
vk::VertexInputAttributeDescription(7, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, nx)), // naomi2 normal
};
@@ -439,12 +439,29 @@ private:
{
vk::VertexInputAttributeDescription(0, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, x)), // pos
};
const vk::VertexInputAttributeDescription* attributeDescription = vertexInputLightAttributeDescriptions;
u32 attributeDescriptionSize = std::size(vertexInputLightAttributeDescriptions);
if (full)
{
attributeDescription = vertexInputAttributeDescriptions;
if (naomi2)
{
attributeDescriptionSize = std::size(vertexInputAttributeDescriptions);
}
else
{
// naomi2 normal not needed
attributeDescriptionSize = std::size(vertexInputAttributeDescriptions) - 1;
}
}
return vk::PipelineVertexInputStateCreateInfo(
vk::PipelineVertexInputStateCreateFlags(),
std::size(vertexBindingDescriptions),
vertexBindingDescriptions,
full ? std::size(vertexInputAttributeDescriptions) : std::size(vertexInputLightAttributeDescriptions),
full ? vertexInputAttributeDescriptions : vertexInputLightAttributeDescriptions);
vk::PipelineVertexInputStateCreateFlags(),
std::size(vertexBindingDescriptions), vertexBindingDescriptions,
attributeDescriptionSize, attributeDescription
);
}
void CreatePipeline(u32 listType, bool autosort, const PolyParam& pp, Pass pass, int gpuPalette);
+6 -2
View File
@@ -28,10 +28,10 @@ vk::UniqueRenderPass RenderPasses::MakeRenderPass(bool initial, bool last, bool
attach0,
// OP+PT color attachment
vk::AttachmentDescription(vk::AttachmentDescriptionFlags(), vk::Format::eR8G8B8A8Unorm, vk::SampleCountFlagBits::e1,
initial ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad,
loadClear && initial ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad,
last ? vk::AttachmentStoreOp::eDontCare : vk::AttachmentStoreOp::eStore,
vk::AttachmentLoadOp::eDontCare, vk::AttachmentStoreOp::eDontCare,
initial ? vk::ImageLayout::eUndefined : attach0.initialLayout, attach0.finalLayout),
loadClear && initial ? vk::ImageLayout::eUndefined : attach0.finalLayout, attach0.finalLayout), // initial layout is eUndefined for rtt, so use final layout instead
// OP+PT depth attachment
vk::AttachmentDescription(vk::AttachmentDescriptionFlags(), GetContext()->GetDepthFormat(), vk::SampleCountFlagBits::e1,
initial ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad,
@@ -89,6 +89,10 @@ vk::UniqueRenderPass RenderPasses::MakeRenderPass(bool initial, bool last, bool
vk::AccessFlagBits::eInputAttachmentRead | vk::AccessFlagBits::eShaderRead,
vk::AccessFlagBits::eDepthStencilAttachmentRead | vk::AccessFlagBits::eDepthStencilAttachmentWrite,
vk::DependencyFlagBits::eByRegion);
dependencies.emplace_back(1, 1, vk::PipelineStageFlagBits::eFragmentShader, vk::PipelineStageFlagBits::eFragmentShader,
vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite,
vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite,
vk::DependencyFlagBits::eByRegion);
dependencies.emplace_back(2, 2, vk::PipelineStageFlagBits::eFragmentShader, vk::PipelineStageFlagBits::eFragmentShader,
vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite,
vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite,
+1 -1
View File
@@ -44,7 +44,7 @@ protected:
virtual vk::AttachmentDescription GetAttachment0Description(bool initial, bool last, bool loadClear) const
{
return vk::AttachmentDescription(vk::AttachmentDescriptionFlags(), vk::Format::eR8G8B8A8Unorm, vk::SampleCountFlagBits::e1,
loadClear ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad, vk::AttachmentStoreOp::eStore,
initial && loadClear ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad, vk::AttachmentStoreOp::eStore,
vk::AttachmentLoadOp::eDontCare, vk::AttachmentStoreOp::eDontCare,
config::EmulateFramebuffer && initial ? vk::ImageLayout::eTransferSrcOptimal : vk::ImageLayout::eShaderReadOnlyOptimal,
config::EmulateFramebuffer && last ? vk::ImageLayout::eTransferSrcOptimal : vk::ImageLayout::eShaderReadOnlyOptimal);
+16 -16
View File
@@ -37,11 +37,11 @@ layout (push_constant) uniform constants
} pushConstants;
layout (location = 0) in vec4 in_pos;
layout (location = 1) in uvec4 in_base;
layout (location = 2) in uvec4 in_offs;
layout (location = 1) in vec4 in_base;
layout (location = 2) in vec4 in_offs;
layout (location = 3) in mediump vec2 in_uv;
layout (location = 4) in uvec4 in_base1; // New for OIT, only for OP/PT with 2-volume
layout (location = 5) in uvec4 in_offs1;
layout (location = 4) in vec4 in_base1; // New for OIT, only for OP/PT with 2-volume
layout (location = 5) in vec4 in_offs1;
layout (location = 6) in mediump vec2 in_uv1;
layout (location = 0) INTERPOLATION out highp vec4 vtx_base;
@@ -59,11 +59,11 @@ void main()
vpos /= vpos.z;
vpos.z = vpos.w;
#endif
vtx_base = vec4(in_base) / 255.0;
vtx_offs = vec4(in_offs) / 255.0;
vtx_base = in_base;
vtx_offs = in_offs;
vtx_uv = vec3(in_uv, vpos.z);
vtx_base1 = vec4(in_base1) / 255.0;
vtx_offs1 = vec4(in_offs1) / 255.0;
vtx_base1 = in_base1;
vtx_offs1 = in_offs1;
vtx_uv1 = in_uv1;
#if pp_Gouraud == 1 && DIV_POS_Z != 1
vtx_base *= vpos.z;
@@ -638,11 +638,11 @@ layout (push_constant) uniform constants
} pushConstants;
layout (location = 0) in vec4 in_pos;
layout (location = 1) in uvec4 in_base;
layout (location = 2) in uvec4 in_offs;
layout (location = 1) in vec4 in_base;
layout (location = 2) in vec4 in_offs;
layout (location = 3) in mediump vec2 in_uv;
layout (location = 4) in uvec4 in_base1;
layout (location = 5) in uvec4 in_offs1;
layout (location = 4) in vec4 in_base1;
layout (location = 5) in vec4 in_offs1;
layout (location = 6) in mediump vec2 in_uv1;
layout (location = 7) in vec3 in_normal;
@@ -677,16 +677,16 @@ void wDivide(inout vec4 vpos)
void main()
{
vec4 vpos = n2Uniform.mvMat * in_pos;
vtx_base = vec4(in_base) / 255.0;
vtx_offs = vec4(in_offs) / 255.0;
vtx_base = in_base;
vtx_offs = in_offs;
#if LIGHT_ON == 1
vec3 vnorm = normalize(mat3(n2Uniform.normalMat) * in_normal);
#endif
#if pp_TwoVolumes == 1
vtx_base1 = vec4(in_base1) / 255.0;
vtx_offs1 = vec4(in_offs1) / 255.0;
vtx_base1 = in_base1;
vtx_offs1 = in_offs1;
vtx_uv1 = in_uv1;
#if LIGHT_ON == 1
// FIXME need offset0 and offset1 for bump maps
+1 -1
View File
@@ -89,7 +89,7 @@ void VulkanOverlay::Prepare(vk::CommandBuffer cmdBuffer, bool vmu, bool crosshai
texture->deferDeleteResource(VulkanContext::Instance());
texture = createTexture(cmdBuffer, 48, 32, (u8*)vmu_lcd_data[i]);
#ifdef VK_DEBUG
VulkanContext::Instance()->setObjectName((VkImageView)texture->GetImageView(), vk::ImageView::objectType, "VMU " + std::to_string(i));
VulkanContext::Instance()->setObjectName(texture->GetImageView(), "VMU " + std::to_string(i));
#endif
this->vmuLastChanged[i] = ::vmuLastChanged[i];
}
+27 -5
View File
@@ -31,7 +31,7 @@ void PipelineManager::CreateModVolPipeline(ModVolMode mode, int cullMode, bool n
if (mode == ModVolMode::Final)
{
pipelineVertexInputStateCreateInfo = GetMainVertexInputStateCreateInfo(false);
pipelineVertexInputStateCreateInfo = GetMainVertexInputStateCreateInfo(false, naomi2);
pipelineInputAssemblyStateCreateInfo = vk::PipelineInputAssemblyStateCreateInfo(vk::PipelineInputAssemblyStateCreateFlags(),
vk::PrimitiveTopology::eTriangleStrip);
}
@@ -161,7 +161,7 @@ void PipelineManager::CreateModVolPipeline(ModVolMode mode, int cullMode, bool n
void PipelineManager::CreateDepthPassPipeline(int cullMode, bool naomi2)
{
// Vertex input state
vk::PipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo = GetMainVertexInputStateCreateInfo(false);
vk::PipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo = GetMainVertexInputStateCreateInfo(false, false);
// Input assembly state
vk::PipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateCreateInfo
(
@@ -189,6 +189,17 @@ void PipelineManager::CreateDepthPassPipeline(int cullMode, bool naomi2)
0.0f, // depthBiasSlopeFactor
1.0f // lineWidth
);
// Dreamcast uses the last vertex as the provoking vertex, but Vulkan uses the first.
// Utilize VK_EXT_provoking_vertex when available to set the provoking vertex to be the
// last vertex
vk::PipelineRasterizationProvokingVertexStateCreateInfoEXT provokingVertexInfo{};
if (GetContext()->hasProvokingVertex())
{
provokingVertexInfo.provokingVertexMode = vk::ProvokingVertexModeEXT::eLastVertex;
pipelineRasterizationStateCreateInfo.pNext = &provokingVertexInfo;
}
vk::PipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo;
// Depth and stencil
@@ -262,7 +273,7 @@ void PipelineManager::CreateDepthPassPipeline(int cullMode, bool naomi2)
void PipelineManager::CreatePipeline(u32 listType, bool sortTriangles, const PolyParam& pp, int gpuPalette, bool dithering)
{
vk::PipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo = GetMainVertexInputStateCreateInfo();
vk::PipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo = GetMainVertexInputStateCreateInfo(true, pp.isNaomi2());
// Input assembly state
vk::PipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateCreateInfo;
@@ -295,6 +306,17 @@ void PipelineManager::CreatePipeline(u32 listType, bool sortTriangles, const Pol
0.0f, // depthBiasSlopeFactor
1.0f // lineWidth
);
// Dreamcast uses the last vertex as the provoking vertex, but Vulkan uses the first.
// Utilize VK_EXT_provoking_vertex when available to set the provoking vertex to be the
// last vertex
vk::PipelineRasterizationProvokingVertexStateCreateInfoEXT provokingVertexInfo{};
if (GetContext()->hasProvokingVertex())
{
provokingVertexInfo.provokingVertexMode = vk::ProvokingVertexModeEXT::eLastVertex;
pipelineRasterizationStateCreateInfo.pNext = &provokingVertexInfo;
}
vk::PipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo;
// Depth and stencil
@@ -375,7 +397,7 @@ void PipelineManager::CreatePipeline(u32 listType, bool sortTriangles, const Pol
FragmentShaderParams params = {};
params.alphaTest = listType == ListType_Punch_Through;
params.bumpmap = pp.tcw.PixelFmt == PixelBumpMap;
params.clamping = pp.tsp.ColorClamp && (pvrrc.fog_clamp_min.full != 0 || pvrrc.fog_clamp_max.full != 0xffffffff);
params.clamping = pp.tsp.ColorClamp;
params.insideClipTest = (pp.tileclip >> 28) == 3;
params.fog = config::Fog ? pp.tsp.FogCtrl : 2;
params.gouraud = pp.pcw.Gouraud;
@@ -421,7 +443,7 @@ void OSDPipeline::CreatePipeline()
static const vk::VertexInputBindingDescription vertexInputBindingDescription(0, sizeof(OSDVertex));
static const std::array<vk::VertexInputAttributeDescription, 3> vertexInputAttributeDescriptions = {
vk::VertexInputAttributeDescription(0, 0, vk::Format::eR32G32Sfloat, offsetof(OSDVertex, x)), // pos
vk::VertexInputAttributeDescription(1, 0, vk::Format::eR8G8B8A8Uint, offsetof(OSDVertex, r)), // color
vk::VertexInputAttributeDescription(1, 0, vk::Format::eR8G8B8A8Unorm, offsetof(OSDVertex, r)), // color
vk::VertexInputAttributeDescription(2, 0, vk::Format::eR32G32Sfloat, offsetof(OSDVertex, u)), // tex coord
};
vk::PipelineVertexInputStateCreateInfo vertexInputStateCreateInfo(
+29 -8
View File
@@ -286,7 +286,7 @@ private:
return cullMode | ((int)naomi2 << 2) | ((int)(!settings.platform.isNaomi2() && config::NativeDepthInterpolation) << 3);
}
vk::PipelineVertexInputStateCreateInfo GetMainVertexInputStateCreateInfo(bool full = true) const
vk::PipelineVertexInputStateCreateInfo GetMainVertexInputStateCreateInfo(bool full = true, bool naomi2 = false) const
{
// Vertex input state
static const vk::VertexInputBindingDescription vertexBindingDescriptions[] =
@@ -296,8 +296,8 @@ private:
static const vk::VertexInputAttributeDescription vertexInputAttributeDescriptions[] =
{
vk::VertexInputAttributeDescription(0, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, x)), // pos
vk::VertexInputAttributeDescription(1, 0, vk::Format::eR8G8B8A8Uint, offsetof(Vertex, col)), // base color
vk::VertexInputAttributeDescription(2, 0, vk::Format::eR8G8B8A8Uint, offsetof(Vertex, spc)), // offset color
vk::VertexInputAttributeDescription(1, 0, vk::Format::eR8G8B8A8Unorm, offsetof(Vertex, col)), // base color
vk::VertexInputAttributeDescription(2, 0, vk::Format::eR8G8B8A8Unorm, offsetof(Vertex, spc)), // offset color
vk::VertexInputAttributeDescription(3, 0, vk::Format::eR32G32Sfloat, offsetof(Vertex, u)), // tex coord
vk::VertexInputAttributeDescription(4, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, nx)), // naomi2 normal
};
@@ -305,12 +305,33 @@ private:
{
vk::VertexInputAttributeDescription(0, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, x)), // pos
};
const vk::VertexInputAttributeDescription* attributeDescription = vertexInputLightAttributeDescriptions;
u32 attributeDescriptionSize = std::size(vertexInputLightAttributeDescriptions);
if (full)
{
attributeDescription = vertexInputAttributeDescriptions;
if (naomi2)
{
attributeDescriptionSize = std::size(vertexInputAttributeDescriptions);
}
else
{
// naomi2 normal not needed
attributeDescriptionSize = std::size(vertexInputAttributeDescriptions) - 1;
}
}
return vk::PipelineVertexInputStateCreateInfo(
vk::PipelineVertexInputStateCreateFlags(),
std::size(vertexBindingDescriptions),
vertexBindingDescriptions,
full ? std::size(vertexInputAttributeDescriptions) : std::size(vertexInputLightAttributeDescriptions),
full ? vertexInputAttributeDescriptions : vertexInputLightAttributeDescriptions);
std::size(vertexBindingDescriptions), vertexBindingDescriptions,
attributeDescriptionSize, attributeDescription
);
}
void CreatePipeline(u32 listType, bool sortTriangles, const PolyParam& pp, int gpuPalette, bool dithering);
@@ -398,7 +419,7 @@ public:
vk::SamplerCreateInfo(vk::SamplerCreateFlags(), vk::Filter::eLinear, vk::Filter::eLinear,
vk::SamplerMipmapMode::eLinear, vk::SamplerAddressMode::eClampToEdge, vk::SamplerAddressMode::eClampToEdge,
vk::SamplerAddressMode::eClampToEdge, 0.0f, false, 16.0f, false,
vk::CompareOp::eNever, 0.0f, 0.0f, vk::BorderColor::eFloatOpaqueBlack));
vk::CompareOp::eNever, 0.0f, vk::LodClampNone, vk::BorderColor::eFloatOpaqueBlack));
}
if (this->renderPass != renderPass)
{
+26 -3
View File
@@ -23,6 +23,19 @@
#include <memory>
namespace {
size_t hashQuadVertex(const QuadVertex& vertex)
{
size_t seed = 0;
seed ^= std::hash<float>{}(vertex.x) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
seed ^= std::hash<float>{}(vertex.y) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
seed ^= std::hash<float>{}(vertex.z) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
seed ^= std::hash<float>{}(vertex.u) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
seed ^= std::hash<float>{}(vertex.v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
return seed;
}
}
static VulkanContext *GetContext()
{
return VulkanContext::Instance();
@@ -137,7 +150,7 @@ void QuadPipeline::Init(ShaderManager *shaderManager, vk::RenderPass renderPass,
vk::SamplerAddressMode::eClampToEdge,
vk::SamplerAddressMode::eClampToEdge,
vk::SamplerAddressMode::eClampToEdge, 0.0f, false,
16.0f, false, vk::CompareOp::eNever, 0.0f, 0.0f,
16.0f, false, vk::CompareOp::eNever, 0.0f, vk::LodClampNone,
vk::BorderColor::eFloatOpaqueBlack));
}
if (!nearestSampler)
@@ -149,7 +162,7 @@ void QuadPipeline::Init(ShaderManager *shaderManager, vk::RenderPass renderPass,
vk::SamplerAddressMode::eClampToEdge,
vk::SamplerAddressMode::eClampToEdge,
vk::SamplerAddressMode::eClampToEdge, 0.0f, false,
16.0f, false, vk::CompareOp::eNever, 0.0f, 0.0f,
16.0f, false, vk::CompareOp::eNever, 0.0f, vk::LodClampNone,
vk::BorderColor::eFloatOpaqueBlack));
}
if (this->renderPass != renderPass)
@@ -169,7 +182,7 @@ void QuadDrawer::Init(QuadPipeline *pipeline)
descSet.reset();
}
void QuadDrawer::Draw(vk::CommandBuffer commandBuffer, vk::ImageView imageView, QuadVertex vertices[], bool nearestFilter, const float *color)
void QuadDrawer::Draw(vk::CommandBuffer commandBuffer, vk::ImageView imageView, QuadVertex vertices[4], bool nearestFilter, const float *color)
{
VulkanContext *context = GetContext();
auto &descSet = descriptorSets[context->GetCurrentImageIndex()];
@@ -198,3 +211,13 @@ void QuadDrawer::Draw(vk::CommandBuffer commandBuffer, vk::ImageView imageView,
commandBuffer.pushConstants(pipeline->GetPipelineLayout(), vk::ShaderStageFlagBits::eFragment, 0, sizeof(float) * 4, color);
buffer->Draw(commandBuffer);
}
size_t QuadBuffer::hashQuadVertexData(QuadVertex vertices[4]) const
{
size_t seed = 0;
seed ^= hashQuadVertex(vertices[0]) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
seed ^= hashQuadVertex(vertices[1]) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
seed ^= hashQuadVertex(vertices[2]) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
seed ^= hashQuadVertex(vertices[3]) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
return seed;
}
+18 -3
View File
@@ -50,11 +50,12 @@ public:
commandBuffer.draw(4, 1, 0, 0);
}
void Update(QuadVertex vertices[] = nullptr)
void Update(QuadVertex vertices[4] = nullptr)
{
if (vertices == nullptr)
{
static QuadVertex defaultVtx[] {
static QuadVertex defaultVtx[4]
{
{ -1.f, -1.f, 0.f, 0.f, 0.f },
{ 1.f, -1.f, 0.f, 1.f, 0.f },
{ -1.f, 1.f, 0.f, 0.f, 1.f },
@@ -62,10 +63,24 @@ public:
};
vertices = defaultVtx;
};
const size_t quadVerticesHash = hashQuadVertexData(vertices);;
if (quadVerticesHash == lastUploadHash)
{
// data already uploaded
return;
}
// new data to upload, update hash
lastUploadHash = quadVerticesHash;
buffer->upload(sizeof(QuadVertex) * 4, vertices);
}
private:
std::unique_ptr<BufferData> buffer;
size_t hashQuadVertexData(QuadVertex vertices[4]) const;
size_t lastUploadHash = 0;
};
class QuadPipeline
@@ -119,7 +134,7 @@ public:
QuadDrawer& operator=(const QuadDrawer &) = delete;
void Init(QuadPipeline *pipeline);
void Draw(vk::CommandBuffer commandBuffer, vk::ImageView imageView, QuadVertex vertices[] = nullptr, bool nearestFilter = false, const float *color = nullptr);
void Draw(vk::CommandBuffer commandBuffer, vk::ImageView imageView, QuadVertex vertices[4] = nullptr, bool nearestFilter = false, const float *color = nullptr);
private:
QuadPipeline *pipeline = nullptr;
std::unique_ptr<QuadBuffer> buffer;
+11 -10
View File
@@ -30,8 +30,8 @@ layout (std140, set = 0, binding = 0) uniform VertexShaderUniforms
} uniformBuffer;
layout (location = 0) in vec4 in_pos;
layout (location = 1) in uvec4 in_base;
layout (location = 2) in uvec4 in_offs;
layout (location = 1) in vec4 in_base;
layout (location = 2) in vec4 in_offs;
layout (location = 3) in mediump vec2 in_uv;
layout (location = 0) INTERPOLATION out highp vec4 vtx_base;
@@ -45,8 +45,8 @@ void main()
vpos /= vpos.z;
vpos.z = vpos.w;
#endif
vtx_base = vec4(in_base) / 255.0;
vtx_offs = vec4(in_offs) / 255.0;
vtx_base = in_base;
vtx_offs = in_offs;
vtx_uv = vec3(in_uv, vpos.z);
#if pp_Gouraud == 1 && DIV_POS_Z != 1
vtx_base *= vpos.z;
@@ -396,14 +396,14 @@ void main()
static const char OSDVertexShaderSource[] = R"(
layout (location = 0) in vec4 inPos;
layout (location = 1) in uvec4 inColor;
layout (location = 1) in vec4 inColor;
layout (location = 2) in vec2 inUV;
layout (location = 0) out lowp vec4 outColor;
layout (location = 1) out mediump vec2 outUV;
void main()
{
outColor = inColor / 255.0;
outColor = inColor;
outUV = inUV;
gl_Position = inPos;
}
@@ -659,8 +659,8 @@ layout (std140, set = 0, binding = 0) uniform VertexShaderUniforms
} uniformBuffer;
layout (location = 0) in vec4 in_pos;
layout (location = 1) in uvec4 in_base;
layout (location = 2) in uvec4 in_offs;
layout (location = 1) in vec4 in_base;
layout (location = 2) in vec4 in_offs;
layout (location = 3) in mediump vec2 in_uv;
layout (location = 4) in vec3 in_normal;
@@ -684,8 +684,8 @@ void wDivide(inout vec4 vpos)
void main()
{
vec4 vpos = n2Uniform.mvMat * in_pos;
vtx_base = vec4(in_base) / 255.0;
vtx_offs = vec4(in_offs) / 255.0;
vtx_base = in_base;
vtx_offs = in_offs;
vec3 vnorm = normalize(mat3(n2Uniform.normalMat) * in_normal);
@@ -743,6 +743,7 @@ void wDivide(inout vec4 vpos)
void main()
{
vec4 vpos = n2Uniform.mvMat * in_pos;
vpos.z = min(vpos.z, -0.001);
vpos = n2Uniform.projMat * vpos;
wDivide(vpos);
+17 -7
View File
@@ -25,6 +25,9 @@
void setImageLayout(vk::CommandBuffer const& commandBuffer, vk::Image image, vk::Format format, u32 mipmapLevels, vk::ImageLayout oldImageLayout, vk::ImageLayout newImageLayout)
{
static const float scopeColor[4] = { 0.75f, 0.75f, 0.0f, 1.0f };
CommandBufferDebugScope _(commandBuffer, "setImageLayout", scopeColor);
vk::AccessFlags sourceAccessMask;
switch (oldImageLayout)
{
@@ -229,9 +232,10 @@ void Texture::Init(u32 width, u32 height, vk::Format format, u32 dataSize, bool
CreateImage(imageTiling, usageFlags, initialLayout, vk::ImageAspectFlagBits::eColor);
}
void Texture::CreateImage(vk::ImageTiling tiling, const vk::ImageUsageFlags& usage, vk::ImageLayout initialLayout,
const vk::ImageAspectFlags& aspectMask)
void Texture::CreateImage(vk::ImageTiling tiling, vk::ImageUsageFlags usage, vk::ImageLayout initialLayout,
vk::ImageAspectFlags aspectMask)
{
this->usageFlags = usage;
vk::ImageCreateInfo imageCreateInfo(vk::ImageCreateFlags(), vk::ImageType::e2D, format, vk::Extent3D(extent, 1), mipmapLevels, 1,
vk::SampleCountFlagBits::e1, tiling, usage,
vk::SharingMode::eExclusive, nullptr, initialLayout);
@@ -250,8 +254,8 @@ void Texture::CreateImage(vk::ImageTiling tiling, const vk::ImageUsageFlags& usa
#ifdef VK_DEBUG
char name[128];
sprintf(name, "texture @ %x", startAddress);
VulkanContext::Instance()->setObjectName((VkImage)image.get(), vk::Image::objectType, name);
VulkanContext::Instance()->setObjectName((VkImageView)imageView.get(), vk::ImageView::objectType, name);
VulkanContext::Instance()->setObjectName(image.get(), name);
VulkanContext::Instance()->setObjectName(imageView.get(), name);
#endif
}
@@ -259,6 +263,9 @@ void Texture::SetImage(u32 srcSize, const void *srcData, bool isNew, bool genMip
{
verify((bool)commandBuffer);
static const float scopeColor[4] = { 1.0f, 1.0f, 0.0f, 1.0f };
CommandBufferDebugScope _(commandBuffer, "SetImage", scopeColor);
if (!isNew && !needsStaging)
setImageLayout(commandBuffer, image.get(), format, mipmapLevels, vk::ImageLayout::eShaderReadOnlyOptimal, vk::ImageLayout::eGeneral);
@@ -353,6 +360,9 @@ void Texture::SetImage(u32 srcSize, const void *srcData, bool isNew, bool genMip
void Texture::GenerateMipmaps()
{
static const float scopeColor[4] = { 0.75f, 0.75f, 0.0f, 1.0f };
CommandBufferDebugScope _(commandBuffer, "GenerateMipmaps", scopeColor);
u32 mipWidth = extent.width;
u32 mipHeight = extent.height;
vk::ImageMemoryBarrier barrier(vk::AccessFlagBits::eTransferWrite, vk::AccessFlagBits::eTransferRead,
@@ -443,7 +453,7 @@ void FramebufferAttachment::Init(u32 width, u32 height, vk::Format format, const
image = device.createImageUnique(imageCreateInfo);
#ifdef VK_DEBUG
if (!name.empty())
VulkanContext::Instance()->setObjectName((VkImage)image.get(), vk::Image::objectType, name);
VulkanContext::Instance()->setObjectName(image.get(), name);
#endif
VmaAllocationCreateInfo allocCreateInfo = { VmaAllocationCreateFlags(), VmaMemoryUsage::VMA_MEMORY_USAGE_GPU_ONLY };
@@ -458,7 +468,7 @@ void FramebufferAttachment::Init(u32 width, u32 height, vk::Format format, const
imageView = device.createImageViewUnique(imageViewCreateInfo);
#ifdef VK_DEBUG
if (!name.empty())
VulkanContext::Instance()->setObjectName((VkImageView)imageView.get(), vk::ImageView::objectType, name);
VulkanContext::Instance()->setObjectName(imageView.get(), name);
#endif
if ((usage & vk::ImageUsageFlagBits::eDepthStencilAttachment) && (usage & vk::ImageUsageFlagBits::eInputAttachment))
@@ -468,7 +478,7 @@ void FramebufferAttachment::Init(u32 width, u32 height, vk::Format format, const
stencilView = device.createImageViewUnique(imageViewCreateInfo);
#ifdef VK_DEBUG
if (!name.empty())
VulkanContext::Instance()->setObjectName((VkImageView)stencilView.get(), vk::ImageView::objectType, name);
VulkanContext::Instance()->setObjectName(stencilView.get(), name);
#endif
}
}
+9 -3
View File
@@ -69,13 +69,14 @@ public:
private:
void Init(u32 width, u32 height, vk::Format format ,u32 dataSize, bool mipmapped, bool mipmapsIncluded);
void SetImage(u32 size, const void *data, bool isNew, bool genMipmaps);
void CreateImage(vk::ImageTiling tiling, const vk::ImageUsageFlags& usage, vk::ImageLayout initialLayout,
const vk::ImageAspectFlags& aspectMask);
void CreateImage(vk::ImageTiling tiling, vk::ImageUsageFlags usage, vk::ImageLayout initialLayout,
vk::ImageAspectFlags aspectMask);
void GenerateMipmaps();
vk::Format format = vk::Format::eUndefined;
vk::Extent2D extent;
u32 mipmapLevels = 1;
vk::ImageUsageFlags usageFlags;
bool needsStaging = false;
std::unique_ptr<BufferData> stagingBufferData;
vk::CommandBuffer commandBuffer;
@@ -124,6 +125,11 @@ public:
const vk::SamplerAddressMode vRepeat = tsp.ClampV ? vk::SamplerAddressMode::eClampToEdge
: tsp.FlipV ? vk::SamplerAddressMode::eMirroredRepeat : vk::SamplerAddressMode::eRepeat;
// The W-axis is unused for 2D textures
// Try to keep all three of the wrapping-modes the same by just repeating vRepeat for wRepeat
// BestPractices-Arm-vkCreateSampler-different-wrapping-modes
const vk::SamplerAddressMode wRepeat = vRepeat;
const bool anisotropicFiltering = config::AnisotropicFiltering > 1 && VulkanContext::Instance()->SupportsSamplerAnisotropy()
&& filter == vk::Filter::eLinear && !punchThrough;
#ifndef __APPLE__
@@ -136,7 +142,7 @@ public:
return samplers.emplace(
std::make_pair(samplerHash, VulkanContext::Instance()->GetDevice().createSamplerUnique(
vk::SamplerCreateInfo(vk::SamplerCreateFlags(), filter, filter,
mipmapMode, uRepeat, vRepeat, vk::SamplerAddressMode::eClampToEdge, mipLodBias,
mipmapMode, uRepeat, vRepeat, wRepeat, mipLodBias,
anisotropicFiltering, std::min((float)config::AnisotropicFiltering, VulkanContext::Instance()->GetMaxSamplerAnisotropy()),
false, vk::CompareOp::eNever,
0.0f, vk::LodClampNone, vk::BorderColor::eFloatOpaqueBlack)))).first->second.get();
+97 -32
View File
@@ -71,6 +71,9 @@ bool VkCreateDevice(retro_vulkan_context* context, VkInstance instance, VkPhysic
physicalDevice = vkinstance.enumeratePhysicalDevices().front();
}
context->gpu = (VkPhysicalDevice)physicalDevice;
const vk::PhysicalDeviceProperties physicalDeviceProperties = physicalDevice.getProperties();
std::vector<vk::QueueFamilyProperties> queueFamilyProperties = physicalDevice.getQueueFamilyProperties();
// get the first index into queueFamilyProperties which supports graphics and compute
@@ -124,54 +127,116 @@ bool VkCreateDevice(retro_vulkan_context* context, VkInstance instance, VkPhysic
DEBUG_LOG(RENDERER, "Using distinct Graphics and Present queue families");
}
vk::PhysicalDeviceFeatures supportedFeatures;
physicalDevice.getFeatures(&supportedFeatures);
VulkanContext::Instance()->fragmentStoresAndAtomics = supportedFeatures.fragmentStoresAndAtomics;
VulkanContext::Instance()->samplerAnisotropy = supportedFeatures.samplerAnisotropy;
std::set<std::string> supportedExtensions;
const auto deviceExtensionProperties = physicalDevice.enumerateDeviceExtensionProperties();
for (const auto& property : deviceExtensionProperties)
{
supportedExtensions.insert(property.extensionName);
}
std::vector<const char*> enabledExtensions;
const auto tryAddDeviceExtension = [&supportedExtensions = std::as_const(supportedExtensions), &enabledExtensions]
(std::string_view extensionName) -> bool
{
if (supportedExtensions.count(extensionName.data()))
{
enabledExtensions.push_back(extensionName.data());
NOTICE_LOG(RENDERER, "Device extension enabled: %s", extensionName.data());
return true;
}
NOTICE_LOG(RENDERER, "Device extension unavailable: %s", extensionName.data());
return false;
};
// Required swapchain extension
tryAddDeviceExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
// Enable VK_KHR_dedicated_allocation if available
bool getMemReq2Supported = false;
VulkanContext::Instance()->dedicatedAllocationSupported = false;
std::vector<const char *> deviceExtensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME };
for (unsigned i = 0; i < num_required_device_extensions; i++)
deviceExtensions.push_back(required_device_extensions[i]);
for (const auto& property : physicalDevice.enumerateDeviceExtensionProperties())
if (physicalDeviceProperties.apiVersion >= VK_API_VERSION_1_1)
{
if (!strcmp(property.extensionName, VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME))
// Core in Vulkan 1.1
VulkanContext::Instance()->dedicatedAllocationSupported = true;
}
else
{
const bool getMemReq2Supported = tryAddDeviceExtension(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME);
if (getMemReq2Supported)
{
deviceExtensions.push_back(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME);
getMemReq2Supported = true;
}
else if (!strcmp(property.extensionName, VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME))
{
deviceExtensions.push_back(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME);
VulkanContext::Instance()->dedicatedAllocationSupported = true;
VulkanContext::Instance()->dedicatedAllocationSupported = tryAddDeviceExtension(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME);
}
}
VulkanContext::Instance()->dedicatedAllocationSupported &= getMemReq2Supported;
// Check for VK_KHR_get_physical_device_properties2
// Core as of Vulkan 1.1
const bool getPhysicalDeviceProperties2Supported =
(physicalDeviceProperties.apiVersion >= VK_API_VERSION_1_1)
? true : tryAddDeviceExtension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
if (getPhysicalDeviceProperties2Supported)
{
// Enable VK_EXT_provoking_vertex if available
VulkanContext::Instance()->provokingVertexSupported = tryAddDeviceExtension(VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME);
}
// Get device features
vk::PhysicalDeviceFeatures2 featuresChain{};
vk::PhysicalDeviceFeatures& features = featuresChain.features;
vk::PhysicalDeviceProvokingVertexFeaturesEXT provokingVertexFeatures{};
if (VulkanContext::Instance()->provokingVertexSupported)
{
featuresChain.pNext = &provokingVertexFeatures;
}
// Get the physical device's features
if (getPhysicalDeviceProperties2Supported && featuresChain.pNext)
{
physicalDevice.getFeatures2(&featuresChain);
}
else
{
physicalDevice.getFeatures(&features);
}
if (VulkanContext::Instance()->provokingVertexSupported)
{
VulkanContext::Instance()->provokingVertexSupported &= provokingVertexFeatures.provokingVertexLast;
}
VulkanContext::Instance()->samplerAnisotropy = features.samplerAnisotropy;
VulkanContext::Instance()->fragmentStoresAndAtomics = features.fragmentStoresAndAtomics;
// create a Device
float queuePriority = 1.0f;
vk::DeviceQueueCreateInfo deviceQueueCreateInfos[] = {
vk::DeviceQueueCreateInfo deviceQueueCreateInfo[] = {
vk::DeviceQueueCreateInfo(vk::DeviceQueueCreateFlags(), context->queue_family_index, 1, &queuePriority),
vk::DeviceQueueCreateInfo(vk::DeviceQueueCreateFlags(), context->presentation_queue_family_index, 1, &queuePriority),
};
vk::PhysicalDeviceFeatures features(*required_features);
if (VulkanContext::Instance()->fragmentStoresAndAtomics)
features.fragmentStoresAndAtomics = true;
if (VulkanContext::Instance()->samplerAnisotropy)
features.samplerAnisotropy = true;
vk::Device device = physicalDevice.createDevice(vk::DeviceCreateInfo(vk::DeviceCreateFlags(),
context->queue_family_index == context->presentation_queue_family_index ? 1 : 2, deviceQueueCreateInfos,
num_required_device_layers, required_device_layers, deviceExtensions.size(), &deviceExtensions[0], &features));
context->device = (VkDevice)device;
bool singleQueue = context->queue_family_index == context->presentation_queue_family_index;
vk::DeviceCreateInfo deviceCreateInfo;
if (singleQueue)
deviceCreateInfo = vk::DeviceCreateInfo(vk::DeviceCreateFlags(), deviceQueueCreateInfo[0], nullptr, enabledExtensions);
else
deviceCreateInfo = vk::DeviceCreateInfo(vk::DeviceCreateFlags(), deviceQueueCreateInfo, nullptr, enabledExtensions);
if (getPhysicalDeviceProperties2Supported)
deviceCreateInfo.pNext = &featuresChain;
else
deviceCreateInfo.setPEnabledFeatures(&features);
vk::Device newDevice = physicalDevice.createDevice(deviceCreateInfo);
context->device = (VkDevice)newDevice;
#if VULKAN_HPP_DISPATCH_LOADER_DYNAMIC == 1
VULKAN_HPP_DEFAULT_DISPATCHER.init(context->device);
#endif
// Queues
context->queue = (VkQueue)device.getQueue(context->queue_family_index, 0);
context->presentation_queue = (VkQueue)device.getQueue(context->presentation_queue_family_index, 0);
context->queue = (VkQueue)newDevice.getQueue(context->queue_family_index, 0);
context->presentation_queue = (VkQueue)newDevice.getQueue(context->presentation_queue_family_index, 0);
return true;
}
@@ -329,7 +394,7 @@ void VulkanContext::PresentFrame(vk::Image image, vk::ImageView imageView, const
getVideoShift(shiftX, shiftY);
beginFrame(extent);
QuadVertex vtx[] {
QuadVertex vtx[4] {
{ -1, -1, 0, 0, 0 },
{ 1, -1, 0, 1, 0 },
{ -1, 1, 0, 0, 1 },
+7 -2
View File
@@ -92,6 +92,7 @@ public:
bool SupportsSamplerAnisotropy() const { return samplerAnisotropy; }
bool SupportsDedicatedAllocation() const { return dedicatedAllocationSupported; }
bool hasPerPixel() override { return fragmentStoresAndAtomics; }
bool hasProvokingVertex() { return provokingVertexSupported; }
const VMAllocator& GetAllocator() const { return allocator; }
vk::DeviceSize GetMaxMemoryAllocationSize() const { return maxMemoryAllocationSize; }
f32 GetMaxSamplerAnisotropy() const { return samplerAnisotropy ? maxSamplerAnisotropy : 1.f; }
@@ -100,7 +101,10 @@ public:
commandPool.addToFlight(object);
}
#ifdef VK_DEBUG
void setObjectName(VkHandle object, vk::ObjectType objectType, const std::string& name) {}
template<typename HandleType, typename = std::enable_if_t<vk::isVulkanHandleType<HandleType>::value>>
void setObjectName(const HandleType& object, const std::string& name)
{
}
#endif
constexpr static int VENDOR_AMD = 0x1022;
@@ -126,10 +130,11 @@ private:
bool optimalTilingSupported1555 = false;
bool optimalTilingSupported4444 = false;
public:
bool fragmentStoresAndAtomics = false;
bool samplerAnisotropy = false;
f32 maxSamplerAnisotropy = 0.f;
bool dedicatedAllocationSupported = false;
bool fragmentStoresAndAtomics = false;
bool provokingVertexSupported = false;
private:
u32 vendorID = 0;
+2
View File
@@ -55,6 +55,8 @@ void VMAllocator::Init(vk::PhysicalDevice physicalDevice, vk::Device device, vk:
allocatorInfo.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT;
allocatorInfo.physicalDevice = (VkPhysicalDevice)physicalDevice;
// Top-out at vulkan 1.1
allocatorInfo.vulkanApiVersion = (physicalDevice.getProperties().apiVersion >= VK_API_VERSION_1_1) ? VK_API_VERSION_1_1 : VK_API_VERSION_1_0;
allocatorInfo.device = (VkDevice)device;
allocatorInfo.instance = (VkInstance)instance;
#if !defined(NDEBUG) || defined(DEBUGFAST)
+171 -83
View File
@@ -158,10 +158,9 @@ bool VulkanContext::InitInstance(const char** extensions, uint32_t extensions_co
bool vulkan11 = false;
if (VULKAN_HPP_DEFAULT_DISPATCHER.vkEnumerateInstanceVersion != nullptr)
{
u32 apiVersion = vk::enumerateInstanceVersion();
const u32 apiVersion = vk::enumerateInstanceVersion();
vulkan11 = VK_API_VERSION_MAJOR(apiVersion) > 1
|| (VK_API_VERSION_MAJOR(apiVersion) == 1 && VK_API_VERSION_MINOR(apiVersion) >= 1);
vulkan11 = (apiVersion >= VK_API_VERSION_1_1);
}
vk::ApplicationInfo applicationInfo("Flycast", 1, "Flycast", 1, vulkan11 ? VK_API_VERSION_1_1 : VK_API_VERSION_1_0);
@@ -173,11 +172,11 @@ bool VulkanContext::InitInstance(const char** extensions, uint32_t extensions_co
//layer_names.push_back("VK_LAYER_ARM_AGA");
#ifdef VK_DEBUG
#ifndef __ANDROID__
vext.push_back("VK_EXT_debug_utils");
vext.push_back("VK_EXT_debug_report");
vext.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
vext.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
layer_names.push_back("VK_LAYER_KHRONOS_validation");
#else
vext.push_back("VK_EXT_debug_report"); // NDK <= 19?
vext.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); // NDK <= 19?
layer_names.push_back("VK_LAYER_GOOGLE_threading");
layer_names.push_back("VK_LAYER_LUNARG_parameter_validation");
layer_names.push_back("VK_LAYER_LUNARG_core_validation");
@@ -207,25 +206,50 @@ bool VulkanContext::InitInstance(const char** extensions, uint32_t extensions_co
#endif
#endif
const auto devices = instance->enumeratePhysicalDevices();
auto devices = instance->enumeratePhysicalDevices();
if (devices.empty())
{
ERROR_LOG(RENDERER, "Vulkan error: no physical devices found");
return false;
}
// Choose a discrete gpu if there's one, otherwise just pick the first one
physicalDevice = nullptr;
for (const auto& phyDev : devices)
{
if (phyDev.getProperties().deviceType == vk::PhysicalDeviceType::eDiscreteGpu)
// The order of physical-devices provided by the driver should be somewhat preserved with stable-partitions/stable-sorts
// Prefer GPUs that support optimal R5G5B5/R5G6B5A1/R4G4B4A4
const auto supportsOptimalFormat = [](vk::Format format)
{
physicalDevice = phyDev;
break;
return [format](const vk::PhysicalDevice& physicalDevice) -> bool
{
const vk::FormatProperties formatProperties = physicalDevice.getFormatProperties(format);
return (formatProperties.optimalTilingFeatures & vk::FormatFeatureFlagBits::eSampledImage)
&& (formatProperties.optimalTilingFeatures & vk::FormatFeatureFlagBits::eBlitDst)
&& (formatProperties.optimalTilingFeatures & vk::FormatFeatureFlagBits::eBlitSrc);
};
};
std::stable_partition(devices.begin(), devices.end(), supportsOptimalFormat(vk::Format::eR5G6B5UnormPack16));
std::stable_partition(devices.begin(), devices.end(), supportsOptimalFormat(vk::Format::eR5G5B5A1UnormPack16));
std::stable_partition(devices.begin(), devices.end(), supportsOptimalFormat(vk::Format::eR4G4B4A4UnormPack16));
// Prefer GPUs that support fragmentStoresAndAtomics
std::stable_partition(
devices.begin(), devices.end(),
[](const vk::PhysicalDevice& physicalDevice) -> bool
{
return !!physicalDevice.getFeatures().fragmentStoresAndAtomics;
}
}
if (!physicalDevice)
physicalDevice = devices.front();
);
// Finally, prefer Discrete GPUs
std::stable_partition(
devices.begin(), devices.end(),
[](const vk::PhysicalDevice& physicalDevice) -> bool
{
return physicalDevice.getProperties().deviceType == vk::PhysicalDeviceType::eDiscreteGpu;
}
);
// Top of the device-list is the _most_ qualified GPU
physicalDevice = devices.front();
vk::PhysicalDeviceProperties properties = physicalDevice.getProperties();
if (vulkan11 && properties.apiVersion >= VK_API_VERSION_1_1)
@@ -265,11 +289,6 @@ bool VulkanContext::InitInstance(const char** extensions, uint32_t extensions_co
optimalTilingSupported4444 = true;
else
NOTICE_LOG(RENDERER, "eR4G4B4A4UnormPack16 not supported for optimal tiling");
const auto features = physicalDevice.getFeatures();
fragmentStoresAndAtomics = !!features.fragmentStoresAndAtomics;
samplerAnisotropy = !!features.samplerAnisotropy;
if (!fragmentStoresAndAtomics)
NOTICE_LOG(RENDERER, "Fragment stores & atomic not supported: no per-pixel sorting");
ShaderCompiler::Init();
@@ -333,6 +352,8 @@ bool VulkanContext::InitDevice()
return false;
try
{
const vk::PhysicalDeviceProperties physicalDeviceProperties = physicalDevice.getProperties();
std::vector<vk::QueueFamilyProperties> queueFamilyProperties = physicalDevice.getQueueFamilyProperties();
#ifdef VK_DEBUG
std::for_each(queueFamilyProperties.begin(), queueFamilyProperties.end(),
@@ -382,56 +403,117 @@ bool VulkanContext::InitDevice()
else
DEBUG_LOG(RENDERER, "Using distinct Graphics and Present queue families");
// Enable VK_KHR_dedicated_allocation if available
bool getMemReq2Supported = false;
dedicatedAllocationSupported = false;
std::vector<const char *> deviceExtensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME };
for (const auto& property : physicalDevice.enumerateDeviceExtensionProperties())
std::set<std::string> supportedExtensions;
const auto deviceExtensionProperties = physicalDevice.enumerateDeviceExtensionProperties();
for (const auto& property : deviceExtensionProperties)
{
if (!strcmp(property.extensionName, VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME))
{
deviceExtensions.push_back(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME);
getMemReq2Supported = true;
}
else if (!strcmp(property.extensionName, VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME))
{
deviceExtensions.push_back(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME);
dedicatedAllocationSupported = true;
}
else if (!strcmp(property.extensionName, "VK_KHR_portability_subset"))
deviceExtensions.push_back("VK_KHR_portability_subset");
else if (!strcmp(property.extensionName, "VK_EXT_metal_objects"))
deviceExtensions.push_back("VK_EXT_metal_objects");
#ifdef VK_DEBUG
else if (!strcmp(property.extensionName, VK_EXT_DEBUG_MARKER_EXTENSION_NAME))
{
NOTICE_LOG(RENDERER, "Debug extension %s available", property.extensionName.data());
deviceExtensions.push_back(VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
}
else if(!strcmp(property.extensionName, VK_EXT_DEBUG_REPORT_EXTENSION_NAME))
{
NOTICE_LOG(RENDERER, "Debug extension %s available", property.extensionName.data());
deviceExtensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
}
else if (!strcmp(property.extensionName, VK_EXT_DEBUG_UTILS_EXTENSION_NAME))
{
NOTICE_LOG(RENDERER, "Debug extension %s available", property.extensionName.data());
deviceExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
}
#endif
supportedExtensions.insert(property.extensionName);
}
dedicatedAllocationSupported &= getMemReq2Supported;
std::vector<const char*> enabledExtensions;
const auto tryAddDeviceExtension = [&supportedExtensions = std::as_const(supportedExtensions), &enabledExtensions]
(std::string_view extensionName) -> bool
{
if (supportedExtensions.count(extensionName.data()))
{
enabledExtensions.push_back(extensionName.data());
NOTICE_LOG(RENDERER, "Device extension enabled: %s", extensionName.data());
return true;
}
NOTICE_LOG(RENDERER, "Device extension unavailable: %s", extensionName.data());
return false;
};
// Required swapchain extension
tryAddDeviceExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
#ifdef VK_ENABLE_BETA_EXTENSIONS
tryAddDeviceExtension(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME);
#endif
#ifdef VK_USE_PLATFORM_METAL_EXT
tryAddDeviceExtension(VK_EXT_METAL_OBJECTS_EXTENSION_NAME);
#endif
#ifdef VK_DEBUG
tryAddDeviceExtension(VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
#endif
// Enable VK_KHR_dedicated_allocation if available
if (physicalDeviceProperties.apiVersion >= VK_API_VERSION_1_1)
{
// Core in Vulkan 1.1
dedicatedAllocationSupported = true;
}
else
{
const bool getMemReq2Supported = tryAddDeviceExtension(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME);
if (getMemReq2Supported)
{
dedicatedAllocationSupported = tryAddDeviceExtension(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME);
}
}
// Check for VK_KHR_get_physical_device_properties2
// Core as of Vulkan 1.1
const bool getPhysicalDeviceProperties2Supported =
(physicalDeviceProperties.apiVersion >= VK_API_VERSION_1_1)
? true : tryAddDeviceExtension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
if (getPhysicalDeviceProperties2Supported)
{
// Enable VK_EXT_provoking_vertex if available
provokingVertexSupported = tryAddDeviceExtension(VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME);
}
// Get device features
vk::PhysicalDeviceFeatures2 featuresChain{};
vk::PhysicalDeviceFeatures& features = featuresChain.features;
vk::PhysicalDeviceProvokingVertexFeaturesEXT provokingVertexFeatures{};
if (provokingVertexSupported)
{
featuresChain.pNext = &provokingVertexFeatures;
}
// Get the physical device's features
if (getPhysicalDeviceProperties2Supported && featuresChain.pNext)
{
physicalDevice.getFeatures2(&featuresChain);
}
else
{
physicalDevice.getFeatures(&features);
}
if (provokingVertexSupported)
{
provokingVertexSupported &= provokingVertexFeatures.provokingVertexLast;
}
samplerAnisotropy = features.samplerAnisotropy;
fragmentStoresAndAtomics = features.fragmentStoresAndAtomics;
if (!fragmentStoresAndAtomics)
NOTICE_LOG(RENDERER, "Fragment stores & atomic not supported: no per-pixel sorting");
// create a UniqueDevice
float queuePriority = 1.0f;
vk::DeviceQueueCreateInfo deviceQueueCreateInfo(vk::DeviceQueueCreateFlags(), graphicsQueueIndex, 1, &queuePriority);
vk::PhysicalDeviceFeatures features;
if (fragmentStoresAndAtomics)
features.fragmentStoresAndAtomics = true;
if (samplerAnisotropy)
features.samplerAnisotropy = true;
device = physicalDevice.createDeviceUnique(vk::DeviceCreateInfo(vk::DeviceCreateFlags(), deviceQueueCreateInfo,
nullptr, deviceExtensions, &features));
if (getPhysicalDeviceProperties2Supported)
{
vk::DeviceCreateInfo deviceCreateInfo(vk::DeviceCreateFlags(), deviceQueueCreateInfo,
nullptr, enabledExtensions);
deviceCreateInfo.pNext = &featuresChain;
device = physicalDevice.createDeviceUnique(deviceCreateInfo);
}
else
{
device = physicalDevice.createDeviceUnique(vk::DeviceCreateInfo(vk::DeviceCreateFlags(), deviceQueueCreateInfo,
nullptr, enabledExtensions, &features));
}
#if VULKAN_HPP_DISPATCH_LOADER_DYNAMIC == 1
VULKAN_HPP_DEFAULT_DISPATCHER.init(*device);
@@ -690,13 +772,16 @@ void VulkanContext::CreateSwapChain()
framebuffers.reserve(imageViews.size());
drawFences.reserve(imageViews.size());
renderCompleteSemaphores.reserve(imageViews.size());
imageAcquiredSemaphores.reserve(imageViews.size());
for (auto const& view : imageViews)
{
framebuffers.push_back(device->createFramebufferUnique(vk::FramebufferCreateInfo(vk::FramebufferCreateFlags(), *renderPass,
view.get(), width, height, 1)));
drawFences.push_back(device->createFenceUnique(vk::FenceCreateInfo(vk::FenceCreateFlagBits::eSignaled)));
}
renderCompleteSemaphores.reserve(imageViews.size() + 1);
imageAcquiredSemaphores.reserve(imageViews.size() + 1);
for (unsigned i = 0; i < imageViews.size() + 1; i++)
{
renderCompleteSemaphores.push_back(device->createSemaphoreUnique(vk::SemaphoreCreateInfo()));
imageAcquiredSemaphores.push_back(device->createSemaphoreUnique(vk::SemaphoreCreateInfo()));
}
@@ -737,17 +822,15 @@ bool VulkanContext::init()
SDL_Vulkan_GetInstanceExtensions((SDL_Window *)window, &extensionsCount, NULL);
extensions.resize(extensionsCount + extensions.size());
SDL_Vulkan_GetInstanceExtensions((SDL_Window *)window, &extensionsCount, &extensions[extensions.size() - extensionsCount]);
#elif defined(_WIN32)
#elif defined(VK_USE_PLATFORM_WIN32_KHR)
extern void CreateMainWindow();
CreateMainWindow();
extensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
#elif defined(VK_USE_PLATFORM_IOS_MVK)
extensions.push_back(VK_MVK_IOS_SURFACE_EXTENSION_NAME);
#elif defined(VK_USE_PLATFORM_METAL_EXT)
extensions.push_back(VK_EXT_METAL_SURFACE_EXTENSION_NAME);
#elif defined(SUPPORT_X11)
#elif defined(VK_USE_PLATFORM_XLIB_KHR)
extensions.push_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME);
#elif defined(__ANDROID__)
#elif defined(VK_USE_PLATFORM_ANDROID_KHR)
extensions.push_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME);
#endif
if (!InitInstance(&extensions[0], extensions.size())) {
@@ -772,18 +855,15 @@ bool VulkanContext::init()
settings.display.dpi = roundf(std::max(hdpi, vdpi));
sdl_fix_steamdeck_dpi(sdlWin);
#elif defined(_WIN32)
#elif defined(VK_USE_PLATFORM_WIN32_KHR)
vk::Win32SurfaceCreateInfoKHR createInfo(vk::Win32SurfaceCreateFlagsKHR(), GetModuleHandle(NULL), (HWND)window);
surface = instance->createWin32SurfaceKHRUnique(createInfo);
#elif defined(SUPPORT_X11)
#elif defined(VK_USE_PLATFORM_XLIB_KHR)
vk::XlibSurfaceCreateInfoKHR createInfo(vk::XlibSurfaceCreateFlagsKHR(), (Display*)display, (Window)window);
surface = instance->createXlibSurfaceKHRUnique(createInfo);
#elif defined(__ANDROID__)
#elif defined(VK_USE_PLATFORM_ANDROID_KHR)
vk::AndroidSurfaceCreateInfoKHR createInfo(vk::AndroidSurfaceCreateFlagsKHR(), (struct ANativeWindow*)window);
surface = instance->createAndroidSurfaceKHRUnique(createInfo);
#elif defined(VK_USE_PLATFORM_IOS_MVK)
vk::IOSSurfaceCreateInfoMVK createInfo(vk::IOSSurfaceCreateFlagsMVK(), window);
surface = instance->createIOSSurfaceMVKUnique(createInfo);
#elif defined(VK_USE_PLATFORM_METAL_EXT)
vk::MetalSurfaceCreateInfoEXT createInfo(vk::MetalSurfaceCreateFlagsEXT(), window);
surface = instance->createMetalSurfaceEXTUnique(createInfo);
@@ -866,14 +946,14 @@ void VulkanContext::Present() noexcept
DoSwapAutomation();
vk::Result res = presentQueue.presentKHR(vk::PresentInfoKHR(1, &(*renderCompleteSemaphores[currentSemaphore]), 1, &(*swapChain), &currentImage));
(void)res;
currentSemaphore = (currentSemaphore + 1) % imageViews.size();
currentSemaphore = (currentSemaphore + 1) % renderCompleteSemaphores.size();
if (lastFrameView && IsValid() && !gui_is_open())
for (int i = 1; i < swapInterval; i++)
{
PresentFrame(vk::Image(), lastFrameView, lastFrameExtent, lastFrameAR);
res = presentQueue.presentKHR(vk::PresentInfoKHR(1, &(*renderCompleteSemaphores[currentSemaphore]), 1, &(*swapChain), &currentImage));
currentSemaphore = (currentSemaphore + 1) % imageViews.size();
currentSemaphore = (currentSemaphore + 1) % renderCompleteSemaphores.size();
}
} catch (const vk::SystemError& e) {
// Happens when resizing the window
@@ -897,7 +977,7 @@ void VulkanContext::Present() noexcept
void VulkanContext::DrawFrame(vk::ImageView imageView, const vk::Extent2D& extent, float aspectRatio)
{
QuadVertex vtx[] {
QuadVertex vtx[4] {
{ -1, -1, 0, 0, 0 },
{ 1, -1, 0, 1, 0 },
{ -1, 1, 0, 0, 1 },
@@ -911,6 +991,10 @@ void VulkanContext::DrawFrame(vk::ImageView imageView, const vk::Extent2D& exten
vtx[2].y = vtx[3].y = vtx[0].y + 2;
vk::CommandBuffer commandBuffer = GetCurrentCommandBuffer();
static const float scopeColor[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
CommandBufferDebugScope _(commandBuffer, "DrawFrame", scopeColor);
if (config::Rotate90)
quadRotatePipeline->BindPipeline(commandBuffer);
else
@@ -1264,6 +1348,10 @@ bool VulkanContext::GetLastFrame(std::vector<u8>& data, int& width, int& height)
vk::UniqueCommandBuffer commandBuffer = std::move(device->allocateCommandBuffersUnique(
vk::CommandBufferAllocateInfo(*commandPools.back(), vk::CommandBufferLevel::ePrimary, 1)).front());
commandBuffer->begin(vk::CommandBufferBeginInfo(vk::CommandBufferUsageFlagBits::eOneTimeSubmit));
static const float scopeColor[4] = { 1.0f, 1.0f, 0.0f, 1.0f };
CommandBufferDebugScope _(commandBuffer.get(), "GetLastFrame", scopeColor);
// render pass
vk::AttachmentDescription attachmentDescription = vk::AttachmentDescription(vk::AttachmentDescriptionFlags(), vk::Format::eR8G8B8A8Unorm, vk::SampleCountFlagBits::e1,
vk::AttachmentLoadOp::eClear, vk::AttachmentStoreOp::eStore, vk::AttachmentLoadOp::eDontCare, vk::AttachmentStoreOp::eDontCare,
@@ -1287,7 +1375,7 @@ bool VulkanContext::GetLastFrame(std::vector<u8>& data, int& width, int& height)
pipeline.BindPipeline(*commandBuffer);
// Draw
QuadVertex vtx[] {
QuadVertex vtx[4] {
{ -1, -1, 0, 0, 0 },
{ 1, -1, 0, 1, 0 },
{ -1, 1, 0, 0, 1 },
Loaded 100 of 131 files, more files were not shown because too many files have changed in this diff. Show more