Merge remote-tracking branch 'origin/master' into dev

This commit is contained in:
Flyinghead 2022-06-15 19:25:41 +02:00
commit 5b3a414987
67 changed files with 1496 additions and 933 deletions

View File

@ -30,8 +30,8 @@ jobs:
- name: Set up build environment (macOS)
run: |
brew update
brew install libao ldid libomp ninja pulseaudio
wget https://sdk.lunarg.com/sdk/download/1.3.204.1/mac/vulkansdk-macos-1.3.204.1.dmg
brew install libao ldid ninja pulseaudio
wget https://sdk.lunarg.com/sdk/download/1.3.211.0/mac/vulkansdk-macos-1.3.211.0.dmg
hdiutil attach ./vulkansdk-macos-*.dmg
sudo /Volumes/vulkansdk-macos-*/InstallVulkan.app/Contents/MacOS/InstallVulkan --root $HOME/VulkanSDK --accept-licenses --default-answer --confirm-command install
hdiutil detach /Volumes/vulkansdk-macos-*
@ -67,6 +67,10 @@ jobs:
fetch-depth: 0
submodules: true
- name: Compile a universal OpenMP
run: brew reinstall --build-from-source --formula ./shell/apple/libomp.rb
if: runner.os == 'macOS'
- uses: actions/cache@v3
with:
path: ${{ env.CCACHE_DIR }}

View File

@ -201,20 +201,22 @@ if(NOT LIBRETRO)
endif()
endif()
find_package(OpenMP)
if(OpenMP_CXX_FOUND AND NOT APPLE AND USE_OPENMP)
if(MINGW)
target_link_libraries(${PROJECT_NAME} PRIVATE "-static -lgomp -lpthread")
target_compile_options(${PROJECT_NAME} PRIVATE -fopenmp)
elseif(ANDROID)
# Reference: https://android.googlesource.com/platform/ndk/+/refs/heads/master/tests/device/openmp/CMakeLists.txt
set_target_properties(${PROJECT_NAME} PROPERTIES COMPILE_OPTIONS -fopenmp)
target_link_libraries(${PROJECT_NAME} PRIVATE -fopenmp -static-openmp)
else()
target_link_libraries(${PROJECT_NAME} PRIVATE OpenMP::OpenMP_CXX)
if(USE_OPENMP)
find_package(OpenMP)
if(OpenMP_CXX_FOUND)
if(MINGW)
target_link_libraries(${PROJECT_NAME} PRIVATE "-static -lgomp -lpthread")
target_compile_options(${PROJECT_NAME} PRIVATE -fopenmp)
elseif(ANDROID)
# Reference: https://android.googlesource.com/platform/ndk/+/refs/heads/master/tests/device/openmp/CMakeLists.txt
set_target_properties(${PROJECT_NAME} PROPERTIES COMPILE_OPTIONS -fopenmp)
target_link_libraries(${PROJECT_NAME} PRIVATE -fopenmp -static-openmp)
elseif(APPLE)
target_link_libraries(${PROJECT_NAME} PRIVATE OpenMP::OpenMP_CXX -static-openmp)
else()
target_link_libraries(${PROJECT_NAME} PRIVATE OpenMP::OpenMP_CXX)
endif()
endif()
else()
target_compile_definitions(${PROJECT_NAME} PRIVATE TARGET_NO_OPENMP)
endif()
option(BUILD_SHARED_LIBS "Build shared library" OFF)
@ -891,6 +893,8 @@ target_sources(${PROJECT_NAME} PRIVATE
target_sources(${PROJECT_NAME} PRIVATE
core/wsi/context.h
core/wsi/libretro.cpp
core/wsi/libretro.h
core/wsi/switcher.cpp)
if(USE_OPENGL)
@ -900,8 +904,6 @@ if(USE_OPENGL)
core/wsi/egl.h
core/wsi/gl_context.cpp
core/wsi/gl_context.h
core/wsi/libretro.cpp
core/wsi/libretro.h
core/wsi/osx.cpp
core/wsi/osx.h
core/wsi/sdl.cpp
@ -1394,13 +1396,15 @@ if(NOT LIBRETRO)
find_library(AUDIO_TOOLBOX_LIBRARY AudioToolbox)
find_library(OPENGL_LIBRARY OpenGL)
find_library(IOSURFACE_LIBRARY IOSurface)
find_library(MULTITOUCH_SUPPORT_LIBRARY MultitouchSupport /System/Library/PrivateFrameworks)
target_link_libraries(${PROJECT_NAME} PRIVATE
${AUDIO_UNIT_LIBRARY}
${FOUNDATION_LIBRARY}
${AUDIO_TOOLBOX_LIBRARY}
${OPENGL_LIBRARY}
${IOSURFACE_LIBRARY})
${IOSURFACE_LIBRARY}
${MULTITOUCH_SUPPORT_LIBRARY})
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy "$ENV{VULKAN_SDK}/lib/libMoltenVK.dylib"
@ -1439,7 +1443,7 @@ if(NOT LIBRETRO)
set_target_properties(${PROJECT_NAME} PROPERTIES RESOURCE "${ResourceFiles}")
else()
target_sources(${PROJECT_NAME} PRIVATE shell/windows/flycast.rc)
target_link_libraries(${PROJECT_NAME} PRIVATE dsound opengl32 winmm ws2_32 wsock32 xinput9_1_0)
target_link_libraries(${PROJECT_NAME} PRIVATE dsound opengl32 winmm ws2_32 wsock32 xinput9_1_0 cfgmgr32)
endif()
endif()
endif()

View File

@ -7,7 +7,7 @@
![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/).
**Flycast** is a multi-platform Sega Dreamcast, Naomi, Naomi 2, and Atomiswave emulator derived from [**reicast**](https://github.com/skmp/reicast-emulator).
Information about configuration and supported features can be found on [**TheArcadeStriker's flycast wiki**](https://github.com/TheArcadeStriker/flycast-wiki/wiki).
@ -17,27 +17,31 @@ Join us on our [**Discord server**](https://discord.gg/X8YWP8w) for a chat.
### Flatpak (Linux)
1. [Set up Flatpak](https://www.flatpak.org/setup/)
1. [Set up Flatpak](https://www.flatpak.org/setup/).
2. Install Flycast from [Flathub](https://flathub.org/apps/details/org.flycast.Flycast)
2. Install Flycast from [Flathub](https://flathub.org/apps/details/org.flycast.Flycast):
`flatpak install -y org.flycast.Flycast`
3. Run Flycast
3. Run Flycast:
`flatpak run org.flycast.Flycast`
### Homebrew (MacOS)
1. [Set up Homebrew](https://brew.sh)
1. [Set up Homebrew](https://brew.sh).
2. Install Flycast via Homebrew
2. Install Flycast via Homebrew:
`brew install --cask flycast`
### Xbox One
### Xbox One/Series
Open [**gamr13's github page**](https://gamr13.github.io/) from your console.
#### Retail:
Open [**gamr13's github page**](https://gamr13.github.io/) from your Xbox console.
#### Dev Mode:
Grab the latest build from [**the builds page**](https://flyinghead.github.io/flycast-builds/), or the [**GitHub Actions**](https://github.com/flyinghead/flycast/actions/workflows/uwp.yml). Then install it using the **Xbox Device Portal**.
### Binaries

View File

@ -129,6 +129,8 @@ Option<int> GGPODelay("GGPODelay", 0, "network");
Option<bool> NetworkStats("Stats", true, "network");
Option<int> GGPOAnalogAxes("GGPOAnalogAxes", 0, "network");
Option<bool> GGPOChat("GGPOChat", true, "network");
Option<bool> GGPOChatTimeoutToggle("GGPOChatTimeoutToggle", true, "network");
Option<int> GGPOChatTimeout("GGPOChatTimeout", 10, "network");
#ifdef SUPPORT_DISPMANX
Option<bool> DispmanxMaintainAspect("maintain_aspect", true, "dispmanx");

View File

@ -487,6 +487,8 @@ extern Option<int> GGPODelay;
extern Option<bool> NetworkStats;
extern Option<int> GGPOAnalogAxes;
extern Option<bool> GGPOChat;
extern Option<bool> GGPOChatTimeoutToggle;
extern Option<int> GGPOChatTimeout;
#ifdef SUPPORT_DISPMANX
extern Option<bool> DispmanxMaintainAspect;

View File

@ -83,7 +83,7 @@ const WidescreenCheat CheatManager::widescreen_cheats[] =
{ "T8116D 50", nullptr, { 0x2E5530 }, { 0x43700000 } }, // Dead or Alive 2 (PAL)
{ "T3601N", nullptr, { 0x2F0670 }, { 0x43700000 } }, // Dead or Alive 2 (USA)
{ "T3602M", nullptr, { 0x2FF798 }, { 0x43700000 } }, // Dead or Alive 2 (JP)
{ "T3601M", nullptr, { 0x2FBBD0, 0x1E6658 }, { 0x43700000, 0x4414C000 } }, // Dead or Alive 2: Limited Edition (JP) (code 1 fixes HUD)
{ "T3601M", nullptr, { 0x2FBBD0 }, { 0x43700000 } }, // Dead or Alive 2: Limited Edition (JP)
{ "T2401N", nullptr, { 0x8BD5B4, 0x8BD5E4 }, { 0x43F00000, 0x3F400000 } }, // Death Crimson OX (USA)
{ "T23201M", nullptr, { 0x819F44, 0x819F74 }, { 0x43F00000, 0x3F400000 } }, // Death Crimson 2 (JP)
{ "T17714D50", nullptr, { 0x0D2ED0, 0x0D2ED4 }, { 0x3FAAAAAB, 0x3F400000 } }, // Donald Duck: Quack Attack (PAL) (Code 1 corrects the HUD)
@ -92,6 +92,7 @@ const WidescreenCheat CheatManager::widescreen_cheats[] =
{ "T17720N", nullptr, { 0xF97F40, 0x08DCF4 }, { 0x00000168, 0 } }, // Dragonriders: Chronicles of Pern (USA) (Code 1 removes black bars)
{ "MK-5101350", nullptr, { 0x4FCBC0 }, { 0x43700000 } }, // Dynamite Cop (PAL)
{ "MK-51013", nullptr, { 0x4FCBC0 }, { 0x43700000 } }, // Dynamite Cop (USA)
{ "HDR-0020", nullptr, { 0x4FA848 }, { 0x43700000 } }, // Dynamite Deka 2 (JP)
// Draconus: Cult of the Wyrm (USA)
// Code 1-2 increases drawing distance
{ "T40203N", nullptr, { 0x49D7F4, 0x49D8CC, 0x49D6F8 }, { 0x3F07C3BB, 0x447A0000, 0x3FAAAAAB } },
@ -302,7 +303,7 @@ const WidescreenCheat CheatManager::naomi_widescreen_cheats[] =
{ "TOY FIGHTER", nullptr, { 0x133E58 }, { 0x43700000 } },
{ "LUPIN THE THIRD -THE SHOOTING-", nullptr, { 0x045490 }, { 0x3F400000 } },
{ "VF4 FINAL TUNED JAPAN", nullptr, { 0x02B834, 0x0AFB90 }, { 0x3FE38E39, 0x3FE38E39 } },
{ "DYNAMITE DEKA EX", nullptr, { 0x0E3598 }, { 0x3FE38E38 } },
{ "DYNAMITE DEKA EX", nullptr, { 0x0E3598, 0x0C8E84 }, { 0x3FE38E38, 0x3FE38E38 } },
{ "DEAD OR ALIVE 2", "doa2m", { 0x085B5C, 0x086AE8 }, { 0x3FE38E39, 0x3FE38E39 } },
{ "SLASHOUT JAPAN VERSION", nullptr, { 0x3DDBE4 }, { 0x43CFDC86 } },
{ "SPIKERS BATTLE JAPAN VERSION", nullptr, { 0x3626C4 }, { 0x43A551B0 } },
@ -312,6 +313,8 @@ const WidescreenCheat CheatManager::naomi_widescreen_cheats[] =
{ "INITIAL D Ver.3", "initdv3e", { 0x1D0B34 }, { 0x3FE38E39 } },
{ "AIRLINE PILOTS IN JAPAN", "alpilotj", { 0x1D62550 }, { 0x43700000 } },
{ "MONKEY BALL JAPAN VERSION", nullptr, { 0x345B4, 0x45244, 0x454CC }, { 0x3FE38E39, 0x3FE38E39, 0x3FE38E39 } },
{ "ZOMBIE REVENGE IN JAPAN", "zombrvn", { 0x7A4808 }, { 0x43700000 } },
{ "ZOMBIE REVENGE IN JAPAN", "zombrvno", { 0x7A2E50 }, { 0x43700000 } },
{ nullptr },
};

View File

@ -56,9 +56,9 @@ struct Cheat
bool builtIn;
Cheat(Type type = Type::disabled, const std::string& description = "", bool enabled = false, u32 size = 0, u32 address = 0,
u8 valueMask = 0, u32 repeatCount = 1, u32 repeatValueIncrement = 0,
u32 value = 0, u8 valueMask = 0, u32 repeatCount = 1, u32 repeatValueIncrement = 0,
u32 repeatAddressIncrement = 0, u32 destAddress = 0, bool builtIn = false)
: type(type), description(description), enabled(enabled), size(size), address(address), valueMask(valueMask),
: type(type), description(description), enabled(enabled), size(size), address(address), value(value), valueMask(valueMask),
repeatCount(repeatCount), repeatValueIncrement(repeatValueIncrement), repeatAddressIncrement(repeatAddressIncrement),
destAddress(destAddress), builtIn(builtIn)
{

View File

@ -152,7 +152,8 @@ struct flash_syscfg_block {
// last set time (seconds since 1/1/1950 00:00)
u16 time_lo;
u16 time_hi;
u8 unknown1;
// in 15 mins increment, from -48 (West) to +52 (East), unused
int8_t time_zone;
u8 lang;
u8 mono;
u8 autostart;

View File

@ -140,6 +140,7 @@ static void fixUpDCFlash()
memset(&syscfg, 0xff, sizeof(syscfg));
syscfg.time_lo = 0;
syscfg.time_hi = 0;
syscfg.time_zone = 0;
syscfg.lang = 0;
syscfg.mono = 0;
syscfg.autostart = 1;
@ -160,12 +161,27 @@ static void fixUpDCFlash()
if (!memcmp(console_id, "\377\377\377\377\377\377", 6))
{
srand(now);
u8 sum = 0;
for (int i = 0; i < 6; i++)
{
console_id[i] = rand();
console_id[i + 0xA0] = console_id[i]; // copy at 1A0F8
sum += console_id[i];
}
console_id[-1] = console_id[0xA0 - 1] = sum;
console_id[-2] = console_id[0xA0 - 2] = ~sum;
}
else
{
// Fix checksum
u8 sum = 0;
for (int i = 0; i < 6; i++)
sum += console_id[i];
console_id[-1] = console_id[0xA0 - 1] = sum;
console_id[-2] = console_id[0xA0 - 2] = ~sum;
}
// must be != 0xff
console_id[7] = console_id[0xA0 + 7] = 0xfe;
}
}

View File

@ -234,6 +234,8 @@ void rend_start_render(TA_context *ctx)
FillBGP(ctx);
ctx->rend.isRTT = (FB_W_SOF1 & 0x1000000) != 0;
ctx->rend.fb_W_SOF1 = FB_W_SOF1;
ctx->rend.fb_W_CTRL.full = FB_W_CTRL.full;
ctx->rend.fb_X_CLIP = FB_X_CLIP;
ctx->rend.fb_Y_CLIP = FB_Y_CLIP;
@ -268,8 +270,7 @@ void rend_vblank()
if (!render_called && fb_dirty && FB_R_CTRL.fb_enable)
{
DEBUG_LOG(PVR, "Direct framebuffer write detected");
TA_context *ctx = new TA_context();
ctx->Alloc();
TA_context *ctx = tactx_Alloc();
ctx->rend.isRenderFramebuffer = true;
rend_start_render(ctx);
fb_dirty = false;

View File

@ -1319,7 +1319,7 @@ static void sendPolygon(ICHList *list)
{
PolyParam pp{};
pp.pcw.Shadow = list->pcw.shadow;
pp.pcw.Texture = list->pcw.texture;
pp.pcw.Texture = 0;
pp.pcw.Offset = list->pcw.offset;
pp.pcw.Gouraud = list->pcw.gouraud;
pp.pcw.Volume = list->pcw.volume;
@ -1395,7 +1395,7 @@ static void sendPolygon(ICHList *list)
break;
PolyParam pp{};
pp.pcw.Shadow = list->pcw.shadow;
pp.pcw.Texture = list->pcw.texture;
pp.pcw.Texture = 0;
pp.pcw.Offset = list->pcw.offset;
pp.pcw.Gouraud = list->pcw.gouraud;
pp.pcw.Volume = list->pcw.volume;
@ -1582,6 +1582,12 @@ static void executeCommand(u8 *data, int size)
if (link->offset & 0x80000000)
{
// elan v10 only
if (link->size > VRAM_SIZE)
{
WARN_LOG(PVR, "Texture DMA from %x to %x (%x invalid)", DMAC_SAR(2), link->vramAddress & 0x1ffffff8, link->size);
size = 0;
break;
}
DEBUG_LOG(PVR, "Texture DMA from %x to %x (%x)", DMAC_SAR(2), link->vramAddress & 0x1ffffff8, link->size);
memcpy(&vram[link->vramAddress & VRAM_MASK], &mem_b[DMAC_SAR(2) & RAM_MASK], link->size);
reg74 |= 1;
@ -1589,6 +1595,12 @@ static void executeCommand(u8 *data, int size)
else if (link->offset & 0x20000000)
{
// elan v10 only
if (link->size > VRAM_SIZE)
{
WARN_LOG(PVR, "Texture DMA from eram %x -> %x (%x invalid)", link->offset & ELAN_RAM_MASK, link->vramAddress & VRAM_MASK, link->size);
size = 0;
break;
}
DEBUG_LOG(PVR, "Texture DMA from eram %x -> %x (%x)", link->offset & ELAN_RAM_MASK, link->vramAddress & VRAM_MASK, link->size);
memcpy(&vram[link->vramAddress & VRAM_MASK], &RAM[link->offset & ELAN_RAM_MASK], link->size);
reg74 |= 1;

View File

@ -101,13 +101,28 @@ void FinishRender(TA_context* ctx)
frame_finished.Set();
}
static std::mutex mtx_pool;
static std::vector<TA_context*> ctx_pool;
static std::vector<TA_context*> ctx_list;
static TA_context *tactx_Alloc()
TA_context *tactx_Alloc()
{
TA_context *ctx = new TA_context();
ctx->Alloc();
TA_context *ctx = nullptr;
mtx_pool.lock();
if (!ctx_pool.empty())
{
ctx = ctx_pool.back();
ctx_pool.pop_back();
}
mtx_pool.unlock();
if (ctx == nullptr)
{
ctx = new TA_context();
ctx->Alloc();
}
return ctx;
}
@ -115,7 +130,17 @@ static void tactx_Recycle(TA_context* ctx)
{
if (ctx->nextContext != nullptr)
tactx_Recycle(ctx->nextContext);
delete ctx;
mtx_pool.lock();
if (ctx_pool.size() > 3)
{
delete ctx;
}
else
{
ctx->Reset();
ctx_pool.push_back(ctx);
}
mtx_pool.unlock();
}
static TA_context *tactx_Find(u32 addr, bool allocnew)
@ -162,6 +187,12 @@ void tactx_Term()
for (TA_context *ctx : ctx_list)
delete ctx;
ctx_list.clear();
mtx_pool.lock();
for (TA_context *ctx : ctx_pool)
delete ctx;
ctx_pool.clear();
mtx_pool.unlock();
}
const u32 NULL_CONTEXT = ~0u;

View File

@ -232,6 +232,8 @@ struct rend_context
FB_X_CLIP_type fb_X_CLIP;
FB_Y_CLIP_type fb_Y_CLIP;
u32 fb_W_LINESTRIDE;
u32 fb_W_SOF1;
FB_W_CTRL_type fb_W_CTRL;
RGBAColor fog_clamp_min;
RGBAColor fog_clamp_max;
@ -327,19 +329,19 @@ struct TA_context
{
tad.Reset((u8*)allocAligned(32, TA_DATA_SIZE));
rend.verts.InitBytes(16 * 1024 * 1024, &rend.Overrun, "verts"); //up to 4 mb of vtx data/frame = ~ 96k vtx/frame
rend.idx.Init(512 * 1024, &rend.Overrun, "idx"); //up to 120K indexes ( idx have stripification overhead )
rend.global_param_op.Init(32768, &rend.Overrun, "global_param_op");
rend.verts.Init(320 * 1024, &rend.Overrun, "verts");
rend.idx.Init(320 * 1024, &rend.Overrun, "idx");
rend.global_param_op.Init(16384, &rend.Overrun, "global_param_op");
rend.global_param_pt.Init(5120, &rend.Overrun, "global_param_pt");
rend.global_param_mvo.Init(4096, &rend.Overrun, "global_param_mvo");
rend.global_param_tr.Init(32768, &rend.Overrun, "global_param_tr");
rend.global_param_tr.Init(10240, &rend.Overrun, "global_param_tr");
rend.global_param_mvo_tr.Init(4096, &rend.Overrun, "global_param_mvo_tr");
rend.modtrig.Init(16384, &rend.Overrun, "modtrig");
rend.render_passes.Init(sizeof(RenderPass) * MAX_PASSES, &rend.Overrun, "render_passes"); // 10 render passes
rend.matrices.Init(2000, &rend.Overrun, "matrices");
rend.lightModels.Init(100, &rend.Overrun, "lightModels");
rend.lightModels.Init(150, &rend.Overrun, "lightModels");
Reset();
}
@ -348,6 +350,7 @@ struct TA_context
{
verify(tad.End() - tad.thd_root <= TA_DATA_SIZE);
tad.Clear();
nextContext = nullptr;
rend_inuse.lock();
rend.Clear();
rend.proc_end = rend.proc_start = tad.thd_root;
@ -377,6 +380,7 @@ extern tad_context ta_tad;
TA_context* tactx_Pop(u32 addr);
void tactx_Term();
TA_context *tactx_Alloc();
/*
Ta Context

View File

@ -54,6 +54,16 @@ void os_SetupInput()
#endif
}
void os_TermInput()
{
#if defined(USE_EVDEV)
input_evdev_close();
#endif
#if defined(USE_SDL)
input_sdl_quit();
#endif
}
void UpdateInputState()
{
#if defined(USE_EVDEV)
@ -399,9 +409,6 @@ int main(int argc, char* argv[])
mainui_loop();
#if defined(USE_EVDEV)
input_evdev_close();
#endif
#if defined(SUPPORT_X11)
x11_window_destroy();
#endif

View File

@ -91,6 +91,7 @@ bool NaomiNetwork::startNetwork()
if (config::ActAsServer)
{
enableNetworkBroadcast(true);
const auto timeout = seconds(20);
NOTICE_LOG(NETWORK, "Waiting for slave connections");
steady_clock::time_point start_time = steady_clock::now();
@ -110,6 +111,7 @@ bool NaomiNetwork::startNetwork()
break;
std::this_thread::sleep_for(milliseconds(20));
}
enableNetworkBroadcast(false);
if (!slaves.empty())
{
NOTICE_LOG(NETWORK, "Master starting: %zd slaves", slaves.size());

View File

@ -50,6 +50,7 @@ public:
void shutdown()
{
enableNetworkBroadcast(false);
emu.setNetworkState(false);
closesocket(sock);
sock = INVALID_SOCKET;

View File

@ -125,3 +125,9 @@ static inline const char *inet_ntop(int af, const void* src, char* dst, int cnt)
return dst;
}
#endif
#if defined(__ANDROID__) && !defined(LIBRETRO)
void enableNetworkBroadcast(bool enable);
#else
static inline void enableNetworkBroadcast(bool enable) {}
#endif

View File

@ -90,7 +90,9 @@ struct MaxSpeedNetPipe : public SerialPipe
return true;
}
void shutdown() {
void shutdown()
{
enableNetworkBroadcast(false);
if (VALID(sock))
closesocket(sock);
sock = INVALID_SOCKET;
@ -174,6 +176,7 @@ private:
{
peerAddress.sin_addr.s_addr = addr.sin_addr.s_addr;
peerAddress.sin_port = addr.sin_port;
enableNetworkBroadcast(false);
}
}
}
@ -235,6 +238,8 @@ private:
freeaddrinfo(resultAddr);
}
}
else
enableNetworkBroadcast(true);
serial_setPipe(this);
}

View File

@ -77,6 +77,7 @@ void flycast_term()
lua::term();
emu.term();
gui_term();
os_TermInput();
}
void dc_savestate(int index)

View File

@ -10,6 +10,7 @@ double os_GetSeconds();
void os_DoEvents();
void os_CreateWindow();
void os_SetupInput();
void os_TermInput();
void os_InstallFaultHandler();
void os_UninstallFaultHandler();

View File

@ -692,9 +692,7 @@ void gdrom_hle_op()
discType = 0;
break;
default:
if (gd_hle_state.status == GDC_BUSY)
status = GD_STAT_BUSY;
else if (gd_hle_state.status == GDC_CONTINUE || SecNumber.Status == GD_PLAY)
if (gd_hle_state.status == GDC_CONTINUE || SecNumber.Status == GD_PLAY)
status = GD_STAT_PLAY;
else
status = GD_STAT_PAUSE;

View File

@ -198,8 +198,7 @@ static void reios_sys_system() {
debugf("reios_sys_system: SYSINFO_INIT");
// 0x00-0x07: system_id
// 0x08-0x0c: system_props
// 0x0d-0x0f: padding (zeroed out)
// 0x10-0x17: settings
// 0x0d-0x17: padding (zeroed out)
u8 data[24] = {0};
// read system_id from 0x0001a056
@ -210,11 +209,6 @@ static void reios_sys_system() {
for (u32 i = 0; i < 5; i++)
data[8 + i] = flashrom->Read8(0x1a000 + i);
// system settings
flash_syscfg_block syscfg{};
verify(static_cast<DCFlashChip*>(flashrom)->ReadBlock(FLASH_PT_USER, FLASH_USER_SYSCFG, &syscfg));
memcpy(&data[16], &syscfg.time_lo, 8);
memcpy(GetMemPtr(0x8c000068, sizeof(data)), data, sizeof(data));
p_sh4rcb->cntx.r[0] = 0;
@ -308,7 +302,7 @@ static void reios_sys_flashrom() {
r5 = pointer to destination buffer
r6 = number of bytes to read
Returns:
r0 = number of read bytes if successful, -1 if read failed
r0 = 0 if successful, -1 if read failed
*/
u32 offset = p_sh4rcb->cntx.r[4];
u32 dest = p_sh4rcb->cntx.r[5];
@ -318,7 +312,7 @@ static void reios_sys_flashrom() {
for (u32 i = 0; i < size; i++)
WriteMem8(dest++, flashrom->Read8(offset + i));
p_sh4rcb->cntx.r[0] = size;
p_sh4rcb->cntx.r[0] = 0;
}
break;

View File

@ -9,7 +9,7 @@
#include <mutex>
#include <xxhash.h>
#ifndef TARGET_NO_OPENMP
#ifdef _OPENMP
#include <omp.h>
#endif
@ -28,7 +28,6 @@ float fb_scale_x, fb_scale_y;
const std::array<f32, 16> D_Adjust_LoD_Bias = {
0.f, -4.f, -2.f, -1.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f
};
static void rend_text_invl(vram_block* bl);
u32 detwiddle[2][11][1024];
//input : address in the yyyyyxxxxx format
@ -214,39 +213,6 @@ void vramlock_list_add(vram_block* block)
std::mutex vramlist_lock;
void libCore_vramlock_Lock(u32 start_offset64, u32 end_offset64, BaseTextureCacheData *texture)
{
if (end_offset64 > VRAM_SIZE - 1)
{
WARN_LOG(PVR, "vramlock_Lock_64: end_offset64>(VRAM_SIZE-1) \n Tried to lock area out of vram , possibly bug on the pvr plugin");
end_offset64 = VRAM_SIZE - 1;
}
if (start_offset64 > end_offset64)
{
WARN_LOG(PVR, "vramlock_Lock_64: start_offset64>end_offset64 \n Tried to lock negative block , possibly bug on the pvr plugin");
return;
}
vram_block *block = new vram_block();
block->end = end_offset64;
block->start = start_offset64;
block->texture = texture;
{
std::lock_guard<std::mutex> lock(vramlist_lock);
if (texture->lock_block == nullptr)
{
// This also protects vram if needed
vramlock_list_add(block);
texture->lock_block = block;
}
else
delete block;
}
}
bool VramLockedWriteOffset(size_t offset)
{
if (offset >= VRAM_SIZE)
@ -262,7 +228,7 @@ bool VramLockedWriteOffset(size_t offset)
{
if (lock != nullptr)
{
rend_text_invl(lock);
lock->texture->invalidate();
if (lock != nullptr)
{
@ -295,7 +261,7 @@ static void libCore_vramlock_Unlock_block_wb(vram_block* block)
delete block;
}
#ifndef TARGET_NO_OPENMP
#ifdef _OPENMP
static inline int getThreadCount()
{
int tcount = omp_get_num_procs() - 1;
@ -324,7 +290,7 @@ static struct xbrz::ScalerCfg xbrz_cfg;
void UpscalexBRZ(int factor, u32* source, u32* dest, int width, int height, bool has_alpha)
{
#ifndef TARGET_NO_OPENMP
#ifdef _OPENMP
parallelize([=](int start, int end) {
xbrz::scale(factor, source, dest, width, height, has_alpha ? xbrz::ColorFormat::ARGB : xbrz::ColorFormat::RGB,
xbrz_cfg, start, end);
@ -445,25 +411,66 @@ bool BaseTextureCacheData::NeedsUpdate() {
return rc;
}
void BaseTextureCacheData::protectVRam()
{
u32 end = sa + size - 1;
if (end >= VRAM_SIZE)
{
WARN_LOG(PVR, "protectVRam: end >= VRAM_SIZE. Tried to lock area out of vram");
end = VRAM_SIZE - 1;
}
if (sa_tex > end)
{
WARN_LOG(PVR, "vramlock_Lock: sa_tex > end. Tried to lock negative block");
return;
}
vram_block *block = new vram_block();
block->end = end;
block->start = sa_tex;
block->texture = this;
{
std::lock_guard<std::mutex> lock(vramlist_lock);
if (lock_block == nullptr)
{
// This also protects vram if needed
vramlock_list_add(block);
lock_block = block;
}
else
delete block;
}
}
void BaseTextureCacheData::unprotectVRam()
{
std::lock_guard<std::mutex> lock(vramlist_lock);
if (lock_block)
libCore_vramlock_Unlock_block_wb(lock_block);
lock_block = nullptr;
}
bool BaseTextureCacheData::Delete()
{
if (custom_load_in_progress > 0)
return false;
{
std::lock_guard<std::mutex> lock(vramlist_lock);
if (lock_block)
libCore_vramlock_Unlock_block_wb(lock_block);
lock_block = nullptr;
}
unprotectVRam();
free(custom_image_data);
custom_image_data = nullptr;
return true;
}
void BaseTextureCacheData::Create()
BaseTextureCacheData::BaseTextureCacheData(TSP tsp, TCW tcw)
{
this->tsp = tsp;
this->tcw = tcw;
//Reset state info ..
Updates = 0;
dirty = FrameCount;
@ -777,7 +784,7 @@ void BaseTextureCacheData::Update()
height = original_h;
//lock the texture to detect changes in it
libCore_vramlock_Lock(sa_tex, sa + size - 1, this);
protectVRam();
UploadToGPU(upscaled_w, upscaled_h, (u8*)temp_tex_buffer, IsMipmapped(), mipmapped);
if (config::DumpTextures)
@ -931,14 +938,9 @@ template void ReadFramebuffer<RGBAPacker>(PixelBuffer<u32>& pb, int& width, int&
template void ReadFramebuffer<BGRAPacker>(PixelBuffer<u32>& pb, int& width, int& height);
template<int Red, int Green, int Blue, int Alpha>
void WriteTextureToVRam(u32 width, u32 height, u8 *data, u16 *dst, u32 fb_w_ctrl_in, u32 linestride)
void WriteTextureToVRam(u32 width, u32 height, u8 *data, u16 *dst, FB_W_CTRL_type fb_w_ctrl, u32 linestride)
{
FB_W_CTRL_type fb_w_ctrl;
if (fb_w_ctrl_in != ~0u)
fb_w_ctrl.full = fb_w_ctrl_in;
else
fb_w_ctrl = FB_W_CTRL;
u32 padding = (linestride == ~0u ? FB_W_LINESTRIDE.stride * 8 : linestride);
u32 padding = linestride;
if (padding / 2 > width)
padding = padding / 2 - width;
else
@ -980,16 +982,33 @@ void WriteTextureToVRam(u32 width, u32 height, u8 *data, u16 *dst, u32 fb_w_ctrl
dst += padding;
}
}
template void WriteTextureToVRam<0, 1, 2, 3>(u32 width, u32 height, u8 *data, u16 *dst, u32 fb_w_ctrl_in, u32 linestride);
template void WriteTextureToVRam<2, 1, 0, 3>(u32 width, u32 height, u8 *data, u16 *dst, u32 fb_w_ctrl_in, u32 linestride);
template void WriteTextureToVRam<0, 1, 2, 3>(u32 width, u32 height, u8 *data, u16 *dst, FB_W_CTRL_type fb_w_ctrl, u32 linestride);
template void WriteTextureToVRam<2, 1, 0, 3>(u32 width, u32 height, u8 *data, u16 *dst, FB_W_CTRL_type fb_w_ctrl, u32 linestride);
static void rend_text_invl(vram_block* bl)
void BaseTextureCacheData::invalidate()
{
BaseTextureCacheData* texture = bl->texture;
texture->dirty = FrameCount;
texture->lock_block = nullptr;
dirty = FrameCount;
libCore_vramlock_Unlock_block_wb(bl);
libCore_vramlock_Unlock_block_wb(lock_block);
lock_block = nullptr;
}
void getRenderToTextureDimensions(u32& width, u32& height, u32& pow2Width, u32& pow2Height)
{
pow2Width = 8;
while (pow2Width < width)
pow2Width *= 2;
pow2Height = 8;
while (pow2Height < height)
pow2Height *= 2;
if (!config::RenderToTextureBuffer)
{
float upscale = config::RenderResolution / 480.f;
width *= upscale;
height *= upscale;
pow2Width *= upscale;
pow2Height *= upscale;
}
}
#ifdef TEST_AUTOMATION

View File

@ -564,7 +564,6 @@ struct vram_block
bool VramLockedWriteOffset(size_t offset);
bool VramLockedWrite(u8* address);
void libCore_vramlock_Lock(u32 start_offset, u32 end_offset, BaseTextureCacheData *texture);
void UpscalexBRZ(int factor, u32* source, u32* dest, int width, int height, bool has_alpha);
@ -573,7 +572,37 @@ enum class TextureType { _565, _5551, _4444, _8888, _8 };
class BaseTextureCacheData
{
protected:
BaseTextureCacheData(TSP tsp, TCW tcw);
public:
BaseTextureCacheData(BaseTextureCacheData&& other)
{
tsp = other.tsp;
tcw = other.tcw;
tex_type = other.tex_type;
sa_tex = other.sa_tex;
dirty = other.dirty;
std::swap(lock_block, other.lock_block);
sa = other.sa;
width = other.width;
height = other.height;
size = other.size;
tex = other.tex;
texconv = other.texconv;
texconv32 = other.texconv32;
texconv8 = other.texconv8;
Updates = other.Updates;
palette_hash = other.palette_hash;
texture_hash = other.texture_hash;
old_texture_hash = other.old_texture_hash;
std::swap(custom_image_data, other.custom_image_data);
custom_width = other.custom_width;
custom_height = other.custom_height;
custom_load_in_progress = 0;
gpuPalette = other.gpuPalette;
}
TSP tsp; //dreamcast texture parameters
TCW tcw;
@ -638,7 +667,6 @@ public:
return custom_load_in_progress == 0 && custom_image_data != NULL;
}
void Create();
void ComputeHash();
void Update();
virtual void UploadToGPU(int width, int height, u8 *temp_tex_buffer, bool mipmapped, bool mipmapsIncluded = false) = 0;
@ -648,6 +676,10 @@ public:
bool NeedsUpdate();
virtual bool Delete();
virtual ~BaseTextureCacheData() = default;
void protectVRam();
void unprotectVRam();
void invalidate();
static bool IsGpuHandledPaletted(TSP tsp, TCW tcw)
{
// Some palette textures are handled on the GPU
@ -692,15 +724,36 @@ public:
}
else //create if not existing
{
texture = &cache[key];
texture->tsp = tsp;
texture->tcw = tcw;
texture = &cache.emplace(std::make_pair(key, Texture(tsp, tcw))).first->second;
}
return texture;
}
Texture *getRTTexture(u32 address, u32 fb_packmode, u32 width, u32 height)
{
// TexAddr : (address), Reserved : 0, StrideSel : 0, ScanOrder : 1
TCW tcw{ { address >> 3, 0, 0, 1 } };
switch (fb_packmode)
{
case 0:
case 3:
tcw.PixelFmt = Pixel1555;
break;
case 1:
tcw.PixelFmt = Pixel565;
break;
case 2:
tcw.PixelFmt = Pixel4444;
break;
}
TSP tsp{};
for (tsp.TexU = 0; tsp.TexU <= 7 && (8u << tsp.TexU) < width; tsp.TexU++);
for (tsp.TexV = 0; tsp.TexV <= 7 && (8u << tsp.TexV) < height; tsp.TexV++);
return getTextureCacheData(tsp, tcw);
}
void CollectCleanup()
{
std::vector<u64> list;
@ -718,7 +771,7 @@ public:
for (u64 id : list)
{
if (cache[id].Delete())
if (cache.find(id)->second.Delete())
cache.erase(id);
}
}
@ -745,7 +798,8 @@ protected:
template<typename Packer = RGBAPacker>
void ReadFramebuffer(PixelBuffer<u32>& pb, int& width, int& height);
template<int Red = 0, int Green = 1, int Blue = 2, int Alpha = 3>
void WriteTextureToVRam(u32 width, u32 height, u8 *data, u16 *dst, u32 fb_w_ctrl = -1, u32 linestride = -1);
void WriteTextureToVRam(u32 width, u32 height, u8 *data, u16 *dst, FB_W_CTRL_type fb_w_ctrl, u32 linestride);
void getRenderToTextureDimensions(u32& width, u32& height, u32& pow2Width, u32& pow2Height);
static inline void MakeFogTexture(u8 *tex_data)
{

View File

@ -282,9 +282,6 @@ BaseTextureCacheData *DX11Renderer::GetTexture(TSP tsp, TCW tcw)
//lookup texture
DX11Texture* tf = texCache.getTextureCacheData(tsp, tcw);
if (tf->texture == nullptr)
tf->Create();
//update if needed
if (tf->NeedsUpdate())
tf->Update();
@ -325,7 +322,7 @@ void DX11Renderer::configVertexShader()
if (pvrrc.isRTT)
{
prepareRttRenderTarget(FB_W_SOF1 & VRAM_MASK);
prepareRttRenderTarget(pvrrc.fb_W_SOF1 & VRAM_MASK);
}
else
{
@ -416,8 +413,6 @@ void DX11Renderer::setupPixelShaderConstants()
bool DX11Renderer::Render()
{
u32 texAddress = FB_W_SOF1 & VRAM_MASK;
// make sure to unbind the framebuffer view before setting it as render target
ID3D11ShaderResourceView *nullView = nullptr;
deviceContext->PSSetShaderResources(0, 1, &nullView);
@ -450,7 +445,7 @@ bool DX11Renderer::Render()
if (is_rtt)
{
readRttRenderTarget(texAddress);
readRttRenderTarget(pvrrc.fb_W_SOF1 & VRAM_MASK);
}
else
{
@ -1047,21 +1042,11 @@ void DX11Renderer::prepareRttRenderTarget(u32 texAddress)
u32 fbw = pvrrc.getFramebufferWidth();
u32 fbh = pvrrc.getFramebufferHeight();
DEBUG_LOG(RENDERER, "RTT packmode=%d stride=%d - %d x %d @ %06x",
FB_W_CTRL.fb_packmode, pvrrc.fb_W_LINESTRIDE * 8, fbw, fbh, texAddress);
// Find the smallest power of two texture that fits the viewport
u32 fbh2 = 2;
while (fbh2 < fbh)
fbh2 *= 2;
u32 fbw2 = 2;
while (fbw2 < fbw)
fbw2 *= 2;
if (!config::RenderToTextureBuffer)
{
fbw = (u32)(fbw * config::RenderResolution / 480.f);
fbh = (u32)(fbh * config::RenderResolution / 480.f);
fbw2 = (u32)(fbw2 * config::RenderResolution / 480.f);
fbh2 = (u32)(fbh2 * config::RenderResolution / 480.f);
}
pvrrc.fb_W_CTRL.fb_packmode, pvrrc.fb_W_LINESTRIDE * 8, fbw, fbh, texAddress);
u32 fbw2;
u32 fbh2;
getRenderToTextureDimensions(fbw, fbh, fbw2, fbh2);
createTexAndRenderTarget(rttTexture, rttRenderTarget, fbw2, fbh2);
createDepthTexAndView(rttDepthTex, rttDepthTexView, fbw2, fbh2);
deviceContext->ClearDepthStencilView(rttDepthTexView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 0.f, 0);
@ -1080,7 +1065,6 @@ void DX11Renderer::readRttRenderTarget(u32 texAddress)
{
u32 w = pvrrc.getFramebufferWidth();
u32 h = pvrrc.getFramebufferHeight();
const u8 fb_packmode = FB_W_CTRL.fb_packmode;
if (config::RenderToTextureBuffer)
{
D3D11_TEXTURE2D_DESC desc;
@ -1125,37 +1109,14 @@ void DX11Renderer::readRttRenderTarget(u32 texAddress)
deviceContext->Unmap(stagingTex, 0);
u16 *dst = (u16 *)&vram[texAddress];
WriteTextureToVRam<2, 1, 0, 3>(w, h, (u8 *)tmp_buf.data(), dst, -1, pvrrc.fb_W_LINESTRIDE * 8);
WriteTextureToVRam<2, 1, 0, 3>(w, h, (u8 *)tmp_buf.data(), dst, pvrrc.fb_W_CTRL, pvrrc.fb_W_LINESTRIDE * 8);
}
else
{
//memset(&vram[gl.rtt.texAddress], 0, size);
if (w <= 1024 && h <= 1024)
{
// TexAddr : (address), Reserved : 0, StrideSel : 0, ScanOrder : 1
TCW tcw = { { texAddress >> 3, 0, 0, 1 } };
switch (fb_packmode) {
case 0:
case 3:
tcw.PixelFmt = Pixel1555;
break;
case 1:
tcw.PixelFmt = Pixel565;
break;
case 2:
tcw.PixelFmt = Pixel4444;
break;
}
TSP tsp = { 0 };
for (tsp.TexU = 0; tsp.TexU <= 7 && (8u << tsp.TexU) < w; tsp.TexU++)
;
for (tsp.TexV = 0; tsp.TexV <= 7 && (8u << tsp.TexV) < h; tsp.TexV++)
;
DX11Texture* texture = texCache.getTextureCacheData(tsp, tcw);
if (!texture->texture)
texture->Create();
DX11Texture* texture = texCache.getRTTexture(texAddress, pvrrc.fb_W_CTRL.fb_packmode, w, h);
texture->texture = rttTexture;
rttTexture.reset();
@ -1168,7 +1129,7 @@ void DX11Renderer::readRttRenderTarget(u32 texAddress)
device->CreateShaderResourceView(texture->texture, &viewDesc, &texture->textureView.get());
texture->dirty = 0;
libCore_vramlock_Lock(texture->sa_tex, texture->sa + texture->size - 1, texture);
texture->protectVRam();
}
}
}

View File

@ -25,6 +25,12 @@
class DX11Texture final : public BaseTextureCacheData
{
public:
DX11Texture(TSP tsp = {}, TCW tcw = {}) : BaseTextureCacheData(tsp, tcw) {}
DX11Texture(DX11Texture&& other) : BaseTextureCacheData(std::move(other)) {
std::swap(texture, other.texture);
std::swap(textureView, other.textureView);
}
ComPtr<ID3D11Texture2D> texture;
ComPtr<ID3D11ShaderResourceView> textureView;

View File

@ -126,6 +126,8 @@ struct DX11OITRenderer : public DX11Renderer
depthView.reset();
viewDesc.Format = DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS;
device->CreateShaderResourceView(depthStencilTex2, &viewDesc, &depthView.get());
createDepthTexAndView(depthTex, depthTexView, maxWidth, maxHeight, DXGI_FORMAT_R32G8X24_TYPELESS);
}
}
@ -635,11 +637,10 @@ struct DX11OITRenderer : public DX11Renderer
ID3D11ShaderResourceView *p = nullptr;
deviceContext->PSSetShaderResources(0, 1, &p);
// To avoid DEVICE_DRAW_RENDERTARGETVIEW_NOT_SET warnings
deviceContext->OMSetRenderTargets(1, &fbRenderTarget.get(), depthTexView);
deviceContext->OMSetRenderTargets(1, &fbRenderTarget.get(), nullptr);
configVertexShader();
bool is_rtt = pvrrc.isRTT;
u32 texAddress = FB_W_SOF1 & VRAM_MASK;
deviceContext->IASetInputLayout(mainInputLayout);
@ -662,7 +663,7 @@ struct DX11OITRenderer : public DX11Renderer
if (is_rtt)
{
readRttRenderTarget(texAddress);
readRttRenderTarget(pvrrc.fb_W_SOF1 & VRAM_MASK);
}
else
{

View File

@ -218,9 +218,6 @@ BaseTextureCacheData *D3DRenderer::GetTexture(TSP tsp, TCW tcw)
//lookup texture
D3DTexture* tf = texCache.getTextureCacheData(tsp, tcw);
if (tf->texture == nullptr)
tf->Create();
//update if needed
if (tf->NeedsUpdate())
tf->Update();
@ -868,21 +865,11 @@ void D3DRenderer::prepareRttRenderTarget(u32 texAddress)
u32 fbw = pvrrc.getFramebufferWidth();
u32 fbh = pvrrc.getFramebufferHeight();
DEBUG_LOG(RENDERER, "RTT packmode=%d stride=%d - %d x %d @ %06x",
FB_W_CTRL.fb_packmode, pvrrc.fb_W_LINESTRIDE * 8, fbw, fbh, texAddress);
// Find the smallest power of two texture that fits the viewport
u32 fbh2 = 2;
while (fbh2 < fbh)
fbh2 *= 2;
u32 fbw2 = 2;
while (fbw2 < fbw)
fbw2 *= 2;
if (!config::RenderToTextureBuffer)
{
fbw = (u32)(fbw * config::RenderResolution / 480.f);
fbh = (u32)(fbh * config::RenderResolution / 480.f);
fbw2 = (u32)(fbw2 * config::RenderResolution / 480.f);
fbh2 = (u32)(fbh2 * config::RenderResolution / 480.f);
}
pvrrc.fb_W_CTRL.fb_packmode, pvrrc.fb_W_LINESTRIDE * 8, fbw, fbh, texAddress);
u32 fbw2;
u32 fbh2;
getRenderToTextureDimensions(fbw, fbh, fbw2, fbh2);
rttTexture.reset();
device->CreateTexture(fbw2, fbh2, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &rttTexture.get(), NULL);
@ -903,7 +890,6 @@ void D3DRenderer::readRttRenderTarget(u32 texAddress)
{
u32 w = pvrrc.getFramebufferWidth();
u32 h = pvrrc.getFramebufferHeight();
const u8 fb_packmode = FB_W_CTRL.fb_packmode;
if (config::RenderToTextureBuffer)
{
D3DSURFACE_DESC rttDesc;
@ -934,41 +920,17 @@ void D3DRenderer::readRttRenderTarget(u32 texAddress)
verifyWin(offscreenSurface->UnlockRect());
u16 *dst = (u16 *)&vram[texAddress];
WriteTextureToVRam<2, 1, 0, 3>(w, h, (u8 *)tmp_buf.data(), dst, -1, pvrrc.fb_W_LINESTRIDE * 8);
WriteTextureToVRam<2, 1, 0, 3>(w, h, (u8 *)tmp_buf.data(), dst, pvrrc.fb_W_CTRL, pvrrc.fb_W_LINESTRIDE * 8);
}
else
{
//memset(&vram[gl.rtt.texAddress], 0, size);
if (w <= 1024 && h <= 1024)
{
// TexAddr : (address), Reserved : 0, StrideSel : 0, ScanOrder : 1
TCW tcw = { { texAddress >> 3, 0, 0, 1 } };
switch (fb_packmode) {
case 0:
case 3:
tcw.PixelFmt = Pixel1555;
break;
case 1:
tcw.PixelFmt = Pixel565;
break;
case 2:
tcw.PixelFmt = Pixel4444;
break;
}
TSP tsp = { 0 };
for (tsp.TexU = 0; tsp.TexU <= 7 && (8u << tsp.TexU) < w; tsp.TexU++)
;
for (tsp.TexV = 0; tsp.TexV <= 7 && (8u << tsp.TexV) < h; tsp.TexV++)
;
D3DTexture* texture = texCache.getTextureCacheData(tsp, tcw);
if (!texture->texture)
texture->Create();
D3DTexture* texture = texCache.getRTTexture(texAddress, pvrrc.fb_W_CTRL.fb_packmode, w, h);
texture->texture = rttTexture;
texture->dirty = 0;
libCore_vramlock_Lock(texture->sa_tex, texture->sa + texture->size - 1, texture);
texture->protectVRam();
}
}
}
@ -981,7 +943,7 @@ bool D3DRenderer::Render()
backbuffer.reset();
verifyWin(device->GetRenderTarget(0, &backbuffer.get()));
u32 texAddress = FB_W_SOF1 & VRAM_MASK;
u32 texAddress = pvrrc.fb_W_SOF1 & VRAM_MASK;
if (is_rtt)
{
prepareRttRenderTarget(texAddress);

View File

@ -24,6 +24,11 @@
class D3DTexture final : public BaseTextureCacheData
{
public:
D3DTexture(TSP tsp = {}, TCW tcw = {}) : BaseTextureCacheData(tsp, tcw) {}
D3DTexture(D3DTexture&& other) : BaseTextureCacheData(std::move(other)) {
std::swap(texture, other.texture);
}
ComPtr<IDirect3DTexture9> texture;
std::string GetId() override { return std::to_string((uintptr_t)texture.get()); }
void UploadToGPU(int width, int height, u8* temp_tex_buffer, bool mipmapped,

View File

@ -209,7 +209,7 @@ struct gl_ctx
bool directXfer;
u32 width;
u32 height;
u32 fb_w_ctrl;
FB_W_CTRL_type fb_w_ctrl;
u32 linestride;
} rtt;
@ -337,6 +337,12 @@ extern struct ShaderUniforms_t
class TextureCacheData final : public BaseTextureCacheData
{
public:
TextureCacheData(TSP tsp, TCW tcw) : BaseTextureCacheData(tsp, tcw), texID(glcache.GenTexture()) {
}
TextureCacheData(TextureCacheData&& other) : BaseTextureCacheData(std::move(other)) {
std::swap(texID, other.texID);
}
GLuint texID; //gl texture
std::string GetId() override { return std::to_string(texID); }
void UploadToGPU(int width, int height, u8 *temp_tex_buffer, bool mipmapped, bool mipmapsIncluded = false) override;

View File

@ -123,7 +123,7 @@ bool TextureCacheData::Delete()
GLuint BindRTT(bool withDepthBuffer)
{
GLenum channels, format;
switch(FB_W_CTRL.fb_packmode)
switch(pvrrc.fb_W_CTRL.fb_packmode)
{
case 0: //0x0 0555 KRGB 16 bit (default) Bit 15 is the value of fb_kval[7].
channels = GL_RGBA;
@ -148,7 +148,7 @@ GLuint BindRTT(bool withDepthBuffer)
case 4: //0x4 888 RGB 24 bit packed
case 5: //0x5 0888 KRGB 32 bit K is the value of fk_kval.
case 6: //0x6 8888 ARGB 32 bit
WARN_LOG(RENDERER, "Unsupported render to texture format: %d", FB_W_CTRL.fb_packmode);
WARN_LOG(RENDERER, "Unsupported render to texture format: %d", pvrrc.fb_W_CTRL.fb_packmode);
return 0;
case 7: //7 invalid
@ -157,8 +157,8 @@ GLuint BindRTT(bool withDepthBuffer)
}
u32 fbw = pvrrc.getFramebufferWidth();
u32 fbh = pvrrc.getFramebufferHeight();
u32 texAddress = FB_W_SOF1 & VRAM_MASK;
DEBUG_LOG(RENDERER, "RTT packmode=%d stride=%d - %d x %d @ %06x", FB_W_CTRL.fb_packmode, pvrrc.fb_W_LINESTRIDE * 8,
u32 texAddress = pvrrc.fb_W_SOF1 & VRAM_MASK;
DEBUG_LOG(RENDERER, "RTT packmode=%d stride=%d - %d x %d @ %06x", pvrrc.fb_W_CTRL.fb_packmode, pvrrc.fb_W_LINESTRIDE * 8,
fbw, fbh, texAddress);
if (gl.rtt.texAddress != ~0u)
@ -172,20 +172,9 @@ GLuint BindRTT(bool withDepthBuffer)
if (gl.rtt.depthb != 0)
glDeleteRenderbuffers(1, &gl.rtt.depthb);
// Find the smallest power of two texture that fits the viewport
u32 fbh2 = 2;
while (fbh2 < fbh)
fbh2 *= 2;
u32 fbw2 = 2;
while (fbw2 < fbw)
fbw2 *= 2;
if (!config::RenderToTextureBuffer)
{
fbw *= config::RenderResolution / 480.f;
fbh *= config::RenderResolution / 480.f;
fbw2 *= config::RenderResolution / 480.f;
fbh2 *= config::RenderResolution / 480.f;
}
u32 fbw2;
u32 fbh2;
getRenderToTextureDimensions(fbw, fbh, fbw2, fbh2);
#ifdef GL_PIXEL_PACK_BUFFER
if (gl.gl_major >= 3 && config::RenderToTextureBuffer)
@ -269,7 +258,7 @@ void ReadRTTBuffer()
u32 w = pvrrc.getFramebufferWidth();
u32 h = pvrrc.getFramebufferHeight();
const u8 fb_packmode = FB_W_CTRL.fb_packmode;
const u8 fb_packmode = pvrrc.fb_W_CTRL.fb_packmode;
if (config::RenderToTextureBuffer)
{
@ -313,7 +302,7 @@ void ReadRTTBuffer()
gl.rtt.directXfer = false;
if (gl.gl_major >= 3)
{
gl.rtt.fb_w_ctrl = FB_W_CTRL.full;
gl.rtt.fb_w_ctrl = pvrrc.fb_W_CTRL;
glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, 0);
}
else
@ -324,7 +313,7 @@ void ReadRTTBuffer()
u8 *p = (u8 *)tmp_buf.data();
glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, p);
WriteTextureToVRam(w, h, p, dst, -1, gl.rtt.linestride);
WriteTextureToVRam(w, h, p, dst, pvrrc.fb_W_CTRL, gl.rtt.linestride);
gl.rtt.texAddress = ~0;
}
}
@ -338,33 +327,12 @@ void ReadRTTBuffer()
//memset(&vram[gl.rtt.texAddress], 0, size);
if (w <= 1024 && h <= 1024)
{
// TexAddr : (address), Reserved : 0, StrideSel : 0, ScanOrder : 1
TCW tcw = { { gl.rtt.texAddress >> 3, 0, 0, 1 } };
switch (fb_packmode) {
case 0:
case 3:
tcw.PixelFmt = Pixel1555;
break;
case 1:
tcw.PixelFmt = Pixel565;
break;
case 2:
tcw.PixelFmt = Pixel4444;
break;
}
TSP tsp = { 0 };
for (tsp.TexU = 0; tsp.TexU <= 7 && (8u << tsp.TexU) < w; tsp.TexU++);
for (tsp.TexV = 0; tsp.TexV <= 7 && (8u << tsp.TexV) < h; tsp.TexV++);
TextureCacheData *texture_data = TexCache.getTextureCacheData(tsp, tcw);
if (texture_data->texID != 0)
glcache.DeleteTextures(1, &texture_data->texID);
else
texture_data->Create();
TextureCacheData *texture_data = TexCache.getRTTexture(gl.rtt.texAddress, fb_packmode, w, h);
glcache.DeleteTextures(1, &texture_data->texID);
texture_data->texID = gl.rtt.tex;
gl.rtt.tex = 0;
texture_data->dirty = 0;
libCore_vramlock_Lock(texture_data->sa_tex, texture_data->sa + texture_data->size - 1, texture_data);
texture_data->protectVRam();
}
gl.rtt.texAddress = ~0;
}
@ -413,11 +381,6 @@ BaseTextureCacheData *gl_GetTexture(TSP tsp, TCW tcw)
//lookup texture
TextureCacheData* tf = TexCache.getTextureCacheData(tsp, tcw);
if (tf->texID == 0)
{
tf->Create();
tf->texID = glcache.GenTexture();
}
readAsyncPixelBuffer(tf->sa_tex);
//update if needed

View File

@ -1908,7 +1908,7 @@ static void gui_display_settings()
ImGui::Spacing();
header("Texture Upscaling");
{
#ifndef TARGET_NO_OPENMP
#ifdef _OPENMP
OptionArrowButtons("Texture Upscaling", config::TextureUpscale, 1, 8,
"Upscale textures with the xBRZ algorithm. Only on fast platforms and for certain 2D games");
OptionSlider("Texture Max Size", config::MaxFilteredTextureSize, 8, 1024,
@ -2100,6 +2100,19 @@ static void gui_display_settings()
OptionRadioButton<int>("Full", config::GGPOAnalogAxes, 2, "Use the left thumbstick horizontal and vertical axes");
OptionCheckbox("Enable Chat", config::GGPOChat, "Open the chat window when a chat message is received");
if (config::GGPOChat)
{
OptionCheckbox("Enable Chat Window Timeout", config::GGPOChatTimeoutToggle, "Automatically close chat window after 20 seconds");
if (config::GGPOChatTimeoutToggle)
{
char chatTimeout[256];
sprintf(chatTimeout, "%d", (int)config::GGPOChatTimeout);
ImGui::InputText("Chat Window Timeout (seconds)", chatTimeout, sizeof(chatTimeout), ImGuiInputTextFlags_CharsDecimal, nullptr, nullptr);
ImGui::SameLine();
ShowHelpMarker("Sets duration that chat window stays open after new message is received.");
config::GGPOChatTimeout.set(atoi(chatTimeout));
}
}
OptionCheckbox("Network Statistics", config::NetworkStats,
"Display network statistics on screen");
}

View File

@ -21,6 +21,7 @@
#include "gui.h"
#include "imgui/imgui.h"
#include "network/ggpo.h"
#include <chrono>
class Chat
{
@ -34,6 +35,10 @@ class Chat
const ImVec4 WHITE { 1, 1, 1, 1 };
const ImVec4 YELLOW { 1, 1, 0, 1 };
bool manual_open = false;
bool enable_timeout = false;
std::chrono::steady_clock::time_point launch_time;
std::string playerName(bool remote)
{
if (remote)
@ -43,6 +48,15 @@ class Chat
}
public:
void toggle_timeout()
{
if (config::GGPOChatTimeoutToggle && !manual_open)
{
enable_timeout = true;
launch_time = std::chrono::steady_clock::now();
}
}
void reset()
{
visible = false;
@ -51,6 +65,16 @@ public:
void display()
{
auto timeout = std::chrono::seconds(config::GGPOChatTimeout.get());
if (enable_timeout &&
std::chrono::steady_clock::now() - launch_time > timeout)
{
visible = false;
enable_timeout = false;
manual_open = false;
}
if (!visible)
return;
@ -85,6 +109,7 @@ public:
lines.push_back(std::make_pair(WHITE, line));
buf[0] = '\0';
newMessage = true;
enable_timeout = false;
}
ImGui::SetKeyboardFocusHere(-1);
}
@ -101,17 +126,22 @@ public:
void receive(int playerNum, const std::string& msg)
{
if (config::GGPOChat)
if (config::GGPOChat && !visible)
{
visible = true;
toggle_timeout();
}
std::string line = "<" + playerName(true) + "> " + msg;
lines.push_back(std::make_pair(YELLOW, line));
newMessage = true;
}
void toggle()
void toggle(bool manual = false)
{
visible = !visible;
focus = visible;
if (manual)
manual_open = manual;
}
void setLocalPlayerName(const std::string& name) {

View File

@ -170,7 +170,6 @@ void select_file_popup(const char *prompt, StringCallback callback,
if (entry->d_type == DT_DIR)
is_dir = true;
if (entry->d_type == DT_UNKNOWN || entry->d_type == DT_LNK)
#endif
{
struct stat st;
if (flycast::stat(child_path.c_str(), &st) != 0)
@ -178,11 +177,28 @@ void select_file_popup(const char *prompt, StringCallback callback,
if (S_ISDIR(st.st_mode))
is_dir = true;
}
if (is_dir && flycast::access(child_path.c_str(), R_OK) == 0)
#else // _WIN32
nowide::wstackstring wname;
if (wname.convert(child_path.c_str()))
{
DWORD attr = GetFileAttributesW(wname.c_str());
if (attr != INVALID_FILE_ATTRIBUTES)
{
if (attr & FILE_ATTRIBUTE_HIDDEN)
continue;
if (attr & FILE_ATTRIBUTE_DIRECTORY)
is_dir = true;
}
}
#endif
if (is_dir)
{
if (name == "..")
dotdot_seen = true;
subfolders.push_back(name);
if (flycast::access(child_path.c_str(), R_OK) == 0)
{
if (name == "..")
dotdot_seen = true;
subfolders.push_back(name);
}
}
else
{
@ -270,7 +286,7 @@ void select_file_popup(const char *prompt, StringCallback callback,
#endif
child_path = path + native_separator + name;
}
if (ImGui::Selectable(name.c_str()))
if (ImGui::Selectable(name == ".." ? ".. Up to Parent Directory" : name.c_str()))
{
subfolders_read = false;
select_current_directory = child_path;

View File

@ -376,26 +376,18 @@ void TextureDrawer::Init(SamplerManager *samplerManager, ShaderManager *shaderMa
vk::CommandBuffer TextureDrawer::BeginRenderPass()
{
DEBUG_LOG(RENDERER, "RenderToTexture packmode=%d stride=%d - %d x %d @ %06x", FB_W_CTRL.fb_packmode, pvrrc.fb_W_LINESTRIDE * 8,
pvrrc.fb_X_CLIP.max + 1, pvrrc.fb_Y_CLIP.max + 1, FB_W_SOF1 & VRAM_MASK);
DEBUG_LOG(RENDERER, "RenderToTexture packmode=%d stride=%d - %d x %d @ %06x", pvrrc.fb_W_CTRL.fb_packmode, pvrrc.fb_W_LINESTRIDE * 8,
pvrrc.fb_X_CLIP.max + 1, pvrrc.fb_Y_CLIP.max + 1, pvrrc.fb_W_SOF1 & VRAM_MASK);
matrices.CalcMatrices(&pvrrc);
textureAddr = FB_W_SOF1 & VRAM_MASK;
textureAddr = pvrrc.fb_W_SOF1 & VRAM_MASK;
u32 origWidth = pvrrc.getFramebufferWidth();
u32 origHeight = pvrrc.getFramebufferHeight();
u32 heightPow2 = 8;
while (heightPow2 < origHeight)
heightPow2 *= 2;
u32 widthPow2 = 8;
while (widthPow2 < origWidth)
widthPow2 *= 2;
float upscale = 1.f;
if (!config::RenderToTextureBuffer)
upscale = config::RenderResolution / 480.f;
u32 upscaledWidth = origWidth * upscale;
u32 upscaledHeight = origHeight * upscale;
widthPow2 *= upscale;
heightPow2 *= upscale;
u32 upscaledWidth = origWidth;
u32 upscaledHeight = origHeight;
u32 widthPow2;
u32 heightPow2;
getRenderToTextureDimensions(upscaledWidth, upscaledHeight, widthPow2, heightPow2);
rttPipelineManager->CheckSettingsChange();
VulkanContext *context = GetContext();
@ -420,33 +412,8 @@ vk::CommandBuffer TextureDrawer::BeginRenderPass()
if (!config::RenderToTextureBuffer)
{
// TexAddr : fb_rtt.TexAddr, Reserved : 0, StrideSel : 0, ScanOrder : 1
TCW tcw = { { textureAddr >> 3, 0, 0, 1 } };
switch (FB_W_CTRL.fb_packmode) {
case 0:
case 3:
tcw.PixelFmt = Pixel1555;
break;
case 1:
tcw.PixelFmt = Pixel565;
break;
case 2:
tcw.PixelFmt = Pixel4444;
break;
}
TSP tsp = { { 0 } };
for (tsp.TexU = 0; tsp.TexU <= 7 && (8u << tsp.TexU) < origWidth; tsp.TexU++);
for (tsp.TexV = 0; tsp.TexV <= 7 && (8u << tsp.TexV) < origHeight; tsp.TexV++);
texture = textureCache->getTextureCacheData(tsp, tcw);
if (texture->IsNew())
{
texture->Create();
texture->SetPhysicalDevice(GetContext()->GetPhysicalDevice());
texture->SetDevice(device);
}
else if (textureCache->IsInFlight(texture))
texture = textureCache->getRTTexture(textureAddr, pvrrc.fb_W_CTRL.fb_packmode, origWidth, origHeight);
if (textureCache->IsInFlight(texture))
{
texture->readOnlyImageView = *texture->imageView;
textureCache->DestroyLater(texture);
@ -503,8 +470,10 @@ vk::CommandBuffer TextureDrawer::BeginRenderPass()
commandBuffer.beginRenderPass(vk::RenderPassBeginInfo(rttPipelineManager->GetRenderPass(), *framebuffers[GetCurrentImage()],
vk::Rect2D( { 0, 0 }, { width, height }), 2, clear_colors), vk::SubpassContents::eInline);
commandBuffer.setViewport(0, vk::Viewport(0.0f, 0.0f, (float)upscaledWidth, (float)upscaledHeight, 1.0f, 0.0f));
baseScissor = vk::Rect2D(vk::Offset2D(pvrrc.fb_X_CLIP.min * upscale, pvrrc.fb_Y_CLIP.min * upscale),
vk::Extent2D(upscaledWidth, upscaledHeight));
u32 minX = pvrrc.fb_X_CLIP.min;
u32 minY = pvrrc.fb_Y_CLIP.min;
getRenderToTextureDimensions(minX, minY, widthPow2, heightPow2);
baseScissor = vk::Rect2D(vk::Offset2D(minX, minY), vk::Extent2D(upscaledWidth, upscaledHeight));
commandBuffer.setScissor(0, baseScissor);
currentCommandBuffer = commandBuffer;
@ -551,14 +520,14 @@ void TextureDrawer::EndRenderPass()
PixelBuffer<u32> tmpBuf;
tmpBuf.init(clippedWidth, clippedHeight);
colorAttachment->GetBufferData()->download(clippedWidth * clippedHeight * 4, tmpBuf.data());
WriteTextureToVRam(clippedWidth, clippedHeight, (u8 *)tmpBuf.data(), dst, -1, pvrrc.fb_W_LINESTRIDE * 8);
WriteTextureToVRam(clippedWidth, clippedHeight, (u8 *)tmpBuf.data(), dst, pvrrc.fb_W_CTRL, pvrrc.fb_W_LINESTRIDE * 8);
}
else
{
//memset(&vram[fb_rtt.TexAddr << 3], '\0', size);
texture->dirty = 0;
libCore_vramlock_Lock(texture->sa_tex, texture->sa + texture->size - 1, texture);
texture->protectVRam();
}
Drawer::EndRenderPass();
}

View File

@ -143,17 +143,20 @@ protected:
vk::DeviceSize packNaomi2Lights(BufferPacker& packer)
{
constexpr static N2LightModel noLight{};
vk::DeviceSize offset = packer.addUniform(&noLight, sizeof(noLight));
size_t n2LightSize = sizeof(N2LightModel) + align(sizeof(N2LightModel), GetContext()->GetUniformBufferAlignment());
if (n2LightSize == sizeof(N2LightModel))
return packer.addUniform(pvrrc.lightModels.head(), pvrrc.lightModels.bytes());
vk::DeviceSize offset = (vk::DeviceSize)-1;
for (const N2LightModel& model : pvrrc.lightModels)
{
vk::DeviceSize o = packer.addUniform(&model, sizeof(N2LightModel));
if (offset == (vk::DeviceSize)-1)
offset = o;
packer.addUniform(pvrrc.lightModels.head(), pvrrc.lightModels.bytes());
}
else
{
for (const N2LightModel& model : pvrrc.lightModels)
packer.addUniform(&model, sizeof(N2LightModel));
}
return offset;
}

View File

@ -273,6 +273,7 @@ bool OITDrawer::Draw(const Texture *fogTexture, const Texture *paletteTexture)
currentScissor = vk::Rect2D();
bool firstFrameAfterInit = oitBuffers->isFirstFrameAfterInit();
oitBuffers->OnNewFrame(cmdBuffer);
setFirstProvokingVertex(pvrrc);
@ -362,7 +363,7 @@ bool OITDrawer::Draw(const Texture *fogTexture, const Texture *paletteTexture)
// TR
if (current_pass.autosort)
{
if (!oitBuffers->isFirstFrameAfterInit())
if (!firstFrameAfterInit)
DrawList(cmdBuffer, ListType_Translucent, true, Pass::OIT, pvrrc.global_param_tr, previous_pass.tr_count, current_pass.tr_count);
}
else
@ -382,7 +383,7 @@ bool OITDrawer::Draw(const Texture *fogTexture, const Texture *paletteTexture)
}
SetScissor(cmdBuffer, baseScissor);
if (oitBuffers->isFirstFrameAfterInit())
if (firstFrameAfterInit)
{
// missing the transparent stuff on the first frame cuz I'm lazy
// Clear
@ -393,6 +394,7 @@ bool OITDrawer::Draw(const Texture *fogTexture, const Texture *paletteTexture)
vk::MemoryBarrier memoryBarrier(vk::AccessFlagBits::eShaderWrite, vk::AccessFlagBits::eShaderRead);
cmdBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eFragmentShader, vk::PipelineStageFlagBits::eFragmentShader,
vk::DependencyFlagBits::eByRegion, 1, &memoryBarrier, 0, nullptr, 0, nullptr);
firstFrameAfterInit = false;
}
// Tr modifier volumes
if (GetContext()->GetVendorID() != VulkanContext::VENDOR_QUALCOMM) // Adreno bug
@ -457,7 +459,7 @@ void OITDrawer::MakeBuffers(int width, int height)
depthAttachments[1]->GetImageView(),
};
vk::FramebufferCreateInfo createInfo(vk::FramebufferCreateFlags(), pipelineManager->GetRenderPass(true, true),
ARRAY_SIZE(attachments), attachments, width, height, 1);
ARRAY_SIZE(attachments), attachments, maxWidth, maxHeight, 1);
tempFramebuffers[0] = GetContext()->GetDevice().createFramebufferUnique(createInfo);
attachments[0] = attachments[1];
attachments[1] = colorAttachments[1]->GetImageView();
@ -497,29 +499,20 @@ void OITScreenDrawer::MakeFramebuffers(const vk::Extent2D& viewport)
vk::CommandBuffer OITTextureDrawer::NewFrame()
{
DEBUG_LOG(RENDERER, "RenderToTexture packmode=%d stride=%d - %d x %d @ %06x", FB_W_CTRL.fb_packmode, pvrrc.fb_W_LINESTRIDE * 8,
pvrrc.fb_X_CLIP.max + 1, pvrrc.fb_Y_CLIP.max + 1, FB_W_SOF1 & VRAM_MASK);
DEBUG_LOG(RENDERER, "RenderToTexture packmode=%d stride=%d - %d x %d @ %06x", pvrrc.fb_W_CTRL.fb_packmode, pvrrc.fb_W_LINESTRIDE * 8,
pvrrc.fb_X_CLIP.max + 1, pvrrc.fb_Y_CLIP.max + 1, pvrrc.fb_W_SOF1 & VRAM_MASK);
NewImage();
matrices.CalcMatrices(&pvrrc);
textureAddr = FB_W_SOF1 & VRAM_MASK;
textureAddr = pvrrc.fb_W_SOF1 & VRAM_MASK;
u32 origWidth = pvrrc.getFramebufferWidth();
u32 origHeight = pvrrc.getFramebufferHeight();
float upscale = 1.f;
if (!config::RenderToTextureBuffer)
upscale = config::RenderResolution / 480.f;
u32 heightPow2 = 8;
while (heightPow2 < origHeight)
heightPow2 *= 2;
u32 widthPow2 = 8;
while (widthPow2 < origWidth)
widthPow2 *= 2;
u32 upscaledWidth = origWidth * upscale;
u32 upscaledHeight = origHeight * upscale;
widthPow2 *= upscale;
heightPow2 *= upscale;
u32 upscaledWidth = origWidth;
u32 upscaledHeight = origHeight;
u32 widthPow2;
u32 heightPow2;
getRenderToTextureDimensions(upscaledWidth, upscaledHeight, widthPow2, heightPow2);
rttPipelineManager->CheckSettingsChange();
VulkanContext *context = GetContext();
@ -535,33 +528,8 @@ vk::CommandBuffer OITTextureDrawer::NewFrame()
if (!config::RenderToTextureBuffer)
{
// TexAddr : fb_rtt.TexAddr, Reserved : 0, StrideSel : 0, ScanOrder : 1
TCW tcw = { { textureAddr >> 3, 0, 0, 1 } };
switch (FB_W_CTRL.fb_packmode) {
case 0:
case 3:
tcw.PixelFmt = Pixel1555;
break;
case 1:
tcw.PixelFmt = Pixel565;
break;
case 2:
tcw.PixelFmt = Pixel4444;
break;
}
TSP tsp = {};
for (tsp.TexU = 0; tsp.TexU <= 7 && (8u << tsp.TexU) < origWidth; tsp.TexU++);
for (tsp.TexV = 0; tsp.TexV <= 7 && (8u << tsp.TexV) < origHeight; tsp.TexV++);
texture = textureCache->getTextureCacheData(tsp, tcw);
if (texture->IsNew())
{
texture->Create();
texture->SetPhysicalDevice(GetContext()->GetPhysicalDevice());
texture->SetDevice(device);
}
else if (textureCache->IsInFlight(texture))
texture = textureCache->getRTTexture(textureAddr, pvrrc.fb_W_CTRL.fb_packmode, origWidth, origHeight);
if (textureCache->IsInFlight(texture))
{
texture->readOnlyImageView = *texture->imageView;
textureCache->DestroyLater(texture);
@ -619,8 +587,10 @@ vk::CommandBuffer OITTextureDrawer::NewFrame()
rttPipelineManager->GetRenderPass(true, true), ARRAY_SIZE(imageViews), imageViews, widthPow2, heightPow2, 1));
commandBuffer.setViewport(0, vk::Viewport(0.0f, 0.0f, (float)upscaledWidth, (float)upscaledHeight, 1.0f, 0.0f));
baseScissor = vk::Rect2D(vk::Offset2D(pvrrc.fb_X_CLIP.min * upscale, pvrrc.fb_Y_CLIP.min * upscale),
vk::Extent2D(upscaledWidth, upscaledHeight));
u32 minX = pvrrc.fb_X_CLIP.min;
u32 minY = pvrrc.fb_Y_CLIP.min;
getRenderToTextureDimensions(minX, minY, widthPow2, heightPow2);
baseScissor = vk::Rect2D(vk::Offset2D(minX, minY), vk::Extent2D(upscaledWidth, upscaledHeight));
commandBuffer.setScissor(0, baseScissor);
currentCommandBuffer = commandBuffer;
@ -669,14 +639,14 @@ void OITTextureDrawer::EndFrame()
PixelBuffer<u32> tmpBuf;
tmpBuf.init(clippedWidth, clippedHeight);
colorAttachment->GetBufferData()->download(clippedWidth * clippedHeight * 4, tmpBuf.data());
WriteTextureToVRam(clippedWidth, clippedHeight, (u8 *)tmpBuf.data(), dst, -1, pvrrc.fb_W_LINESTRIDE * 8);
WriteTextureToVRam(clippedWidth, clippedHeight, (u8 *)tmpBuf.data(), dst, pvrrc.fb_W_CTRL, pvrrc.fb_W_LINESTRIDE * 8);
}
else
{
//memset(&vram[fb_rtt.TexAddr << 3], '\0', size);
texture->dirty = 0;
libCore_vramlock_Lock(texture->sa_tex, texture->sa + texture->size - 1, texture);
texture->protectVRam();
}
OITDrawer::EndFrame();
}

View File

@ -186,13 +186,13 @@ public:
uniBufferInfo = vk::DescriptorBufferInfo{ buffer, uniformOffset + polyNumber * size, sizeof(N2VertexShaderUniforms) };
writeDescriptorSets.emplace_back(perPolyDescSet, 2, 0, 1, vk::DescriptorType::eUniformBuffer, nullptr, &uniBufferInfo, nullptr);
size = sizeof(N2LightModel) + align(sizeof(N2LightModel), uniformAlignment);
// light at index 0 is no light
if (poly.lightModel != nullptr)
{
size = sizeof(N2LightModel) + align(sizeof(N2LightModel), uniformAlignment);
lightBufferInfo = vk::DescriptorBufferInfo{ buffer, lightOffset + (poly.lightModel - pvrrc.lightModels.head()) * size, sizeof(N2LightModel) };
writeDescriptorSets.emplace_back(perPolyDescSet, 3, 0, 1, vk::DescriptorType::eUniformBuffer, nullptr, &lightBufferInfo, nullptr);
}
// TODO no light
lightBufferInfo = vk::DescriptorBufferInfo{ buffer, lightOffset + (poly.lightModel - pvrrc.lightModels.head() + 1) * size, sizeof(N2LightModel) };
else
lightBufferInfo = vk::DescriptorBufferInfo{ buffer, lightOffset, sizeof(N2LightModel) };
writeDescriptorSets.emplace_back(perPolyDescSet, 3, 0, 1, vk::DescriptorType::eUniformBuffer, nullptr, &lightBufferInfo, nullptr);
}
getContext()->GetDevice().updateDescriptorSets(writeDescriptorSets, nullptr);

View File

@ -54,11 +54,8 @@ void VulkanOverlay::Term()
std::unique_ptr<Texture> VulkanOverlay::createTexture(vk::CommandBuffer commandBuffer, int width, int height, u8 *data)
{
VulkanContext *context = VulkanContext::Instance();
auto texture = std::unique_ptr<Texture>(new Texture());
texture->tex_type = TextureType::_8888;
texture->SetDevice(context->GetDevice());
texture->SetPhysicalDevice(context->GetPhysicalDevice());
texture->SetCommandBuffer(commandBuffer);
texture->UploadToGPU(width, height, data, false);
texture->SetCommandBuffer(nullptr);
@ -198,6 +195,7 @@ void VulkanOverlay::Draw(vk::CommandBuffer commandBuffer, vk::Extent2D viewport,
if (crosshair && crosshairsNeeded())
{
pipeline->BindPipeline(commandBuffer);
bool imageViewBound = false;
for (size_t i = 0; i < config::CrosshairColor.size(); i++)
{
if (config::CrosshairColor[i] == 0)
@ -228,7 +226,8 @@ void VulkanOverlay::Draw(vk::CommandBuffer commandBuffer, vk::Extent2D viewport,
((color >> 16) & 0xff) / 255.f,
((color >> 24) & 0xff) / 255.f
};
xhairDrawer->Draw(commandBuffer, i == 0 ? xhairTexture->GetImageView() : vk::ImageView(), vtx, true, xhairColor);
xhairDrawer->Draw(commandBuffer, !imageViewBound ? xhairTexture->GetImageView() : vk::ImageView(), vtx, true, xhairColor);
imageViewBound = true;
}
}
}

View File

@ -103,13 +103,13 @@ public:
uniBufferInfo = vk::DescriptorBufferInfo{ buffer, uniformOffset + polyNumber * size, sizeof(N2VertexShaderUniforms) };
writeDescriptorSets.emplace_back(perPolyDescSet, 2, 0, 1, vk::DescriptorType::eUniformBuffer, nullptr, &uniBufferInfo, nullptr);
size = sizeof(N2LightModel) + align(sizeof(N2LightModel), uniformAlignment);
// light at index 0 is no light
if (poly.lightModel != nullptr)
{
size = sizeof(N2LightModel) + align(sizeof(N2LightModel), uniformAlignment);
lightBufferInfo = vk::DescriptorBufferInfo{ buffer, lightOffset + (poly.lightModel - pvrrc.lightModels.head()) * size, sizeof(N2LightModel) };
writeDescriptorSets.emplace_back(perPolyDescSet, 3, 0, 1, vk::DescriptorType::eUniformBuffer, nullptr, &lightBufferInfo, nullptr);
}
// TODO no light
lightBufferInfo = vk::DescriptorBufferInfo{ buffer, lightOffset + (poly.lightModel - pvrrc.lightModels.head() + 1) * size, sizeof(N2LightModel) };
else
lightBufferInfo = vk::DescriptorBufferInfo{ buffer, lightOffset, sizeof(N2LightModel) };
writeDescriptorSets.emplace_back(perPolyDescSet, 3, 0, 1, vk::DescriptorType::eUniformBuffer, nullptr, &lightBufferInfo, nullptr);
}
getContext()->GetDevice().updateDescriptorSets(writeDescriptorSets, nullptr);

View File

@ -36,18 +36,33 @@ void setImageLayout(vk::CommandBuffer const& commandBuffer, vk::Image image, vk:
class Texture final : public BaseTextureCacheData
{
public:
Texture(TSP tsp = {}, TCW tcw = {}) : BaseTextureCacheData(tsp, tcw) {
this->physicalDevice = VulkanContext::Instance()->GetPhysicalDevice();
this->device = VulkanContext::Instance()->GetDevice();
}
Texture(Texture&& other) : BaseTextureCacheData(std::move(other)) {
std::swap(format, other.format);
std::swap(extent, other.extent);
std::swap(mipmapLevels, other.mipmapLevels);
std::swap(needsStaging, other.needsStaging);
std::swap(stagingBufferData, other.stagingBufferData);
std::swap(commandBuffer, other.commandBuffer);
std::swap(allocation, other.allocation);
std::swap(image, other.image);
std::swap(imageView, other.imageView);
std::swap(readOnlyImageView, other.readOnlyImageView);
std::swap(physicalDevice, other.physicalDevice);
std::swap(device, other.device);
}
void UploadToGPU(int width, int height, u8 *data, bool mipmapped, bool mipmapsIncluded = false) override;
u64 GetIntId() { return (u64)reinterpret_cast<uintptr_t>(this); }
std::string GetId() override { char s[20]; sprintf(s, "%p", this); return s; }
bool IsNew() const { return !image.get(); }
vk::ImageView GetImageView() const { return *imageView; }
vk::ImageView GetReadOnlyImageView() const { return readOnlyImageView ? readOnlyImageView : *imageView; }
void SetCommandBuffer(vk::CommandBuffer commandBuffer) { this->commandBuffer = commandBuffer; }
bool Force32BitTexture(TextureType type) const override { return !VulkanContext::Instance()->IsFormatSupported(type); }
void SetPhysicalDevice(vk::PhysicalDevice physicalDevice) { this->physicalDevice = physicalDevice; }
void SetDevice(vk::Device device) { this->device = device; }
private:
void Init(u32 width, u32 height, vk::Format format ,u32 dataSize, bool mipmapped, bool mipmapsIncluded);
void SetImage(u32 size, void *data, bool isNew, bool genMipmaps);

View File

@ -436,10 +436,10 @@ bool VulkanContext::InitDevice()
{ vk::DescriptorType::eUniformTexelBuffer, 2 },
{ vk::DescriptorType::eStorageTexelBuffer, 2 },
{ vk::DescriptorType::eUniformBuffer, 80000 },
{ vk::DescriptorType::eStorageBuffer, 50 },
{ vk::DescriptorType::eStorageBuffer, 100 },
{ vk::DescriptorType::eUniformBufferDynamic, 2 },
{ vk::DescriptorType::eStorageBufferDynamic, 2 },
{ vk::DescriptorType::eInputAttachment, 50 }
{ vk::DescriptorType::eInputAttachment, 100 }
};
descriptorPool = device->createDescriptorPoolUnique(vk::DescriptorPoolCreateInfo(vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet,
40000, ARRAY_SIZE(pool_sizes), pool_sizes));

View File

@ -45,10 +45,6 @@ protected:
texCommandPool.BeginFrame();
vjoyTexture = std::unique_ptr<Texture>(new Texture());
vjoyTexture->tex_type = TextureType::_8888;
vjoyTexture->tcw.full = 0;
vjoyTexture->tsp.full = 0;
vjoyTexture->SetPhysicalDevice(GetContext()->GetPhysicalDevice());
vjoyTexture->SetDevice(GetContext()->GetDevice());
vk::CommandBuffer cmdBuffer = texCommandPool.Allocate();
cmdBuffer.begin(vk::CommandBufferBeginInfo(vk::CommandBufferUsageFlagBits::eOneTimeSubmit));
vjoyTexture->SetCommandBuffer(cmdBuffer);
@ -97,13 +93,6 @@ public:
{
Texture* tf = textureCache.getTextureCacheData(tsp, tcw);
if (tf->IsNew())
{
tf->Create();
tf->SetPhysicalDevice(GetContext()->GetPhysicalDevice());
tf->SetDevice(GetContext()->GetDevice());
}
//update if needed
if (tf->NeedsUpdate())
{
@ -247,10 +236,6 @@ protected:
{
curTexture = std::unique_ptr<Texture>(new Texture());
curTexture->tex_type = TextureType::_8888;
curTexture->tcw.full = 0;
curTexture->tsp.full = 0;
curTexture->SetPhysicalDevice(GetContext()->GetPhysicalDevice());
curTexture->SetDevice(GetContext()->GetDevice());
}
curTexture->SetCommandBuffer(texCommandBuffer);
curTexture->UploadToGPU(width, height, (u8*)pb.data(), false);
@ -325,8 +310,6 @@ protected:
if (!fogTexture)
{
fogTexture = std::unique_ptr<Texture>(new Texture());
fogTexture->SetPhysicalDevice(GetContext()->GetPhysicalDevice());
fogTexture->SetDevice(GetContext()->GetDevice());
fogTexture->tex_type = TextureType::_8;
fog_needs_update = true;
}
@ -347,8 +330,6 @@ protected:
if (!paletteTexture)
{
paletteTexture = std::unique_ptr<Texture>(new Texture());
paletteTexture->SetPhysicalDevice(GetContext()->GetPhysicalDevice());
paletteTexture->SetDevice(GetContext()->GetDevice());
paletteTexture->tex_type = TextureType::_8888;
forcePaletteUpdate();
}

View File

@ -44,6 +44,16 @@ static bool gameRunning;
static bool mouseCaptured;
static std::string clipboardText;
static struct SDLDeInit
{
~SDLDeInit() {
if (initialized)
SDL_Quit();
}
bool initialized = false;
} sqlDeinit;
static void sdl_open_joystick(int index)
{
SDL_Joystick *pJoystick = SDL_JoystickOpen(index);
@ -176,8 +186,8 @@ void input_sdl_init()
if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0)
die("SDL: error initializing Joystick subsystem");
}
sqlDeinit.initialized = true;
SDL_SetRelativeMouseMode(SDL_FALSE);
@ -211,6 +221,12 @@ void input_sdl_init()
}
}
void input_sdl_quit()
{
SDLGamepad::closeAllGamepads();
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
}
inline void SDLMouse::setAbsPos(int x, int y) {
int width, height;
SDL_GetWindowSize(window, &width, &height);
@ -603,6 +619,7 @@ void sdl_window_create()
SDL_Vulkan_LoadLibrary("libvulkan.dylib");
#endif
}
sqlDeinit.initialized = true;
initRenderApi();
// ImGui copy & paste
ImGui::GetIO().GetClipboardTextFn = getClipboardText;
@ -622,4 +639,5 @@ void sdl_window_destroy()
#endif
termRenderApi();
SDL_DestroyWindow(window);
SDL_QuitSubSystem(SDL_INIT_VIDEO);
}

View File

@ -4,6 +4,7 @@
void input_sdl_init();
void input_sdl_handle();
void input_sdl_quit();
void sdl_window_create();
void sdl_window_set_text(const char* text);
void sdl_window_destroy();

View File

@ -365,6 +365,11 @@ public:
else
return NULL;
}
static void closeAllGamepads()
{
while (!sdl_gamepads.empty())
sdl_gamepads.begin()->second->close();
}
static void UpdateRumble()
{
for (auto& pair : sdl_gamepads)

View File

@ -2,6 +2,25 @@
#include "input/keyboard_device.h"
#include "sdl.h"
#ifdef __APPLE__
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOTypes.h>
#include <stack>
// Rumbling Taptic Engine by Private MultitouchSupport.framework
extern "C" {
typedef void *MTDeviceRef;
bool MTDeviceIsAvailable(void);
MTDeviceRef MTDeviceCreateDefault(void);
OSStatus MTDeviceGetDeviceID(MTDeviceRef, uint64_t*) __attribute__ ((weak_import));
CFTypeRef MTActuatorCreateFromDeviceID(UInt64 deviceID);
IOReturn MTActuatorOpen(CFTypeRef actuatorRef);
IOReturn MTActuatorClose(CFTypeRef actuatorRef);
IOReturn MTActuatorActuate(CFTypeRef actuatorRef, SInt32 actuationID, UInt32 unknown1, Float32 unknown2, Float32 unknown3);
bool MTActuatorIsOpen(CFTypeRef actuatorRef);
enum ActuatePattern { minimal = 3, weak = 5, medium = 4, strong = 6 };
}
#endif
class SDLKeyboardDevice : public KeyboardDeviceTemplate<SDL_Scancode>
{
public:
@ -35,6 +54,12 @@ public:
}
else
input_mapper = getDefaultMapping();
#ifdef __APPLE__
uint64_t deviceID;
if ( MTDeviceIsAvailable() && MTDeviceGetDeviceID(MTDeviceCreateDefault(), &deviceID) == 0 && (vib_device = MTActuatorCreateFromDeviceID(deviceID)) != NULL && MTActuatorOpen(vib_device) == kIOReturnSuccess)
rumbleEnabled = true;
#endif
}
const char *get_button_name(u32 code) override
@ -45,6 +70,59 @@ public:
return name;
}
#ifdef __APPLE__
void rumble(float power, float inclination, u32 duration_ms) override
{
if (rumbleEnabled)
{
vib_stop_time = os_GetSeconds() + duration_ms / 1000.0;
__block int pattern;
if (power >= 0.75)
pattern = ActuatePattern::strong;
else if (power >= 0.5)
pattern = ActuatePattern::medium;
else if (power >= 0.25)
pattern = ActuatePattern::weak;
else if (power > 0)
pattern = ActuatePattern::minimal;
else
{
while(!vib_timer_stack.empty())
{
dispatch_source_cancel(vib_timer_stack.top());
vib_timer_stack.pop();
}
return;
}
// Since the Actuator API does not support duration
// using a interval timer with `10ms * rumblePower percentage` to fake it
__block dispatch_source_t _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
vib_timer_stack.push(_timer);
dispatch_source_set_timer(_timer, DISPATCH_TIME_NOW, 10 * NSEC_PER_MSEC * rumblePower / 100.f, 0);
dispatch_source_set_event_handler(_timer, ^{
if ( vib_stop_time - os_GetSeconds() < 0 )
{
dispatch_source_cancel(_timer);
return;
}
MTActuatorActuate(vib_device, pattern, 0, 0.0, 0.0);
});
dispatch_resume(_timer);
}
}
~SDLKeyboardDevice() {
if (rumbleEnabled)
{
MTActuatorClose(vib_device);
CFRelease(vib_device);
}
}
#endif
protected:
u8 convert_keycode(SDL_Scancode scancode) override
{
@ -53,4 +131,11 @@ protected:
else
return (u8)scancode;
}
#ifdef __APPLE__
private:
std::stack<dispatch_source_t> vib_timer_stack;
CFTypeRef vib_device = NULL;
double vib_stop_time = 0;
#endif
};

View File

@ -20,6 +20,9 @@
#ifndef TARGET_UWP
#include <hidusage.h>
#include <map>
#include <cfgmgr32.h>
#include <initguid.h>
#include <devpkey.h>
#include "hw/maple/maple_devs.h"
#ifndef CALLBACK
@ -324,6 +327,17 @@ static void destroyWindow()
UnregisterClassA("flycastRawInput", nullptr);
}
#ifndef _MSC_VER
// missing from mingw's cfgmgr32.h
extern "C" CMAPI CONFIGRET CM_Get_Device_Interface_PropertyW(
LPCWSTR pszDeviceInterface,
const DEVPROPKEY *PropertyKey,
DEVPROPTYPE *PropertyType,
PBYTE PropertyBuffer,
PULONG PropertyBufferSize,
ULONG ulFlags);
#endif
static void findDevices()
{
u32 numDevices;
@ -338,10 +352,10 @@ static void findDevices()
RAWINPUTDEVICELIST& device = deviceList[i];
if (device.dwType == RIM_TYPEMOUSE || device.dwType == RIM_TYPEKEYBOARD)
{
// Get the device name
// Get the device interface instance id
std::string name;
std::string uniqueId;
u32 size;
u32 size = 0;
GetRawInputDeviceInfo(device.hDevice, RIDI_DEVICENAME, nullptr, &size);
if (size > 0)
{
@ -349,13 +363,50 @@ static void findDevices()
u32 res = GetRawInputDeviceInfo(device.hDevice, RIDI_DEVICENAME, &deviceNameData[0], &size);
if (res != (u32)-1)
{
std::string deviceName(&deviceNameData[0], std::strlen(&deviceNameData[0]));
if (deviceName.substr(0, 8) == "\\\\?\\HID#")
deviceName = deviceName.substr(8);
uniqueId = (device.dwType == RIM_TYPEMOUSE ? "raw_mouse_" : "raw_keyboard_") + deviceName;
if (deviceName.length() > 17 && deviceName.substr(0, 4) == "VID_" && deviceName.substr(8, 5) == "&PID_")
deviceName = deviceName.substr(0, 17);
name = (device.dwType == RIM_TYPEMOUSE ? "Mouse " : "Keyboard ") + deviceName;
std::string deviceId(&deviceNameData[0], std::strlen(&deviceNameData[0]));
// Now get the device instance id from the interface instance
nowide::wstackstring wname;
if (wname.convert(deviceId.c_str()))
{
DEVPROPTYPE propType;
ULONG bufSize = 0;
if (CM_Get_Device_Interface_PropertyW(wname.c_str(), &DEVPKEY_Device_InstanceId, &propType, nullptr, &bufSize, 0) == CR_BUFFER_SMALL)
{
std::vector<wchar_t> buf;
buf.resize(bufSize / sizeof(wchar_t));
if (CM_Get_Device_Interface_PropertyW(wname.c_str(), &DEVPKEY_Device_InstanceId, &propType, (PBYTE)buf.data(), &bufSize, 0) == CR_SUCCESS)
{
// Locate the device using the device instance id
DEVINST devInst;
if (CM_Locate_DevNodeW(&devInst, &buf[0], CM_LOCATE_DEVNODE_NORMAL) == CR_SUCCESS)
{
// Finally, get the "friendly" device name
bufSize = 0;
if (CM_Get_DevNode_PropertyW(devInst, &DEVPKEY_NAME, &propType, nullptr, &bufSize, 0) == CR_BUFFER_SMALL)
{
buf.resize(bufSize / sizeof(wchar_t));
if (CM_Get_DevNode_PropertyW(devInst, &DEVPKEY_NAME, &propType, (PBYTE)buf.data(), &bufSize, 0) == CR_SUCCESS)
{
nowide::stackstring nwname;
if (nwname.convert(&buf[0]))
name = nwname.c_str();
}
}
}
}
}
}
if (deviceId.substr(0, 8) == "\\\\?\\HID#")
deviceId = deviceId.substr(8);
uniqueId = (device.dwType == RIM_TYPEMOUSE ? "raw_mouse_" : "raw_keyboard_") + deviceId;
if (name.empty())
{
name = deviceId;
if (name.length() > 17 && name.substr(0, 4) == "VID_" && name.substr(8, 5) == "&PID_")
name = name.substr(0, 17);
name = (device.dwType == RIM_TYPEMOUSE ? "Mouse " : "Keyboard ") + name;
}
}
}
uintptr_t handle = (uintptr_t)device.hDevice;

View File

@ -217,6 +217,16 @@ void os_SetupInput()
#endif
}
void os_TermInput()
{
#if defined(USE_SDL)
input_sdl_quit();
#endif
#ifndef TARGET_UWP
if (config::UseRawInput)
rawinput::term();
#endif
}
static void setupPath()
{

View File

@ -19,7 +19,14 @@
#include "libretro.h"
#ifdef LIBRETRO
#if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES)
LibretroGraphicsContext theGLContext;
#endif
#if defined(HAVE_D3D11) && !(defined(HAVE_OPENGL) || defined(HAVE_OPENGLES))
#include "context.h"
#endif
GraphicsContext *GraphicsContext::instance;
#endif

View File

@ -17,7 +17,7 @@
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifdef LIBRETRO
#if defined(LIBRETRO) && (defined(HAVE_OPENGL) || defined(HAVE_OPENGLES))
#include "gl_context.h"
#include <libretro.h>
#include <glsm/glsm.h>

View File

@ -15,6 +15,8 @@
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-feature

View File

@ -3,6 +3,7 @@ package com.reicast.emulator;
import android.app.Application;
import android.content.Context;
import android.content.SharedPreferences;
import android.net.wifi.WifiManager;
import android.preference.PreferenceManager;
import android.util.Log;
@ -14,6 +15,8 @@ import com.reicast.emulator.emu.JNIdc;
public class Emulator extends Application {
private static Context context;
private static BaseGLActivity currentActivity;
private WifiManager wifiManager = null;
private WifiManager.MulticastLock multicastLock = null;
// see MapleDeviceType in hw/maple/maple_devs.h
public static final int MDT_Microphone = 2;
@ -92,4 +95,20 @@ public class Emulator extends Application {
static {
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}
public void enableNetworkBroadcast(boolean enable) {
if (enable) {
if (wifiManager == null)
wifiManager = (WifiManager)Emulator.context.getSystemService(Context.WIFI_SERVICE);
if (multicastLock == null)
multicastLock = wifiManager.createMulticastLock("Flycast");
if (multicastLock != null && !multicastLock.isHeld())
multicastLock.acquire();
}
else
{
if (multicastLock != null && multicastLock.isHeld())
multicastLock.release();
}
}
}

View File

@ -130,6 +130,9 @@ void common_linux_setup();
void os_SetupInput()
{
}
void os_TermInput()
{
}
void os_SetWindowText(char const *Text)
{
@ -626,3 +629,10 @@ extern "C" JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_setButtons
memcpy(DefaultOSDButtons.data(), b, len);
env->ReleaseByteArrayElements(data, b, JNI_ABORT);
}
void enableNetworkBroadcast(bool enable)
{
JNIEnv *env = jvm_attacher.getEnv();
jmethodID enableNetworkBroadcastMID = env->GetMethodID(env->GetObjectClass(g_emulator), "enableNetworkBroadcast", "(Z)V");
env->CallVoidMethod(g_emulator, enableNetworkBroadcastMID, enable);
}

View File

@ -9,6 +9,7 @@
import Foundation
import Network
@available(iOS 12, tvOS 12, watchOS 5, macOS 10.14, *)
public class NetworkConnection: NSObject, Connection
{
public let nwConnection: NWConnection
@ -52,11 +53,8 @@ public class NetworkConnection: NSObject, Connection
default: self.nwConnection.cancel()
}
}
}
extension NetworkConnection
{
override public var description: String {
override public var description: String {
return "\(self.nwConnection.endpoint) (Network)"
}
}

View File

@ -5,7 +5,6 @@
// Created by Riley Testut on 5/30/19.
// Copyright © 2019 Riley Testut. All rights reserved.
//
import Foundation
import Network
@ -15,327 +14,340 @@ import UIKit
public enum ConnectionError: LocalizedError
{
case serverNotFound
case connectionFailed(Server)
case connectionDropped(Server)
case unknownUDID
public var errorDescription: String? {
switch self
{
case .serverNotFound: return NSLocalizedString("Could not find AltServer.", comment: "")
case .connectionFailed: return NSLocalizedString("Could not connect to AltServer.", comment: "")
case .connectionDropped: return NSLocalizedString("The connection to AltServer was dropped.", comment: "")
case .unknownUDID: return NSLocalizedString("This device's UDID could not be determined.", comment: "")
}
}
case serverNotFound
case connectionFailed(Server)
case connectionDropped(Server)
case unknownUDID
case unsupportedOS
public var errorDescription: String? {
switch self
{
case .serverNotFound: return NSLocalizedString("Could not find AltServer.", comment: "")
case .connectionFailed: return NSLocalizedString("Could not connect to AltServer.", comment: "")
case .connectionDropped: return NSLocalizedString("The connection to AltServer was dropped.", comment: "")
case .unknownUDID: return NSLocalizedString("This device's UDID could not be determined.", comment: "")
case .unsupportedOS: return NSLocalizedString("This device's OS version is too old to run AltKit.", comment: "")
}
}
}
@objc(ALTServerManager) @objcMembers
public class ServerManager: NSObject
{
public static let shared = ServerManager()
private(set) var isDiscovering = false
private(set) var discoveredServers = [Server]()
public var discoveredServerHandler: ((Server) -> Void)?
public var lostServerHandler: ((Server) -> Void)?
public var callbackQueue: DispatchQueue = .main
// Allow other AltKit queues to target this one.
internal let dispatchQueue = DispatchQueue(label: "io.altstore.altkit.ServerManager", qos: .utility, autoreleaseFrequency: .workItem)
private let serviceBrowser = NetServiceBrowser()
private var resolvingServices = Set<NetService>()
private var autoconnectGroup: DispatchGroup?
private var ignoredServers = Set<Server>()
private override init()
{
super.init()
self.serviceBrowser.delegate = self
self.serviceBrowser.includesPeerToPeer = false
NotificationCenter.default.addObserver(self, selector: #selector(ServerManager.didEnterBackground(_:)), name: UIApplication.didEnterBackgroundNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(ServerManager.willEnterForeground(_:)), name: UIApplication.willEnterForegroundNotification, object: nil)
}
public static let shared = ServerManager()
public private(set) dynamic var isDiscovering = false
public private(set) dynamic var discoveredServers = [Server]()
public var discoveredServerHandler: ((Server) -> Void)?
public var lostServerHandler: ((Server) -> Void)?
public var callbackQueue: DispatchQueue = .main
// Allow other AltKit queues to target this one.
internal let dispatchQueue = DispatchQueue(label: "io.altstore.altkit.ServerManager", qos: .utility, autoreleaseFrequency: .workItem)
private var serviceBrowser: NetServiceBrowser?
private var resolvingServices = Set<NetService>()
private var autoconnectGroup: DispatchGroup?
private var ignoredServers = Set<Server>()
private override init()
{
super.init()
NotificationCenter.default.addObserver(self, selector: #selector(ServerManager.didEnterBackground(_:)), name: UIApplication.didEnterBackgroundNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(ServerManager.willEnterForeground(_:)), name: UIApplication.willEnterForegroundNotification, object: nil)
}
}
public extension ServerManager
{
@objc
func startDiscovering()
{
guard !self.isDiscovering else { return }
self.isDiscovering = true
self.serviceBrowser.searchForServices(ofType: ALTServerServiceType, inDomain: "")
}
@objc
func stopDiscovering()
{
guard self.isDiscovering else { return }
self.isDiscovering = false
self.discoveredServers.removeAll()
self.ignoredServers.removeAll()
self.resolvingServices.removeAll()
self.serviceBrowser.stop()
}
func connect(to server: Server, completion: @escaping (Result<ServerConnection, Error>) -> Void)
{
var didFinish = false
func finish(_ result: Result<ServerConnection, Error>)
{
guard !didFinish else { return }
didFinish = true
self.ignoredServers.insert(server)
self.callbackQueue.async {
completion(result)
}
}
self.dispatchQueue.async {
print("Connecting to service:", server.service)
let connection = NWConnection(to: .service(name: server.service.name, type: server.service.type, domain: server.service.domain, interface: nil), using: .tcp)
connection.stateUpdateHandler = { [unowned connection] (state) in
switch state
{
case .failed(let error):
print("Failed to connect to service \(server.service.name).", error)
finish(.failure(ConnectionError.connectionFailed(server)))
case .cancelled: finish(.failure(CocoaError(.userCancelled)))
case .ready:
let networkConnection = NetworkConnection(connection)
let serverConnection = ServerConnection(server: server, connection: networkConnection)
finish(.success(serverConnection))
case .waiting: break
case .setup: break
case .preparing: break
@unknown default: break
}
}
connection.start(queue: self.dispatchQueue)
}
}
func autoconnect(completion: @escaping (Result<ServerConnection, Error>) -> Void)
{
self.dispatchQueue.async {
if case let availableServers = self.discoveredServers.filter({ !self.ignoredServers.contains($0) }),
let server = availableServers.first(where: { $0.isPreferred }) ?? availableServers.first
{
return self.connect(to: server, completion: completion)
}
self.autoconnectGroup = DispatchGroup()
self.autoconnectGroup?.enter()
self.autoconnectGroup?.notify(queue: self.dispatchQueue) {
self.autoconnectGroup = nil
guard
case let availableServers = self.discoveredServers.filter({ !self.ignoredServers.contains($0) }),
let server = availableServers.first(where: { $0.isPreferred }) ?? availableServers.first
else { return self.autoconnect(completion: completion) }
self.connect(to: server, completion: completion)
}
}
}
@objc
func startDiscovering()
{
guard !self.isDiscovering else { return }
self.isDiscovering = true
DispatchQueue.main.async {
// NetServiceBrowser must be initialized on main thread.
// https://stackoverflow.com/questions/3526661/nsnetservicebrowser-delegate-not-called-when-searching
let serviceBrowser = NetServiceBrowser()
serviceBrowser.delegate = self
serviceBrowser.includesPeerToPeer = false
serviceBrowser.searchForServices(ofType: ALTServerServiceType, inDomain: "")
self.serviceBrowser = serviceBrowser
}
}
@objc
func stopDiscovering()
{
guard self.isDiscovering else { return }
self.isDiscovering = false
self.discoveredServers.removeAll()
self.ignoredServers.removeAll()
self.resolvingServices.removeAll()
self.serviceBrowser?.stop()
self.serviceBrowser = nil
}
func connect(to server: Server, completion: @escaping (Result<ServerConnection, Error>) -> Void)
{
var didFinish = false
func finish(_ result: Result<ServerConnection, Error>)
{
guard !didFinish else { return }
didFinish = true
self.ignoredServers.insert(server)
self.callbackQueue.async {
completion(result)
}
}
self.dispatchQueue.async {
guard #available(iOS 12, tvOS 12, watchOS 5, macOS 10.14, *) else {
finish(.failure(ConnectionError.unsupportedOS))
return
}
print("Connecting to service:", server.service)
let connection = NWConnection(to: .service(name: server.service.name, type: server.service.type, domain: server.service.domain, interface: nil), using: .tcp)
connection.stateUpdateHandler = { [unowned connection] (state) in
switch state
{
case .failed(let error):
print("Failed to connect to service \(server.service.name).", error)
finish(.failure(ConnectionError.connectionFailed(server)))
case .cancelled: finish(.failure(CocoaError(.userCancelled)))
case .ready:
let networkConnection = NetworkConnection(connection)
let serverConnection = ServerConnection(server: server, connection: networkConnection)
finish(.success(serverConnection))
case .waiting: break
case .setup: break
case .preparing: break
@unknown default: break
}
}
connection.start(queue: self.dispatchQueue)
}
}
func autoconnect(completion: @escaping (Result<ServerConnection, Error>) -> Void)
{
self.dispatchQueue.async {
if case let availableServers = self.discoveredServers.filter({ !self.ignoredServers.contains($0) }),
let server = availableServers.first(where: { $0.isPreferred }) ?? availableServers.first
{
return self.connect(to: server, completion: completion)
}
self.autoconnectGroup = DispatchGroup()
self.autoconnectGroup?.enter()
self.autoconnectGroup?.notify(queue: self.dispatchQueue) {
self.autoconnectGroup = nil
guard
case let availableServers = self.discoveredServers.filter({ !self.ignoredServers.contains($0) }),
let server = availableServers.first(where: { $0.isPreferred }) ?? availableServers.first
else { return self.autoconnect(completion: completion) }
self.connect(to: server, completion: completion)
}
}
}
}
public extension ServerManager
{
@objc(sharedManager)
class var __shared: ServerManager {
return ServerManager.shared
}
@objc(connectToServer:completionHandler:)
func __connect(to server: Server, completion: @escaping (ServerConnection?, Error?) -> Void)
{
self.connect(to: server) { result in
completion(result.value, result.error)
}
}
@objc(autoconnectWithCompletionHandler:)
func __autoconnect(completion: @escaping (ServerConnection?, Error?) -> Void)
{
self.autoconnect { result in
completion(result.value, result.error)
}
}
@objc(sharedManager)
class var __shared: ServerManager {
return ServerManager.shared
}
@objc(connectToServer:completionHandler:)
func __connect(to server: Server, completion: @escaping (ServerConnection?, Error?) -> Void)
{
self.connect(to: server) { result in
completion(result.value, result.error)
}
}
@objc(autoconnectWithCompletionHandler:)
func __autoconnect(completion: @escaping (ServerConnection?, Error?) -> Void)
{
self.autoconnect { result in
completion(result.value, result.error)
}
}
}
private extension ServerManager
{
func addDiscoveredServer(_ server: Server)
{
self.dispatchQueue.async {
let serverID = Bundle.main.object(forInfoDictionaryKey: "ALTServerID") as? String
server.isPreferred = (server.id == serverID)
guard !self.discoveredServers.contains(server) else { return }
self.discoveredServers.append(server)
if let callback = self.discoveredServerHandler
{
self.callbackQueue.async {
callback(server)
}
}
}
}
func removeDiscoveredServer(_ server: Server)
{
self.dispatchQueue.async {
guard let index = self.discoveredServers.firstIndex(of: server) else { return }
self.discoveredServers.remove(at: index)
if let callback = self.lostServerHandler
{
self.callbackQueue.async {
callback(server)
}
}
}
}
func addDiscoveredServer(_ server: Server)
{
self.dispatchQueue.async {
let serverID = Bundle.main.object(forInfoDictionaryKey: "ALTServerID") as? String
server.isPreferred = (server.id == serverID)
guard !self.discoveredServers.contains(server) else { return }
self.discoveredServers.append(server)
if let callback = self.discoveredServerHandler
{
self.callbackQueue.async {
callback(server)
}
}
}
}
func removeDiscoveredServer(_ server: Server)
{
self.dispatchQueue.async {
guard let index = self.discoveredServers.firstIndex(of: server) else { return }
self.discoveredServers.remove(at: index)
if let callback = self.lostServerHandler
{
self.callbackQueue.async {
callback(server)
}
}
}
}
}
@objc
private extension ServerManager
{
@objc
func didEnterBackground(_ notification: Notification)
{
guard self.isDiscovering else { return }
self.resolvingServices.removeAll()
self.discoveredServers.removeAll()
self.serviceBrowser.stop()
}
@objc
func willEnterForeground(_ notification: Notification)
{
guard self.isDiscovering else { return }
self.serviceBrowser.searchForServices(ofType: ALTServerServiceType, inDomain: "")
}
@objc
func didEnterBackground(_ notification: Notification)
{
guard self.isDiscovering else { return }
self.resolvingServices.removeAll()
self.discoveredServers.removeAll()
self.serviceBrowser?.stop()
}
@objc
func willEnterForeground(_ notification: Notification)
{
guard self.isDiscovering else { return }
self.serviceBrowser?.searchForServices(ofType: ALTServerServiceType, inDomain: "")
}
}
extension ServerManager: NetServiceBrowserDelegate
{
public func netServiceBrowserWillSearch(_ browser: NetServiceBrowser)
{
print("Discovering servers...")
}
public func netServiceBrowserDidStopSearch(_ browser: NetServiceBrowser)
{
print("Stopped discovering servers.")
}
public func netServiceBrowser(_ browser: NetServiceBrowser, didNotSearch errorDict: [String : NSNumber])
{
print("Failed to discover servers.", errorDict)
}
public func netServiceBrowser(_ browser: NetServiceBrowser, didFind service: NetService, moreComing: Bool)
{
self.dispatchQueue.async {
service.delegate = self
if let txtData = service.txtRecordData(), let server = Server(service: service, txtData: txtData)
{
self.addDiscoveredServer(server)
}
else
{
service.resolve(withTimeout: 3.0)
self.resolvingServices.insert(service)
}
self.autoconnectGroup?.enter()
if !moreComing
{
self.autoconnectGroup?.leave()
}
}
}
public func netServiceBrowser(_ browser: NetServiceBrowser, didRemove service: NetService, moreComing: Bool)
{
if let server = self.discoveredServers.first(where: { $0.service == service })
{
self.removeDiscoveredServer(server)
}
}
public func netServiceBrowserWillSearch(_ browser: NetServiceBrowser)
{
print("Discovering servers...")
}
public func netServiceBrowserDidStopSearch(_ browser: NetServiceBrowser)
{
print("Stopped discovering servers.")
}
public func netServiceBrowser(_ browser: NetServiceBrowser, didNotSearch errorDict: [String : NSNumber])
{
print("Failed to discover servers.", errorDict)
}
public func netServiceBrowser(_ browser: NetServiceBrowser, didFind service: NetService, moreComing: Bool)
{
self.dispatchQueue.async {
service.delegate = self
if let txtData = service.txtRecordData(), let server = Server(service: service, txtData: txtData)
{
self.addDiscoveredServer(server)
}
else
{
service.resolve(withTimeout: 3.0)
self.resolvingServices.insert(service)
}
self.autoconnectGroup?.enter()
if !moreComing
{
self.autoconnectGroup?.leave()
}
}
}
public func netServiceBrowser(_ browser: NetServiceBrowser, didRemove service: NetService, moreComing: Bool)
{
if let server = self.discoveredServers.first(where: { $0.service == service })
{
self.removeDiscoveredServer(server)
}
}
}
extension ServerManager: NetServiceDelegate
{
public func netServiceDidResolveAddress(_ service: NetService)
{
defer {
self.dispatchQueue.async {
guard self.resolvingServices.contains(service) else { return }
self.resolvingServices.remove(service)
self.autoconnectGroup?.leave()
}
}
guard let data = service.txtRecordData(), let server = Server(service: service, txtData: data) else { return }
self.addDiscoveredServer(server)
}
public func netService(_ sender: NetService, didNotResolve errorDict: [String : NSNumber])
{
print("Error resolving net service \(sender).", errorDict)
self.dispatchQueue.async {
guard self.resolvingServices.contains(sender) else { return }
self.resolvingServices.remove(sender)
self.autoconnectGroup?.leave()
}
}
public func netService(_ sender: NetService, didUpdateTXTRecord data: Data)
{
let txtDict = NetService.dictionary(fromTXTRecord: data)
print("Service \(sender) updated TXT Record:", txtDict)
}
public func netServiceDidStop(_ sender: NetService)
{
self.dispatchQueue.async {
guard self.resolvingServices.contains(sender) else { return }
self.resolvingServices.remove(sender)
self.autoconnectGroup?.leave()
}
}
public func netServiceDidResolveAddress(_ service: NetService)
{
defer {
self.dispatchQueue.async {
guard self.resolvingServices.contains(service) else { return }
self.resolvingServices.remove(service)
self.autoconnectGroup?.leave()
}
}
guard let data = service.txtRecordData(), let server = Server(service: service, txtData: data) else { return }
self.addDiscoveredServer(server)
}
public func netService(_ sender: NetService, didNotResolve errorDict: [String : NSNumber])
{
print("Error resolving net service \(sender).", errorDict)
self.dispatchQueue.async {
guard self.resolvingServices.contains(sender) else { return }
self.resolvingServices.remove(sender)
self.autoconnectGroup?.leave()
}
}
public func netService(_ sender: NetService, didUpdateTXTRecord data: Data)
{
let txtDict = NetService.dictionary(fromTXTRecord: data)
print("Service \(sender) updated TXT Record:", txtDict)
}
public func netServiceDidStop(_ sender: NetService)
{
self.dispatchQueue.async {
guard self.resolvingServices.contains(sender) else { return }
self.resolvingServices.remove(sender)
self.autoconnectGroup?.leave()
}
}
}

View File

@ -27,6 +27,7 @@
UITouch *joyTouch;
CGPoint joyBias;
std::shared_ptr<IOSVirtualGamepad> virtualGamepad;
NSMutableDictionary *touchToButton;
}
@end
@ -38,10 +39,23 @@
[super viewDidLoad];
virtualGamepad = std::make_shared<IOSVirtualGamepad>();
GamepadDevice::Register(virtualGamepad);
touchToButton = [[NSMutableDictionary alloc] init];
}
- (void)showController:(UIView *)parentView
{
if (!cfgLoadBool("help", "PauseGameTip", false))
{
UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"Help Tip"
message:@"To pause the game, press Up+Down or Left+Right on the virtual DPad."
preferredStyle:UIAlertControllerStyleAlert];
[self presentViewController:alert animated:YES completion:nil];
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
cfgSaveBool("help", "PauseGameTip", true);
}];
[alert addAction:defaultAction];
}
[parentView addSubview:self.view];
}
@ -55,16 +69,6 @@
return self.view.window != nil;
}
- (IBAction)keycodeDown:(id)sender
{
virtualGamepad->gamepad_btn_input((u32)((UIButton *)sender).tag, true);
}
- (IBAction)keycodeUp:(id)sender
{
virtualGamepad->gamepad_btn_input((u32)((UIButton *)sender).tag, false);
}
- (void)resetTouch
{
joyTouch = nil;
@ -76,29 +80,42 @@
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event;
{
if (joyTouch == nil) {
for (UITouch *touch in touches) {
CGPoint loc = [touch locationInView:[self joystickBackground]];
for (UITouch *touch in touches) {
if (joyTouch == nil) {
CGPoint loc = [touch locationInView:self.joystickBackground];
if ([self.joystickBackground pointInside:loc withEvent:event]) {
joyTouch = touch;
joyBias = loc;
virtualGamepad->gamepad_axis_input(IOS_AXIS_LX, 0);
virtualGamepad->gamepad_axis_input(IOS_AXIS_LY, 0);
break;
continue;
}
}
CGPoint point = [touch locationInView:self.view];
UIView *touchedView = [self.view hitTest:point withEvent:nil];
NSValue *key = [NSValue valueWithPointer:(const void *)touch];
if (touchedView.tag != 0 && touchToButton[key] == nil) {
touchToButton[key] = touchedView;
// button down
virtualGamepad->gamepad_btn_input((u32)touchedView.tag, true);
}
}
[super touchesBegan:touches withEvent:event];
}
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event;
{
if (joyTouch != nil) {
for (UITouch *touch in touches) {
if (touch == joyTouch) {
[self resetTouch];
break;
}
for (UITouch *touch in touches) {
if (touch == joyTouch) {
[self resetTouch];
continue;
}
NSValue *key = [NSValue valueWithPointer:(const void *)touch];
UIView *button = touchToButton[key];
if (button != nil) {
[touchToButton removeObjectForKey:key];
// button up
virtualGamepad->gamepad_btn_input((u32)button.tag, false);
}
}
[super touchesEnded:touches withEvent:event];
@ -106,20 +123,35 @@
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event;
{
if (joyTouch != nil) {
for (UITouch *touch in touches) {
if (touch == joyTouch) {
CGPoint pos = [touch locationInView:[self joystickBackground]];
pos.x -= joyBias.x;
pos.y -= joyBias.y;
pos.x = std::max<CGFloat>(std::min<CGFloat>(25.0, pos.x), -25.0);
pos.y = std::max<CGFloat>(std::min<CGFloat>(25.0, pos.y), -25.0);
self.joyXConstraint.constant = pos.x;
self.joyYConstraint.constant = pos.y;
virtualGamepad->gamepad_axis_input(IOS_AXIS_LX, (s8)std::round(pos.x * 32767.0 / 25.0));
virtualGamepad->gamepad_axis_input(IOS_AXIS_LY, (s8)std::round(pos.y * 32767.0 / 25.0));
break;
}
for (UITouch *touch in touches) {
if (touch == joyTouch) {
CGPoint pos = [touch locationInView:[self joystickBackground]];
pos.x -= joyBias.x;
pos.y -= joyBias.y;
pos.x = std::max<CGFloat>(std::min<CGFloat>(25.0, pos.x), -25.0);
pos.y = std::max<CGFloat>(std::min<CGFloat>(25.0, pos.y), -25.0);
self.joyXConstraint.constant = pos.x;
self.joyYConstraint.constant = pos.y;
virtualGamepad->gamepad_axis_input(IOS_AXIS_LX, (s8)std::round(pos.x * 32767.0 / 25.0));
virtualGamepad->gamepad_axis_input(IOS_AXIS_LY, (s8)std::round(pos.y * 32767.0 / 25.0));
continue;
}
CGPoint point = [touch locationInView:self.view];
UIView *touchedView = [self.view hitTest:point withEvent:nil];
NSValue *key = [NSValue valueWithPointer:(const void *)touch];
UIView *button = touchToButton[key];
if (button != nil && touchedView.tag != button.tag) {
// button up
virtualGamepad->gamepad_btn_input((u32)button.tag, false);
touchToButton[key] = touchedView;
// button down
virtualGamepad->gamepad_btn_input((u32)touchedView.tag, true);
}
else if (button == nil && touchedView.tag != 0)
{
touchToButton[key] = touchedView;
// button down
virtualGamepad->gamepad_btn_input((u32)touchedView.tag, true);
}
}
[super touchesMoved:touches withEvent:event];
@ -127,12 +159,17 @@
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event;
{
if (joyTouch != nil) {
for (UITouch *touch in touches) {
if (touch == joyTouch) {
[self resetTouch];
break;
}
for (UITouch *touch in touches) {
if (touch == joyTouch) {
[self resetTouch];
continue;
}
NSValue *key = [NSValue valueWithPointer:(const void *)touch];
UIView *button = touchToButton[key];
if (button != nil) {
[touchToButton removeObjectForKey:key];
// button up
virtualGamepad->gamepad_btn_input((u32)button.tag, false);
}
}
[super touchesCancelled:touches withEvent:event];

View File

@ -21,16 +21,9 @@
<view multipleTouchEnabled="YES" alpha="0.5" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="3M7-1s-N5r">
<rect key="frame" x="0.0" y="0.0" width="812" height="375"/>
<subviews>
<button opaque="NO" multipleTouchEnabled="YES" tag="14" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="8Gl-Iv-u8L" userLabel="LT-Button">
<view opaque="NO" multipleTouchEnabled="YES" tag="14" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="8Gl-Iv-u8L" userLabel="LT-Button">
<rect key="frame" x="598" y="146" width="80" height="40"/>
<state key="normal">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<action selector="keycodeDown:" destination="-1" eventType="touchDown" id="34L-sO-g81"/>
<action selector="keycodeUp:" destination="-1" eventType="touchUpInside" id="iDv-U3-6OX"/>
</connections>
</button>
</view>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="RTrigger" translatesAutoresizingMaskIntoConstraints="NO" id="Cjn-zx-eSs">
<rect key="frame" x="688" y="146" width="80" height="40"/>
<constraints>
@ -38,16 +31,9 @@
<constraint firstAttribute="height" constant="40" id="uA3-z1-wS7"/>
</constraints>
</imageView>
<button opaque="NO" multipleTouchEnabled="YES" tag="15" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="V8J-vG-dlF" userLabel="RT-Button">
<view opaque="NO" multipleTouchEnabled="YES" tag="15" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="V8J-vG-dlF" userLabel="RT-Button">
<rect key="frame" x="688" y="146" width="80" height="40"/>
<state key="normal">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<action selector="keycodeDown:" destination="-1" eventType="touchDown" id="vPf-qF-m13"/>
<action selector="keycodeUp:" destination="-1" eventType="touchUpInside" id="hQh-8f-5jG"/>
</connections>
</button>
</view>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="JoystickButton" translatesAutoresizingMaskIntoConstraints="NO" id="ivh-8r-bw3" userLabel="JoystickThumbpad">
<rect key="frame" x="64" y="255" width="100" height="100"/>
<constraints>
@ -65,62 +51,43 @@
<constraint firstAttribute="height" constant="140" id="fly-c3-Ajo"/>
</constraints>
</imageView>
<button opaque="NO" multipleTouchEnabled="YES" tag="7" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="rp6-Nd-1qa" userLabel="L-Button">
<view opaque="NO" multipleTouchEnabled="YES" tag="10" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="bc4-Lc-Ghm" userLabel="UR-Button">
<rect key="frame" x="134" y="85" width="50" height="50"/>
</view>
<view opaque="NO" multipleTouchEnabled="YES" tag="11" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="7rQ-cq-4t8" userLabel="UL-Button">
<rect key="frame" x="44" y="85" width="50" height="50"/>
</view>
<view opaque="NO" multipleTouchEnabled="YES" tag="12" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="f6y-OJ-0oT" userLabel="DL-Button">
<rect key="frame" x="44" y="175" width="50" height="50"/>
</view>
<view opaque="NO" multipleTouchEnabled="YES" tag="7" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="rp6-Nd-1qa" userLabel="L-Button">
<rect key="frame" x="44" y="135" width="46" height="40"/>
<constraints>
<constraint firstAttribute="width" constant="46" id="MX4-af-OJg"/>
<constraint firstAttribute="height" constant="40" id="yGw-c7-7x8"/>
</constraints>
<state key="normal">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<action selector="keycodeDown:" destination="-1" eventType="touchDown" id="3Yw-AP-xVf"/>
<action selector="keycodeUp:" destination="-1" eventType="touchUpInside" id="5gI-j0-ANf"/>
</connections>
</button>
<button opaque="NO" multipleTouchEnabled="YES" tag="8" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="CVH-hw-R8F" userLabel="R-Button">
</view>
<view opaque="NO" multipleTouchEnabled="YES" tag="8" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="CVH-hw-R8F" userLabel="R-Button">
<rect key="frame" x="138" y="135" width="46" height="40"/>
<constraints>
<constraint firstAttribute="height" constant="40" id="9OW-42-b64"/>
<constraint firstAttribute="width" constant="46" id="grQ-i7-YHe"/>
</constraints>
<state key="normal">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<action selector="keycodeDown:" destination="-1" eventType="touchDown" id="2Dv-zb-f8V"/>
<action selector="keycodeUp:" destination="-1" eventType="touchUpInside" id="woi-3Y-IfD"/>
</connections>
</button>
<button opaque="NO" multipleTouchEnabled="YES" tag="5" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="WMD-Fv-ibu" userLabel="U-Button">
</view>
<view opaque="NO" multipleTouchEnabled="YES" tag="5" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="WMD-Fv-ibu" userLabel="U-Button">
<rect key="frame" x="94" y="85" width="40" height="46"/>
<constraints>
<constraint firstAttribute="height" constant="46" id="2gU-xW-ddx"/>
<constraint firstAttribute="width" constant="40" id="oUv-ex-E9I"/>
</constraints>
<state key="normal">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<action selector="keycodeDown:" destination="-1" eventType="touchDown" id="kT6-yy-ZtY"/>
<action selector="keycodeUp:" destination="-1" eventType="touchUpInside" id="R0R-dl-GAG"/>
</connections>
</button>
<button opaque="NO" multipleTouchEnabled="YES" tag="6" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="s7g-nq-lRU" userLabel="D-Button">
</view>
<view opaque="NO" multipleTouchEnabled="YES" tag="6" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="s7g-nq-lRU" userLabel="D-Button">
<rect key="frame" x="94" y="179" width="40" height="46"/>
<constraints>
<constraint firstAttribute="width" constant="40" id="3IG-k8-ER3"/>
<constraint firstAttribute="height" constant="46" id="4vu-3O-H8J"/>
</constraints>
<state key="normal">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<action selector="keycodeDown:" destination="-1" eventType="touchDown" id="Wck-mk-4Py"/>
<action selector="keycodeUp:" destination="-1" eventType="touchUpInside" id="Qox-hz-p3A"/>
</connections>
</button>
</view>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="ABXYPad" translatesAutoresizingMaskIntoConstraints="NO" id="xbP-E4-fCE">
<rect key="frame" x="608" y="205" width="160" height="160"/>
<constraints>
@ -128,62 +95,34 @@
<constraint firstAttribute="height" constant="160" id="lVn-RY-tWm"/>
</constraints>
</imageView>
<button opaque="NO" multipleTouchEnabled="YES" tag="3" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="iwO-7q-c8H" userLabel="X-Button">
<view opaque="NO" multipleTouchEnabled="YES" tag="3" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="iwO-7q-c8H" userLabel="X-Button">
<rect key="frame" x="608" y="255" width="60" height="60"/>
<constraints>
<constraint firstAttribute="height" constant="60" id="4Yf-ri-Ccb"/>
<constraint firstAttribute="width" constant="60" id="mnO-1S-Phd"/>
</constraints>
<state key="normal">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<action selector="keycodeDown:" destination="-1" eventType="touchDown" id="IBH-TK-vfV"/>
<action selector="keycodeUp:" destination="-1" eventType="touchUpInside" id="dhr-NT-lcF"/>
</connections>
</button>
<button opaque="NO" multipleTouchEnabled="YES" tag="2" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="7LB-OY-vh3" userLabel="B-Button">
</view>
<view opaque="NO" multipleTouchEnabled="YES" tag="2" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="7LB-OY-vh3" userLabel="B-Button">
<rect key="frame" x="708" y="255" width="60" height="60"/>
<constraints>
<constraint firstAttribute="width" constant="60" id="1YC-G4-kwg"/>
<constraint firstAttribute="height" constant="60" id="LHC-wi-Yap"/>
</constraints>
<state key="normal">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<action selector="keycodeDown:" destination="-1" eventType="touchDown" id="dhg-58-L8C"/>
<action selector="keycodeUp:" destination="-1" eventType="touchUpInside" id="zqg-KK-Wxb"/>
</connections>
</button>
<button opaque="NO" multipleTouchEnabled="YES" tag="4" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="hGZ-v7-VA5" userLabel="Y-Button">
</view>
<view opaque="NO" multipleTouchEnabled="YES" tag="4" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="hGZ-v7-VA5" userLabel="Y-Button">
<rect key="frame" x="658" y="205" width="60" height="60"/>
<constraints>
<constraint firstAttribute="width" constant="60" id="gv4-it-7ly"/>
<constraint firstAttribute="height" constant="60" id="hx2-N9-Lba"/>
</constraints>
<state key="normal">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<action selector="keycodeDown:" destination="-1" eventType="touchDown" id="tyb-H4-TqJ"/>
<action selector="keycodeUp:" destination="-1" eventType="touchUpInside" id="oai-Xb-scl"/>
</connections>
</button>
<button opaque="NO" multipleTouchEnabled="YES" tag="1" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="iKO-3z-Ias" userLabel="A-Button">
</view>
<view opaque="NO" multipleTouchEnabled="YES" tag="1" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="iKO-3z-Ias" userLabel="A-Button">
<rect key="frame" x="658" y="305" width="60" height="60"/>
<constraints>
<constraint firstAttribute="height" constant="60" id="Dgi-6d-cHy"/>
<constraint firstAttribute="width" constant="60" id="L1S-2O-QBB"/>
</constraints>
<state key="normal">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<action selector="keycodeDown:" destination="-1" eventType="touchDown" id="Ysa-m4-KnN"/>
<action selector="keycodeUp:" destination="-1" eventType="touchUpInside" id="MTf-ND-WNy"/>
</connections>
</button>
</view>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="Start" translatesAutoresizingMaskIntoConstraints="NO" id="9K0-cV-7zu">
<rect key="frame" x="366" y="310" width="80" height="40"/>
<constraints>
@ -191,20 +130,13 @@
<constraint firstAttribute="width" constant="80" id="BpH-iH-but"/>
</constraints>
</imageView>
<button opaque="NO" multipleTouchEnabled="YES" tag="9" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="VtI-tC-PSX" userLabel="S-Button">
<view opaque="NO" multipleTouchEnabled="YES" tag="9" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="VtI-tC-PSX" userLabel="S-Button">
<rect key="frame" x="379" y="310" width="54" height="40"/>
<constraints>
<constraint firstAttribute="width" constant="54" id="HjR-fP-Q1s"/>
<constraint firstAttribute="height" constant="40" id="MK6-rm-vpj"/>
</constraints>
<state key="normal">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<action selector="keycodeDown:" destination="-1" eventType="touchDown" id="kwd-jB-5Wn"/>
<action selector="keycodeUp:" destination="-1" eventType="touchUpInside" id="gHx-tA-QlF"/>
</connections>
</button>
</view>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="LTrigger" translatesAutoresizingMaskIntoConstraints="NO" id="H57-MD-elm">
<rect key="frame" x="598" y="146" width="80" height="40"/>
<constraints>
@ -212,25 +144,37 @@
<constraint firstAttribute="width" constant="80" id="Pj7-YU-3Ze"/>
</constraints>
</imageView>
<view opaque="NO" multipleTouchEnabled="YES" tag="13" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ccY-OZ-Jlp" userLabel="DR-Button">
<rect key="frame" x="134" y="175" width="50" height="50"/>
</view>
</subviews>
<viewLayoutGuide key="safeArea" id="DJj-LJ-xnv"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="WMD-Fv-ibu" firstAttribute="leading" secondItem="7rQ-cq-4t8" secondAttribute="trailing" id="1Qw-rO-H58"/>
<constraint firstItem="f6y-OJ-0oT" firstAttribute="top" secondItem="rp6-Nd-1qa" secondAttribute="bottom" id="2bH-uq-Rdf"/>
<constraint firstItem="f6y-OJ-0oT" firstAttribute="leading" secondItem="FLe-Gr-hny" secondAttribute="leading" id="2hm-bn-5R6"/>
<constraint firstItem="s7g-nq-lRU" firstAttribute="centerX" secondItem="FLe-Gr-hny" secondAttribute="centerX" id="4bj-Mc-SN7"/>
<constraint firstItem="7rQ-cq-4t8" firstAttribute="leading" secondItem="FLe-Gr-hny" secondAttribute="leading" id="4xu-d6-7ej"/>
<constraint firstItem="bc4-Lc-Ghm" firstAttribute="trailing" secondItem="FLe-Gr-hny" secondAttribute="trailing" id="6Hu-3B-LKi"/>
<constraint firstItem="V8J-vG-dlF" firstAttribute="height" secondItem="Cjn-zx-eSs" secondAttribute="height" id="77t-Hb-AxC"/>
<constraint firstItem="DJj-LJ-xnv" firstAttribute="trailing" secondItem="xbP-E4-fCE" secondAttribute="trailing" id="84h-PU-NtS"/>
<constraint firstItem="OMP-L6-n0A" firstAttribute="top" secondItem="FLe-Gr-hny" secondAttribute="bottom" constant="20" id="9o9-R0-BDC"/>
<constraint firstItem="ivh-8r-bw3" firstAttribute="centerY" secondItem="OMP-L6-n0A" secondAttribute="centerY" id="AGO-kZ-c5L"/>
<constraint firstItem="8Gl-Iv-u8L" firstAttribute="height" secondItem="H57-MD-elm" secondAttribute="height" id="BMW-0T-VdJ"/>
<constraint firstItem="ccY-OZ-Jlp" firstAttribute="bottom" secondItem="FLe-Gr-hny" secondAttribute="bottom" id="CKI-9B-7L9"/>
<constraint firstItem="8Gl-Iv-u8L" firstAttribute="width" secondItem="H57-MD-elm" secondAttribute="width" id="DV9-P9-Sxd"/>
<constraint firstItem="Cjn-zx-eSs" firstAttribute="leading" secondItem="H57-MD-elm" secondAttribute="trailing" constant="10" id="ENl-Fx-mge"/>
<constraint firstItem="iwO-7q-c8H" firstAttribute="centerY" secondItem="xbP-E4-fCE" secondAttribute="centerY" id="Eo6-Na-ui9"/>
<constraint firstItem="xbP-E4-fCE" firstAttribute="top" secondItem="Cjn-zx-eSs" secondAttribute="bottom" constant="19" id="FSR-y8-8rc"/>
<constraint firstItem="Cjn-zx-eSs" firstAttribute="centerY" secondItem="V8J-vG-dlF" secondAttribute="centerY" id="FfW-WI-ybP"/>
<constraint firstItem="CVH-hw-R8F" firstAttribute="trailing" secondItem="FLe-Gr-hny" secondAttribute="trailing" id="Ftp-zB-WsM"/>
<constraint firstItem="ccY-OZ-Jlp" firstAttribute="trailing" secondItem="FLe-Gr-hny" secondAttribute="trailing" id="GAa-0J-bbN"/>
<constraint firstItem="7rQ-cq-4t8" firstAttribute="top" secondItem="FLe-Gr-hny" secondAttribute="top" id="GJc-OP-hcg"/>
<constraint firstItem="FLe-Gr-hny" firstAttribute="leading" secondItem="DJj-LJ-xnv" secondAttribute="leading" id="Gw8-6c-aZQ"/>
<constraint firstItem="rp6-Nd-1qa" firstAttribute="centerY" secondItem="FLe-Gr-hny" secondAttribute="centerY" id="HGb-0C-429"/>
<constraint firstItem="hGZ-v7-VA5" firstAttribute="centerX" secondItem="xbP-E4-fCE" secondAttribute="centerX" id="IgA-69-RT3"/>
<constraint firstItem="CVH-hw-R8F" firstAttribute="top" secondItem="bc4-Lc-Ghm" secondAttribute="bottom" id="Isi-K8-3p0"/>
<constraint firstItem="7LB-OY-vh3" firstAttribute="trailing" secondItem="xbP-E4-fCE" secondAttribute="trailing" id="JtE-KF-Pdd"/>
<constraint firstItem="7LB-OY-vh3" firstAttribute="centerY" secondItem="xbP-E4-fCE" secondAttribute="centerY" id="LG8-OE-Xe1"/>
<constraint firstItem="H57-MD-elm" firstAttribute="centerY" secondItem="8Gl-Iv-u8L" secondAttribute="centerY" id="Mhe-dn-JZ9"/>
@ -238,11 +182,17 @@
<constraint firstItem="iKO-3z-Ias" firstAttribute="centerX" secondItem="xbP-E4-fCE" secondAttribute="centerX" id="UCC-w9-GvH"/>
<constraint firstItem="Cjn-zx-eSs" firstAttribute="centerY" secondItem="H57-MD-elm" secondAttribute="centerY" id="UEI-il-naq"/>
<constraint firstItem="9K0-cV-7zu" firstAttribute="centerX" secondItem="3M7-1s-N5r" secondAttribute="centerX" id="VeR-d1-FAy"/>
<constraint firstItem="f6y-OJ-0oT" firstAttribute="bottom" secondItem="FLe-Gr-hny" secondAttribute="bottom" id="X7u-ha-keM"/>
<constraint firstItem="ccY-OZ-Jlp" firstAttribute="top" secondItem="CVH-hw-R8F" secondAttribute="bottom" id="YVx-Ts-phy"/>
<constraint firstItem="s7g-nq-lRU" firstAttribute="bottom" secondItem="FLe-Gr-hny" secondAttribute="bottom" id="Ygo-58-kU2"/>
<constraint firstItem="WMD-Fv-ibu" firstAttribute="top" secondItem="FLe-Gr-hny" secondAttribute="top" id="YzW-LZ-S7F"/>
<constraint firstItem="s7g-nq-lRU" firstAttribute="leading" secondItem="f6y-OJ-0oT" secondAttribute="trailing" id="axe-4o-r6w"/>
<constraint firstItem="bc4-Lc-Ghm" firstAttribute="top" secondItem="FLe-Gr-hny" secondAttribute="top" id="eXk-0E-XgT"/>
<constraint firstAttribute="bottom" secondItem="OMP-L6-n0A" secondAttribute="bottom" constant="10" id="gDw-g8-7jn"/>
<constraint firstItem="bc4-Lc-Ghm" firstAttribute="leading" secondItem="WMD-Fv-ibu" secondAttribute="trailing" id="gHk-lh-K78"/>
<constraint firstItem="iKO-3z-Ias" firstAttribute="bottom" secondItem="xbP-E4-fCE" secondAttribute="bottom" id="gba-91-EY6"/>
<constraint firstItem="iwO-7q-c8H" firstAttribute="leading" secondItem="xbP-E4-fCE" secondAttribute="leading" id="iBC-eZ-hcV"/>
<constraint firstItem="rp6-Nd-1qa" firstAttribute="top" secondItem="7rQ-cq-4t8" secondAttribute="bottom" id="kbU-bz-reP"/>
<constraint firstItem="VtI-tC-PSX" firstAttribute="centerX" secondItem="9K0-cV-7zu" secondAttribute="centerX" id="lJs-z7-uYy"/>
<constraint firstItem="Cjn-zx-eSs" firstAttribute="centerX" secondItem="V8J-vG-dlF" secondAttribute="centerX" id="lmY-iZ-lW5"/>
<constraint firstAttribute="bottom" secondItem="xbP-E4-fCE" secondAttribute="bottom" constant="10" id="ner-TB-GTz"/>
@ -250,6 +200,7 @@
<constraint firstItem="DJj-LJ-xnv" firstAttribute="trailing" secondItem="Cjn-zx-eSs" secondAttribute="trailing" id="qTc-91-kbO"/>
<constraint firstItem="H57-MD-elm" firstAttribute="centerX" secondItem="8Gl-Iv-u8L" secondAttribute="centerX" id="sNB-9U-qXY"/>
<constraint firstItem="V8J-vG-dlF" firstAttribute="width" secondItem="Cjn-zx-eSs" secondAttribute="width" id="sxe-fC-pvB"/>
<constraint firstItem="ccY-OZ-Jlp" firstAttribute="leading" secondItem="s7g-nq-lRU" secondAttribute="trailing" id="t0o-uL-23I"/>
<constraint firstItem="DJj-LJ-xnv" firstAttribute="bottom" secondItem="9K0-cV-7zu" secondAttribute="bottom" constant="4" id="wWR-HB-aLq"/>
<constraint firstItem="WMD-Fv-ibu" firstAttribute="centerX" secondItem="FLe-Gr-hny" secondAttribute="centerX" id="xqT-1Z-AQx"/>
<constraint firstItem="rp6-Nd-1qa" firstAttribute="leading" secondItem="FLe-Gr-hny" secondAttribute="leading" id="zVb-6D-06l"/>
@ -257,7 +208,7 @@
<constraint firstItem="CVH-hw-R8F" firstAttribute="centerY" secondItem="FLe-Gr-hny" secondAttribute="centerY" id="zjZ-0S-Tq9"/>
<constraint firstItem="OMP-L6-n0A" firstAttribute="centerX" secondItem="FLe-Gr-hny" secondAttribute="centerX" id="zow-El-03l"/>
</constraints>
<point key="canvasLocation" x="608.69565217391312" y="48.214285714285715"/>
<point key="canvasLocation" x="608.12807881773404" y="47.200000000000003"/>
</view>
</objects>
<resources>

View File

@ -50,7 +50,13 @@ enum IOSButton {
IOS_BTN_PADDLE4,
IOS_BTN_TOUCHPAD,
IOS_BTN_MAX
IOS_BTN_MAX,
IOS_BTN_UP_RIGHT = 10,
IOS_BTN_UP_LEFT,
IOS_BTN_DOWN_LEFT,
IOS_BTN_DOWN_RIGHT,
};
enum IOSAxis {
IOS_AXIS_L1 = 1,
@ -555,6 +561,27 @@ public:
if (code == IOS_BTN_Y)
code = IOS_BTN_X; // btn3
}
switch (code)
{
case IOS_BTN_UP_RIGHT:
GamepadDevice::gamepad_btn_input(IOS_BTN_UP, pressed);
code = IOS_BTN_RIGHT;
break;
case IOS_BTN_DOWN_RIGHT:
GamepadDevice::gamepad_btn_input(IOS_BTN_DOWN, pressed);
code = IOS_BTN_RIGHT;
break;
case IOS_BTN_DOWN_LEFT:
GamepadDevice::gamepad_btn_input(IOS_BTN_DOWN, pressed);
code = IOS_BTN_LEFT;
break;
case IOS_BTN_UP_LEFT:
GamepadDevice::gamepad_btn_input(IOS_BTN_UP, pressed);
code = IOS_BTN_LEFT;
break;
default:
break;
}
return GamepadDevice::gamepad_btn_input(code, pressed);
}

View File

@ -55,6 +55,8 @@ void UpdateInputState() {
void os_SetupInput() {
}
void os_TermInput() {
}
std::string os_Locale(){
return [[[NSLocale preferredLanguages] objectAtIndex:0] UTF8String];

View File

@ -72,6 +72,13 @@ void os_SetupInput()
#endif
}
void os_TermInput()
{
#if defined(USE_SDL)
input_sdl_quit();
#endif
}
void common_linux_setup();
static int emu_flycast_init();

74
shell/apple/libomp.rb Normal file
View File

@ -0,0 +1,74 @@
class Libomp < Formula
desc "LLVM's OpenMP runtime library"
homepage "https://openmp.llvm.org/"
url "https://github.com/llvm/llvm-project/releases/download/llvmorg-14.0.0/openmp-14.0.0.src.tar.xz"
sha256 "28a1cbdd3dfdd331e4ed2dda2b4477fc418e455c883bd0d1d6acc331118e4688"
license "MIT"
livecheck do
url "https://llvm.org/"
regex(/LLVM (\d+\.\d+\.\d+)/i)
end
bottle do
sha256 cellar: :any, arm64_monterey: "cf1058b26e1a778e523d51562c99b4145aea1b1cb89f1c60b3315677a86c7a08"
sha256 cellar: :any, arm64_big_sur: "bbf77a1a151f00a18e340ab1f655fb87fe787a85834518f1dc44bf0c52ae7d4c"
sha256 cellar: :any, monterey: "e66d2009d6d205c19499dcb453dfac4376ab6bdba805987be00ddbbab65a0818"
sha256 cellar: :any, big_sur: "ed9dc636a5fc8c2a0cfb1643f7932d742ae4805c3f193a9e56cab7d7cf7342e7"
sha256 cellar: :any, catalina: "c72ce9beecde09052e7eac3550b0286ed9bfb2d14f1dd5954705ab5fb25f231b"
sha256 cellar: :any_skip_relocation, x86_64_linux: "9fe14d5f4c8b472de1fad74278da6ba38da7322775b8a88ac61de0c373c4ad10"
end
depends_on "cmake" => :build
depends_on :xcode => :build # Sometimes CLT cannot build arm64
uses_from_macos "llvm" => :build
on_linux do
keg_only "provided by LLVM, which is not keg-only on Linux"
end
def install
# Disable LIBOMP_INSTALL_ALIASES, otherwise the library is installed as
# libgomp alias which can conflict with GCC's libgomp.
args = ["-DLIBOMP_INSTALL_ALIASES=OFF"]
args << "-DOPENMP_ENABLE_LIBOMPTARGET=OFF" if OS.linux?
# Build universal binary
ENV.permit_arch_flags
ENV.runtime_cpu_detection
args << "-DCMAKE_OSX_ARCHITECTURES=arm64;x86_64"
# system "cmake", "-S", "openmp-#{version}.src", "-B", "build/shared", *std_cmake_args, *args
# system "cmake", "--build", "build/shared"
# system "cmake", "--install", "build/shared"
system "cmake", "-S", "openmp-#{version}.src", "-B", "build/static",
"-DLIBOMP_ENABLE_SHARED=OFF",
*std_cmake_args, *args
system "cmake", "--build", "build/static"
system "cmake", "--install", "build/static"
end
test do
(testpath/"test.cpp").write <<~EOS
#include <omp.h>
#include <array>
int main (int argc, char** argv) {
std::array<size_t,2> arr = {0,0};
#pragma omp parallel num_threads(2)
{
size_t tid = omp_get_thread_num();
arr.at(tid) = tid + 1;
}
if(arr.at(0) == 1 && arr.at(1) == 2)
return 0;
else
return 1;
}
EOS
system ENV.cxx, "-Werror", "-Xpreprocessor", "-fopenmp", "test.cpp", "-std=c++11",
"-L#{lib}", "-lomp", "-o", "test"
system "./test"
end
end

View File

@ -24,8 +24,33 @@
#include <vector>
#include <mutex>
/* Detect output refresh rate changes by monitoring
* the last 'VSYNC_SWAP_INTERVAL_FRAMES' frames:
* - Measure average (mean) audio samples per upload
* operation
* - Determine vsync swap interval based on
* expected samples at 60 (or 50) Hz
* - Check that vsync swap interval remains
* 'stable' for at least 'VSYNC_SWAP_INTERVAL_FRAMES' */
#define VSYNC_SWAP_INTERVAL_FRAMES 6
/* Calculated swap interval is 'valid' if it is
* within 'VSYNC_SWAP_INTERVAL_THRESHOLD' of an integer
* value */
#define VSYNC_SWAP_INTERVAL_THRESHOLD 0.05f
extern void setAVInfo(retro_system_av_info& avinfo);
extern retro_environment_t environ_cb;
extern retro_audio_sample_batch_t audio_batch_cb;
extern float libretro_expected_audio_samples_per_run;
extern unsigned libretro_vsync_swap_interval;
extern bool libretro_detect_vsync_swap_interval;
static float audio_samples_per_frame_avg;
static unsigned vsync_swap_interval_last;
static unsigned vsync_swap_interval_conter;
static std::mutex audio_buffer_mutex;
static std::vector<int16_t> audio_buffer;
static size_t audio_buffer_idx;
@ -57,6 +82,10 @@ void retro_audio_init(void)
audio_out_buffer = (int16_t*)malloc(audio_buffer_size * sizeof(int16_t));
drop_samples = false;
audio_samples_per_frame_avg = 0.0f;
vsync_swap_interval_last = 1;
vsync_swap_interval_conter = 0;
}
void retro_audio_deinit(void)
@ -72,6 +101,10 @@ void retro_audio_deinit(void)
audio_out_buffer = nullptr;
drop_samples = true;
audio_samples_per_frame_avg = 0.0f;
vsync_swap_interval_last = 1;
vsync_swap_interval_conter = 0;
}
void retro_audio_flush_buffer(void)
@ -100,6 +133,65 @@ void retro_audio_upload(void)
audio_buffer_mutex.unlock();
/* Attempt to detect changes in output refresh rate */
if (libretro_detect_vsync_swap_interval &&
(num_frames > 0))
{
/* Simple running average (leaky-integrator) */
audio_samples_per_frame_avg = ((1.0f / (float)VSYNC_SWAP_INTERVAL_FRAMES) * (float)num_frames) +
((1.0f - (1.0f / (float)VSYNC_SWAP_INTERVAL_FRAMES)) * audio_samples_per_frame_avg);
float swap_ratio = audio_samples_per_frame_avg /
libretro_expected_audio_samples_per_run;
unsigned swap_integer;
float swap_remainder;
/* If internal frame rate is equal to (within threshold)
* or higher than the default 60 (or 50) Hz, fall back
* to a swap interval of 1 */
if (swap_ratio < (1.0f + VSYNC_SWAP_INTERVAL_THRESHOLD))
{
swap_integer = 1;
swap_remainder = 0.0f;
}
else
{
swap_integer = (unsigned)(swap_ratio + 0.5f);
swap_remainder = swap_ratio - (float)swap_integer;
swap_remainder = (swap_remainder < 0.0f) ?
-swap_remainder : swap_remainder;
}
/* > Swap interval is considered 'valid' if it is
* within VSYNC_SWAP_INTERVAL_THRESHOLD of an integer
* value
* > If valid, check if new swap interval differs from
* previously logged value */
if ((swap_remainder <= VSYNC_SWAP_INTERVAL_THRESHOLD) &&
(swap_integer != libretro_vsync_swap_interval))
{
vsync_swap_interval_conter =
(swap_integer == vsync_swap_interval_last) ?
(vsync_swap_interval_conter + 1) : 0;
/* Check whether swap interval is 'stable' */
if (vsync_swap_interval_conter >= VSYNC_SWAP_INTERVAL_FRAMES)
{
libretro_vsync_swap_interval = swap_integer;
vsync_swap_interval_conter = 0;
/* Notify frontend */
retro_system_av_info avinfo;
setAVInfo(avinfo);
environ_cb(RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO, &avinfo);
}
vsync_swap_interval_last = swap_integer;
}
else
vsync_swap_interval_conter = 0;
}
int16_t *audio_out_buffer_ptr = audio_out_buffer;
while (num_frames > 0)
{

View File

@ -36,6 +36,7 @@
#if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES)
#include <glsm/glsm.h>
#include "wsi/gl_context.h"
#endif
#ifdef HAVE_VULKAN
#include "rend/vulkan/vulkan_context.h"
@ -62,7 +63,6 @@
#include "rend/CustomTexture.h"
#include "rend/osd.h"
#include "cfg/option.h"
#include "wsi/gl_context.h"
#include "version.h"
constexpr char slash = path_default_slash_c();
@ -120,7 +120,8 @@ static bool platformIsDreamcast = true;
static bool platformIsArcade = false;
static bool threadedRenderingEnabled = true;
static bool oitEnabled = false;
#ifndef TARGET_NO_OPENMP
static bool autoSkipFrameEnabled = false;
#ifdef _OPENMP
static bool textureUpscaleEnabled = false;
#endif
static bool vmuScreenSettingsShown = true;
@ -164,6 +165,10 @@ static int framebufferHeight;
static int maxFramebufferWidth;
static int maxFramebufferHeight;
float libretro_expected_audio_samples_per_run;
unsigned libretro_vsync_swap_interval = 1;
bool libretro_detect_vsync_swap_interval = false;
static retro_perf_callback perf_cb;
static retro_get_cpu_features_t perf_get_cpu_features_cb;
@ -172,8 +177,8 @@ static retro_log_printf_t log_cb;
static retro_video_refresh_t video_cb;
static retro_input_poll_t poll_cb;
static retro_input_state_t input_cb;
retro_audio_sample_batch_t audio_batch_cb;
static retro_environment_t environ_cb;
retro_audio_sample_batch_t audio_batch_cb;
retro_environment_t environ_cb;
static retro_rumble_interface rumble;
@ -357,11 +362,14 @@ void retro_deinit()
platformIsArcade = false;
threadedRenderingEnabled = true;
oitEnabled = false;
#ifndef TARGET_NO_OPENMP
autoSkipFrameEnabled = false;
#ifdef _OPENMP
textureUpscaleEnabled = false;
#endif
vmuScreenSettingsShown = true;
lightgunSettingsShown = true;
libretro_vsync_swap_interval = 1;
libretro_detect_vsync_swap_interval = false;
LogManager::Shutdown();
retro_audio_deinit();
@ -483,7 +491,7 @@ static bool set_variable_visibility(void)
}
#endif
#ifndef TARGET_NO_OPENMP
#ifdef _OPENMP
// Only if texture upscaling is enabled
bool textureUpscaleWasEnabled = textureUpscaleEnabled;
textureUpscaleEnabled = false;
@ -500,6 +508,24 @@ static bool set_variable_visibility(void)
}
#endif
// Only if automatic frame skipping is disabled
bool autoSkipFrameWasEnabled = autoSkipFrameEnabled;
autoSkipFrameEnabled = false;
var.key = CORE_OPTION_NAME "_auto_skip_frame";
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value && strcmp(var.value, "disabled"))
autoSkipFrameEnabled = true;
if (first_run ||
(autoSkipFrameEnabled != autoSkipFrameWasEnabled) ||
(threadedRenderingEnabled != threadedRenderingWasEnabled))
{
option_display.visible = (!autoSkipFrameEnabled || !threadedRenderingEnabled);
option_display.key = CORE_OPTION_NAME "_detect_vsync_swap_interval";
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
updated = true;
}
// If categories are supported, no further action is required
if (categoriesSupported)
return updated;
@ -596,11 +622,16 @@ static void setGameGeometry(retro_game_geometry& geometry)
geometry.base_height = 480;
}
static void setAVInfo(retro_system_av_info& avinfo)
void setAVInfo(retro_system_av_info& avinfo)
{
double sample_rate = 44100.0;
double fps = SPG_CONTROL.NTSC ? 59.94 : SPG_CONTROL.PAL ? 50.0 : 60.0;
setGameGeometry(avinfo.geometry);
avinfo.timing.sample_rate = 44100.0;
avinfo.timing.fps = SPG_CONTROL.NTSC ? 59.94 : SPG_CONTROL.PAL ? 50.0 : 60.0;
avinfo.timing.sample_rate = sample_rate;
avinfo.timing.fps = fps / (double)libretro_vsync_swap_interval;
libretro_expected_audio_samples_per_run = sample_rate / fps;
}
static void setRotation()
@ -628,6 +659,7 @@ static void update_variables(bool first_startup)
int prevMaxFramebufferHeight = maxFramebufferHeight;
int prevMaxFramebufferWidth = maxFramebufferWidth;
bool prevRotateScreen = rotate_screen;
bool prevDetectVsyncSwapInterval = libretro_detect_vsync_swap_interval;
config::Settings::instance().setRetroEnvironment(environ_cb);
config::Settings::instance().setOptionDefinitions(option_defs_us);
config::Settings::instance().load(false);
@ -749,6 +781,22 @@ static void update_variables(bool first_startup)
config::PixelBufferSize = 0x20000000u;
#endif
if ((config::AutoSkipFrame != 0) && config::ThreadedRendering)
libretro_detect_vsync_swap_interval = false;
else
{
var.key = CORE_OPTION_NAME "_detect_vsync_swap_interval";
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
{
if (!strcmp(var.value, "enabled"))
libretro_detect_vsync_swap_interval = true;
else if (!strcmp(var.value, "disabled"))
libretro_detect_vsync_swap_interval = false;
}
else
libretro_detect_vsync_swap_interval = false;
}
if (first_startup)
{
if (config::ThreadedRendering)
@ -972,6 +1020,16 @@ static void update_variables(bool first_startup)
if (rotate_game)
config::Widescreen.override(false);
setFramebufferSize();
bool avInfoChanged = false;
if ((libretro_detect_vsync_swap_interval != prevDetectVsyncSwapInterval) &&
!libretro_detect_vsync_swap_interval &&
(libretro_vsync_swap_interval != 1))
{
libretro_vsync_swap_interval = 1;
avInfoChanged = true;
}
if ((prevMaxFramebufferWidth < maxFramebufferWidth || prevMaxFramebufferHeight < maxFramebufferHeight)
// TODO crash with dx11
&& config::RendererType != RenderType::DirectX11 && config::RendererType != RenderType::DirectX11_OIT)
@ -980,6 +1038,7 @@ static void update_variables(bool first_startup)
setAVInfo(avinfo);
environ_cb(RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO, &avinfo);
rend_resize_renderer();
avInfoChanged = false;
}
else if (prevFramebufferWidth != framebufferWidth || prevFramebufferHeight != framebufferHeight || geometryChanged)
{
@ -988,6 +1047,13 @@ static void update_variables(bool first_startup)
environ_cb(RETRO_ENVIRONMENT_SET_GEOMETRY, &geometry);
rend_resize_renderer();
}
if (avInfoChanged)
{
retro_system_av_info avinfo;
setAVInfo(avinfo);
environ_cb(RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO, &avinfo);
}
}
}

View File

@ -473,6 +473,20 @@ struct retro_core_option_v2_definition option_defs_us[] = {
},
"disabled",
},
{
CORE_OPTION_NAME "_detect_vsync_swap_interval",
"Detect Frame Rate Changes",
NULL,
"Notify frontend when internal frame rate changes (e.g. from 60 fps to 30 fps). Improves frame pacing in games that run at a locked 30 fps or 20 fps, but should be disabled for games with unlocked (unstable) frame rates (e.g. Ecco the Dolphin, Unreal Tournament). Note: Unavailable when 'Auto Skip Frame' is enabled.",
NULL,
"video",
{
{ "disabled", NULL },
{ "enabled", NULL },
{ NULL, NULL },
},
"disabled",
},
{
CORE_OPTION_NAME "_pvr2_filtering",
"PowerVR2 Post-processing Filter",
@ -487,7 +501,7 @@ struct retro_core_option_v2_definition option_defs_us[] = {
},
"disabled",
},
#ifndef TARGET_NO_OPENMP
#ifdef _OPENMP
{
CORE_OPTION_NAME "_texupscale",
"Texture Upscaling (xBRZ)",

View File

@ -643,7 +643,7 @@ struct retro_core_option_v2_definition option_defs_ar[] = {
},
"disabled",
},
#ifndef TARGET_NO_OPENMP
#ifdef _OPENMP
{
CORE_OPTION_NAME "_texupscale",
CORE_OPTION_NAME_TEXUPSCALE_LABEL_AR,
@ -2178,7 +2178,7 @@ struct retro_core_option_v2_definition option_defs_ast[] = {
},
"disabled",
},
#ifndef TARGET_NO_OPENMP
#ifdef _OPENMP
{
CORE_OPTION_NAME "_texupscale",
CORE_OPTION_NAME_TEXUPSCALE_LABEL_AST,
@ -3713,7 +3713,7 @@ struct retro_core_option_v2_definition option_defs_ca[] = {
},
"disabled",
},
#ifndef TARGET_NO_OPENMP
#ifdef _OPENMP
{
CORE_OPTION_NAME "_texupscale",
CORE_OPTION_NAME_TEXUPSCALE_LABEL_CA,
@ -5248,7 +5248,7 @@ struct retro_core_option_v2_definition option_defs_chs[] = {
},
"disabled",
},
#ifndef TARGET_NO_OPENMP
#ifdef _OPENMP
{
CORE_OPTION_NAME "_texupscale",
CORE_OPTION_NAME_TEXUPSCALE_LABEL_CHS,
@ -6783,7 +6783,7 @@ struct retro_core_option_v2_definition option_defs_cht[] = {
},
"disabled",
},
#ifndef TARGET_NO_OPENMP
#ifdef _OPENMP
{
CORE_OPTION_NAME "_texupscale",
CORE_OPTION_NAME_TEXUPSCALE_LABEL_CHT,
@ -8318,7 +8318,7 @@ struct retro_core_option_v2_definition option_defs_cs[] = {
},
"disabled",
},
#ifndef TARGET_NO_OPENMP
#ifdef _OPENMP
{
CORE_OPTION_NAME "_texupscale",
CORE_OPTION_NAME_TEXUPSCALE_LABEL_CS,
@ -9853,7 +9853,7 @@ struct retro_core_option_v2_definition option_defs_cy[] = {
},
"disabled",
},
#ifndef TARGET_NO_OPENMP
#ifdef _OPENMP
{
CORE_OPTION_NAME "_texupscale",
CORE_OPTION_NAME_TEXUPSCALE_LABEL_CY,
@ -11388,7 +11388,7 @@ struct retro_core_option_v2_definition option_defs_da[] = {
},
"disabled",
},
#ifndef TARGET_NO_OPENMP
#ifdef _OPENMP
{
CORE_OPTION_NAME "_texupscale",
CORE_OPTION_NAME_TEXUPSCALE_LABEL_DA,
@ -12923,7 +12923,7 @@ struct retro_core_option_v2_definition option_defs_de[] = {
},
"disabled",
},
#ifndef TARGET_NO_OPENMP
#ifdef _OPENMP
{
CORE_OPTION_NAME "_texupscale",
CORE_OPTION_NAME_TEXUPSCALE_LABEL_DE,
@ -14458,7 +14458,7 @@ struct retro_core_option_v2_definition option_defs_el[] = {
},
"disabled",
},
#ifndef TARGET_NO_OPENMP
#ifdef _OPENMP
{
CORE_OPTION_NAME "_texupscale",
CORE_OPTION_NAME_TEXUPSCALE_LABEL_EL,
@ -15993,7 +15993,7 @@ struct retro_core_option_v2_definition option_defs_eo[] = {
},
"disabled",
},
#ifndef TARGET_NO_OPENMP
#ifdef _OPENMP
{
CORE_OPTION_NAME "_texupscale",
CORE_OPTION_NAME_TEXUPSCALE_LABEL_EO,
@ -17528,7 +17528,7 @@ struct retro_core_option_v2_definition option_defs_es[] = {
},
"disabled",
},
#ifndef TARGET_NO_OPENMP
#ifdef _OPENMP
{
CORE_OPTION_NAME "_texupscale",
CORE_OPTION_NAME_TEXUPSCALE_LABEL_ES,
@ -19063,7 +19063,7 @@ struct retro_core_option_v2_definition option_defs_fa[] = {
},
"disabled",
},
#ifndef TARGET_NO_OPENMP
#ifdef _OPENMP
{
CORE_OPTION_NAME "_texupscale",
CORE_OPTION_NAME_TEXUPSCALE_LABEL_FA,
@ -20598,7 +20598,7 @@ struct retro_core_option_v2_definition option_defs_fi[] = {
},
"disabled",
},
#ifndef TARGET_NO_OPENMP
#ifdef _OPENMP
{
CORE_OPTION_NAME "_texupscale",
CORE_OPTION_NAME_TEXUPSCALE_LABEL_FI,
@ -22133,7 +22133,7 @@ struct retro_core_option_v2_definition option_defs_fr[] = {
},
"disabled",
},
#ifndef TARGET_NO_OPENMP
#ifdef _OPENMP
{
CORE_OPTION_NAME "_texupscale",
CORE_OPTION_NAME_TEXUPSCALE_LABEL_FR,
@ -23668,7 +23668,7 @@ struct retro_core_option_v2_definition option_defs_gl[] = {
},
"disabled",
},
#ifndef TARGET_NO_OPENMP
#ifdef _OPENMP
{
CORE_OPTION_NAME "_texupscale",
CORE_OPTION_NAME_TEXUPSCALE_LABEL_GL,
@ -25203,7 +25203,7 @@ struct retro_core_option_v2_definition option_defs_he[] = {
},
"disabled",
},
#ifndef TARGET_NO_OPENMP
#ifdef _OPENMP
{
CORE_OPTION_NAME "_texupscale",
CORE_OPTION_NAME_TEXUPSCALE_LABEL_HE,
@ -26738,7 +26738,7 @@ struct retro_core_option_v2_definition option_defs_hu[] = {
},
"disabled",
},
#ifndef TARGET_NO_OPENMP
#ifdef _OPENMP
{
CORE_OPTION_NAME "_texupscale",
CORE_OPTION_NAME_TEXUPSCALE_LABEL_HU,
@ -28273,7 +28273,7 @@ struct retro_core_option_v2_definition option_defs_id[] = {
},
"disabled",
},
#ifndef TARGET_NO_OPENMP
#ifdef _OPENMP
{
CORE_OPTION_NAME "_texupscale",
CORE_OPTION_NAME_TEXUPSCALE_LABEL_ID,
@ -29808,7 +29808,7 @@ struct retro_core_option_v2_definition option_defs_it[] = {
},
"disabled",
},
#ifndef TARGET_NO_OPENMP
#ifdef _OPENMP
{
CORE_OPTION_NAME "_texupscale",
CORE_OPTION_NAME_TEXUPSCALE_LABEL_IT,
@ -31343,7 +31343,7 @@ struct retro_core_option_v2_definition option_defs_ja[] = {
},
"disabled",
},
#ifndef TARGET_NO_OPENMP
#ifdef _OPENMP
{
CORE_OPTION_NAME "_texupscale",
CORE_OPTION_NAME_TEXUPSCALE_LABEL_JA,
@ -32878,7 +32878,7 @@ struct retro_core_option_v2_definition option_defs_ko[] = {
},
"disabled",
},
#ifndef TARGET_NO_OPENMP
#ifdef _OPENMP
{
CORE_OPTION_NAME "_texupscale",
CORE_OPTION_NAME_TEXUPSCALE_LABEL_KO,
@ -34413,7 +34413,7 @@ struct retro_core_option_v2_definition option_defs_mt[] = {
},
"disabled",
},
#ifndef TARGET_NO_OPENMP
#ifdef _OPENMP
{
CORE_OPTION_NAME "_texupscale",
CORE_OPTION_NAME_TEXUPSCALE_LABEL_MT,
@ -35948,7 +35948,7 @@ struct retro_core_option_v2_definition option_defs_nl[] = {
},
"disabled",
},
#ifndef TARGET_NO_OPENMP
#ifdef _OPENMP
{
CORE_OPTION_NAME "_texupscale",
CORE_OPTION_NAME_TEXUPSCALE_LABEL_NL,
@ -37483,7 +37483,7 @@ struct retro_core_option_v2_definition option_defs_no[] = {
},
"disabled",
},
#ifndef TARGET_NO_OPENMP
#ifdef _OPENMP
{
CORE_OPTION_NAME "_texupscale",
CORE_OPTION_NAME_TEXUPSCALE_LABEL_NO,
@ -39018,7 +39018,7 @@ struct retro_core_option_v2_definition option_defs_oc[] = {
},
"disabled",
},
#ifndef TARGET_NO_OPENMP
#ifdef _OPENMP
{
CORE_OPTION_NAME "_texupscale",
CORE_OPTION_NAME_TEXUPSCALE_LABEL_OC,
@ -40553,7 +40553,7 @@ struct retro_core_option_v2_definition option_defs_pl[] = {
},
"disabled",
},
#ifndef TARGET_NO_OPENMP
#ifdef _OPENMP
{
CORE_OPTION_NAME "_texupscale",
CORE_OPTION_NAME_TEXUPSCALE_LABEL_PL,
@ -42088,7 +42088,7 @@ struct retro_core_option_v2_definition option_defs_pt_br[] = {
},
"disabled",
},
#ifndef TARGET_NO_OPENMP
#ifdef _OPENMP
{
CORE_OPTION_NAME "_texupscale",
CORE_OPTION_NAME_TEXUPSCALE_LABEL_PT_BR,
@ -43623,7 +43623,7 @@ struct retro_core_option_v2_definition option_defs_pt_pt[] = {
},
"disabled",
},
#ifndef TARGET_NO_OPENMP
#ifdef _OPENMP
{
CORE_OPTION_NAME "_texupscale",
CORE_OPTION_NAME_TEXUPSCALE_LABEL_PT_PT,
@ -45158,7 +45158,7 @@ struct retro_core_option_v2_definition option_defs_ro[] = {
},
"disabled",
},
#ifndef TARGET_NO_OPENMP
#ifdef _OPENMP
{
CORE_OPTION_NAME "_texupscale",
CORE_OPTION_NAME_TEXUPSCALE_LABEL_RO,
@ -46693,7 +46693,7 @@ struct retro_core_option_v2_definition option_defs_ru[] = {
},
"disabled",
},
#ifndef TARGET_NO_OPENMP
#ifdef _OPENMP
{
CORE_OPTION_NAME "_texupscale",
CORE_OPTION_NAME_TEXUPSCALE_LABEL_RU,
@ -48228,7 +48228,7 @@ struct retro_core_option_v2_definition option_defs_si[] = {
},
"disabled",
},
#ifndef TARGET_NO_OPENMP
#ifdef _OPENMP
{
CORE_OPTION_NAME "_texupscale",
CORE_OPTION_NAME_TEXUPSCALE_LABEL_SI,
@ -49763,7 +49763,7 @@ struct retro_core_option_v2_definition option_defs_sk[] = {
},
"disabled",
},
#ifndef TARGET_NO_OPENMP
#ifdef _OPENMP
{
CORE_OPTION_NAME "_texupscale",
CORE_OPTION_NAME_TEXUPSCALE_LABEL_SK,
@ -51298,7 +51298,7 @@ struct retro_core_option_v2_definition option_defs_sr[] = {
},
"disabled",
},
#ifndef TARGET_NO_OPENMP
#ifdef _OPENMP
{
CORE_OPTION_NAME "_texupscale",
CORE_OPTION_NAME_TEXUPSCALE_LABEL_SR,
@ -52833,7 +52833,7 @@ struct retro_core_option_v2_definition option_defs_sv[] = {
},
"disabled",
},
#ifndef TARGET_NO_OPENMP
#ifdef _OPENMP
{
CORE_OPTION_NAME "_texupscale",
CORE_OPTION_NAME_TEXUPSCALE_LABEL_SV,
@ -54368,7 +54368,7 @@ struct retro_core_option_v2_definition option_defs_tr[] = {
},
"disabled",
},
#ifndef TARGET_NO_OPENMP
#ifdef _OPENMP
{
CORE_OPTION_NAME "_texupscale",
CORE_OPTION_NAME_TEXUPSCALE_LABEL_TR,
@ -55903,7 +55903,7 @@ struct retro_core_option_v2_definition option_defs_uk[] = {
},
"disabled",
},
#ifndef TARGET_NO_OPENMP
#ifdef _OPENMP
{
CORE_OPTION_NAME "_texupscale",
CORE_OPTION_NAME_TEXUPSCALE_LABEL_UK,
@ -57438,7 +57438,7 @@ struct retro_core_option_v2_definition option_defs_val[] = {
},
"disabled",
},
#ifndef TARGET_NO_OPENMP
#ifdef _OPENMP
{
CORE_OPTION_NAME "_texupscale",
CORE_OPTION_NAME_TEXUPSCALE_LABEL_VAL,
@ -58973,7 +58973,7 @@ struct retro_core_option_v2_definition option_defs_vn[] = {
},
"disabled",
},
#ifndef TARGET_NO_OPENMP
#ifdef _OPENMP
{
CORE_OPTION_NAME "_texupscale",
CORE_OPTION_NAME_TEXUPSCALE_LABEL_VN,

View File

@ -22,6 +22,9 @@ HWND getNativeHwnd()
void os_SetupInput()
{
}
void os_TermInput()
{
}
void UpdateInputState()
{