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
|
[](https://github.com/flyinghead/flycast/actions/workflows/c-cpp.yml)
|
||||||
===========
|
[](https://github.com/flyinghead/flycast/actions/workflows/android.yml)
|
||||||
**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)
|

|
||||||
|
|
||||||
|
**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.
|
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/).
|
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.
|
**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.
|
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;
|
RendererOption RendererType;
|
||||||
Option<bool> UseMipmaps("rend.UseMipmaps", true);
|
Option<bool> UseMipmaps("rend.UseMipmaps", true);
|
||||||
Option<bool> Widescreen("rend.WideScreen");
|
Option<bool> Widescreen("rend.WideScreen");
|
||||||
|
Option<bool> SuperWidescreen("rend.SuperWideScreen");
|
||||||
Option<bool> ShowFPS("rend.ShowFPS");
|
Option<bool> ShowFPS("rend.ShowFPS");
|
||||||
Option<bool> RenderToTextureBuffer("rend.RenderToTextureBuffer");
|
Option<bool> RenderToTextureBuffer("rend.RenderToTextureBuffer");
|
||||||
Option<int> RenderToTextureUpscale("rend.RenderToTextureUpscale", 1);
|
Option<int> RenderToTextureUpscale("rend.RenderToTextureUpscale", 1);
|
||||||
|
|
|
@ -356,6 +356,7 @@ private:
|
||||||
extern RendererOption RendererType;
|
extern RendererOption RendererType;
|
||||||
extern Option<bool> UseMipmaps;
|
extern Option<bool> UseMipmaps;
|
||||||
extern Option<bool> Widescreen;
|
extern Option<bool> Widescreen;
|
||||||
|
extern Option<bool> SuperWidescreen;
|
||||||
extern Option<bool> ShowFPS;
|
extern Option<bool> ShowFPS;
|
||||||
extern Option<bool> RenderToTextureBuffer;
|
extern Option<bool> RenderToTextureBuffer;
|
||||||
extern Option<int> RenderToTextureUpscale;
|
extern Option<int> RenderToTextureUpscale;
|
||||||
|
|
|
@ -46,6 +46,7 @@ bool dc_is_load_done();
|
||||||
void dc_cancel_load();
|
void dc_cancel_load();
|
||||||
void dc_get_load_status();
|
void dc_get_load_status();
|
||||||
bool dc_is_running();
|
bool dc_is_running();
|
||||||
|
void dc_resize_renderer();
|
||||||
|
|
||||||
enum class Event {
|
enum class Event {
|
||||||
Start,
|
Start,
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
#include "debug/gdb_server.h"
|
#include "debug/gdb_server.h"
|
||||||
|
|
||||||
settings_t settings;
|
settings_t settings;
|
||||||
|
extern int screen_width, screen_height;
|
||||||
|
|
||||||
cThread emu_thread(&dc_run, NULL);
|
cThread emu_thread(&dc_run, NULL);
|
||||||
|
|
||||||
|
@ -527,7 +528,7 @@ static void dc_start_game(const char *path)
|
||||||
{
|
{
|
||||||
// Boot BIOS
|
// Boot BIOS
|
||||||
if (!LoadRomFiles())
|
if (!LoadRomFiles())
|
||||||
throw ReicastException("No BIOS file found");
|
throw ReicastException("No BIOS file found in " + get_writable_data_path(""));
|
||||||
TermDrive();
|
TermDrive();
|
||||||
InitDrive();
|
InitDrive();
|
||||||
}
|
}
|
||||||
|
@ -734,15 +735,16 @@ void SaveSettings()
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void dc_resume()
|
void dc_resize_renderer()
|
||||||
{
|
{
|
||||||
SetMemoryHandlers();
|
|
||||||
settings.aica.NoBatch = config::ForceWindowsCE || config::DSPEnabled;
|
|
||||||
int hres;
|
int hres;
|
||||||
int vres = config::RenderResolution;
|
int vres = config::RenderResolution;
|
||||||
if (config::Widescreen && !config::Rotate90)
|
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)
|
else if (config::Rotate90)
|
||||||
{
|
{
|
||||||
|
@ -755,6 +757,13 @@ void dc_resume()
|
||||||
}
|
}
|
||||||
if (renderer != nullptr)
|
if (renderer != nullptr)
|
||||||
renderer->Resize(hres, vres);
|
renderer->Resize(hres, vres);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dc_resume()
|
||||||
|
{
|
||||||
|
SetMemoryHandlers();
|
||||||
|
settings.aica.NoBatch = config::ForceWindowsCE || config::DSPEnabled;
|
||||||
|
dc_resize_renderer();
|
||||||
|
|
||||||
EventManager::event(Event::Resume);
|
EventManager::event(Event::Resume);
|
||||||
if (!emu_thread.thread.joinable())
|
if (!emu_thread.thread.joinable())
|
||||||
|
|
|
@ -1,41 +1,139 @@
|
||||||
#include "audiostream.h"
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include "oslib.h"
|
#include "audiostream.h"
|
||||||
#include <initguid.h>
|
#include <initguid.h>
|
||||||
#include <dsound.h>
|
#include <dsound.h>
|
||||||
#ifdef USE_SDL
|
#ifdef USE_SDL
|
||||||
#include "sdl/sdl.h"
|
#include "sdl/sdl.h"
|
||||||
#endif
|
#endif
|
||||||
|
#include <vector>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <atomic>
|
||||||
|
#include <thread>
|
||||||
|
#include "stdclass.h"
|
||||||
|
|
||||||
#define verifyc(x) verify(!FAILED(x))
|
#define verifyc(x) verify(!FAILED(x))
|
||||||
|
|
||||||
void* SoundThread(void* param);
|
|
||||||
#define V2_BUFFERSZ (16*1024)
|
|
||||||
|
|
||||||
static IDirectSound8* dsound;
|
static IDirectSound8* dsound;
|
||||||
static IDirectSoundBuffer8* buffer;
|
static IDirectSoundBuffer8* buffer;
|
||||||
|
static std::vector<HANDLE> notificationEvents;
|
||||||
|
|
||||||
static IDirectSoundCapture8 *dcapture;
|
static IDirectSoundCapture8 *dcapture;
|
||||||
static IDirectSoundCaptureBuffer8 *capture_buffer;
|
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()
|
static void directsound_init()
|
||||||
{
|
{
|
||||||
verifyc(DirectSoundCreate8(NULL,&dsound,NULL));
|
verifyc(DirectSoundCreate8(NULL, &dsound, NULL));
|
||||||
|
|
||||||
#ifdef USE_SDL
|
#ifdef USE_SDL
|
||||||
verifyc(dsound->SetCooperativeLevel(sdl_get_native_hwnd(), DSSCL_PRIORITY));
|
verifyc(dsound->SetCooperativeLevel(sdl_get_native_hwnd(), DSSCL_PRIORITY));
|
||||||
#else
|
#else
|
||||||
verifyc(dsound->SetCooperativeLevel((HWND)libPvr_GetRenderTarget(), DSSCL_PRIORITY));
|
verifyc(dsound->SetCooperativeLevel((HWND)libPvr_GetRenderTarget(), DSSCL_PRIORITY));
|
||||||
#endif
|
#endif
|
||||||
IDirectSoundBuffer* buffer_;
|
|
||||||
|
|
||||||
WAVEFORMATEX wfx;
|
|
||||||
DSBUFFERDESC desc;
|
|
||||||
|
|
||||||
// Set up WAV format structure.
|
// Set up WAV format structure.
|
||||||
|
WAVEFORMATEX wfx;
|
||||||
memset(&wfx, 0, sizeof(WAVEFORMATEX));
|
memset(&wfx, 0, sizeof(WAVEFORMATEX));
|
||||||
wfx.wFormatTag = WAVE_FORMAT_PCM;
|
wfx.wFormatTag = WAVE_FORMAT_PCM;
|
||||||
wfx.nChannels = 2;
|
wfx.nChannels = 2;
|
||||||
|
@ -45,104 +143,64 @@ static void directsound_init()
|
||||||
wfx.wBitsPerSample = 16;
|
wfx.wBitsPerSample = 16;
|
||||||
|
|
||||||
// Set up DSBUFFERDESC structure.
|
// Set up DSBUFFERDESC structure.
|
||||||
|
DSBUFFERDESC desc;
|
||||||
ds_ring_size=8192*wfx.nBlockAlign;
|
|
||||||
|
|
||||||
memset(&desc, 0, sizeof(DSBUFFERDESC));
|
memset(&desc, 0, sizeof(DSBUFFERDESC));
|
||||||
desc.dwSize = sizeof(DSBUFFERDESC);
|
desc.dwSize = sizeof(DSBUFFERDESC);
|
||||||
desc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GLOBALFOCUS;
|
desc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GLOBALFOCUS;
|
||||||
|
desc.dwBufferBytes = SAMPLE_BYTES * 2;
|
||||||
desc.dwBufferBytes = ds_ring_size;
|
|
||||||
desc.lpwfxFormat = &wfx;
|
desc.lpwfxFormat = &wfx;
|
||||||
|
|
||||||
verifyc(dsound->CreateSoundBuffer(&desc,&buffer_,0));
|
// Create the buffer
|
||||||
verifyc(buffer_->QueryInterface(IID_IDirectSoundBuffer8,(void**)&buffer));
|
IDirectSoundBuffer* buffer_;
|
||||||
|
verifyc(dsound->CreateSoundBuffer(&desc, &buffer_, 0));
|
||||||
|
verifyc(buffer_->QueryInterface(IID_IDirectSoundBuffer8, (void**)&buffer));
|
||||||
buffer_->Release();
|
buffer_->Release();
|
||||||
|
|
||||||
//Play the buffer !
|
// Set up notifications
|
||||||
verifyc(buffer->Play(0,0,DSBPLAY_LOOPING));
|
IDirectSoundNotify *bufferNotify;
|
||||||
|
verifyc(buffer->QueryInterface(IID_IDirectSoundNotify8, (void**)&bufferNotify));
|
||||||
}
|
notificationEvents.clear();
|
||||||
|
std::vector<DSBPOSITIONNOTIFY> posNotify;
|
||||||
|
for (int i = 0; notificationOffset(i) < desc.dwBufferBytes; i++)
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
void* ptr1,* ptr2;
|
notificationEvents.push_back(CreateEvent(nullptr, false, false, nullptr));
|
||||||
DWORD ptr1sz,ptr2sz;
|
posNotify.push_back({ notificationOffset(i), notificationEvents.back() });
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
return 0;
|
bufferNotify->SetNotificationPositions(posNotify.size(), &posNotify[0]);
|
||||||
//ds_ring_size
|
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)
|
static u32 directsound_push(const void* frame, u32 samples, bool wait)
|
||||||
{
|
{
|
||||||
while (!directsound_push_nw(frame, samples) && wait)
|
while (!ringBuffer.write((const u8 *)frame, samples * 4) && wait)
|
||||||
//DEBUG_LOG(AUDIO, "FAILED waiting on audio FAILED %d", directsound_getusedSamples())
|
pushWait.Wait();
|
||||||
;
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void directsound_term()
|
static void directsound_term()
|
||||||
{
|
{
|
||||||
|
audioThreadRunning = false;
|
||||||
|
audioThread.join();
|
||||||
buffer->Stop();
|
buffer->Stop();
|
||||||
|
|
||||||
|
for (HANDLE event : notificationEvents)
|
||||||
|
CloseHandle(event);
|
||||||
buffer->Release();
|
buffer->Release();
|
||||||
dsound->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.
|
// If wait, then wait for the buffer to be smaller than a certain size.
|
||||||
stream_mutex.lock();
|
stream_mutex.lock();
|
||||||
if (wait) {
|
if (wait) {
|
||||||
while (sample_count + samples > config::AudioBufferSize) {
|
while (sample_count + samples > (u32)config::AudioBufferSize) {
|
||||||
stream_mutex.unlock();
|
stream_mutex.unlock();
|
||||||
read_wait.Wait();
|
read_wait.Wait();
|
||||||
read_wait.Reset();
|
|
||||||
stream_mutex.lock();
|
stream_mutex.lock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,21 +56,28 @@ class GameScanner
|
||||||
|
|
||||||
void add_game_directory(const std::string& path)
|
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);
|
DirectoryTree tree(path);
|
||||||
|
std::string emptyParentPath = "";
|
||||||
for (const DirectoryTree::item& item : tree)
|
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) == "._")
|
if (item.name.substr(0, 2) == "._")
|
||||||
// Ignore Mac OS turds
|
// Ignore Mac OS turds
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -714,15 +714,11 @@ static bool RenderFrame(int width, int height)
|
||||||
|
|
||||||
//setup render target first
|
//setup render target first
|
||||||
if (is_rtt)
|
if (is_rtt)
|
||||||
{
|
|
||||||
output_fbo = BindRTT(false);
|
output_fbo = BindRTT(false);
|
||||||
if (output_fbo == 0)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
|
||||||
output_fbo = init_output_framebuffer(rendering_width, rendering_height);
|
output_fbo = init_output_framebuffer(rendering_width, rendering_height);
|
||||||
}
|
if (output_fbo == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
glcache.Disable(GL_SCISSOR_TEST);
|
glcache.Disable(GL_SCISSOR_TEST);
|
||||||
|
|
||||||
|
|
|
@ -1101,7 +1101,8 @@ bool RenderFrame(int width, int height)
|
||||||
}
|
}
|
||||||
else
|
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;
|
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
|
// Check that our FBO creation was successful
|
||||||
GLuint uStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
GLuint uStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||||
|
|
||||||
verify(uStatus == GL_FRAMEBUFFER_COMPLETE);
|
if (uStatus != GL_FRAMEBUFFER_COMPLETE)
|
||||||
|
return 0;
|
||||||
|
|
||||||
glcache.Disable(GL_SCISSOR_TEST);
|
glcache.Disable(GL_SCISSOR_TEST);
|
||||||
glcache.ClearColor(0.f, 0.f, 0.f, 0.f);
|
glcache.ClearColor(0.f, 0.f, 0.f, 0.f);
|
||||||
|
|
|
@ -52,6 +52,7 @@ int screen_dpi = 96;
|
||||||
static bool inited = false;
|
static bool inited = false;
|
||||||
float scaling = 1;
|
float scaling = 1;
|
||||||
GuiState gui_state = GuiState::Main;
|
GuiState gui_state = GuiState::Main;
|
||||||
|
static bool commandLineStart;
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
static bool touch_up;
|
static bool touch_up;
|
||||||
#endif
|
#endif
|
||||||
|
@ -235,7 +236,25 @@ void gui_init()
|
||||||
{
|
{
|
||||||
io.Fonts->AddFontFromFileTTF((fontDir + "PingFang.ttc").c_str(), 17.f * scaling, &font_cfg, GetGlyphRangesChineseSimplifiedOfficial());
|
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
|
#endif
|
||||||
INFO_LOG(RENDERER, "Screen DPI is %d, size %d x %d. Scaling by %.2f", screen_dpi, screen_width, screen_height, scaling);
|
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,
|
if (ImGui::Button("Exit", ImVec2(300 * scaling + ImGui::GetStyle().ColumnsMinSpacing + ImGui::GetStyle().FramePadding.x * 2 - 1,
|
||||||
50 * scaling)))
|
50 * scaling)))
|
||||||
{
|
{
|
||||||
// Exit to main menu
|
if (!commandLineStart)
|
||||||
dc_term_game();
|
{
|
||||||
gui_state = GuiState::Main;
|
// Exit to main menu
|
||||||
game_started = false;
|
dc_term_game();
|
||||||
settings.imgread.ImagePath[0] = '\0';
|
gui_state = GuiState::Main;
|
||||||
|
game_started = false;
|
||||||
|
settings.imgread.ImagePath[0] = '\0';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Exit emulator
|
||||||
|
dc_exit();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
|
@ -1011,6 +1038,16 @@ static void gui_display_settings()
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ShowHelpMarker("The directories where your games are stored");
|
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))
|
if (ImGui::ListBoxHeader("Home Directory", 1))
|
||||||
{
|
{
|
||||||
ImGui::AlignTextToFramePadding();
|
ImGui::AlignTextToFramePadding();
|
||||||
|
@ -1024,6 +1061,7 @@ static void gui_display_settings()
|
||||||
}
|
}
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ShowHelpMarker("The directory where Flycast saves configuration files and VMUs. BIOS files should be in a subfolder named \"data\"");
|
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,
|
if (OptionCheckbox("Hide Legacy Naomi Roms", config::HideLegacyNaomiRoms,
|
||||||
"Hide .bin, .dat and .lst files from the content browser"))
|
"Hide .bin, .dat and .lst files from the content browser"))
|
||||||
scanner.refresh();
|
scanner.refresh();
|
||||||
|
@ -1287,6 +1325,20 @@ static void gui_display_settings()
|
||||||
OptionCheckbox("Fog", config::Fog, "Enable fog effects");
|
OptionCheckbox("Fog", config::Fog, "Enable fog effects");
|
||||||
OptionCheckbox("Widescreen", config::Widescreen,
|
OptionCheckbox("Widescreen", config::Widescreen,
|
||||||
"Draw geometry outside of the normal 4:3 aspect ratio. May produce graphical glitches in the revealed areas");
|
"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,
|
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.");
|
"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");
|
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("Disable Sound", config::DisableSound, "Disable the emulator sound output");
|
||||||
OptionCheckbox("Enable DSP", config::DSPEnabled,
|
OptionCheckbox("Enable DSP", config::DSPEnabled,
|
||||||
"Enable the Dreamcast Digital Sound Processor. Only recommended on fast platforms");
|
"Enable the Dreamcast Digital Sound Processor. Only recommended on fast platforms");
|
||||||
#if !defined(_WIN32)
|
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
OptionCheckbox("Automatic Latency", config::AutoLatency,
|
OptionCheckbox("Automatic Latency", config::AutoLatency,
|
||||||
"Automatically set audio latency. Recommended");
|
"Automatically set audio latency. Recommended");
|
||||||
|
@ -1427,7 +1478,6 @@ static void gui_display_settings()
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ShowHelpMarker("Sets the maximum audio latency. Not supported by all audio drivers.");
|
ShowHelpMarker("Sets the maximum audio latency. Not supported by all audio drivers.");
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
audiobackend_t* backend = nullptr;
|
audiobackend_t* backend = nullptr;
|
||||||
std::string backend_name = config::AudioBackend;
|
std::string backend_name = config::AudioBackend;
|
||||||
|
@ -1960,6 +2010,9 @@ void gui_display_ui()
|
||||||
std::string game_file = settings.imgread.ImagePath;
|
std::string game_file = settings.imgread.ImagePath;
|
||||||
if (!game_file.empty())
|
if (!game_file.empty())
|
||||||
{
|
{
|
||||||
|
#ifndef __ANDROID__
|
||||||
|
commandLineStart = true;
|
||||||
|
#endif
|
||||||
gui_start_game(game_file);
|
gui_start_game(game_file);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include "oslib/oslib.h"
|
#include "oslib/oslib.h"
|
||||||
#include "wsi/context.h"
|
#include "wsi/context.h"
|
||||||
#include "cfg/option.h"
|
#include "cfg/option.h"
|
||||||
|
#include "emulator.h"
|
||||||
|
|
||||||
bool mainui_enabled;
|
bool mainui_enabled;
|
||||||
u32 MainFrameCount;
|
u32 MainFrameCount;
|
||||||
|
@ -59,6 +60,7 @@ bool mainui_rend_frame()
|
||||||
void mainui_init()
|
void mainui_init()
|
||||||
{
|
{
|
||||||
rend_init_renderer();
|
rend_init_renderer();
|
||||||
|
dc_resize_renderer();
|
||||||
}
|
}
|
||||||
|
|
||||||
void mainui_term()
|
void mainui_term()
|
||||||
|
|
|
@ -564,25 +564,24 @@ void VulkanContext::CreateSwapChain()
|
||||||
// The FIFO present mode is guaranteed by the spec to be supported
|
// The FIFO present mode is guaranteed by the spec to be supported
|
||||||
vk::PresentModeKHR swapchainPresentMode = vk::PresentModeKHR::eFifo;
|
vk::PresentModeKHR swapchainPresentMode = vk::PresentModeKHR::eFifo;
|
||||||
// Use FIFO on mobile, prefer Mailbox on desktop
|
// 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()))
|
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");
|
INFO_LOG(RENDERER, "Using mailbox present mode");
|
||||||
swapchainPresentMode = vk::PresentModeKHR::eMailbox;
|
swapchainPresentMode = vk::PresentModeKHR::eMailbox;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#ifdef TEST_AUTOMATION
|
#endif
|
||||||
if (presentMode == vk::PresentModeKHR::eImmediate)
|
if (!swapOnVSync && presentMode == vk::PresentModeKHR::eImmediate)
|
||||||
{
|
{
|
||||||
INFO_LOG(RENDERER, "Using immediate present mode");
|
INFO_LOG(RENDERER, "Using immediate present mode");
|
||||||
swapchainPresentMode = vk::PresentModeKHR::eImmediate;
|
swapchainPresentMode = vk::PresentModeKHR::eImmediate;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
vk::SurfaceTransformFlagBitsKHR preTransform = (surfaceCapabilities.supportedTransforms & vk::SurfaceTransformFlagBitsKHR::eIdentity) ? vk::SurfaceTransformFlagBitsKHR::eIdentity : surfaceCapabilities.currentTransform;
|
vk::SurfaceTransformFlagBitsKHR preTransform = (surfaceCapabilities.supportedTransforms & vk::SurfaceTransformFlagBitsKHR::eIdentity) ? vk::SurfaceTransformFlagBitsKHR::eIdentity : surfaceCapabilities.currentTransform;
|
||||||
|
|
||||||
|
@ -802,6 +801,13 @@ void VulkanContext::Present() noexcept
|
||||||
}
|
}
|
||||||
renderDone = false;
|
renderDone = false;
|
||||||
}
|
}
|
||||||
|
#ifndef TEST_AUTOMATION
|
||||||
|
if (swapOnVSync == settings.input.fastForwardMode)
|
||||||
|
{
|
||||||
|
swapOnVSync = !settings.input.fastForwardMode;
|
||||||
|
resized = true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if (resized)
|
if (resized)
|
||||||
try {
|
try {
|
||||||
CreateSwapChain();
|
CreateSwapChain();
|
||||||
|
|
|
@ -140,6 +140,11 @@ private:
|
||||||
u32 width = 0;
|
u32 width = 0;
|
||||||
u32 height = 0;
|
u32 height = 0;
|
||||||
bool resized = false;
|
bool resized = false;
|
||||||
|
#ifndef TEST_AUTOMATION
|
||||||
|
bool swapOnVSync = true;
|
||||||
|
#else
|
||||||
|
bool swapOnVSync = false;
|
||||||
|
#endif
|
||||||
vk::UniqueInstance instance;
|
vk::UniqueInstance instance;
|
||||||
vk::PhysicalDevice physicalDevice;
|
vk::PhysicalDevice physicalDevice;
|
||||||
|
|
||||||
|
|
|
@ -181,7 +181,12 @@ bool EGLGraphicsContext::Init()
|
||||||
#ifdef TARGET_PANDORA
|
#ifdef TARGET_PANDORA
|
||||||
fbdev = open("/dev/fb0", O_RDONLY);
|
fbdev = open("/dev/fb0", O_RDONLY);
|
||||||
#else
|
#else
|
||||||
eglSwapInterval(display, 1);
|
#ifndef TEST_AUTOMATION
|
||||||
|
swapOnVSync = true;
|
||||||
|
#else
|
||||||
|
swapOnVSync = false;
|
||||||
|
#endif
|
||||||
|
eglSwapInterval(display, (int)swapOnVSync);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
PostInit();
|
PostInit();
|
||||||
|
@ -215,12 +220,11 @@ void EGLGraphicsContext::Swap()
|
||||||
{
|
{
|
||||||
#ifdef TEST_AUTOMATION
|
#ifdef TEST_AUTOMATION
|
||||||
do_swap_automation();
|
do_swap_automation();
|
||||||
#endif
|
#else
|
||||||
#if 0 && defined(TARGET_PANDORA)
|
if (swapOnVSync == settings.input.fastForwardMode)
|
||||||
if (fbdev >= 0)
|
|
||||||
{
|
{
|
||||||
int arg = 0;
|
swapOnVSync = !settings.input.fastForwardMode;
|
||||||
ioctl(fbdev,FBIO_WAITFORVSYNC,&arg);
|
eglSwapInterval(display, (int)swapOnVSync);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
eglSwapBuffers(display, surface);
|
eglSwapBuffers(display, surface);
|
||||||
|
|
|
@ -55,6 +55,7 @@ private:
|
||||||
#ifdef TARGET_PANDORA
|
#ifdef TARGET_PANDORA
|
||||||
int fbdev = -1;
|
int fbdev = -1;
|
||||||
#endif
|
#endif
|
||||||
|
bool swapOnVSync = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern EGLGraphicsContext theGLContext;
|
extern EGLGraphicsContext theGLContext;
|
||||||
|
|
|
@ -77,6 +77,14 @@ bool SDLGLGraphicsContext::Init()
|
||||||
INFO_LOG(RENDERER, "Created SDL Window and GL Context successfully");
|
INFO_LOG(RENDERER, "Created SDL Window and GL Context successfully");
|
||||||
|
|
||||||
SDL_GL_MakeCurrent(window, glcontext);
|
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
|
#ifdef GLES
|
||||||
load_gles_symbols();
|
load_gles_symbols();
|
||||||
|
@ -94,6 +102,15 @@ bool SDLGLGraphicsContext::Init()
|
||||||
|
|
||||||
void SDLGLGraphicsContext::Swap()
|
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);
|
SDL_GL_SwapWindow(window);
|
||||||
|
|
||||||
/* Check if drawable has been resized */
|
/* Check if drawable has been resized */
|
||||||
|
|
|
@ -45,6 +45,7 @@ public:
|
||||||
private:
|
private:
|
||||||
SDL_Window* window = nullptr;
|
SDL_Window* window = nullptr;
|
||||||
SDL_GLContext glcontext = nullptr;
|
SDL_GLContext glcontext = nullptr;
|
||||||
|
bool swapOnVSync = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern SDLGLGraphicsContext theGLContext;
|
extern SDLGLGraphicsContext theGLContext;
|
||||||
|
|
|
@ -82,6 +82,21 @@ bool XGLGraphicsContext::Init()
|
||||||
unsigned int tempu;
|
unsigned int tempu;
|
||||||
XGetGeometry(display, window, &win, &temp, &temp, (u32 *)&screen_width, (u32 *)&screen_height, &tempu, &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();
|
PostInit();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -142,32 +157,22 @@ void XGLGraphicsContext::Swap()
|
||||||
{
|
{
|
||||||
#ifdef TEST_AUTOMATION
|
#ifdef TEST_AUTOMATION
|
||||||
do_swap_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
|
#endif
|
||||||
glXSwapBuffers(display, window);
|
glXSwapBuffers(display, window);
|
||||||
|
|
||||||
Window win;
|
Window win;
|
||||||
int temp;
|
int temp;
|
||||||
unsigned int tempu, new_w, new_h;
|
unsigned int tempu;
|
||||||
XGetGeometry(display, window, &win, &temp, &temp, &new_w, &new_h, &tempu, &tempu);
|
XGetGeometry(display, window, &win, &temp, &temp, (u32 *)&screen_width, (u32 *)&screen_height, &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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void XGLGraphicsContext::Term()
|
void XGLGraphicsContext::Term()
|
||||||
|
|
|
@ -41,6 +41,9 @@ private:
|
||||||
Display *display;
|
Display *display;
|
||||||
GLXContext context;
|
GLXContext context;
|
||||||
GLXFBConfig* framebufferConfigs = nullptr;
|
GLXFBConfig* framebufferConfigs = nullptr;
|
||||||
|
PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT = nullptr;
|
||||||
|
int (*glXSwapIntervalMESA)(unsigned int interval) = nullptr;
|
||||||
|
bool swapOnVSync = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern XGLGraphicsContext theGLContext;
|
extern XGLGraphicsContext theGLContext;
|
||||||
|
|
|
@ -35,6 +35,7 @@ import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
import tv.ouya.console.api.OuyaController;
|
import tv.ouya.console.api.OuyaController;
|
||||||
|
|
||||||
|
@ -83,7 +84,8 @@ public abstract class BaseGLActivity extends Activity implements ActivityCompat.
|
||||||
OuyaController.init(this);
|
OuyaController.init(this);
|
||||||
|
|
||||||
String home_directory = prefs.getString(Config.pref_home, "");
|
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) {
|
if (result != null) {
|
||||||
AlertDialog.Builder dlgAlert = new AlertDialog.Builder(this);
|
AlertDialog.Builder dlgAlert = new AlertDialog.Builder(this);
|
||||||
dlgAlert.setMessage("Initialization failed. Please try again and/or reinstall.\n\n"
|
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);
|
STORAGE_PERM_REQUEST);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
storagePermissionGranted = true;
|
||||||
|
|
||||||
|
|
||||||
InputDeviceManager.getInstance().startListening(getApplicationContext());
|
InputDeviceManager.getInstance().startListening(getApplicationContext());
|
||||||
register(this);
|
register(this);
|
||||||
|
|
|
@ -9,7 +9,7 @@ public final class JNIdc
|
||||||
{
|
{
|
||||||
static { System.loadLibrary("flycast"); }
|
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 setExternalStorageDirectories(Object[] pathList);
|
||||||
public static native void setGameUri(String fileName);
|
public static native void setGameUri(String fileName);
|
||||||
public static native void pause();
|
public static native void pause();
|
||||||
|
|
|
@ -85,7 +85,7 @@ JNIEXPORT type JNICALL Java_com_reicast_emulator_emu_JNIdc_get ## setting(JNIEnv
|
||||||
|
|
||||||
extern "C"
|
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_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_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")));
|
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
|
// Initialize platform-specific stuff
|
||||||
common_linux_setup();
|
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, "Config dir is: %s", get_writable_config_path("").c_str());
|
||||||
INFO_LOG(BOOT, "Data dir is: %s", get_writable_data_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)
|
if (first_init)
|
||||||
{
|
{
|
||||||
// Do one-time initialization
|
// 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
|
CC = gcc
|
||||||
CXX = g++
|
CXX = g++
|
||||||
USE_SDL = 1
|
USE_SDL = 1
|
||||||
|
USE_SDLAUDIO = 1
|
||||||
STATIC_LIBZIP = 1
|
STATIC_LIBZIP = 1
|
||||||
ifeq ($(WITH_DYNAREC), x86)
|
ifeq ($(WITH_DYNAREC), x86)
|
||||||
X86_REC := 1
|
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