Merge remote-tracking branch 'origin/master' into fh/directx

This commit is contained in:
Flyinghead 2021-04-29 19:06:38 +02:00
commit 150cfa29e7
32 changed files with 407 additions and 172 deletions

View File

@ -1,19 +1,36 @@
<img src="https://github.com/flyinghead/flycast/raw/master/shell/linux/flycast.png" width="200"/>
# Flycast
Flycast
===========
**Flycast** is a multi-platform Sega Dreamcast, Naomi and Atomiswave emulator derived from [**reicast**](https://reicast.com/)
[![C/C++ CI](https://github.com/flyinghead/flycast/actions/workflows/c-cpp.yml/badge.svg)](https://github.com/flyinghead/flycast/actions/workflows/c-cpp.yml)
[![Android CI](https://github.com/flyinghead/flycast/actions/workflows/android.yml/badge.svg)](https://github.com/flyinghead/flycast/actions/workflows/android.yml)
Information about configuration and supported features can be found on [**TheArcadeStriker's flycast wiki**](https://github.com/TheArcadeStriker/flycast-wiki/wiki)
![flycast logo](https://github.com/flyinghead/flycast/raw/master/shell/linux/flycast.png)
**Flycast** is a multi-platform Sega Dreamcast, Naomi and Atomiswave emulator derived from [**reicast**](https://reicast.com/).
Information about configuration and supported features can be found on [**TheArcadeStriker's flycast wiki**](https://github.com/TheArcadeStriker/flycast-wiki/wiki).
Join us on our [**Discord server**](https://discord.gg/X8YWP8w) for a chat.
Binaries
========
## Install
### Flatpak
1. [Set up Flatpak](https://www.flatpak.org/setup/)
2. Install Flycast from [Flathub](https://flathub.org/apps/details/org.flycast.Flycast)
`flatpak install -y org.flycast.Flycast`
3. Run Flycast
`flatpak run org.flycast.Flycast`
### Binaries
Get fresh builds for your system [on the builds page](https://flyinghead.github.io/flycast-builds/).
**New:** Now automated test results are available as well.
Disclaimer
==========
## Disclaimer
All code contritbuted to this fork is *not* bound by the Individual Contributor License Agreement of the upstream repository (https://github.com/reicast/reicast-emulator) and shall *not* be considered as a contribution to the upstream repository.

View File

@ -61,6 +61,7 @@ OptionString AudioBackend("backend", "auto", "audio");
RendererOption RendererType;
Option<bool> UseMipmaps("rend.UseMipmaps", true);
Option<bool> Widescreen("rend.WideScreen");
Option<bool> SuperWidescreen("rend.SuperWideScreen");
Option<bool> ShowFPS("rend.ShowFPS");
Option<bool> RenderToTextureBuffer("rend.RenderToTextureBuffer");
Option<int> RenderToTextureUpscale("rend.RenderToTextureUpscale", 1);

View File

@ -356,6 +356,7 @@ private:
extern RendererOption RendererType;
extern Option<bool> UseMipmaps;
extern Option<bool> Widescreen;
extern Option<bool> SuperWidescreen;
extern Option<bool> ShowFPS;
extern Option<bool> RenderToTextureBuffer;
extern Option<int> RenderToTextureUpscale;

View File

@ -46,6 +46,7 @@ bool dc_is_load_done();
void dc_cancel_load();
void dc_get_load_status();
bool dc_is_running();
void dc_resize_renderer();
enum class Event {
Start,

View File

@ -40,6 +40,7 @@
#include "debug/gdb_server.h"
settings_t settings;
extern int screen_width, screen_height;
cThread emu_thread(&dc_run, NULL);
@ -527,7 +528,7 @@ static void dc_start_game(const char *path)
{
// Boot BIOS
if (!LoadRomFiles())
throw ReicastException("No BIOS file found");
throw ReicastException("No BIOS file found in " + get_writable_data_path(""));
TermDrive();
InitDrive();
}
@ -734,15 +735,16 @@ void SaveSettings()
#endif
}
void dc_resume()
void dc_resize_renderer()
{
SetMemoryHandlers();
settings.aica.NoBatch = config::ForceWindowsCE || config::DSPEnabled;
int hres;
int vres = config::RenderResolution;
if (config::Widescreen && !config::Rotate90)
{
hres = config::RenderResolution * 16 / 9;
if (config::SuperWidescreen)
hres = config::RenderResolution * screen_width / screen_height ;
else
hres = config::RenderResolution * 16 / 9;
}
else if (config::Rotate90)
{
@ -755,6 +757,13 @@ void dc_resume()
}
if (renderer != nullptr)
renderer->Resize(hres, vres);
}
void dc_resume()
{
SetMemoryHandlers();
settings.aica.NoBatch = config::ForceWindowsCE || config::DSPEnabled;
dc_resize_renderer();
EventManager::event(Event::Resume);
if (!emu_thread.thread.joinable())

View File

@ -1,41 +1,139 @@
#include "audiostream.h"
#ifdef _WIN32
#include "oslib.h"
#include "audiostream.h"
#include <initguid.h>
#include <dsound.h>
#ifdef USE_SDL
#include "sdl/sdl.h"
#endif
#include <vector>
#include <algorithm>
#include <atomic>
#include <thread>
#include "stdclass.h"
#define verifyc(x) verify(!FAILED(x))
void* SoundThread(void* param);
#define V2_BUFFERSZ (16*1024)
static IDirectSound8* dsound;
static IDirectSoundBuffer8* buffer;
static std::vector<HANDLE> notificationEvents;
static IDirectSoundCapture8 *dcapture;
static IDirectSoundCaptureBuffer8 *capture_buffer;
static u32 ds_ring_size;
static std::atomic_bool audioThreadRunning;
static std::thread audioThread;
static cResetEvent pushWait;
constexpr u32 SAMPLE_BYTES = SAMPLE_COUNT * 4;
class RingBuffer
{
std::vector<u8> buffer;
std::atomic_int readCursor { 0 };
std::atomic_int writeCursor { 0 };
u32 readSize() {
return (writeCursor - readCursor + buffer.size()) % buffer.size();
}
u32 writeSize() {
return (readCursor - writeCursor + buffer.size() - 1) % buffer.size();
}
public:
bool write(const u8 *data, u32 size)
{
if (size > writeSize())
return false;
u32 wc = writeCursor;
u32 chunkSize = std::min<u32>(size, buffer.size() - wc);
memcpy(&buffer[wc], data, chunkSize);
wc = (wc + chunkSize) % buffer.size();
size -= chunkSize;
if (size > 0)
{
data += chunkSize;
memcpy(&buffer[wc], data, size);
wc = (wc + size) % buffer.size();
}
writeCursor = wc;
return true;
}
bool read(u8 *data, u32 size)
{
if (size > readSize())
return false;
u32 rc = readCursor;
u32 chunkSize = std::min<u32>(size, buffer.size() - rc);
memcpy(data, &buffer[rc], chunkSize);
rc = (rc + chunkSize) % buffer.size();
size -= chunkSize;
if (size > 0)
{
data += chunkSize;
memcpy(data, &buffer[rc], size);
rc = (rc + size) % buffer.size();
}
readCursor = rc;
return true;
}
void setCapacity(size_t size)
{
std::fill(buffer.begin(), buffer.end(), 0);
buffer.resize(size);
readCursor = 0;
writeCursor = 0;
}
};
static RingBuffer ringBuffer;
static u32 notificationOffset(int index) {
return index * SAMPLE_BYTES;
}
static void audioThreadMain()
{
audioThreadRunning = true;
while (true)
{
u32 rv = WaitForMultipleObjects(notificationEvents.size(), &notificationEvents[0], false, 100);
if (!audioThreadRunning)
break;
if (rv == WAIT_TIMEOUT || rv == WAIT_FAILED)
continue;
rv -= WAIT_OBJECT_0;
void *p1, *p2;
DWORD sz1, sz2;
if (SUCCEEDED(buffer->Lock(notificationOffset(rv), SAMPLE_BYTES, &p1, &sz1, &p2, &sz2, 0)))
{
if (!ringBuffer.read((u8*)p1, sz1))
memset(p1, 0, sz1);
if (sz2 != 0)
{
if (!ringBuffer.read((u8*)p2, sz2))
memset(p2, 0, sz2);
}
buffer->Unlock(p1, sz1, p2, sz2);
pushWait.Set();
}
}
}
static void directsound_init()
{
verifyc(DirectSoundCreate8(NULL,&dsound,NULL));
verifyc(DirectSoundCreate8(NULL, &dsound, NULL));
#ifdef USE_SDL
verifyc(dsound->SetCooperativeLevel(sdl_get_native_hwnd(), DSSCL_PRIORITY));
#else
verifyc(dsound->SetCooperativeLevel((HWND)libPvr_GetRenderTarget(), DSSCL_PRIORITY));
#endif
IDirectSoundBuffer* buffer_;
WAVEFORMATEX wfx;
DSBUFFERDESC desc;
// Set up WAV format structure.
WAVEFORMATEX wfx;
memset(&wfx, 0, sizeof(WAVEFORMATEX));
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = 2;
@ -45,104 +143,64 @@ static void directsound_init()
wfx.wBitsPerSample = 16;
// Set up DSBUFFERDESC structure.
ds_ring_size=8192*wfx.nBlockAlign;
DSBUFFERDESC desc;
memset(&desc, 0, sizeof(DSBUFFERDESC));
desc.dwSize = sizeof(DSBUFFERDESC);
desc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GLOBALFOCUS;
desc.dwBufferBytes = ds_ring_size;
desc.dwBufferBytes = SAMPLE_BYTES * 2;
desc.lpwfxFormat = &wfx;
verifyc(dsound->CreateSoundBuffer(&desc,&buffer_,0));
verifyc(buffer_->QueryInterface(IID_IDirectSoundBuffer8,(void**)&buffer));
// Create the buffer
IDirectSoundBuffer* buffer_;
verifyc(dsound->CreateSoundBuffer(&desc, &buffer_, 0));
verifyc(buffer_->QueryInterface(IID_IDirectSoundBuffer8, (void**)&buffer));
buffer_->Release();
//Play the buffer !
verifyc(buffer->Play(0,0,DSBPLAY_LOOPING));
}
static DWORD wc=0;
static int directsound_getfreesz()
{
DWORD pc,wch;
buffer->GetCurrentPosition(&pc,&wch);
int fsz=0;
if (wc>=pc)
fsz=ds_ring_size-wc+pc;
else
fsz=pc-wc;
fsz-=32;
return fsz;
}
static int directsound_getusedSamples()
{
return (ds_ring_size-directsound_getfreesz())/4;
}
static u32 directsound_push_nw(const void* frame, u32 samplesb)
{
DWORD pc,wch;
u32 bytes=samplesb*4;
buffer->GetCurrentPosition(&pc,&wch);
int fsz=0;
if (wc>=pc)
fsz=ds_ring_size-wc+pc;
else
fsz=pc-wc;
fsz-=32;
//printf("%d: r:%d w:%d (f:%d wh:%d)\n",fsz>bytes,pc,wc,fsz,wch);
if (fsz>bytes)
// Set up notifications
IDirectSoundNotify *bufferNotify;
verifyc(buffer->QueryInterface(IID_IDirectSoundNotify8, (void**)&bufferNotify));
notificationEvents.clear();
std::vector<DSBPOSITIONNOTIFY> posNotify;
for (int i = 0; notificationOffset(i) < desc.dwBufferBytes; i++)
{
void* ptr1,* ptr2;
DWORD ptr1sz,ptr2sz;
const u8* data=(const u8*)frame;
buffer->Lock(wc,bytes,&ptr1,&ptr1sz,&ptr2,&ptr2sz,0);
memcpy(ptr1,data,ptr1sz);
if (ptr2sz)
{
data+=ptr1sz;
memcpy(ptr2,data,ptr2sz);
}
buffer->Unlock(ptr1,ptr1sz,ptr2,ptr2sz);
wc=(wc+bytes)%ds_ring_size;
return 1;
notificationEvents.push_back(CreateEvent(nullptr, false, false, nullptr));
posNotify.push_back({ notificationOffset(i), notificationEvents.back() });
}
return 0;
//ds_ring_size
bufferNotify->SetNotificationPositions(posNotify.size(), &posNotify[0]);
bufferNotify->Release();
// Clear the buffers
void *p1, *p2;
DWORD sz1, sz2;
verifyc(buffer->Lock(0, desc.dwBufferBytes, &p1, &sz1, &p2, &sz2, 0));
verify(p2 == nullptr);
memset(p1, 0, sz1);
verifyc(buffer->Unlock(p1, sz1, p2, sz2));
ringBuffer.setCapacity(config::AudioBufferSize * 4);
// Start the thread
audioThread = std::thread(audioThreadMain);
// Play the buffer !
verifyc(buffer->Play(0, 0, DSBPLAY_LOOPING));
}
static u32 directsound_push(const void* frame, u32 samples, bool wait)
{
while (!directsound_push_nw(frame, samples) && wait)
//DEBUG_LOG(AUDIO, "FAILED waiting on audio FAILED %d", directsound_getusedSamples())
;
while (!ringBuffer.write((const u8 *)frame, samples * 4) && wait)
pushWait.Wait();
return 1;
}
static void directsound_term()
{
audioThreadRunning = false;
audioThread.join();
buffer->Stop();
for (HANDLE event : notificationEvents)
CloseHandle(event);
buffer->Release();
dsound->Release();
}

View File

@ -111,10 +111,9 @@ static u32 sdl2_audio_push(const void* frame, u32 samples, bool wait) {
// If wait, then wait for the buffer to be smaller than a certain size.
stream_mutex.lock();
if (wait) {
while (sample_count + samples > config::AudioBufferSize) {
while (sample_count + samples > (u32)config::AudioBufferSize) {
stream_mutex.unlock();
read_wait.Wait();
read_wait.Reset();
stream_mutex.lock();
}
}

View File

@ -56,21 +56,28 @@ class GameScanner
void add_game_directory(const std::string& path)
{
// FIXME this won't work anymore
if (game_list.empty())
{
++empty_folders_scanned;
if (empty_folders_scanned > 1000)
content_path_looks_incorrect = true;
}
else
{
content_path_looks_incorrect = false;
}
DirectoryTree tree(path);
std::string emptyParentPath = "";
for (const DirectoryTree::item& item : tree)
{
if (running == false)
break;
if (game_list.size() == 0)
{
if(item.parentPath.compare(emptyParentPath))
{
++empty_folders_scanned;
emptyParentPath = item.parentPath;
if (empty_folders_scanned > 1000)
content_path_looks_incorrect = true;
}
}
else
{
content_path_looks_incorrect = false;
}
if (item.name.substr(0, 2) == "._")
// Ignore Mac OS turds
continue;

View File

@ -714,15 +714,11 @@ static bool RenderFrame(int width, int height)
//setup render target first
if (is_rtt)
{
output_fbo = BindRTT(false);
if (output_fbo == 0)
return false;
}
else
{
output_fbo = init_output_framebuffer(rendering_width, rendering_height);
}
if (output_fbo == 0)
return false;
glcache.Disable(GL_SCISSOR_TEST);

View File

@ -1101,7 +1101,8 @@ bool RenderFrame(int width, int height)
}
else
{
init_output_framebuffer(width, height);
if (init_output_framebuffer(width, height) == 0)
return false;
}
bool wide_screen_on = !is_rtt && config::Widescreen && !matrices.IsClipped() && !config::Rotate90;

View File

@ -523,7 +523,8 @@ GLuint init_output_framebuffer(int width, int height)
// Check that our FBO creation was successful
GLuint uStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
verify(uStatus == GL_FRAMEBUFFER_COMPLETE);
if (uStatus != GL_FRAMEBUFFER_COMPLETE)
return 0;
glcache.Disable(GL_SCISSOR_TEST);
glcache.ClearColor(0.f, 0.f, 0.f, 0.f);

View File

@ -52,6 +52,7 @@ int screen_dpi = 96;
static bool inited = false;
float scaling = 1;
GuiState gui_state = GuiState::Main;
static bool commandLineStart;
#ifdef __ANDROID__
static bool touch_up;
#endif
@ -235,7 +236,25 @@ void gui_init()
{
io.Fonts->AddFontFromFileTTF((fontDir + "PingFang.ttc").c_str(), 17.f * scaling, &font_cfg, GetGlyphRangesChineseSimplifiedOfficial());
}
// TODO linux, Android...
#elif defined(__ANDROID__)
if (getenv("FLYCAST_LOCALE") != nullptr)
{
const ImWchar *glyphRanges = nullptr;
std::string locale = getenv("FLYCAST_LOCALE");
if (locale.find("ja") == 0) // Japanese
glyphRanges = io.Fonts->GetGlyphRangesJapanese();
else if (locale.find("ko") == 0) // Korean
glyphRanges = io.Fonts->GetGlyphRangesKorean();
else if (locale.find("zh_TW") == 0) // Traditional Chinese
glyphRanges = GetGlyphRangesChineseTraditionalOfficial();
else if (locale.find("zh_CN") == 0) // Simplified Chinese
glyphRanges = GetGlyphRangesChineseSimplifiedOfficial();
if (glyphRanges != nullptr)
io.Fonts->AddFontFromFileTTF("/system/fonts/NotoSansCJK-Regular.ttc", 17.f * scaling, &font_cfg, glyphRanges);
}
// TODO Linux...
#endif
INFO_LOG(RENDERER, "Screen DPI is %d, size %d x %d. Scaling by %.2f", screen_dpi, screen_width, screen_height, scaling);
@ -472,11 +491,19 @@ static void gui_display_commands()
if (ImGui::Button("Exit", ImVec2(300 * scaling + ImGui::GetStyle().ColumnsMinSpacing + ImGui::GetStyle().FramePadding.x * 2 - 1,
50 * scaling)))
{
// Exit to main menu
dc_term_game();
gui_state = GuiState::Main;
game_started = false;
settings.imgread.ImagePath[0] = '\0';
if (!commandLineStart)
{
// Exit to main menu
dc_term_game();
gui_state = GuiState::Main;
game_started = false;
settings.imgread.ImagePath[0] = '\0';
}
else
{
// Exit emulator
dc_exit();
}
}
ImGui::End();
@ -1011,6 +1038,16 @@ static void gui_display_settings()
ImGui::SameLine();
ShowHelpMarker("The directories where your games are stored");
#ifdef __linux__
if (ImGui::ListBoxHeader("Data Directory", 1))
{
ImGui::AlignTextToFramePadding();
ImGui::Text("%s", get_writable_data_path("").c_str());
ImGui::ListBoxFooter();
}
ImGui::SameLine();
ShowHelpMarker("The directory containing BIOS files, as well as saved VMUs and states");
#else
if (ImGui::ListBoxHeader("Home Directory", 1))
{
ImGui::AlignTextToFramePadding();
@ -1024,6 +1061,7 @@ static void gui_display_settings()
}
ImGui::SameLine();
ShowHelpMarker("The directory where Flycast saves configuration files and VMUs. BIOS files should be in a subfolder named \"data\"");
#endif
if (OptionCheckbox("Hide Legacy Naomi Roms", config::HideLegacyNaomiRoms,
"Hide .bin, .dat and .lst files from the content browser"))
scanner.refresh();
@ -1287,6 +1325,20 @@ static void gui_display_settings()
OptionCheckbox("Fog", config::Fog, "Enable fog effects");
OptionCheckbox("Widescreen", config::Widescreen,
"Draw geometry outside of the normal 4:3 aspect ratio. May produce graphical glitches in the revealed areas");
if (!config::Widescreen)
{
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
}
ImGui::Indent();
OptionCheckbox("Super Widescreen", config::SuperWidescreen,
"Use the full width of the screen or window when its aspect ratio is greater than 16:9");
ImGui::Unindent();
if (!config::Widescreen)
{
ImGui::PopItemFlag();
ImGui::PopStyleVar();
}
OptionCheckbox("Widescreen Game Cheats", config::WidescreenGameHacks,
"Modify the game so that it displays in 16:9 anamorphic format and use horizontal screen stretching. Only some games are supported.");
OptionCheckbox("Show FPS Counter", config::ShowFPS, "Show on-screen frame/sec counter");
@ -1414,7 +1466,6 @@ static void gui_display_settings()
OptionCheckbox("Disable Sound", config::DisableSound, "Disable the emulator sound output");
OptionCheckbox("Enable DSP", config::DSPEnabled,
"Enable the Dreamcast Digital Sound Processor. Only recommended on fast platforms");
#if !defined(_WIN32)
#ifdef __ANDROID__
OptionCheckbox("Automatic Latency", config::AutoLatency,
"Automatically set audio latency. Recommended");
@ -1427,7 +1478,6 @@ static void gui_display_settings()
ImGui::SameLine();
ShowHelpMarker("Sets the maximum audio latency. Not supported by all audio drivers.");
}
#endif
audiobackend_t* backend = nullptr;
std::string backend_name = config::AudioBackend;
@ -1960,6 +2010,9 @@ void gui_display_ui()
std::string game_file = settings.imgread.ImagePath;
if (!game_file.empty())
{
#ifndef __ANDROID__
commandLineStart = true;
#endif
gui_start_game(game_file);
return;
}

View File

@ -24,6 +24,7 @@
#include "oslib/oslib.h"
#include "wsi/context.h"
#include "cfg/option.h"
#include "emulator.h"
bool mainui_enabled;
u32 MainFrameCount;
@ -59,6 +60,7 @@ bool mainui_rend_frame()
void mainui_init()
{
rend_init_renderer();
dc_resize_renderer();
}
void mainui_term()

View File

@ -564,25 +564,24 @@ void VulkanContext::CreateSwapChain()
// The FIFO present mode is guaranteed by the spec to be supported
vk::PresentModeKHR swapchainPresentMode = vk::PresentModeKHR::eFifo;
// Use FIFO on mobile, prefer Mailbox on desktop
#if HOST_CPU != CPU_ARM && HOST_CPU != CPU_ARM64 && !defined(__ANDROID__)
for (auto& presentMode : physicalDevice.getSurfacePresentModesKHR(GetSurface()))
{
if (presentMode == vk::PresentModeKHR::eMailbox && vendorID != VENDOR_ATI && vendorID != VENDOR_AMD)
#if HOST_CPU != CPU_ARM && HOST_CPU != CPU_ARM64 && !defined(__ANDROID__)
if (swapOnVSync && presentMode == vk::PresentModeKHR::eMailbox
&& vendorID != VENDOR_ATI && vendorID != VENDOR_AMD)
{
INFO_LOG(RENDERER, "Using mailbox present mode");
swapchainPresentMode = vk::PresentModeKHR::eMailbox;
break;
}
#ifdef TEST_AUTOMATION
if (presentMode == vk::PresentModeKHR::eImmediate)
#endif
if (!swapOnVSync && presentMode == vk::PresentModeKHR::eImmediate)
{
INFO_LOG(RENDERER, "Using immediate present mode");
swapchainPresentMode = vk::PresentModeKHR::eImmediate;
break;
}
#endif
}
#endif
vk::SurfaceTransformFlagBitsKHR preTransform = (surfaceCapabilities.supportedTransforms & vk::SurfaceTransformFlagBitsKHR::eIdentity) ? vk::SurfaceTransformFlagBitsKHR::eIdentity : surfaceCapabilities.currentTransform;
@ -802,6 +801,13 @@ void VulkanContext::Present() noexcept
}
renderDone = false;
}
#ifndef TEST_AUTOMATION
if (swapOnVSync == settings.input.fastForwardMode)
{
swapOnVSync = !settings.input.fastForwardMode;
resized = true;
}
#endif
if (resized)
try {
CreateSwapChain();

View File

@ -140,6 +140,11 @@ private:
u32 width = 0;
u32 height = 0;
bool resized = false;
#ifndef TEST_AUTOMATION
bool swapOnVSync = true;
#else
bool swapOnVSync = false;
#endif
vk::UniqueInstance instance;
vk::PhysicalDevice physicalDevice;

View File

@ -181,7 +181,12 @@ bool EGLGraphicsContext::Init()
#ifdef TARGET_PANDORA
fbdev = open("/dev/fb0", O_RDONLY);
#else
eglSwapInterval(display, 1);
#ifndef TEST_AUTOMATION
swapOnVSync = true;
#else
swapOnVSync = false;
#endif
eglSwapInterval(display, (int)swapOnVSync);
#endif
PostInit();
@ -215,12 +220,11 @@ void EGLGraphicsContext::Swap()
{
#ifdef TEST_AUTOMATION
do_swap_automation();
#endif
#if 0 && defined(TARGET_PANDORA)
if (fbdev >= 0)
#else
if (swapOnVSync == settings.input.fastForwardMode)
{
int arg = 0;
ioctl(fbdev,FBIO_WAITFORVSYNC,&arg);
swapOnVSync = !settings.input.fastForwardMode;
eglSwapInterval(display, (int)swapOnVSync);
}
#endif
eglSwapBuffers(display, surface);

View File

@ -55,6 +55,7 @@ private:
#ifdef TARGET_PANDORA
int fbdev = -1;
#endif
bool swapOnVSync = false;
};
extern EGLGraphicsContext theGLContext;

View File

@ -77,6 +77,14 @@ bool SDLGLGraphicsContext::Init()
INFO_LOG(RENDERER, "Created SDL Window and GL Context successfully");
SDL_GL_MakeCurrent(window, glcontext);
#ifndef TEST_AUTOMATION
// Swap at vsync
swapOnVSync = true;
#else
// Swap immediately
swapOnVSync = false;
#endif
SDL_GL_SetSwapInterval((int)swapOnVSync);
#ifdef GLES
load_gles_symbols();
@ -94,6 +102,15 @@ bool SDLGLGraphicsContext::Init()
void SDLGLGraphicsContext::Swap()
{
#ifdef TEST_AUTOMATION
do_swap_automation();
#else
if (swapOnVSync == settings.input.fastForwardMode)
{
swapOnVSync = !settings.input.fastForwardMode;
SDL_GL_SetSwapInterval((int)swapOnVSync);
}
#endif
SDL_GL_SwapWindow(window);
/* Check if drawable has been resized */

View File

@ -45,6 +45,7 @@ public:
private:
SDL_Window* window = nullptr;
SDL_GLContext glcontext = nullptr;
bool swapOnVSync = false;
};
extern SDLGLGraphicsContext theGLContext;

View File

@ -82,6 +82,21 @@ bool XGLGraphicsContext::Init()
unsigned int tempu;
XGetGeometry(display, window, &win, &temp, &temp, (u32 *)&screen_width, (u32 *)&screen_height, &tempu, &tempu);
#ifndef TEST_AUTOMATION
swapOnVSync = true;
#else
swapOnVSync = false;
#endif
glXSwapIntervalMESA = (int (*)(unsigned))glXGetProcAddress((const GLubyte*)"glXSwapIntervalMESA");
if (glXSwapIntervalMESA != nullptr)
glXSwapIntervalMESA((unsigned)swapOnVSync);
else
{
glXSwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC)glXGetProcAddress((const GLubyte*)"glXSwapIntervalEXT");
if (glXSwapIntervalEXT != nullptr)
glXSwapIntervalEXT(display, window, (int)swapOnVSync);
}
PostInit();
return true;
@ -142,32 +157,22 @@ void XGLGraphicsContext::Swap()
{
#ifdef TEST_AUTOMATION
do_swap_automation();
#else
if (swapOnVSync == settings.input.fastForwardMode)
{
swapOnVSync = !settings.input.fastForwardMode;
if (glXSwapIntervalMESA != nullptr)
glXSwapIntervalMESA((unsigned)swapOnVSync);
else if (glXSwapIntervalEXT != nullptr)
glXSwapIntervalEXT(display, window, (int)swapOnVSync);
}
#endif
glXSwapBuffers(display, window);
Window win;
int temp;
unsigned int tempu, new_w, new_h;
XGetGeometry(display, window, &win, &temp, &temp, &new_w, &new_h, &tempu, &tempu);
//if resized, clear up the draw buffers, to avoid out-of-draw-area junk data
if (new_w != screen_width || new_h != screen_height) {
screen_width = new_w;
screen_height = new_h;
}
#if 0
//handy to debug really stupid render-not-working issues ...
glcache.ClearColor(0, 0.5, 1, 1);
glClear(GL_COLOR_BUFFER_BIT);
glXSwapBuffers(display, window);
glcache.ClearColor(1, 0.5, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
glXSwapBuffers(display, window);
#endif
unsigned int tempu;
XGetGeometry(display, window, &win, &temp, &temp, (u32 *)&screen_width, (u32 *)&screen_height, &tempu, &tempu);
}
void XGLGraphicsContext::Term()

View File

@ -41,6 +41,9 @@ private:
Display *display;
GLXContext context;
GLXFBConfig* framebufferConfigs = nullptr;
PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT = nullptr;
int (*glXSwapIntervalMESA)(unsigned int interval) = nullptr;
bool swapOnVSync = false;
};
extern XGLGraphicsContext theGLContext;

View File

@ -35,6 +35,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import tv.ouya.console.api.OuyaController;
@ -83,7 +84,8 @@ public abstract class BaseGLActivity extends Activity implements ActivityCompat.
OuyaController.init(this);
String home_directory = prefs.getString(Config.pref_home, "");
String result = JNIdc.initEnvironment((Emulator)getApplicationContext(), home_directory);
String result = JNIdc.initEnvironment((Emulator)getApplicationContext(), home_directory,
Locale.getDefault().toString());
if (result != null) {
AlertDialog.Builder dlgAlert = new AlertDialog.Builder(this);
dlgAlert.setMessage("Initialization failed. Please try again and/or reinstall.\n\n"
@ -114,6 +116,9 @@ public abstract class BaseGLActivity extends Activity implements ActivityCompat.
},
STORAGE_PERM_REQUEST);
}
else
storagePermissionGranted = true;
InputDeviceManager.getInstance().startListening(getApplicationContext());
register(this);

View File

@ -9,7 +9,7 @@ public final class JNIdc
{
static { System.loadLibrary("flycast"); }
public static native String initEnvironment(Emulator emulator, String homeDirectory);
public static native String initEnvironment(Emulator emulator, String homeDirectory, String locale);
public static native void setExternalStorageDirectories(Object[] pathList);
public static native void setGameUri(String fileName);
public static native void pause();

View File

@ -85,7 +85,7 @@ JNIEXPORT type JNICALL Java_com_reicast_emulator_emu_JNIdc_get ## setting(JNIEnv
extern "C"
{
JNIEXPORT jstring JNICALL Java_com_reicast_emulator_emu_JNIdc_initEnvironment(JNIEnv *env, jobject obj, jobject emulator, jstring homeDirectory) __attribute__((visibility("default")));
JNIEXPORT jstring JNICALL Java_com_reicast_emulator_emu_JNIdc_initEnvironment(JNIEnv *env, jobject obj, jobject emulator, jstring homeDirectory, jstring locale) __attribute__((visibility("default")));
JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_setExternalStorageDirectories(JNIEnv *env, jobject obj, jobjectArray pathList) __attribute__((visibility("default")));
JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_setGameUri(JNIEnv *env,jobject obj,jstring fileName) __attribute__((visibility("default")));
JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_pause(JNIEnv *env,jobject obj) __attribute__((visibility("default")));
@ -184,7 +184,7 @@ void os_SetWindowText(char const *Text)
{
}
JNIEXPORT jstring JNICALL Java_com_reicast_emulator_emu_JNIdc_initEnvironment(JNIEnv *env, jobject obj, jobject emulator, jstring homeDirectory)
JNIEXPORT jstring JNICALL Java_com_reicast_emulator_emu_JNIdc_initEnvironment(JNIEnv *env, jobject obj, jobject emulator, jstring homeDirectory, jstring locale)
{
// Initialize platform-specific stuff
common_linux_setup();
@ -227,7 +227,13 @@ JNIEXPORT jstring JNICALL Java_com_reicast_emulator_emu_JNIdc_initEnvironment(JN
}
INFO_LOG(BOOT, "Config dir is: %s", get_writable_config_path("").c_str());
INFO_LOG(BOOT, "Data dir is: %s", get_writable_data_path("").c_str());
if (locale != nullptr)
{
const char *jchar = env->GetStringUTFChars(locale, 0);
std::string localeName = jchar;
env->ReleaseStringUTFChars(locale, jchar);
setenv("FLYCAST_LOCALE", localeName.c_str(), 1);
}
if (first_init)
{
// Do one-time initialization

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

BIN
shell/imgs/screenshot1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

BIN
shell/imgs/screenshot2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

BIN
shell/imgs/screenshot3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 984 KiB

BIN
shell/imgs/screenshot4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 789 KiB

View File

@ -277,6 +277,7 @@ else ifneq (,$(findstring win32,$(platform)))
CC = gcc
CXX = g++
USE_SDL = 1
USE_SDLAUDIO = 1
STATIC_LIBZIP = 1
ifeq ($(WITH_DYNAREC), x86)
X86_REC := 1

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop-application">
<id>org.flycast.Flycast</id>
<name>Flycast</name>
<metadata_license>CC0-1.0</metadata_license>
<project_license>GPL-2.0</project_license>
<summary>Multiplatform Sega Dreamcast, Naomi and Atomiswave emulator.</summary>
<description>
<p>Flycast is a multi-platform Sega Dreamcast, Naomi and Atomiswave emulator derived from reicast.</p>
</description>
<content_rating type="oars-1.1" />
<launchable type="desktop-id">org.flycast.Flycast.desktop</launchable>
<provides>
<binary>flycast</binary>
<id>flycast.desktop</id>
</provides>
<screenshots>
<screenshot type="default"><image>https://raw.githubusercontent.com/flyinghead/flycast/master/shell/imgs/screenshot1.png</image></screenshot>
<screenshot><image>https://raw.githubusercontent.com/flyinghead/flycast/master/shell/imgs/screenshot2.png</image></screenshot>
<screenshot><image>https://raw.githubusercontent.com/flyinghead/flycast/master/shell/imgs/screenshot3.png</image></screenshot>
<screenshot><image>https://raw.githubusercontent.com/flyinghead/flycast/master/shell/imgs/screenshot4.png</image></screenshot>
</screenshots>
<categories>
<category>Games</category>
<category>Emulator</category>
</categories>
<url type="homepage">https://github.com/flyinghead/flycast#readme</url>
<url type="bugtracker">https://github.com/flyinghead/flycast/issues</url>
<url type="help">https://github.com/TheArcadeStriker/flycast-wiki/wiki</url>
<url type="faq">https://github.com/TheArcadeStriker/flycast-wiki/wiki/Frequently-Asked-Questions-(F.A.Q.)</url>
<developer_name>Flyinghead</developer_name>
<releases>
<release date="2021-04-08" version="nightly" />
</releases>
</component>