Merge remote-tracking branch 'origin/master' into fh/directx
This commit is contained in:
commit
150cfa29e7
35
README.md
35
README.md
|
@ -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/)
|
||||
[](https://github.com/flyinghead/flycast/actions/workflows/c-cpp.yml)
|
||||
[](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** 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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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(), ¬ificationEvents[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();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -55,6 +55,7 @@ private:
|
|||
#ifdef TARGET_PANDORA
|
||||
int fbdev = -1;
|
||||
#endif
|
||||
bool swapOnVSync = false;
|
||||
};
|
||||
|
||||
extern EGLGraphicsContext theGLContext;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -45,6 +45,7 @@ public:
|
|||
private:
|
||||
SDL_Window* window = nullptr;
|
||||
SDL_GLContext glcontext = nullptr;
|
||||
bool swapOnVSync = false;
|
||||
};
|
||||
|
||||
extern SDLGLGraphicsContext theGLContext;
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 |
Binary file not shown.
After Width: | Height: | Size: 79 KiB |
Binary file not shown.
After Width: | Height: | Size: 41 KiB |
Binary file not shown.
After Width: | Height: | Size: 984 KiB |
Binary file not shown.
After Width: | Height: | Size: 789 KiB |
|
@ -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
|
||||
|
|
|
@ -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>
|
Loading…
Reference in New Issue