10 Commits
Author SHA1 Message Date
Flyinghead 72b8e67141 vk: fix some validation errors
renderCompleteSemaphore must be indexed by currentImage because the
driver may return any image index when acquiring.
Transition the RTT depth buffer to correct layout after creation.
2026-11-09 18:15:18 +01:00
Flyinghead 1b7c7d63b7 reios: pass correct data size to GetMemPtr when reading sectors
so that it falls backs to reading one sector at a time
2026-11-08 13:07:35 +01:00
Flyinghead 222c9530da bba: don't [de]serialize bba if not available on the current platform
Fixes MINIDUMP-AGM
2026-11-08 13:01:58 +01:00
Flyinghead eb7f4418f1 ci: enable macOS build 2026-11-08 12:29:55 +01:00
github-actions[bot] 7adbf3caef Fetch translations & Recreate libretro_core_options_intl.h 2026-10-31 01:33:35 +00:00
Joseph MattielloandClaude Opus 5.5 1b63c8a367 dreamcast: Default broadcast follows the region
Broadcast has a Default entry in the settings (value 4), but
fixUpDCFlash() only writes values in range, so Default left the flash
byte alone: NTSC from the factory block on a fresh flash, or whatever
the last explicit choice had written. A PAL game booted in NTSC with
Broadcast on Default.

Region's own Default is resolved from IP.BIN just above; resolve
Broadcast the same way: PAL for Europe, NTSC otherwise. Applied with
override() at load, like the region, so the saved setting stays Default.

Testing: compiles on macOS arm64.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-27 11:48:03 +01:00
Chris Dixon 11ba75f843 libretro: add a Vblank Pacing option (#2562)
Without threaded rendering a run ends when a frame is presented, or
after 50 ms of emulated time without one. Frontends that pace retro_run
by the frame rate instead of blocking on audio, Kodi among them, then
get several vblanks per run through loading screens and have to drop
the extra audio. With the option on, every run ends at the next vblank.
[ci skip]
2026-10-27 11:47:43 +01:00
github-actions[bot] e88b0ced22 Fetch translations & Recreate libretro_core_options_intl.h 2026-10-24 00:37:48 +00:00
Flyinghead 6d81b99816 add pull request template. Fix issue templates. 2026-10-23 11:20:53 +02:00
Flyinghead 53efedbbe7 libretro: don't call the frontend on the emulator thread
Fixes crash with many WinCE games. Issue #2442
2026-10-23 11:20:08 +02:00
18 changed files with 2806 additions and 2078 deletions

No files matched your search

+8 -9
View File
@@ -7,8 +7,8 @@ assignees: ''
---
**Please Note: This form is the minimum required information for submitting bugs.**
**Removing this form may lead to your issue being closed until it is completed.**
<!-- Please Note: This form is the minimum required information for submitting bugs. -->
<!-- Removing this form may lead to your issue being closed until it is completed. -->
Platform / OS / Hardware:
@@ -18,24 +18,23 @@ Hardware:
**_Description of the Issue_**
Add a short, concise description of the issue here
<!-- Add a short, concise description of the issue here -->
**_Debugging Steps Tested_**
* Fill in any steps already tried here
* Begin each new line with an asterisk
* If no steps are required, please skip
<!-- Fill in any steps already tried here -->
<!-- Begin each new line with an asterisk -->
<!-- If no steps are required, please skip -->
**_Logs Gathered_**
```
Please paste the contents of the log / logs here
You may leave this field blank if you have none.
Delete this field if you have none.
```
**_Screenshots_**
(Replace this line with any issue screenshots)
<!-- Replace this line with any issue screenshots -->
+4 -4
View File
@@ -8,13 +8,13 @@ assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
<!-- A clear and concise description of what you want to happen. -->
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
**Additional context**
Add any other context or screenshots about the feature request here.
<!-- Add any other context or screenshots about the feature request here. -->
+11
View File
@@ -0,0 +1,11 @@
## Description
<!-- Summarize the change and its motivation. -->
<!-- Use "Fixes #123" for an issue this PR closes, or "Related to #123" otherwise. -->
## Checklist
- [ ] This PR targets `dev`, unless it is a critical, low-risk bug fix.
- [ ] I linked the relevant issue or discussion, if one exists.
- [ ] I added or updated tests where appropriate.
- [ ] I tested the change on the relevant platform(s) and noted any limitations.
- [ ] For UI changes, I included screenshots or a recording where helpful.
- [ ] I kept the change focused, followed existing project patterns and avoided code duplication.
+2 -1
View File
@@ -36,7 +36,6 @@ jobs:
brew list -1 | grep python | while read formula; do brew unlink $formula; brew link --overwrite $formula; done
brew update || :
brew install libao ldid pulseaudio
brew uninstall --ignore-dependencies zstd
moltenvk_tag="v1.4.2"
moltenvk_root="$RUNNER_TEMP/MoltenVK-$moltenvk_tag"
@@ -106,6 +105,8 @@ jobs:
fi
done
echo "CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH" >> $GITHUB_ENV
# After the last brew download: brew's own curl links brew's zstd
brew uninstall --ignore-dependencies zstd
if: matrix.config.name == 'apple-darwin'
- uses: actions/cache@v6
+1
View File
@@ -110,6 +110,7 @@ Option<int64_t> PixelBufferSize("rend.PixelBufferSize", 512_MB);
Option<int> AnisotropicFiltering("rend.AnisotropicFiltering", 1);
Option<int> TextureFiltering("rend.TextureFiltering", 0); // Default
Option<bool> ThreadedRendering("rend.ThreadedRendering", true);
Option<bool> VblankPacing("");
Option<bool> DupeFrames("rend.DupeFrames", false);
Option<int> PerPixelLayers("rend.PerPixelLayers", 32);
#ifdef TARGET_UWP
+1
View File
@@ -471,6 +471,7 @@ extern Option<int64_t> PixelBufferSize;
extern Option<int> AnisotropicFiltering;
extern Option<int> TextureFiltering; // 0: default, 1: force nearest, 2: force linear
extern Option<bool> ThreadedRendering;
extern Option<bool> VblankPacing;
extern Option<bool> DupeFrames;
extern Option<bool> NativeDepthInterpolation;
extern Option<bool> EmulateFramebuffer;
+8 -1
View File
@@ -238,6 +238,13 @@ static void loadSpecialSettings()
}
else
WARN_LOG(BOOT, "No region specified in IP.BIN");
// Broadcast left on Default follows the region, the way Region's own
// Default follows IP.BIN above: PAL for Europe, NTSC otherwise.
// fixUpDCFlash() skips the out-of-range Default value, which left the
// flash byte at the factory setting (NTSC) or at whatever the last
// explicit choice wrote.
if (config::Broadcast == 4)
config::Broadcast.override(config::Region == 2 ? 1 : 0);
if (config::Cable <= 1 && (!ip_meta.supportsVGA()
|| prod_id == "T-12504N" // Caesar's Palace (NTSC)
|| prod_id == "12502D-50")) // Caesar's Palace (PAL)
@@ -1088,7 +1095,7 @@ void Emulator::vblank()
EventManager::event(Event::VBlank);
runner.execTasks();
// Time out if a frame hasn't been rendered for 50 ms
if (sh4_sched_now64() - startTime <= 50_sh4ms)
if (!config::VblankPacing && sh4_sched_now64() - startTime <= 50_sh4ms)
return;
if (ggpo::active())
ggpo::endOfFrame();
+4
View File
@@ -292,6 +292,8 @@ void pci_dma_write(PCIDevice *dev, dma_addr_t addr, const void *buf, dma_addr_t
void bba_Serialize(Serializer& ser)
{
if (rtl8139device == nullptr)
return;
ser << GAPS_regs;
ser << GAPS_ram;
ser << dmaOffset;
@@ -301,6 +303,8 @@ void bba_Serialize(Serializer& ser)
void bba_Deserialize(Deserializer& deser)
{
if (rtl8139device == nullptr)
return;
deser >> GAPS_regs;
deser >> GAPS_ram;
deser >> dmaOffset;
+1 -1
View File
@@ -268,7 +268,7 @@ private:
if (renderer->Present())
{
presented = true;
if (!config::ThreadedRendering && !ggpo::active())
if (!config::ThreadedRendering && !config::VblankPacing && !ggpo::active())
emu.getSh4Executor()->Stop();
#ifdef LIBRETRO
retro_rend_present();
+2 -2
View File
@@ -42,7 +42,7 @@ static u32 lightgun_hpos;
static bool maple_int_pending;
#ifdef LIBRETRO
extern bool retro_refresh_av_info(double fps);
extern void retro_setfps(double fps);
#endif
void CalculateSync()
@@ -64,7 +64,7 @@ void CalculateSync()
sh4_sched_request(vblank_schid, Line_Cycles);
#ifdef LIBRETRO
retro_refresh_av_info((double)SH4_MAIN_CLOCK / ((double)Line_Cycles * (double)pvr_numscanlines));
retro_setfps((double)SH4_MAIN_CLOCK / ((double)Line_Cycles * (double)pvr_numscanlines));
#endif
}
+1 -1
View File
@@ -187,7 +187,7 @@ static void readSectors(u32 addr, u32 sector, u32 count, bool virtualAddr)
gd_hle_state.cur_sector = sector + count - 1;
if (!virtualAddr || !mmu_enabled())
{
u8 * pDst = GetMemPtr(addr, 0);
u8 * pDst = GetMemPtr(addr, count * 2048);
if (pDst != NULL)
{
+2
View File
@@ -445,6 +445,8 @@ vk::CommandBuffer TextureDrawer::BeginRenderPass()
depthAttachment->Init(width, height, GetContext()->GetDepthFormat(),
vk::ImageUsageFlagBits::eDepthStencilAttachment | vk::ImageUsageFlagBits::eTransientAttachment,
"RTT DEPTH ATTACHMENT");
setImageLayout(commandBuffer, depthAttachment->GetImage(), GetContext()->GetDepthFormat(),
1, vk::ImageLayout::eUndefined, vk::ImageLayout::eDepthStencilAttachmentOptimal);
}
vk::Image colorImage;
vk::ImageView colorImageView;
+13 -17
View File
@@ -870,7 +870,7 @@ void VulkanContext::CreateSwapChain()
nullptr, nullptr);
renderPass = device->createRenderPassUnique(vk::RenderPassCreateInfo(vk::RenderPassCreateFlags(),
attachmentDescription, subpass));
attachmentDescription, subpass));
// Framebuffers, fences, semaphores
@@ -882,13 +882,14 @@ void VulkanContext::CreateSwapChain()
view.get(), width, height, 1)));
drawFences.push_back(device->createFenceUnique(vk::FenceCreateInfo(vk::FenceCreateFlagBits::eSignaled)));
}
renderCompleteSemaphores.reserve(imageViews.size() + 1);
// indexed with currentImage
renderCompleteSemaphores.reserve(imageViews.size());
for (unsigned i = 0; i < imageViews.size(); i++)
renderCompleteSemaphores.push_back(device->createSemaphoreUnique(vk::SemaphoreCreateInfo()));
// indexed with currentSemaphore
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()));
}
inFlightObjects.resize(imageViews.size());
currentSemaphore = 0;
quadPipeline->Init(shaderManager.get(), *renderPass, 0);
@@ -1032,9 +1033,10 @@ void VulkanContext::EndFrame(vk::CommandBuffer overlayCmdBuffer)
if (overlayCmdBuffer)
allCmdBuffers.push_back(overlayCmdBuffer);
allCmdBuffers.push_back(commandBuffer);
vk::SubmitInfo submitInfo(*imageAcquiredSemaphores[currentSemaphore], wait_stage, allCmdBuffers, *renderCompleteSemaphores[currentSemaphore]);
vk::SubmitInfo submitInfo(*imageAcquiredSemaphores[currentSemaphore], wait_stage, allCmdBuffers, *renderCompleteSemaphores[currentImage]);
device->resetFences(*drawFences[currentImage]);
graphicsQueue.submit(submitInfo, *drawFences[currentImage]);
currentSemaphore = (currentSemaphore + 1) % imageAcquiredSemaphores.size();
assert(rendering);
rendering = false;
renderDone = true;
@@ -1046,7 +1048,7 @@ void VulkanContext::Present() noexcept
{
try {
DoSwapAutomation();
vk::Result res = swappyvk::present(presentQueue, vk::PresentInfoKHR(1, &(*renderCompleteSemaphores[currentSemaphore]), 1, &(*swapChain), &currentImage));
vk::Result res = swappyvk::present(presentQueue, vk::PresentInfoKHR(1, &(*renderCompleteSemaphores[currentImage]), 1, &(*swapChain), &currentImage));
#if defined(VK_USE_PLATFORM_METAL_EXT) || defined(__APPLE__)
// presentKHR lists eSuboptimalKHR as a success code so it is
// returned rather than thrown. Treat it as a request to
@@ -1059,25 +1061,19 @@ void VulkanContext::Present() noexcept
if (res == vk::Result::eSuboptimalKHR)
resized = true;
#endif
currentSemaphore = (currentSemaphore + 1) % renderCompleteSemaphores.size();
#ifndef SWAPPY
if (lastFrameView && IsValid() && !gui_is_open()
&& swapOnVSync && config::DupeFrames)
{
const int interval = swapInterval * gameSwapInterval;
for (int i = 1; i < interval; i++)
for (int i = 1; i < interval && !resized; i++)
{
PresentFrame(vk::Image(), lastFrameView, lastFrameExtent, lastFrameAR);
res = presentQueue.presentKHR(vk::PresentInfoKHR(1, &(*renderCompleteSemaphores[currentSemaphore]), 1, &(*swapChain), &currentImage));
currentSemaphore = (currentSemaphore + 1) % renderCompleteSemaphores.size();
res = presentQueue.presentKHR(vk::PresentInfoKHR(1, &(*renderCompleteSemaphores[currentImage]), 1, &(*swapChain), &currentImage));
#if defined(VK_USE_PLATFORM_METAL_EXT) || defined(__APPLE__)
if (res == vk::Result::eSuboptimalKHR)
{
// Stop queuing additional dupe presents into a swap
// chain that we are about to tear down: the extra
// in-flight semaphores would just delay waitIdle().
resized = true;
break;
}
#endif
}
}
#endif
+9 -6
View File
@@ -764,16 +764,19 @@ bool setAVInfo(retro_system_av_info& avinfo)
return true;
}
bool retro_refresh_av_info(double fps)
void retro_setfps(double fps)
{
if (fps > 0 && fps < 100)
fps_spg = fps;
}
static bool retro_refresh_av_info()
{
retro_system_av_info avinfo;
if (first_run || game_data.empty())
return false;
if (fps > 0 && fps < 100)
fps_spg = fps;
if (setAVInfo(avinfo))
{
environ_cb(RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO, &avinfo);
@@ -805,7 +808,7 @@ void retro_resize_renderer(int w, int h, float aspectRatio)
else
{
// Check if timing change is needed instead
if (retro_refresh_av_info(0))
if (retro_refresh_av_info())
return;
retro_game_geometry geometry;
@@ -2886,7 +2889,7 @@ static void updateLightgunCoordinates(u32 port)
lightgun_params[port].y = mo_y_abs[port];
}
void updateLightgunCoordinatesFromAnalogStick(int port)
static void updateLightgunCoordinatesFromAnalogStick(int port)
{
int x = input_cb(port, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X);
mo_x_abs[port] = 320 + x * 320 / 32767;
+14
View File
@@ -644,6 +644,20 @@ struct retro_core_option_v2_definition option_defs_us[] = {
},
"enabled",
},
{
CORE_OPTION_NAME "_vblank_pacing",
"Vblank Pacing",
NULL,
"Each frame the frontend runs emulates exactly one vblank, whether or not the game drew. For frontends that pace the core by its frame rate rather than by blocking on audio; without it, loading screens that draw nothing run several vblanks per frame and their sound arrives in bursts. Needs Threaded Rendering disabled.",
NULL,
"performance",
{
{ "disabled", NULL },
{ "enabled", NULL },
{ NULL, NULL },
},
"disabled",
},
{
CORE_OPTION_NAME "_auto_skip_frame",
"Auto Skip Frame",
File diff suppressed because it is too large. Load diff
+1
View File
@@ -89,6 +89,7 @@ Option<bool> IntegerScale("");
Option<bool> LinearInterpolation("", true);
Option<bool> VSync("", true);
Option<bool> ThreadedRendering(CORE_OPTION_NAME "_threaded_rendering", true);
Option<bool> VblankPacing(CORE_OPTION_NAME "_vblank_pacing");
Option<int> AnisotropicFiltering(CORE_OPTION_NAME "_anisotropic_filtering");
Option<int> TextureFiltering(CORE_OPTION_NAME "_texture_filtering");
Option<bool> PowerVR2Filter(CORE_OPTION_NAME "_pvr2_filtering");
+1 -1
View File
@@ -59,7 +59,7 @@ private:
std::vector<BaseOption *> options;
std::string gameId;
retro_environment_t retroEnv = nullptr;
const retro_core_option_v2_definition *optionDefs;
const retro_core_option_v2_definition *optionDefs = nullptr;
template<typename T, bool>
friend class Option;