Merge branch 'master' into integrate_rcheevos

This commit is contained in:
Andre Leiradella 2018-09-27 21:48:34 +01:00
commit d67aa83f48
287 changed files with 18697 additions and 4636 deletions

12
.gitignore vendored
View File

@ -120,6 +120,9 @@ wiiu/wut/elf2rpl/elf2rpl
pkg/apple/iOS/build/ pkg/apple/iOS/build/
pkg/apple/iOS/modules/ pkg/apple/iOS/modules/
pkg/apple/build/
ui/drivers/qt/moc_*
ui/drivers/moc_*
obj-unix/ obj-unix/
.vagrant/ .vagrant/
@ -151,3 +154,12 @@ retroarch.js.mem
# only ignore .js files in the repo root # only ignore .js files in the repo root
/*.js /*.js
# Switch
*.d
exefs/*
retroarch_switch.pfs0
retroarch_switch.lst
retroarch_switch.nacp
retroarch_switch.nro
retroarch_switch.nso

View File

@ -20,6 +20,13 @@ matrix:
script: script:
- CROSS_COMPILE=x86_64-w64-mingw32- CFLAGS="-D_WIN32_WINNT=0x0501" ./configure --disable-d3d8 --disable-d3d9 --disable-d3d10 --disable-d3d11 --disable-d3d12 && make HAVE_ZLIB=1 HAVE_BUILTINZLIB=1 HAVE_RPNG=1 - CROSS_COMPILE=x86_64-w64-mingw32- CFLAGS="-D_WIN32_WINNT=0x0501" ./configure --disable-d3d8 --disable-d3d9 --disable-d3d10 --disable-d3d11 --disable-d3d12 && make HAVE_ZLIB=1 HAVE_BUILTINZLIB=1 HAVE_RPNG=1
- compiler: gcc - compiler: gcc
addons:
# Install a more recent gcc than the default
apt:
packages:
- g++-7
sources:
- ubuntu-toolchain-r-test
- compiler: clang - compiler: clang
addons: addons:
# Install a more recent clang than the default # Install a more recent clang than the default
@ -37,7 +44,17 @@ matrix:
- os: osx - os: osx
osx_image: xcode9.3 osx_image: xcode9.3
script: script:
- xcodebuild -target RetroArch -configuration Release -project pkg/apple/RetroArch_Metal.xcodeproj - cd ~/
- brew install --force-bottle qt5
- git clone --depth=50 https://github.com/libretro/libretro-super
- cd libretro-super/travis
- ./build-retroarch-metal.sh
deploy:
skip_cleanup: true
provider: script
script: cd ../retroarch; bash travis_metal_deploy.sh
on:
branch: master
script: script:
- ./configure - ./configure
@ -60,6 +77,9 @@ addons:
- libsdl-mixer1.2-dev - libsdl-mixer1.2-dev
- libsdl-ttf2.0-dev - libsdl-ttf2.0-dev
- libusb-1.0-0-dev - libusb-1.0-0-dev
- qt5-default
- qt5-qmake
- qtbase5-dev-tools
coverity_scan: coverity_scan:
project: project:
name: "RetroArch" name: "RetroArch"

View File

@ -14,6 +14,13 @@
"*.in": "c", "*.in": "c",
"*.rh": "c", "*.rh": "c",
"array": "c", "array": "c",
"file_stream.h": "c",
"driver.h": "c",
"iosfwd": "c",
"xlocbuf": "c",
"xmemory0": "c",
"ios": "c",
"list": "c"
}, },
"C_Cpp.dimInactiveRegions": false, "C_Cpp.dimInactiveRegions": false,
} }

View File

@ -1,4 +1,32 @@
# 1.7.5 (future) # 1.7.5 (future)
- CAMERA: Fix Video4Linux2 driver that broke years ago.
- CHEATS: Add support for Rumble when increase or decrease by the rumble value.
- CHEEVOS: Support headerless NES hashing.
- CHEEVOS: Prevent loading states before achievements are fully loaded.
- CRT: New porches and interlaced bug fix.
- CRT: New functionality, ability to switch between 15KHz and 31KHz, etc.
- COMMON: Support for "OEM-102" key (usually '\' on Euro keyboards).
- DISCORD: Add 'Ask To Join' Feature.
- IOS: Use safe area to account for notch for iPhone X and adjust main view.
- LOCALIZATION: Update Portuguese / Brazilian translation.
- LOCALIZATION: Update Japanese translation.
- LOCALIZATION: Update Polish translation.
- LOCALIZATION: Update Spanish translation.
- MENU: Add dropdown lists for many settings.
- MENU: Fix crash that could happen when changing core's options on Android.
- MENU/QT/WIMP: Add option to rename playlists.
- MENU/QT/WIMP: Add option to filter extensions inside archives when adding to a playlist.
- MENU/QT/WIMP: Rename playlist entries with 2 single clicks.
- MENU/QT/WIMP: Fix shader parameter checkboxes not working
- METAL: Add screenshot support.
- NETPLAY: Save lobby details received back from server after first announcement.
- OPENGL/GLX: Implement Adaptive VSync - GLX_EXT_swap_control_tear.
- OPENGL/WGL: Implement Adaptive VSync - WGL_EXT_swap_control_tear.
- RUNAHEAD: Fix performance degradation that could happen over time (after approx. 30 mins). Fixed input IDs outside of range 0-35 causing slow performance in runahead.
- SWITCH: Merging of RetroNX Nintendo Switch port, based on libnx SDK.
- VULKAN: Fix race condition in threaded mailbox emulation.
- VULKAN: Maintenance fixes.
- WIIU: Fix menu lag when built with DevKitPro r32.
# 1.7.4 # 1.7.4
- ANDROID: Add sustained performance mode, can be turned on/off in Power Management settings menu. - ANDROID: Add sustained performance mode, can be turned on/off in Power Management settings menu.

2482
Doxyfile Normal file

File diff suppressed because it is too large Load Diff

24
Makefile.apple Normal file
View File

@ -0,0 +1,24 @@
include Makefile.common
# Qt MOC generation, required for QObject-derived classes
ifneq ($(MOC_HEADERS),)
# prefix moc_ to base filename of paths and change extension from h to cpp, so a/b/foo.h becomes a/b/moc_foo.cpp
MOC_SRC := $(join $(addsuffix moc_,$(dir $(MOC_HEADERS))), $(notdir $(MOC_HEADERS:.h=.cpp)))
endif
MOC ?= $(error missing moc path)
.PHONY: generate
$(MOC_SRC):
@$(if $(Q), $(shell echo echo MOC $<),)
$(eval MOC_TMP := $(patsubst %.h,%_moc.cpp,$@))
$(MOC) -o $(MOC_TMP) $<
$(foreach x,$(join $(addsuffix :,$(MOC_SRC)),$(MOC_HEADERS)),$(eval $x))
generate: $(MOC_SRC)
@echo $(MOC_SRC)
print-%:
@echo '$*=$($*)'

View File

@ -399,6 +399,7 @@ ifneq ($(C89_BUILD), 1)
HAVE_GTKPLUS = 0 HAVE_GTKPLUS = 0
ifeq ($(HAVE_SSL), 1) ifeq ($(HAVE_SSL), 1)
ifeq ($(HAVE_NETWORKING), 1)
DEFINES += -DHAVE_SSL DEFINES += -DHAVE_SSL
ifeq ($(DEBUG), 1) ifeq ($(DEBUG), 1)
@ -485,6 +486,7 @@ OBJS_TLS = deps/mbedtls/debug.o \
OBJ += $(OBJS_TLS_CRYPTO) $(OBJS_TLS_X509) $(OBJS_TLS) OBJ += $(OBJS_TLS_CRYPTO) $(OBJS_TLS_X509) $(OBJS_TLS)
endif endif
endif endif
endif
# Miscellaneous # Miscellaneous
@ -525,6 +527,10 @@ ifeq ($(TARGET), retroarch_3ds)
OBJ += gfx/drivers_font/ctr_font.o OBJ += gfx/drivers_font/ctr_font.o
endif endif
ifeq ($(HAVE_LIBNX), 1)
OBJ += gfx/drivers_font/switch_font.o
endif
ifeq ($(HAVE_OSS), 1) ifeq ($(HAVE_OSS), 1)
OBJ += audio/drivers/oss.o OBJ += audio/drivers/oss.o
else ifeq ($(HAVE_OSS_BSD), 1) else ifeq ($(HAVE_OSS_BSD), 1)
@ -708,7 +714,6 @@ endif
ifeq ($(HAVE_STRIPES), 1) ifeq ($(HAVE_STRIPES), 1)
OBJ += menu/drivers/stripes.o OBJ += menu/drivers/stripes.o
DEFINES += -DHAVE_STRIPES DEFINES += -DHAVE_STRIPES
HAVE_MENU_COMMON = 1
endif endif
ifeq ($(HAVE_LAKKA), 1) ifeq ($(HAVE_LAKKA), 1)
@ -719,7 +724,6 @@ ifeq ($(HAVE_MENU_COMMON), 1)
OBJ += menu/menu_driver.o \ OBJ += menu/menu_driver.o \
menu/menu_content.o \ menu/menu_content.o \
menu/menu_input.o \ menu/menu_input.o \
menu/menu_event.o \
menu/menu_entries.o \ menu/menu_entries.o \
menu/menu_setting.o \ menu/menu_setting.o \
menu/menu_networking.o \ menu/menu_networking.o \
@ -821,10 +825,20 @@ ifeq ($(TARGET), retroarch_3ds)
endif endif
ifeq ($(TARGET), retroarch_switch) ifeq ($(TARGET), retroarch_switch)
OBJ += gfx/drivers/switch_gfx.o \ ifeq ($(HAVE_LIBNX), 1)
input/drivers/switch_input.o \ OBJ += menu/drivers_display/menu_display_switch.o \
input/drivers_joypad/switch_joypad.o \ gfx/drivers/switch_nx_gfx.o
audio/drivers/switch_audio.o ifeq ($(HAVE_OPENGL), 1)
OBJ += gfx/drivers_context/switch_ctx.o
endif
else
OBJ += gfx/drivers/switch_gfx.o
endif
OBJ += audio/drivers/switch_audio.o \
audio/drivers/switch_thread_audio.o \
input/drivers/switch_input.o \
input/drivers_joypad/switch_joypad.o \
frontend/drivers/platform_switch.o
endif endif
ifeq ($(HAVE_WAYLAND), 1) ifeq ($(HAVE_WAYLAND), 1)

View File

@ -193,7 +193,7 @@ else ifeq ($(libogc_platform), 1)
RARCH_CONSOLE = 1 RARCH_CONSOLE = 1
ifeq ($(platform), wii) ifeq ($(platform), wii)
HAVE_LANGEXTRA := 1 #HAVE_LANGEXTRA := 1
HAVE_WIIUSB_HID := 1 HAVE_WIIUSB_HID := 1
HAVE_RARCH_EXEC := 1 HAVE_RARCH_EXEC := 1
HAVE_RSOUND := 1 HAVE_RSOUND := 1
@ -250,7 +250,7 @@ else ifeq ($(platform), psp1)
HAVE_RBMP := 1 HAVE_RBMP := 1
HAVE_RTGA := 1 HAVE_RTGA := 1
HAVE_KERNEL_PRX := 1 HAVE_KERNEL_PRX := 1
HAVE_LANGEXTRA := 1 #HAVE_LANGEXTRA := 1
RARCH_CONSOLE = 1 RARCH_CONSOLE = 1
ifeq ($(BUILD_PRX), 1) ifeq ($(BUILD_PRX), 1)

246
Makefile.libnx Normal file
View File

@ -0,0 +1,246 @@
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITPRO)),)
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
endif
TOPDIR ?= $(CURDIR)
include $(DEVKITPRO)/libnx/switch_rules
TARGET := retroarch_switch
DEBUG ?= 0
WHOLE_ARCHIVE_LINK = 0
GRIFFIN_BUILD = 0
OBJ :=
# For threading we need to overwrite some vars with global defines because devkitPro's includes
# make it hard for us. This works for the pthread wrapper
DEFINES_THREAD := -Dpthread_t=Thread -Dpthread_mutex_t=Mutex -Dpthread_mutexattr_t='void*' -Dpthread_attr_t=int -Dpthread_cond_t=CondVar -Dpthread_condattr_t='int' -D_SYS__PTHREADTYPES_H_
DEFINES := -D__SWITCH__=1 -U__linux__ -U__linux -DGLM_FORCE_PURE=1 -DRARCH_CONSOLE -DRARCH_INTERNAL -DGLOBAL_CONFIG_DIR='"/switch"' $(DEFINES_THREAD)
HAVE_CC_RESAMPLER = 1
HAVE_MENU_COMMON = 1
HAVE_RTGA = 1
HAVE_RPNG = 1
HAVE_RJPEG = 1
HAVE_RBMP = 1
HAVE_ZLIB = 1
HAVE_BUILTINZLIB = 1
HAVE_LIBRETRODB = 1
HAVE_STATIC_VIDEO_FILTERS = 1
HAVE_STATIC_AUDIO_FILTERS = 1
HAVE_MENU = 1
HAVE_RUNAHEAD = 1
# RetroArch libnx useful flags
HAVE_OVERLAY = 0
HAVE_THREADS = 1
HAVE_PTHREADS = 1
HAVE_FREETYPE = 0
HAVE_SWITCH = 1
HAVE_LIBNX = 1
HAVE_OPENGL = 0
ifeq ($(HAVE_OPENGL), 1)
HAVE_EGL = 1
HAVE_SHADERPIPELINE = 1
HAVE_RGUI = 1
HAVE_MATERIALUI = 1
HAVE_ZARCH = 0
HAVE_XMB = 0
HAVE_STRIPES = 0
else
HAVE_RGUI = 1
HAVE_ZARCH = 0
HAVE_MATERIALUI = 0
HAVE_XMB = 0
HAVE_STRIPES = 0
endif
include Makefile.common
BLACKLIST :=
#BLACKLIST += input/input_overlay.o
#BLACKLIST += tasks/task_overlay.o
OBJ := $(filter-out $(BLACKLIST),$(OBJ))
#---------------------------------------------------------------------------------
# TARGET is the name of the output
# BUILD is the directory where object files & intermediate files will be placed
# SOURCES is a list of directories containing source code
# DATA is a list of directories containing data files
# INCLUDES is a list of directories containing header files
# EXEFS_SRC is the optional input directory containing data copied into exefs, if anything this normally should only contain "main.npdm".
# ROMFS is the directory containing data to be added to RomFS, relative to the Makefile (Optional)
#
# NO_ICON: if set to anything, do not use icon.
# NO_NACP: if set to anything, no .nacp file is generated.
# APP_TITLE is the name of the app stored in the .nacp file (Optional)
# APP_AUTHOR is the author of the app stored in the .nacp file (Optional)
# APP_VERSION is the version of the app stored in the .nacp file (Optional)
# APP_TITLEID is the titleID of the app stored in the .nacp file (Optional)
# ICON is the filename of the icon (.jpg), relative to the project folder.
# If not set, it attempts to use one of the following (in this order):
# - <Project name>.jpg
# - icon.jpg
# - <libnx folder>/default_icon.jpg
#---------------------------------------------------------------------------------
BUILD := build
SOURCES := $(CURDIR)/source
DATA := data
INCLUDES := include
EXEFS_SRC := exefs_src
#ROMFS := switch/romfs
APP_TITLE := RetroArch
APP_VERSION := 1.0.0
APP_AUTHOR := libretro Team
NO_ICON := 1
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE -mcpu=cortex-a57+crc+fp+simd
CFLAGS := -g -Wall -O3 -ffast-math -ffunction-sections \
$(ARCH) $(DEFINES) -Ideps -Ideps/libz -Ilibretro-common/include -Ideps/stb -I$(LIBNX)/include -I$(PORTLIBS)/include/ -include $(LIBNX)/include/switch.h #$(shell $(DEVKITPRO)/portlibs/switch/bin/freetype-config --cflags)
CFLAGS += $(INCLUDE) -DSWITCH=1 -DHAVE_LIBNX=1 -DNXLINK=1
# The following line works around an issue in newlib that produces a compilation
# error in glm. It will be removed as soon as this issue is resolved.
CFLAGS += -D_GLIBCXX_USE_C99_MATH_TR1 -D_LDBL_EQ_DBL
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs $(ARCH) -Wl,--allow-multiple-definition -Wl,-Map,$(notdir $*.map)
LIBS := -lstdc++ -lbz2 -lpng -lz -lnx -lm
ifeq ($(HAVE_OPENGL), 1)
LIBS := -lEGL -lglapi -ldrm_nouveau $(LIBS)
endif
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS := $(PORTLIBS) $(LIBNX)
#---------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
# rules for different file extensions
#---------------------------------------------------------------------------------
ifneq ($(BUILD),$(notdir $(CURDIR)))
#---------------------------------------------------------------------------------
export OUTPUT := $(TARGET)
export TOPDIR := $(CURDIR)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
#---------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
#---------------------------------------------------------------------------------
ifeq ($(strip $(CPPFILES)),)
#---------------------------------------------------------------------------------
export LD := $(CC)
#---------------------------------------------------------------------------------
else
#---------------------------------------------------------------------------------
export LD := $(CXX)
#---------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------
export OFILES := $(OBJ) libretro_switch.a
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
-I$(CURDIR)/$(BUILD)
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC)
ifeq ($(strip $(ICON)),)
icons := $(wildcard *.jpg)
ifneq (,$(findstring $(TARGET).jpg,$(icons)))
export APP_ICON := $(TOPDIR)/$(TARGET).jpg
else
ifneq (,$(findstring icon.jpg,$(icons)))
export APP_ICON := $(TOPDIR)/icon.jpg
endif
endif
else
export APP_ICON := $(TOPDIR)/$(ICON)
endif
ifeq ($(strip $(NO_ICON)),)
export NROFLAGS += --icon=$(APP_ICON)
endif
ifeq ($(strip $(NO_NACP)),)
export NROFLAGS += --nacp=$(CURDIR)/$(TARGET).nacp
endif
ifneq ($(APP_TITLEID),)
export NACPFLAGS += --titleid=$(APP_TITLEID)
endif
ifneq ($(ROMFS),)
export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS)
endif
DEPENDS := $(OFILES:.o=.d)
.PHONY: clean all
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
all : $(OUTPUT).pfs0 $(OUTPUT).nro
$(OUTPUT).pfs0 : $(OUTPUT).nso
$(OUTPUT).nso : $(OUTPUT).elf
ifeq ($(strip $(NO_NACP)),)
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp
else
$(OUTPUT).nro : $(OUTPUT).elf
endif
$(OUTPUT).elf : $(OBJ)
clean:
rm -f $(OBJ) $(OUTPUT).pfs0 $(OUTPUT).nro $(OUTPUT).elf
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o %_bin.h : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

View File

@ -42,7 +42,7 @@ endif
ifeq ($(strip $(LIBTRANSISTOR_HOME)),) ifeq ($(strip $(LIBTRANSISTOR_HOME)),)
$(error "Please set LIBTRANSISTOR_HOME in your environment. export LIBTRANSISTOR_HOME=<path t>o libtransistor") $(error "Please set LIBTRANSISTOR_HOME in your environment. export LIBTRANSISTOR_HOME=<path/to/libtransistor/dist/>")
endif endif
include $(LIBTRANSISTOR_HOME)/libtransistor.mk include $(LIBTRANSISTOR_HOME)/libtransistor.mk
@ -52,7 +52,7 @@ LIBDIRS := -L.
TARGETS := $(TARGET).nro TARGETS := $(TARGET).nro
CFLAGS += $(INCDIRS) $(DEFINES) -Wunused-command-line-argument CFLAGS += $(INCDIRS) $(DEFINES) -Wno-unused-command-line-argument -Werror-implicit-function-declaration
all: $(TARGETS) all: $(TARGETS)

View File

@ -125,6 +125,7 @@ static const audio_driver_t *audio_drivers[] = {
#endif #endif
#ifdef SWITCH #ifdef SWITCH
&audio_switch, &audio_switch,
&audio_switch_thread,
#endif #endif
&audio_null, &audio_null,
NULL, NULL,

View File

@ -338,6 +338,7 @@ extern audio_driver_t audio_psp;
extern audio_driver_t audio_ctr_csnd; extern audio_driver_t audio_ctr_csnd;
extern audio_driver_t audio_ctr_dsp; extern audio_driver_t audio_ctr_dsp;
extern audio_driver_t audio_switch; extern audio_driver_t audio_switch;
extern audio_driver_t audio_switch_thread;
extern audio_driver_t audio_rwebaudio; extern audio_driver_t audio_rwebaudio;
extern audio_driver_t audio_null; extern audio_driver_t audio_null;

View File

@ -1,4 +1,6 @@
/* RetroArch - A frontend for libretro. /* RetroArch - A frontend for libretro.
* Copyright (C) 2018 - misson20000
* Copyright (C) 2018 - m4xw
* *
* RetroArch is free software: you can redistribute it and/or modify it under the terms * RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found- * of the GNU General Public License as published by the Free Software Found-
@ -17,12 +19,16 @@
#include <malloc.h> #include <malloc.h>
#include <stdint.h> #include <stdint.h>
#include<libtransistor/nx.h> #include "switch_audio_compat.h"
#include<libtransistor/alloc_pages.h>
#include "../audio_driver.h" #include "../audio_driver.h"
#include "../../verbosity.h" #include "../../verbosity.h"
#ifdef HAVE_LIBNX
#define BUFFER_COUNT 5
#else
#define BUFFER_COUNT 3
#endif
static const int sample_rate = 48000; static const int sample_rate = 48000;
static const int max_num_samples = sample_rate; static const int max_num_samples = sample_rate;
static const int num_channels = 2; static const int num_channels = 2;
@ -30,38 +36,57 @@ static const size_t sample_buffer_size = ((max_num_samples * num_channels * size
typedef struct typedef struct
{ {
audio_output_t output;
handle_t event;
audio_output_buffer_t buffers[3];
audio_output_buffer_t *current_buffer;
bool blocking; bool blocking;
bool is_paused; bool is_paused;
uint64_t last_append; uint64_t last_append;
unsigned latency; unsigned latency;
compat_audio_out_buffer buffers[BUFFER_COUNT];
compat_audio_out_buffer *current_buffer;
#ifndef HAVE_LIBNX
audio_output_t output;
handle_t event;
#endif
} switch_audio_t; } switch_audio_t;
static uint32_t switch_audio_data_size(void)
{
#ifdef HAVE_LIBNX
static const int framerate = 1000 / 30;
static const int samplecount = (sample_rate / framerate);
return (samplecount * num_channels * sizeof(uint16_t));
#else
return sample_buffer_size;
#endif
}
static size_t switch_audio_buffer_size(void *data)
{
(void) data;
#ifdef HAVE_LIBNX
return (switch_audio_data_size() + 0xfff) & ~0xfff;
#else
return sample_buffer_size;
#endif
}
static ssize_t switch_audio_write(void *data, const void *buf, size_t size) static ssize_t switch_audio_write(void *data, const void *buf, size_t size)
{ {
size_t to_write = size; size_t to_write = size;
switch_audio_t *swa = (switch_audio_t*) data; switch_audio_t *swa = (switch_audio_t*) data;
#if 0 if (!swa)
RARCH_LOG("write %ld samples\n", size/sizeof(uint16_t)); return -1;
#endif
if (!swa->current_buffer) if (!swa->current_buffer)
{ {
uint32_t num; uint32_t num;
if (audio_ipc_output_get_released_buffer(&swa->output, &num, &swa->current_buffer) != RESULT_OK) if (switch_audio_ipc_output_get_released_buffer(swa, num) != 0)
{ {
RARCH_LOG("Failed to get released buffer?\n"); RARCH_LOG("Failed to get released buffer?\n");
return -1; return -1;
} }
#if 0
RARCH_LOG("got buffer, num %d, ptr %p\n", num, swa->current_buffer);
#endif
if (num < 1) if (num < 1)
swa->current_buffer = NULL; swa->current_buffer = NULL;
@ -73,47 +98,46 @@ static ssize_t switch_audio_write(void *data, const void *buf, size_t size)
while(swa->current_buffer == NULL) while(swa->current_buffer == NULL)
{ {
uint32_t num; uint32_t handle_idx = 0;
uint32_t handle_idx; num = 0;
#ifdef HAVE_LIBNX
if (audoutWaitPlayFinish(&swa->current_buffer, &num, U64_MAX) != 0) { }
#else
svcWaitSynchronization(&handle_idx, &swa->event, 1, 33333333); svcWaitSynchronization(&handle_idx, &swa->event, 1, 33333333);
svcResetSignal(swa->event); svcResetSignal(swa->event);
if (audio_ipc_output_get_released_buffer(&swa->output, &num, &swa->current_buffer) != RESULT_OK) if (switch_audio_ipc_output_get_released_buffer(swa, num) != 0)
return -1; return -1;
#endif
} }
} else { }
else
/* no buffer, nonblocking... */ /* no buffer, nonblocking... */
return 0; return 0;
}
} }
swa->current_buffer->data_size = 0; swa->current_buffer->data_size = 0;
} }
if (to_write > sample_buffer_size - swa->current_buffer->data_size) if (to_write > switch_audio_buffer_size(NULL) - swa->current_buffer->data_size)
to_write = sample_buffer_size - swa->current_buffer->data_size; to_write = switch_audio_buffer_size(NULL) - swa->current_buffer->data_size;
#ifndef HAVE_LIBNX
memcpy(((uint8_t*) swa->current_buffer->sample_data) + swa->current_buffer->data_size, buf, to_write); memcpy(((uint8_t*) swa->current_buffer->sample_data) + swa->current_buffer->data_size, buf, to_write);
#else
memcpy(((uint8_t*) swa->current_buffer->buffer) + swa->current_buffer->data_size, buf, to_write);
#endif
swa->current_buffer->data_size += to_write; swa->current_buffer->data_size += to_write;
swa->current_buffer->buffer_size = sample_buffer_size; swa->current_buffer->buffer_size = switch_audio_buffer_size(NULL);
if (swa->current_buffer->data_size > (48000*swa->latency)/1000) if (swa->current_buffer->data_size > (48000 * swa->latency) / 1000)
{ {
result_t r = audio_ipc_output_append_buffer(&swa->output, swa->current_buffer); if (switch_audio_ipc_output_append_buffer(swa, swa->current_buffer) != 0)
if (r != RESULT_OK) return -1;
{
RARCH_ERR("failed to append buffer: 0x%x\n", r);
return -1;
}
swa->current_buffer = NULL; swa->current_buffer = NULL;
} }
#if 0
RARCH_LOG("submitted %ld samples, %ld samples since last submit\n",
to_write/num_channels/sizeof(uint16_t),
(svcGetSystemTick() - swa->last_append) * sample_rate / 19200000);
#endif
swa->last_append = svcGetSystemTick(); swa->last_append = svcGetSystemTick();
return to_write; return to_write;
@ -125,25 +149,32 @@ static bool switch_audio_stop(void *data)
if (!swa) if (!swa)
return false; return false;
if(!swa->is_paused) { /* TODO/FIXME - fix libnx codepath */
if(audio_ipc_output_stop(&swa->output) != RESULT_OK) #ifndef HAVE_LIBNX
if (!swa->is_paused)
if (switch_audio_ipc_output_stop(swa) != 0)
return false; return false;
}
swa->is_paused = true; swa->is_paused = true;
#endif
return true; return true;
} }
static bool switch_audio_start(void *data, bool is_shutdown) static bool switch_audio_start(void *data, bool is_shutdown)
{ {
switch_audio_t *swa = (switch_audio_t*) data; switch_audio_t *swa = (switch_audio_t*) data;
if (!swa)
return false;
if(swa->is_paused) { /* TODO/FIXME - fix libnx codepath */
if (audio_ipc_output_start(&swa->output) != RESULT_OK) #ifndef HAVE_LIBNX
if (swa->is_paused)
if (switch_audio_ipc_output_start(swa) != 0)
return false; return false;
}
swa->is_paused = false; swa->is_paused = false;
#endif
return true; return true;
} }
@ -159,8 +190,22 @@ static void switch_audio_free(void *data)
{ {
switch_audio_t *swa = (switch_audio_t*) data; switch_audio_t *swa = (switch_audio_t*) data;
if (!swa)
return;
#ifdef HAVE_LIBNX
if (!swa->is_paused)
audoutStopAudioOut();
audoutExit();
int i;
for (i = 0; i < BUFFER_COUNT; i++)
free(swa->buffers[i].buffer);
#else
audio_ipc_output_close(&swa->output); audio_ipc_output_close(&swa->output);
audio_ipc_finalize(); audio_ipc_finalize();
#endif
free(swa); free(swa);
} }
@ -189,11 +234,10 @@ static void switch_audio_set_nonblock_state(void *data, bool state)
} }
static void *switch_audio_init(const char *device, static void *switch_audio_init(const char *device,
unsigned rate, unsigned latency, unsigned rate, unsigned latency,
unsigned block_frames, unsigned block_frames,
unsigned *new_rate) unsigned *new_rate)
{ {
result_t r;
unsigned i; unsigned i;
char names[8][0x20]; char names[8][0x20];
uint32_t num_names = 0; uint32_t num_names = 0;
@ -202,12 +246,14 @@ static void *switch_audio_init(const char *device,
if (!swa) if (!swa)
return NULL; return NULL;
r = audio_ipc_init(); if (switch_audio_ipc_init() != 0)
if (r != RESULT_OK)
goto fail; goto fail;
if (audio_ipc_list_outputs(&names[0], 8, &num_names) != RESULT_OK) #ifdef HAVE_LIBNX
if (audoutStartAudioOut() != 0)
goto fail;
#else
if (audio_ipc_list_outputs(&names[0], 8, &num_names) != 0)
goto fail_audio_ipc; goto fail_audio_ipc;
if (num_names != 1) if (num_names != 1)
@ -216,7 +262,7 @@ static void *switch_audio_init(const char *device,
goto fail_audio_ipc; goto fail_audio_ipc;
} }
if (audio_ipc_open_output(names[0], &swa->output) != RESULT_OK) if (audio_ipc_open_output(names[0], &swa->output) != 0)
goto fail_audio_ipc; goto fail_audio_ipc;
if (swa->output.sample_rate != sample_rate) if (swa->output.sample_rate != sample_rate)
@ -239,51 +285,70 @@ static void *switch_audio_init(const char *device,
goto fail_audio_output; goto fail_audio_output;
} }
if (audio_ipc_output_register_buffer_event(&swa->output, &swa->event) != RESULT_OK) if (audio_ipc_output_register_buffer_event(&swa->output, &swa->event) != 0)
goto fail_audio_output; goto fail_audio_output;
#endif
swa->blocking = block_frames; for (i = 0; i < BUFFER_COUNT; i++)
*new_rate = swa->output.sample_rate;
for(i = 0; i < 3; i++)
{ {
swa->buffers[i].buffer_size = switch_audio_buffer_size(NULL);
swa->buffers[i].data_size = switch_audio_data_size();
#ifdef HAVE_LIBNX
swa->buffers[i].next = NULL; /* Unused */
swa->buffers[i].data_offset = 0;
swa->buffers[i].buffer = memalign(0x1000, switch_audio_buffer_size(NULL));
if (swa->buffers[i].buffer == NULL)
goto fail_audio_output;
memset(swa->buffers[i].buffer, 0, switch_audio_buffer_size(NULL));
#else
swa->buffers[i].ptr = &swa->buffers[i].sample_data; swa->buffers[i].ptr = &swa->buffers[i].sample_data;
swa->buffers[i].sample_data = alloc_pages(sample_buffer_size, sample_buffer_size, NULL);
swa->buffers[i].buffer_size = sample_buffer_size;
swa->buffers[i].data_size = sample_buffer_size;
swa->buffers[i].unknown = 0; swa->buffers[i].unknown = 0;
swa->buffers[i].sample_data = alloc_pages(sample_buffer_size, switch_audio_buffer_size(NULL), NULL);
if(swa->buffers[i].sample_data == NULL) if (swa->buffers[i].sample_data == NULL)
goto fail_audio_output; goto fail_audio_output;
#endif
if (audio_ipc_output_append_buffer(&swa->output, &swa->buffers[i]) != RESULT_OK) if (switch_audio_ipc_output_append_buffer(swa, &swa->buffers[i]) != 0)
goto fail_audio_output; goto fail_audio_output;
} }
#ifdef HAVE_LIBNX
*new_rate = audoutGetSampleRate();
#else
*new_rate = swa->output.sample_rate;
#endif
swa->current_buffer = NULL; swa->current_buffer = NULL;
swa->latency = latency; swa->latency = latency;
swa->last_append = svcGetSystemTick(); swa->last_append = svcGetSystemTick();
swa->blocking = block_frames;
swa->is_paused = true; swa->is_paused = true;
RARCH_LOG("[Audio]: Audio initialized\n");
return swa; return swa;
fail_audio_output: fail_audio_output:
/* TODO/FIXME - fix libnx codepath */
#ifndef HAVE_LIBNX
audio_ipc_output_close(&swa->output); audio_ipc_output_close(&swa->output);
#endif
fail_audio_ipc: fail_audio_ipc:
/* TODO/FIXME - fix libnx codepath */
#ifndef HAVE_LIBNX
audio_ipc_finalize(); audio_ipc_finalize();
#endif
fail: fail:
free(swa); if (swa)
free(swa);
return NULL; return NULL;
} }
static size_t switch_audio_buffer_size(void *data)
{
(void) data;
return sample_buffer_size;
}
audio_driver_t audio_switch = { audio_driver_t audio_switch = {
switch_audio_init, switch_audio_init,
switch_audio_write, switch_audio_write,

View File

@ -0,0 +1,93 @@
#ifndef _SWITCH_AUDIO_COMPAT_H
#define _SWITCH_AUDIO_COMPAT_H
#ifdef HAVE_LIBNX
#include <switch.h>
#else
#include <libtransistor/nx.h>
#endif
#ifdef HAVE_LIBNX
/* libnx definitions */
/* threading */
typedef Mutex compat_mutex;
typedef Thread compat_thread;
typedef CondVar compat_condvar;
#define compat_thread_create(thread, func, data, stack_size, prio, cpu) \
threadCreate(thread, func, data, stack_size, prio, cpu)
#define compat_thread_start(thread) \
threadStart(thread)
#define compat_thread_join(thread) \
threadWaitForExit(thread)
#define compat_thread_close(thread) \
threadClose(thread)
#define compat_mutex_create(mutex) \
mutexInit(mutex)
#define compat_mutex_lock(mutex) \
mutexLock(mutex)
#define compat_mutex_unlock(mutex) \
mutexUnlock(mutex)
#define compat_condvar_create(condvar) \
condvarInit(condvar)
#define compat_condvar_wait(condvar, mutex) \
condvarWait(condvar, mutex)
#define compat_condvar_wake_all(condvar) \
condvarWakeAll(condvar)
/* audio */
typedef AudioOutBuffer compat_audio_out_buffer;
#define switch_audio_ipc_init audoutInitialize
#define switch_audio_ipc_finalize audoutExit
#define switch_audio_ipc_output_get_released_buffer(a, b) audoutGetReleasedAudioOutBuffer(&a->current_buffer, &b)
#define switch_audio_ipc_output_append_buffer(a, b) audoutAppendAudioOutBuffer(b)
#define switch_audio_ipc_output_stop(a) audoutStopAudioOut()
#define switch_audio_ipc_output_start(a) audoutStartAudioOut()
#else
/* libtransistor definitions */
typedef result_t Result;
#define R_FAILED(r) ((r) != RESULT_OK)
/* threading */
typedef trn_mutex_t compat_mutex;
typedef trn_thread_t compat_thread;
typedef trn_condvar_t compat_condvar;
#define compat_thread_create(thread, func, data, stack_size, prio, cpu) \
trn_thread_create(thread, func, data, prio, cpu, stack_size, NULL)
#define compat_thread_start(thread) \
trn_thread_start(thread)
#define compat_thread_join(thread) \
trn_thread_join(thread, -1)
#define compat_thread_close(thread) \
trn_thread_destroy(thread)
#define compat_mutex_create(mutex) \
trn_mutex_create(mutex)
#define compat_mutex_lock(mutex) \
trn_mutex_lock(mutex)
#define compat_mutex_unlock(mutex) \
trn_mutex_unlock(mutex)
#define compat_condvar_create(condvar) \
trn_condvar_create(condvar)
#define compat_condvar_wait(condvar, mutex) \
trn_condvar_wait(condvar, mutex, -1)
#define compat_condvar_wake_all(condvar) \
trn_condvar_signal(condvar, -1)
/* audio */
typedef audio_output_buffer_t compat_audio_out_buffer;
#define switch_audio_ipc_init audio_ipc_init
#define switch_audio_ipc_finalize audio_ipc_finalize
#define switch_audio_ipc_output_get_released_buffer(a, b) audio_ipc_output_get_released_buffer(&a->output, &b, &a->current_buffer)
#define switch_audio_ipc_output_append_buffer(a, b) audio_ipc_output_append_buffer(&a->output, b)
#define switch_audio_ipc_output_stop(a) audio_ipc_output_stop(&a->output)
#define switch_audio_ipc_output_start(a) audio_ipc_output_start(&a->output)
#endif
#endif

View File

@ -0,0 +1,448 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2018 - misson20000
* Copyright (C) 2018 - m4xw
* Copyright (C) 2018 - lifajucejo
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <stdint.h>
#include <sys/unistd.h>
#ifdef HAVE_LIBNX
#include <switch.h>
#else
#include <libtransistor/nx.h>
#endif
#include <queues/fifo_queue.h>
#include "../audio_driver.h"
#include "../../verbosity.h"
#include "../../tasks/tasks_internal.h"
#include "switch_audio_compat.h"
static const size_t thread_stack_size = 1024 * 8;
static const int thread_preferred_cpu = 2;
static const int channel_count = 2;
static const size_t sample_size = sizeof(uint16_t);
static const size_t frame_size = channel_count * sample_size;
#define AUDIO_BUFFER_COUNT 2
typedef struct
{
fifo_buffer_t* fifo;
compat_mutex fifoLock;
compat_condvar cond;
compat_mutex condLock;
size_t fifoSize;
volatile bool running;
bool nonblocking;
bool is_paused;
compat_audio_out_buffer buffers[AUDIO_BUFFER_COUNT];
compat_thread thread;
unsigned latency;
uint32_t sampleRate;
#ifndef HAVE_LIBNX
audio_output_t output;
handle_t event;
#endif
} switch_thread_audio_t;
static void mainLoop(void* data)
{
Result rc;
uint32_t released_out_count = 0;
compat_audio_out_buffer *released_out_buffer = NULL;
switch_thread_audio_t *swa = (switch_thread_audio_t*)data;
if (!swa)
return;
RARCH_LOG("[Audio]: start mainLoop cpu %u tid %u\n", svcGetCurrentProcessorNumber(), swa->thread.handle);
while (swa->running)
{
size_t buf_avail, avail, to_write;
if (!released_out_buffer)
{
#ifdef HAVE_LIBNX
rc = audoutWaitPlayFinish(&released_out_buffer, &released_out_count, U64_MAX);
#else
uint32_t handle_idx = 0;
svcWaitSynchronization(&handle_idx, &swa->event, 1, 33333333);
svcResetSignal(swa->event);
rc = audio_ipc_output_get_released_buffer(&swa->output, &released_out_count, &released_out_buffer);
#endif
if (R_FAILED(rc))
{
swa->running = false;
RARCH_LOG("[Audio]: audoutGetReleasedAudioOutBuffer failed: %d\n", (int)rc);
break;
}
released_out_buffer->data_size = 0;
}
buf_avail = released_out_buffer->buffer_size - released_out_buffer->data_size;
compat_mutex_lock(&swa->fifoLock);
avail = fifo_read_avail(swa->fifo);
to_write = MIN(avail, buf_avail);
if (to_write > 0)
{
uint8_t *base;
#ifdef HAVE_LIBNX
base = (uint8_t*) released_out_buffer->buffer;
#else
base = (uint8_t*) released_out_buffer->sample_data;
#endif
fifo_read(swa->fifo, base + released_out_buffer->data_size, to_write);
}
compat_mutex_unlock(&swa->fifoLock);
compat_condvar_wake_all(&swa->cond);
released_out_buffer->data_size += to_write;
if (released_out_buffer->data_size >= released_out_buffer->buffer_size / 2)
{
rc = switch_audio_ipc_output_append_buffer(swa, released_out_buffer);
if (R_FAILED(rc))
{
RARCH_LOG("[Audio]: audoutAppendAudioOutBuffer failed: %d\n", (int)rc);
}
released_out_buffer = NULL;
}
else
svcSleepThread(16000000); /* 16ms */
}
}
static void *switch_thread_audio_init(const char *device, unsigned rate, unsigned latency, unsigned block_frames, unsigned *new_rate)
{
Result rc;
unsigned i;
uint32_t prio;
char names[8][0x20];
uint32_t num_names = 0;
switch_thread_audio_t *swa = (switch_thread_audio_t *)calloc(1, sizeof(*swa));
if (!swa)
return NULL;
swa->running = true;
swa->nonblocking = true;
swa->is_paused = true;
swa->latency = MAX(latency, 8);
rc = switch_audio_ipc_init();
if (R_FAILED(rc))
{
RARCH_LOG("[Audio]: audio init failed %d\n", (int)rc);
free(swa);
return NULL;
}
#ifdef HAVE_LIBNX
rc = audoutStartAudioOut();
if (R_FAILED(rc))
{
RARCH_LOG("[Audio]: audio start init failed: %d\n", (int)rc);
goto fail_audio_ipc;
}
swa->sampleRate = audoutGetSampleRate();
#else
if (audio_ipc_list_outputs(&names[0], 8, &num_names) != RESULT_OK)
goto fail_audio_ipc;
if (num_names != 1)
{
RARCH_ERR("[Audio]: got back more than one AudioOut\n");
goto fail_audio_ipc;
}
if (audio_ipc_open_output(names[0], &swa->output) != RESULT_OK)
goto fail_audio_ipc;
swa->sampleRate = swa->output.sample_rate;
if (swa->output.num_channels != 2)
{
RARCH_ERR("expected %d channels, got %d\n", 2,
swa->output.num_channels);
goto fail_audio_output;
}
if (swa->output.sample_format != PCM_INT16)
{
RARCH_ERR("expected PCM_INT16, got %d\n", swa->output.sample_format);
goto fail_audio_output;
}
if (audio_ipc_output_register_buffer_event(&swa->output, &swa->event) != 0)
goto fail_audio_output;
#endif
*new_rate = swa->sampleRate;
swa->fifoSize = (swa->sampleRate * sample_size * swa->latency) / 1000;
for (i = 0; i < AUDIO_BUFFER_COUNT; i++)
{
#ifdef HAVE_LIBNX
swa->buffers[i].next = NULL; /* Unused */
swa->buffers[i].data_offset = 0;
swa->buffers[i].buffer_size = swa->fifoSize;
swa->buffers[i].data_size = swa->buffers[i].buffer_size;
swa->buffers[i].buffer = memalign(0x1000, swa->buffers[i].buffer_size);
if (swa->buffers[i].buffer == NULL)
goto fail;
memset(swa->buffers[i].buffer, 0, swa->buffers[i].buffer_size);
#else
swa->buffers[i].ptr = &swa->buffers[i].sample_data;
swa->buffers[i].unknown = 0;
swa->buffers[i].buffer_size = swa->fifoSize;
swa->buffers[i].data_size = swa->buffers[i].buffer_size;
swa->buffers[i].sample_data = alloc_pages(swa->buffers[i].buffer_size, swa->buffers[i].buffer_size, NULL);
if (swa->buffers[i].sample_data == NULL)
goto fail_audio_output;
memset(swa->buffers[i].sample_data, 0, swa->buffers[i].buffer_size);
#endif
if (switch_audio_ipc_output_append_buffer(swa, &swa->buffers[i]) != 0)
goto fail_audio_output;
}
compat_mutex_create(&swa->fifoLock);
swa->fifo = fifo_new(swa->fifoSize);
compat_condvar_create(&swa->cond);
RARCH_LOG("[Audio]: switch_thread_audio_init device %s requested rate %hu rate %hu latency %hu block_frames %hu fifoSize %lu\n",
device, rate, swa->sampleRate, swa->latency, block_frames, swa->fifoSize);
svcGetThreadPriority(&prio, 0xffff8000);
rc = compat_thread_create(&swa->thread, &mainLoop, (void*)swa, thread_stack_size, prio - 1, thread_preferred_cpu);
if (R_FAILED(rc))
{
RARCH_LOG("[Audio]: thread creation failed create %u\n", swa->thread.handle);
swa->running = false;
return NULL;
}
if (R_FAILED(compat_thread_start(&swa->thread)))
{
RARCH_LOG("[Audio]: thread creation failed start %u\n", swa->thread.handle);
compat_thread_close(&swa->thread);
swa->running = false;
return NULL;
}
return swa;
fail_audio_output:
#ifndef HAVE_LIBNX
audio_ipc_output_close(&swa->output);
#endif
fail_audio_ipc:
switch_audio_ipc_finalize();
fail:
free(swa); // freeing a null ptr is valid
return NULL;
}
static bool switch_thread_audio_start(void *data, bool is_shutdown)
{
/* RARCH_LOG("[Audio]: switch_thread_audio_start\n"); */
switch_thread_audio_t *swa = (switch_thread_audio_t *)data;
if (!swa)
return false;
swa->is_paused = false;
return true;
}
static bool switch_thread_audio_stop(void *data)
{
switch_thread_audio_t* swa = (switch_thread_audio_t*)data;
if (!swa)
return false;
swa->is_paused = true;
return true;
}
static void switch_thread_audio_free(void *data)
{
unsigned i;
switch_thread_audio_t *swa = (switch_thread_audio_t *)data;
if (!swa)
return;
if (swa->running)
{
swa->running = false;
compat_thread_join(&swa->thread);
compat_thread_close(&swa->thread);
}
switch_audio_ipc_output_stop(swa);
switch_audio_ipc_finalize();
if (swa->fifo)
{
fifo_free(swa->fifo);
swa->fifo = NULL;
}
for (i = 0; i < ARRAY_SIZE(swa->buffers); i++)
{
#ifdef HAVE_LIBNX
free(swa->buffers[i].buffer);
#else
free_pages(swa->buffers[i].sample_data);
#endif
}
free(swa);
swa = NULL;
}
static ssize_t switch_thread_audio_write(void *data, const void *buf, size_t size)
{
size_t avail, written;
switch_thread_audio_t *swa = (switch_thread_audio_t *)data;
if (!swa || !swa->running)
return 0;
if (swa->nonblocking)
{
compat_mutex_lock(&swa->fifoLock);
avail = fifo_write_avail(swa->fifo);
written = MIN(avail, size);
if (written > 0)
fifo_write(swa->fifo, buf, written);
compat_mutex_unlock(&swa->fifoLock);
}
else
{
written = 0;
while (written < size && swa->running)
{
compat_mutex_lock(&swa->fifoLock);
avail = fifo_write_avail(swa->fifo);
if (avail == 0)
{
compat_mutex_unlock(&swa->fifoLock);
compat_mutex_lock(&swa->condLock);
if (swa->running)
compat_condvar_wait(&swa->cond, &swa->condLock);
compat_mutex_unlock(&swa->condLock);
}
else
{
size_t write_amt = MIN(size - written, avail);
fifo_write(swa->fifo, (const char*)buf + written, write_amt);
compat_mutex_unlock(&swa->fifoLock);
written += write_amt;
}
}
}
return written;
}
static bool switch_thread_audio_alive(void *data)
{
switch_thread_audio_t *swa = (switch_thread_audio_t *)data;
if (!swa)
return false;
return !swa->is_paused;
}
static void switch_thread_audio_set_nonblock_state(void *data, bool state)
{
switch_thread_audio_t *swa = (switch_thread_audio_t *)data;
if (swa)
swa->nonblocking = state;
}
static bool switch_thread_audio_use_float(void *data)
{
(void)data;
return false;
}
static size_t switch_thread_audio_write_avail(void *data)
{
size_t val;
switch_thread_audio_t* swa = (switch_thread_audio_t*)data;
compat_mutex_lock(&swa->fifoLock);
val = fifo_write_avail(swa->fifo);
compat_mutex_unlock(&swa->fifoLock);
return val;
}
size_t switch_thread_audio_buffer_size(void *data)
{
switch_thread_audio_t *swa = (switch_thread_audio_t *)data;
if (!swa)
return 0;
return swa->fifoSize;
}
audio_driver_t audio_switch_thread = {
switch_thread_audio_init,
switch_thread_audio_write,
switch_thread_audio_stop,
switch_thread_audio_start,
switch_thread_audio_alive,
switch_thread_audio_set_nonblock_state,
switch_thread_audio_free,
switch_thread_audio_use_float,
"switch_thread",
NULL, /* device_list_new */
NULL, /* device_list_free */
switch_thread_audio_write_avail,
switch_thread_audio_buffer_size
};
/* vim: set ts=3 sw=3 */

View File

@ -70,7 +70,7 @@ typedef struct video4linux
char dev_name[255]; char dev_name[255];
} video4linux_t; } video4linux_t;
static int xioctl(int fd, int request, void *args) static int xioctl(int fd, unsigned long request, void *args)
{ {
int r; int r;
@ -91,7 +91,7 @@ static bool init_mmap(void *data)
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP; req.memory = V4L2_MEMORY_MMAP;
if (xioctl(v4l->fd, (uint8_t)VIDIOC_REQBUFS, &req) == -1) if (xioctl(v4l->fd, VIDIOC_REQBUFS, &req) == -1)
{ {
if (errno == EINVAL) if (errno == EINVAL)
RARCH_ERR("[V4L2]: %s does not support memory mapping.\n", v4l->dev_name); RARCH_ERR("[V4L2]: %s does not support memory mapping.\n", v4l->dev_name);
@ -122,7 +122,7 @@ static bool init_mmap(void *data)
buf.memory = V4L2_MEMORY_MMAP; buf.memory = V4L2_MEMORY_MMAP;
buf.index = v4l->n_buffers; buf.index = v4l->n_buffers;
if (xioctl(v4l->fd, (uint8_t)VIDIOC_QUERYBUF, &buf) == -1) if (xioctl(v4l->fd, VIDIOC_QUERYBUF, &buf) == -1)
{ {
RARCH_ERR("[V4L2]: Error - xioctl VIDIOC_QUERYBUF.\n"); RARCH_ERR("[V4L2]: Error - xioctl VIDIOC_QUERYBUF.\n");
return false; return false;
@ -152,7 +152,7 @@ static bool init_device(void *data)
struct v4l2_cropcap cropcap = {0}; struct v4l2_cropcap cropcap = {0};
video4linux_t *v4l = (video4linux_t*)data; video4linux_t *v4l = (video4linux_t*)data;
if (xioctl(v4l->fd, (uint8_t)VIDIOC_QUERYCAP, &cap) < 0) if (xioctl(v4l->fd, VIDIOC_QUERYCAP, &cap) < 0)
{ {
if (errno == EINVAL) if (errno == EINVAL)
RARCH_ERR("[V4L2]: %s is no V4L2 device.\n", v4l->dev_name); RARCH_ERR("[V4L2]: %s is no V4L2 device.\n", v4l->dev_name);
@ -176,7 +176,7 @@ static bool init_device(void *data)
cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (xioctl(v4l->fd, (uint8_t)VIDIOC_CROPCAP, &cropcap) == 0) if (xioctl(v4l->fd, VIDIOC_CROPCAP, &cropcap) == 0)
{ {
crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
crop.c = cropcap.defrect; crop.c = cropcap.defrect;
@ -190,7 +190,7 @@ static bool init_device(void *data)
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
fmt.fmt.pix.field = V4L2_FIELD_NONE; fmt.fmt.pix.field = V4L2_FIELD_NONE;
if (xioctl(v4l->fd, (uint8_t)VIDIOC_S_FMT, &fmt) < 0) if (xioctl(v4l->fd, VIDIOC_S_FMT, &fmt) < 0)
{ {
RARCH_ERR("[V4L2]: Error - VIDIOC_S_FMT\n"); RARCH_ERR("[V4L2]: Error - VIDIOC_S_FMT\n");
return false; return false;
@ -248,7 +248,7 @@ static bool v4l_start(void *data)
buf.memory = V4L2_MEMORY_MMAP; buf.memory = V4L2_MEMORY_MMAP;
buf.index = i; buf.index = i;
if (xioctl(v4l->fd, (uint8_t)VIDIOC_QBUF, &buf) == -1) if (xioctl(v4l->fd, VIDIOC_QBUF, &buf) == -1)
{ {
RARCH_ERR("[V4L2]: Error - VIDIOC_QBUF.\n"); RARCH_ERR("[V4L2]: Error - VIDIOC_QBUF.\n");
return false; return false;
@ -364,7 +364,7 @@ static bool preprocess_image(void *data)
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP; buf.memory = V4L2_MEMORY_MMAP;
if (xioctl(v4l->fd, (uint8_t)VIDIOC_DQBUF, &buf) == -1) if (xioctl(v4l->fd, VIDIOC_DQBUF, &buf) == -1)
{ {
switch (errno) switch (errno)
{ {
@ -384,7 +384,7 @@ static bool preprocess_image(void *data)
scaler_ctx_scale_direct(ctx, v4l->buffer_output, (const uint8_t*)v4l->buffers[buf.index].start); scaler_ctx_scale_direct(ctx, v4l->buffer_output, (const uint8_t*)v4l->buffers[buf.index].start);
if (xioctl(v4l->fd, (uint8_t)VIDIOC_QBUF, &buf) == -1) if (xioctl(v4l->fd, VIDIOC_QBUF, &buf) == -1)
RARCH_ERR("[V4L2]: VIDIOC_QBUF\n"); RARCH_ERR("[V4L2]: VIDIOC_QBUF\n");
return true; return true;

View File

@ -162,6 +162,7 @@ static cheevos_locals_t cheevos_locals =
bool cheevos_loaded = false; bool cheevos_loaded = false;
bool cheevos_hardcore_active = false; bool cheevos_hardcore_active = false;
bool cheevos_hardcore_paused = false; bool cheevos_hardcore_paused = false;
bool cheevos_state_loaded_flag = false;
int cheats_are_enabled = 0; int cheats_are_enabled = 0;
int cheats_were_enabled = 0; int cheats_were_enabled = 0;
@ -947,6 +948,9 @@ bool cheevos_toggle_hardcore_mode(void)
const char *msg = msg_hash_to_str( const char *msg = msg_hash_to_str(
MSG_CHEEVOS_HARDCORE_MODE_ENABLE); MSG_CHEEVOS_HARDCORE_MODE_ENABLE);
/* reset the state loaded flag in case it was set */
cheevos_state_loaded_flag = false;
/* send reset core cmd to avoid any user /* send reset core cmd to avoid any user
* savestate previusly loaded. */ * savestate previusly loaded. */
command_event(CMD_EVENT_RESET, NULL); command_event(CMD_EVENT_RESET, NULL);
@ -1504,15 +1508,10 @@ found:
memcpy((void*)&coro->header, coro->data, memcpy((void*)&coro->header, coro->data,
sizeof(coro->header)); sizeof(coro->header));
if ( coro->header.id[0] != 'N' if (coro->header.id[0] == 'N'
|| coro->header.id[1] != 'E' && coro->header.id[1] == 'E'
|| coro->header.id[2] != 'S' && coro->header.id[2] == 'S'
|| coro->header.id[3] != 0x1a) && coro->header.id[3] == 0x1a)
{
coro->gameid = 0;
CORO_RET();
}
{ {
size_t romsize = 256; size_t romsize = 256;
/* from FCEU core - compute size using the cart mapper */ /* from FCEU core - compute size using the cart mapper */
@ -1528,24 +1527,64 @@ found:
* we use FCEU_read. */ * we use FCEU_read. */
coro->round = mapper != 53 && mapper != 198 && mapper != 228; coro->round = mapper != 53 && mapper != 198 && mapper != 228;
coro->bytes = coro->round ? romsize : coro->header.rom_size; coro->bytes = coro->round ? romsize : coro->header.rom_size;
}
/* from FCEU core - check if Trainer included in ROM data */ coro->offset = sizeof(coro->header) + (coro->header.rom_type & 4
MD5_Init(&coro->md5);
coro->offset = sizeof(coro->header) + (coro->header.rom_type & 4
? sizeof(coro->header) : 0); ? sizeof(coro->header) : 0);
coro->count = 0x4000 * coro->bytes;
CORO_GOSUB(EVAL_MD5);
if (coro->count < 0x4000 * coro->bytes) /* from FCEU core - check if Trainer included in ROM data */
{ MD5_Init(&coro->md5);
coro->offset = 0xff; coro->count = 0x4000 * coro->bytes;
coro->count = 0x4000 * coro->bytes - coro->count; CORO_GOSUB(EVAL_MD5);
CORO_GOSUB(FILL_MD5);
if (coro->count < 0x4000 * coro->bytes)
{
coro->offset = 0xff;
coro->count = 0x4000 * coro->bytes - coro->count;
CORO_GOSUB(FILL_MD5);
}
MD5_Final(coro->hash, &coro->md5);
CORO_GOTO(GET_GAMEID);
} }
else
{
unsigned i;
size_t chunks = coro->len >> 14;
size_t chunk_size = 0x4000;
MD5_Final(coro->hash, &coro->md5); /* Fall back to headerless hashing
CORO_GOTO(GET_GAMEID); * PRG ROM size is unknown, so test by 16KB chunks */
coro->round = 0;
coro->offset = 0;
for (i = 0; i < chunks; i++)
{
MD5_Init(&coro->md5);
coro->bytes = i + 1;
coro->count = coro->bytes * chunk_size;
CORO_GOSUB(EVAL_MD5);
if (coro->count < 0x4000 * coro->bytes)
{
coro->offset = 0xff;
coro->count = 0x4000 * coro->bytes - coro->count;
CORO_GOSUB(FILL_MD5);
}
MD5_Final(coro->hash, &coro->md5);
CORO_GOSUB(GET_GAMEID);
if (coro->gameid > 0)
{
break;
}
}
CORO_RET();
}
/************************************************************************** /**************************************************************************
* Info Tries to identify a "generic" game * Info Tries to identify a "generic" game

View File

@ -67,8 +67,10 @@ int cheevos_get_console(void);
extern bool cheevos_loaded; extern bool cheevos_loaded;
extern bool cheevos_hardcore_active; extern bool cheevos_hardcore_active;
extern bool cheevos_hardcore_paused; extern bool cheevos_hardcore_paused;
extern bool cheevos_state_loaded_flag;
extern int cheats_are_enabled; extern int cheats_are_enabled;
extern int cheats_were_enabled; extern int cheats_were_enabled;
;
RETRO_END_DECLS RETRO_END_DECLS

View File

@ -98,6 +98,8 @@
#define DEFAULT_NETWORK_CMD_PORT 55355 #define DEFAULT_NETWORK_CMD_PORT 55355
#define STDIN_BUF_SIZE 4096 #define STDIN_BUF_SIZE 4096
extern bool discord_is_inited;
enum cmd_source_t enum cmd_source_t
{ {
CMD_NONE = 0, CMD_NONE = 0,
@ -159,7 +161,7 @@ static const struct cmd_map map[] = {
{ "STATE_SLOT_PLUS", RARCH_STATE_SLOT_PLUS }, { "STATE_SLOT_PLUS", RARCH_STATE_SLOT_PLUS },
{ "STATE_SLOT_MINUS", RARCH_STATE_SLOT_MINUS }, { "STATE_SLOT_MINUS", RARCH_STATE_SLOT_MINUS },
{ "REWIND", RARCH_REWIND }, { "REWIND", RARCH_REWIND },
{ "MOVIE_RECORD_TOGGLE", RARCH_MOVIE_RECORD_TOGGLE }, { "BSV_RECORD_TOGGLE", RARCH_BSV_RECORD_TOGGLE },
{ "PAUSE_TOGGLE", RARCH_PAUSE_TOGGLE }, { "PAUSE_TOGGLE", RARCH_PAUSE_TOGGLE },
{ "FRAMEADVANCE", RARCH_FRAMEADVANCE }, { "FRAMEADVANCE", RARCH_FRAMEADVANCE },
{ "RESET", RARCH_RESET }, { "RESET", RARCH_RESET },
@ -182,6 +184,8 @@ static const struct cmd_map map[] = {
{ "UI_COMPANION_TOGGLE", RARCH_UI_COMPANION_TOGGLE }, { "UI_COMPANION_TOGGLE", RARCH_UI_COMPANION_TOGGLE },
{ "GAME_FOCUS_TOGGLE", RARCH_GAME_FOCUS_TOGGLE }, { "GAME_FOCUS_TOGGLE", RARCH_GAME_FOCUS_TOGGLE },
{ "MENU_TOGGLE", RARCH_MENU_TOGGLE }, { "MENU_TOGGLE", RARCH_MENU_TOGGLE },
{ "RECORDING_TOGGLE", RARCH_RECORDING_TOGGLE },
{ "STREAMING_TOGGLE", RARCH_STREAMING_TOGGLE },
{ "MENU_UP", RETRO_DEVICE_ID_JOYPAD_UP }, { "MENU_UP", RETRO_DEVICE_ID_JOYPAD_UP },
{ "MENU_DOWN", RETRO_DEVICE_ID_JOYPAD_DOWN }, { "MENU_DOWN", RETRO_DEVICE_ID_JOYPAD_DOWN },
{ "MENU_LEFT", RETRO_DEVICE_ID_JOYPAD_LEFT }, { "MENU_LEFT", RETRO_DEVICE_ID_JOYPAD_LEFT },
@ -1656,6 +1660,9 @@ static bool command_event_main_state(unsigned cmd)
case CMD_EVENT_LOAD_STATE: case CMD_EVENT_LOAD_STATE:
if (content_load_state(state_path, false, false)) if (content_load_state(state_path, false, false))
{ {
#ifdef HAVE_CHEEVOS
cheevos_state_loaded_flag = true;
#endif
ret = true; ret = true;
#ifdef HAVE_NETWORKING #ifdef HAVE_NETWORKING
netplay_driver_ctl(RARCH_NETPLAY_CTL_LOAD_SAVESTATE, NULL); netplay_driver_ctl(RARCH_NETPLAY_CTL_LOAD_SAVESTATE, NULL);
@ -1708,14 +1715,12 @@ static bool command_event_resize_windowed_scale(void)
} }
void command_playlist_push_write( void command_playlist_push_write(
void *data, playlist_t *playlist,
const char *path, const char *path,
const char *label, const char *label,
const char *core_path, const char *core_path,
const char *core_name) const char *core_name)
{ {
playlist_t *playlist = (playlist_t*)data;
if (!playlist) if (!playlist)
return; return;
@ -1732,7 +1737,7 @@ void command_playlist_push_write(
} }
void command_playlist_update_write( void command_playlist_update_write(
void *data, playlist_t *plist,
size_t idx, size_t idx,
const char *path, const char *path,
const char *label, const char *label,
@ -1741,7 +1746,6 @@ void command_playlist_update_write(
const char *crc32, const char *crc32,
const char *db_name) const char *db_name)
{ {
playlist_t *plist = (playlist_t*)data;
playlist_t *playlist = plist ? plist : playlist_get_cached(); playlist_t *playlist = plist ? plist : playlist_get_cached();
if (!playlist) if (!playlist)
@ -1777,11 +1781,6 @@ bool command_event(enum event_command cmd, void *data)
switch (cmd) switch (cmd)
{ {
case CMD_EVENT_MENU_REFRESH:
#ifdef HAVE_MENU
menu_driver_ctl(RARCH_MENU_CTL_REFRESH, NULL);
#endif
break;
case CMD_EVENT_SET_PER_GAME_RESOLUTION: case CMD_EVENT_SET_PER_GAME_RESOLUTION:
#if defined(GEKKO) #if defined(GEKKO)
{ {
@ -1842,6 +1841,7 @@ bool command_event(enum event_command cmd, void *data)
case CMD_EVENT_LOAD_CORE: case CMD_EVENT_LOAD_CORE:
{ {
bool success = command_event(CMD_EVENT_LOAD_CORE_PERSIST, NULL); bool success = command_event(CMD_EVENT_LOAD_CORE_PERSIST, NULL);
(void)success;
#ifndef HAVE_DYNAMIC #ifndef HAVE_DYNAMIC
command_event(CMD_EVENT_QUIT, NULL); command_event(CMD_EVENT_QUIT, NULL);
@ -1888,6 +1888,10 @@ bool command_event(enum event_command cmd, void *data)
command_event_init_controllers(); command_event_init_controllers();
break; break;
case CMD_EVENT_RESET: case CMD_EVENT_RESET:
#ifdef HAVE_CHEEVOS
cheevos_state_loaded_flag = false;
cheevos_hardcore_paused = false;
#endif
RARCH_LOG("%s.\n", msg_hash_to_str(MSG_RESET)); RARCH_LOG("%s.\n", msg_hash_to_str(MSG_RESET));
runloop_msg_queue_push(msg_hash_to_str(MSG_RESET), 1, 120, true); runloop_msg_queue_push(msg_hash_to_str(MSG_RESET), 1, 120, true);
@ -1965,6 +1969,15 @@ bool command_event(enum event_command cmd, void *data)
core_unload_game(); core_unload_game();
if (!rarch_ctl(RARCH_CTL_IS_DUMMY_CORE, NULL)) if (!rarch_ctl(RARCH_CTL_IS_DUMMY_CORE, NULL))
core_unload(); core_unload();
#ifdef HAVE_DISCORD
if (discord_is_inited)
{
discord_userdata_t userdata;
userdata.status = DISCORD_PRESENCE_MENU;
command_event(CMD_EVENT_DISCORD_UPDATE, &userdata);
}
#endif
} }
break; break;
case CMD_EVENT_QUIT: case CMD_EVENT_QUIT:
@ -2145,13 +2158,22 @@ TODO: Add a setting for these tweaks */
video_driver_gpu_record_deinit(); video_driver_gpu_record_deinit();
break; break;
case CMD_EVENT_RECORD_DEINIT: case CMD_EVENT_RECORD_DEINIT:
if (!recording_deinit()) {
return false; recording_set_state(false);
streaming_set_state(false);
if (!recording_deinit())
return false;
}
break; break;
case CMD_EVENT_RECORD_INIT: case CMD_EVENT_RECORD_INIT:
command_event(CMD_EVENT_HISTORY_DEINIT, NULL); {
if (!recording_init()) recording_set_state(true);
return false; if (!recording_init())
{
command_event(CMD_EVENT_RECORD_DEINIT, NULL);
return false;
}
}
break; break;
case CMD_EVENT_HISTORY_DEINIT: case CMD_EVENT_HISTORY_DEINIT:
if (g_defaults.content_history) if (g_defaults.content_history)

View File

@ -27,6 +27,8 @@
#include "config.h" #include "config.h"
#endif #endif
#include "playlist.h"
RETRO_BEGIN_DECLS RETRO_BEGIN_DECLS
typedef struct command command_t; typedef struct command command_t;
@ -155,7 +157,6 @@ enum event_command
CMD_EVENT_MENU_PAUSE_LIBRETRO, CMD_EVENT_MENU_PAUSE_LIBRETRO,
/* Toggles menu on/off. */ /* Toggles menu on/off. */
CMD_EVENT_MENU_TOGGLE, CMD_EVENT_MENU_TOGGLE,
CMD_EVENT_MENU_REFRESH,
/* Applies shader changes. */ /* Applies shader changes. */
CMD_EVENT_SHADERS_APPLY_CHANGES, CMD_EVENT_SHADERS_APPLY_CHANGES,
/* A new shader preset has been loaded */ /* A new shader preset has been loaded */
@ -269,14 +270,14 @@ bool command_free(command_t *handle);
bool command_event(enum event_command action, void *data); bool command_event(enum event_command action, void *data);
void command_playlist_push_write( void command_playlist_push_write(
void *data, playlist_t *playlist,
const char *path, const char *path,
const char *label, const char *label,
const char *core_path, const char *core_path,
const char *core_name); const char *core_name);
void command_playlist_update_write( void command_playlist_update_write(
void *data, playlist_t *playlist,
size_t idx, size_t idx,
const char *path, const char *path,
const char *label, const char *label,

View File

@ -20,6 +20,7 @@
#include <boolean.h> #include <boolean.h>
#include <audio/audio_resampler.h> #include <audio/audio_resampler.h>
#include "configuration.h"
#include "gfx/video_defines.h" #include "gfx/video_defines.h"
#include "input/input_driver.h" #include "input/input_driver.h"
@ -67,15 +68,15 @@ static bool bundle_assets_extract_enable = false;
static bool materialui_icons_enable = true; static bool materialui_icons_enable = true;
#endif #endif
static const bool crt_switch_resolution = false; static const unsigned crt_switch_resolution = CRT_SWITCH_NONE;
static const int crt_switch_resolution_super = 2560; static const int crt_switch_resolution_super = 2560;
static const int crt_switch_center_adjust = 0;
static const bool def_history_list_enable = true;
static const bool def_playlist_entry_remove = true;
static const bool def_playlist_entry_rename = true;
static const bool def_history_list_enable = true; static const unsigned int def_user_language = 0;
static const bool def_playlist_entry_remove = true;
static const bool def_playlist_entry_rename = true;
static const unsigned int def_user_language = 0;
#if (defined(_WIN32) && !defined(_XBOX)) || (defined(__linux) && !defined(ANDROID) && !defined(HAVE_LAKKA)) || (defined(__MACH__) && !defined(IOS)) || defined(EMSCRIPTEN) #if (defined(_WIN32) && !defined(_XBOX)) || (defined(__linux) && !defined(ANDROID) && !defined(HAVE_LAKKA)) || (defined(__MACH__) && !defined(IOS)) || defined(EMSCRIPTEN)
static const bool def_mouse_enable = true; static const bool def_mouse_enable = true;
@ -147,6 +148,8 @@ static const bool vsync = true;
static const unsigned max_swapchain_images = 3; static const unsigned max_swapchain_images = 3;
static const bool adaptive_vsync = false;
/* Attempts to hard-synchronize CPU and GPU. /* Attempts to hard-synchronize CPU and GPU.
* Can reduce latency at cost of performance. */ * Can reduce latency at cost of performance. */
static const bool hard_sync = false; static const bool hard_sync = false;
@ -229,7 +232,7 @@ static const float aspect_ratio = DEFAULT_ASPECT_RATIO;
/* 1:1 PAR */ /* 1:1 PAR */
static const bool aspect_ratio_auto = false; static const bool aspect_ratio_auto = false;
#if defined(__CELLOS_LV2) || defined(_XBOX360) #if defined(__CELLOS_LV2) || defined(_XBOX360) || defined(ANDROID_AARCH64)
static unsigned aspect_ratio_idx = ASPECT_RATIO_16_9; static unsigned aspect_ratio_idx = ASPECT_RATIO_16_9;
#elif defined(PSP) #elif defined(PSP)
static unsigned aspect_ratio_idx = ASPECT_RATIO_CORE; static unsigned aspect_ratio_idx = ASPECT_RATIO_CORE;
@ -259,15 +262,17 @@ static const float default_input_overlay_opacity = 0.7f;
static bool default_block_config_read = true; static bool default_block_config_read = true;
static bool quick_menu_show_take_screenshot = true; static bool quick_menu_show_take_screenshot = true;
static bool quick_menu_show_save_load_state = true; static bool quick_menu_show_save_load_state = true;
static bool quick_menu_show_undo_save_load_state = true; static bool quick_menu_show_undo_save_load_state = true;
static bool quick_menu_show_add_to_favorites = true; static bool quick_menu_show_add_to_favorites = true;
static bool quick_menu_show_options = true; static bool quick_menu_show_options = true;
static bool quick_menu_show_controls = true; static bool quick_menu_show_controls = true;
static bool quick_menu_show_cheats = true; static bool quick_menu_show_cheats = true;
static bool quick_menu_show_shaders = true; static bool quick_menu_show_shaders = true;
static bool quick_menu_show_information = true; static bool quick_menu_show_information = true;
static bool quick_menu_show_recording = true;
static bool quick_menu_show_streaming = true;
static bool quick_menu_show_save_core_overrides = true; static bool quick_menu_show_save_core_overrides = true;
static bool quick_menu_show_save_game_overrides = true; static bool quick_menu_show_save_game_overrides = true;
@ -447,9 +452,11 @@ static const bool font_enable = true;
* If your monitor does not run at 60Hz, or something close to it, * If your monitor does not run at 60Hz, or something close to it,
* disable VSync, and leave this at its default. */ * disable VSync, and leave this at its default. */
#ifdef _3DS #ifdef _3DS
static const float refresh_rate = (32730.0 * 8192.0) / 4481134.0 ; static const float refresh_rate = (32730.0 * 8192.0) / 4481134.0 ;
static const float crt_refresh_rate = (32730.0 * 8192.0) / 4481134.0 ;
#else #else
static const float refresh_rate = 60/1.001; static const float refresh_rate = 60/1.001;
static const float crt_refresh_rate = 60/1.001;
#endif #endif
/* Allow games to set rotation. If false, rotation requests are /* Allow games to set rotation. If false, rotation requests are
@ -654,6 +661,10 @@ static const unsigned libretro_log_level = 1;
#define RARCH_DEFAULT_PORT 55435 #define RARCH_DEFAULT_PORT 55435
#endif #endif
#ifndef RARCH_STREAM_DEFAULT_PORT
#define RARCH_STREAM_DEFAULT_PORT 56400
#endif
/* KEYBINDS, JOYPAD */ /* KEYBINDS, JOYPAD */
/* Axis threshold (between 0.0 and 1.0) /* Axis threshold (between 0.0 and 1.0)

View File

@ -75,7 +75,7 @@ static const struct retro_keybind retro_keybinds_1[] = {
{ true, RARCH_STATE_SLOT_PLUS, MENU_ENUM_LABEL_VALUE_INPUT_META_STATE_SLOT_PLUS, RETROK_UNKNOWN, NO_BTN, NO_BTN, 0, AXIS_NONE }, { true, RARCH_STATE_SLOT_PLUS, MENU_ENUM_LABEL_VALUE_INPUT_META_STATE_SLOT_PLUS, RETROK_UNKNOWN, NO_BTN, NO_BTN, 0, AXIS_NONE },
{ true, RARCH_STATE_SLOT_MINUS, MENU_ENUM_LABEL_VALUE_INPUT_META_STATE_SLOT_MINUS, RETROK_UNKNOWN, NO_BTN, NO_BTN, 0, AXIS_NONE }, { true, RARCH_STATE_SLOT_MINUS, MENU_ENUM_LABEL_VALUE_INPUT_META_STATE_SLOT_MINUS, RETROK_UNKNOWN, NO_BTN, NO_BTN, 0, AXIS_NONE },
{ true, RARCH_REWIND, MENU_ENUM_LABEL_VALUE_INPUT_META_REWIND, RETROK_UNKNOWN, NO_BTN, NO_BTN, 0, AXIS_NONE }, { true, RARCH_REWIND, MENU_ENUM_LABEL_VALUE_INPUT_META_REWIND, RETROK_UNKNOWN, NO_BTN, NO_BTN, 0, AXIS_NONE },
{ true, RARCH_MOVIE_RECORD_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_MOVIE_RECORD_TOGGLE, RETROK_UNKNOWN, NO_BTN, NO_BTN, 0, AXIS_NONE }, { true, RARCH_BSV_RECORD_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_BSV_RECORD_TOGGLE, RETROK_UNKNOWN, NO_BTN, NO_BTN, 0, AXIS_NONE },
{ true, RARCH_PAUSE_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_PAUSE_TOGGLE, RETROK_UNKNOWN, NO_BTN, NO_BTN, 0, AXIS_NONE }, { true, RARCH_PAUSE_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_PAUSE_TOGGLE, RETROK_UNKNOWN, NO_BTN, NO_BTN, 0, AXIS_NONE },
{ true, RARCH_FRAMEADVANCE, MENU_ENUM_LABEL_VALUE_INPUT_META_FRAMEADVANCE, RETROK_UNKNOWN, NO_BTN, NO_BTN, 0, AXIS_NONE }, { true, RARCH_FRAMEADVANCE, MENU_ENUM_LABEL_VALUE_INPUT_META_FRAMEADVANCE, RETROK_UNKNOWN, NO_BTN, NO_BTN, 0, AXIS_NONE },
{ true, RARCH_RESET, MENU_ENUM_LABEL_VALUE_INPUT_META_RESET, RETROK_UNKNOWN, NO_BTN, NO_BTN, 0, AXIS_NONE }, { true, RARCH_RESET, MENU_ENUM_LABEL_VALUE_INPUT_META_RESET, RETROK_UNKNOWN, NO_BTN, NO_BTN, 0, AXIS_NONE },
@ -99,6 +99,8 @@ static const struct retro_keybind retro_keybinds_1[] = {
{ true, RARCH_GAME_FOCUS_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_GAME_FOCUS_TOGGLE, RETROK_UNKNOWN, NO_BTN, NO_BTN, 0, AXIS_NONE }, { true, RARCH_GAME_FOCUS_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_GAME_FOCUS_TOGGLE, RETROK_UNKNOWN, NO_BTN, NO_BTN, 0, AXIS_NONE },
{ true, RARCH_UI_COMPANION_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_UI_COMPANION_TOGGLE, RETROK_UNKNOWN, NO_BTN, NO_BTN, 0, AXIS_NONE }, { true, RARCH_UI_COMPANION_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_UI_COMPANION_TOGGLE, RETROK_UNKNOWN, NO_BTN, NO_BTN, 0, AXIS_NONE },
{ true, RARCH_MENU_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_MENU_TOGGLE, RETROK_SPACE, NO_BTN, NO_BTN, 0, AXIS_NONE }, { true, RARCH_MENU_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_MENU_TOGGLE, RETROK_SPACE, NO_BTN, NO_BTN, 0, AXIS_NONE },
{ true, RARCH_RECORDING_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_RECORDING_TOGGLE, RETROK_UNKNOWN, NO_BTN, NO_BTN, 0, AXIS_NONE },
{ true, RARCH_STREAMING_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_STREAMING_TOGGLE, RETROK_UNKNOWN, NO_BTN, NO_BTN, 0, AXIS_NONE },
#else #else
{ true, RETRO_DEVICE_ID_JOYPAD_B, MENU_ENUM_LABEL_VALUE_INPUT_JOYPAD_B, RETROK_z, NO_BTN, NO_BTN, 0, AXIS_NONE }, { true, RETRO_DEVICE_ID_JOYPAD_B, MENU_ENUM_LABEL_VALUE_INPUT_JOYPAD_B, RETROK_z, NO_BTN, NO_BTN, 0, AXIS_NONE },
{ true, RETRO_DEVICE_ID_JOYPAD_Y, MENU_ENUM_LABEL_VALUE_INPUT_JOYPAD_Y, RETROK_a, NO_BTN, NO_BTN, 0, AXIS_NONE }, { true, RETRO_DEVICE_ID_JOYPAD_Y, MENU_ENUM_LABEL_VALUE_INPUT_JOYPAD_Y, RETROK_a, NO_BTN, NO_BTN, 0, AXIS_NONE },
@ -151,7 +153,7 @@ static const struct retro_keybind retro_keybinds_1[] = {
{ true, RARCH_STATE_SLOT_PLUS, MENU_ENUM_LABEL_VALUE_INPUT_META_STATE_SLOT_PLUS, RETROK_F7, NO_BTN, NO_BTN, 0, AXIS_NONE }, { true, RARCH_STATE_SLOT_PLUS, MENU_ENUM_LABEL_VALUE_INPUT_META_STATE_SLOT_PLUS, RETROK_F7, NO_BTN, NO_BTN, 0, AXIS_NONE },
{ true, RARCH_STATE_SLOT_MINUS, MENU_ENUM_LABEL_VALUE_INPUT_META_STATE_SLOT_MINUS, RETROK_F6, NO_BTN, NO_BTN, 0, AXIS_NONE }, { true, RARCH_STATE_SLOT_MINUS, MENU_ENUM_LABEL_VALUE_INPUT_META_STATE_SLOT_MINUS, RETROK_F6, NO_BTN, NO_BTN, 0, AXIS_NONE },
{ true, RARCH_REWIND, MENU_ENUM_LABEL_VALUE_INPUT_META_REWIND, RETROK_r, NO_BTN, NO_BTN, 0, AXIS_NONE }, { true, RARCH_REWIND, MENU_ENUM_LABEL_VALUE_INPUT_META_REWIND, RETROK_r, NO_BTN, NO_BTN, 0, AXIS_NONE },
{ true, RARCH_MOVIE_RECORD_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_MOVIE_RECORD_TOGGLE, RETROK_o, NO_BTN, NO_BTN, 0, AXIS_NONE }, { true, RARCH_BSV_RECORD_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_BSV_RECORD_TOGGLE, RETROK_o, NO_BTN, NO_BTN, 0, AXIS_NONE },
{ true, RARCH_PAUSE_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_PAUSE_TOGGLE, RETROK_p, NO_BTN, NO_BTN, 0, AXIS_NONE }, { true, RARCH_PAUSE_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_PAUSE_TOGGLE, RETROK_p, NO_BTN, NO_BTN, 0, AXIS_NONE },
{ true, RARCH_FRAMEADVANCE, MENU_ENUM_LABEL_VALUE_INPUT_META_FRAMEADVANCE, RETROK_k, NO_BTN, NO_BTN, 0, AXIS_NONE }, { true, RARCH_FRAMEADVANCE, MENU_ENUM_LABEL_VALUE_INPUT_META_FRAMEADVANCE, RETROK_k, NO_BTN, NO_BTN, 0, AXIS_NONE },
{ true, RARCH_RESET, MENU_ENUM_LABEL_VALUE_INPUT_META_RESET, RETROK_h, NO_BTN, NO_BTN, 0, AXIS_NONE }, { true, RARCH_RESET, MENU_ENUM_LABEL_VALUE_INPUT_META_RESET, RETROK_h, NO_BTN, NO_BTN, 0, AXIS_NONE },
@ -175,6 +177,8 @@ static const struct retro_keybind retro_keybinds_1[] = {
{ true, RARCH_GAME_FOCUS_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_GAME_FOCUS_TOGGLE, RETROK_SCROLLOCK, NO_BTN, NO_BTN, 0, AXIS_NONE }, { true, RARCH_GAME_FOCUS_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_GAME_FOCUS_TOGGLE, RETROK_SCROLLOCK, NO_BTN, NO_BTN, 0, AXIS_NONE },
{ true, RARCH_UI_COMPANION_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_UI_COMPANION_TOGGLE, RETROK_F5, NO_BTN, NO_BTN, 0, AXIS_NONE }, { true, RARCH_UI_COMPANION_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_UI_COMPANION_TOGGLE, RETROK_F5, NO_BTN, NO_BTN, 0, AXIS_NONE },
{ true, RARCH_MENU_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_MENU_TOGGLE, RETROK_F1, NO_BTN, NO_BTN, 0, AXIS_NONE }, { true, RARCH_MENU_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_MENU_TOGGLE, RETROK_F1, NO_BTN, NO_BTN, 0, AXIS_NONE },
{ true, RARCH_RECORDING_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_RECORDING_TOGGLE, RETROK_UNKNOWN, NO_BTN, NO_BTN, 0, AXIS_NONE },
{ true, RARCH_STREAMING_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_STREAMING_TOGGLE, RETROK_UNKNOWN, NO_BTN, NO_BTN, 0, AXIS_NONE },
#endif #endif
}; };

View File

@ -54,6 +54,8 @@
#include "../list_special.h" #include "../list_special.h"
#include "record/record_driver.h"
static const char* invalid_filename_chars[] = { static const char* invalid_filename_chars[] = {
/* https://support.microsoft.com/en-us/help/905231/information-about-the-characters-that-you-cannot-use-in-site-names--fo */ /* https://support.microsoft.com/en-us/help/905231/information-about-the-characters-that-you-cannot-use-in-site-names--fo */
"~", "#", "%", "&", "*", "{", "}", "\\", ":", "[", "]", "?", "/", "|", "\'", "\"", "~", "#", "%", "&", "*", "{", "}", "\\", ":", "[", "]", "?", "/", "|", "\'", "\"",
@ -1103,6 +1105,8 @@ static struct config_array_setting *populate_settings_array(settings_t *settings
SETTING_ARRAY("midi_driver", settings->arrays.midi_driver, false, NULL, true); SETTING_ARRAY("midi_driver", settings->arrays.midi_driver, false, NULL, true);
SETTING_ARRAY("midi_input", settings->arrays.midi_input, true, midi_input, true); SETTING_ARRAY("midi_input", settings->arrays.midi_input, true, midi_input, true);
SETTING_ARRAY("midi_output", settings->arrays.midi_output, true, midi_output, true); SETTING_ARRAY("midi_output", settings->arrays.midi_output, true, midi_output, true);
SETTING_ARRAY("youtube_stream_key", settings->arrays.youtube_stream_key, true, NULL, true);
SETTING_ARRAY("twitch_stream_key", settings->arrays.twitch_stream_key, true, NULL, true);
*size = count; *size = count;
return tmp; return tmp;
@ -1160,6 +1164,12 @@ static struct config_path_setting *populate_settings_path(settings_t *settings,
SETTING_PATH("input_overlay", SETTING_PATH("input_overlay",
settings->paths.path_overlay, false, NULL, true); settings->paths.path_overlay, false, NULL, true);
#endif #endif
SETTING_PATH("video_record_config",
settings->paths.path_record_config, false, NULL, true);
SETTING_PATH("video_stream_config",
settings->paths.path_stream_config, false, NULL, true);
SETTING_PATH("video_stream_url",
settings->paths.path_stream_url, false, NULL, true);
SETTING_PATH("video_font_path", SETTING_PATH("video_font_path",
settings->paths.path_font, false, NULL, true); settings->paths.path_font, false, NULL, true);
SETTING_PATH("cursor_directory", SETTING_PATH("cursor_directory",
@ -1234,6 +1244,7 @@ static struct config_bool_setting *populate_settings_bool(settings_t *settings,
struct config_bool_setting *tmp = (struct config_bool_setting*)malloc((*size + 1) * sizeof(struct config_bool_setting)); struct config_bool_setting *tmp = (struct config_bool_setting*)malloc((*size + 1) * sizeof(struct config_bool_setting));
unsigned count = 0; unsigned count = 0;
SETTING_BOOL("crt_switch_resolution_use_custom_refresh_rate", &settings->bools.crt_switch_custom_refresh_enable, true, false, false);
SETTING_BOOL("automatically_add_content_to_playlist", &settings->bools.automatically_add_content_to_playlist, true, automatically_add_content_to_playlist, false); SETTING_BOOL("automatically_add_content_to_playlist", &settings->bools.automatically_add_content_to_playlist, true, automatically_add_content_to_playlist, false);
SETTING_BOOL("ui_companion_start_on_boot", &settings->bools.ui_companion_start_on_boot, true, ui_companion_start_on_boot, false); SETTING_BOOL("ui_companion_start_on_boot", &settings->bools.ui_companion_start_on_boot, true, ui_companion_start_on_boot, false);
SETTING_BOOL("ui_companion_enable", &settings->bools.ui_companion_enable, true, ui_companion_enable, false); SETTING_BOOL("ui_companion_enable", &settings->bools.ui_companion_enable, true, ui_companion_enable, false);
@ -1306,9 +1317,9 @@ static struct config_bool_setting *populate_settings_bool(settings_t *settings,
SETTING_BOOL("video_fullscreen", &settings->bools.video_fullscreen, true, fullscreen, false); SETTING_BOOL("video_fullscreen", &settings->bools.video_fullscreen, true, fullscreen, false);
SETTING_BOOL("bundle_assets_extract_enable", &settings->bools.bundle_assets_extract_enable, true, bundle_assets_extract_enable, false); SETTING_BOOL("bundle_assets_extract_enable", &settings->bools.bundle_assets_extract_enable, true, bundle_assets_extract_enable, false);
SETTING_BOOL("video_vsync", &settings->bools.video_vsync, true, vsync, false); SETTING_BOOL("video_vsync", &settings->bools.video_vsync, true, vsync, false);
SETTING_BOOL("video_adaptive_vsync", &settings->bools.video_adaptive_vsync, true, adaptive_vsync, false);
SETTING_BOOL("video_hard_sync", &settings->bools.video_hard_sync, true, hard_sync, false); SETTING_BOOL("video_hard_sync", &settings->bools.video_hard_sync, true, hard_sync, false);
SETTING_BOOL("video_black_frame_insertion", &settings->bools.video_black_frame_insertion, true, black_frame_insertion, false); SETTING_BOOL("video_black_frame_insertion", &settings->bools.video_black_frame_insertion, true, black_frame_insertion, false);
SETTING_BOOL("crt_switch_resolution", &settings->bools.crt_switch_resolution, true, crt_switch_resolution, false);
SETTING_BOOL("video_disable_composition", &settings->bools.video_disable_composition, true, disable_composition, false); SETTING_BOOL("video_disable_composition", &settings->bools.video_disable_composition, true, disable_composition, false);
SETTING_BOOL("pause_nonactive", &settings->bools.pause_nonactive, true, pause_nonactive, false); SETTING_BOOL("pause_nonactive", &settings->bools.pause_nonactive, true, pause_nonactive, false);
SETTING_BOOL("video_gpu_screenshot", &settings->bools.video_gpu_screenshot, true, gpu_screenshot, false); SETTING_BOOL("video_gpu_screenshot", &settings->bools.video_gpu_screenshot, true, gpu_screenshot, false);
@ -1350,6 +1361,9 @@ static struct config_bool_setting *populate_settings_bool(settings_t *settings,
SETTING_BOOL("menu_battery_level_enable", &settings->bools.menu_battery_level_enable, true, true, false); SETTING_BOOL("menu_battery_level_enable", &settings->bools.menu_battery_level_enable, true, true, false);
SETTING_BOOL("menu_core_enable", &settings->bools.menu_core_enable, true, true, false); SETTING_BOOL("menu_core_enable", &settings->bools.menu_core_enable, true, true, false);
SETTING_BOOL("menu_dynamic_wallpaper_enable", &settings->bools.menu_dynamic_wallpaper_enable, true, false, false); SETTING_BOOL("menu_dynamic_wallpaper_enable", &settings->bools.menu_dynamic_wallpaper_enable, true, false, false);
SETTING_BOOL("quick_menu_show_recording", &settings->bools.quick_menu_show_recording, true, quick_menu_show_recording, false);
SETTING_BOOL("quick_menu_show_streaming", &settings->bools.quick_menu_show_streaming, true, quick_menu_show_streaming, false);
SETTING_BOOL("quick_menu_show_save_load_state", &settings->bools.quick_menu_show_save_load_state, true, quick_menu_show_save_load_state, false);
SETTING_BOOL("quick_menu_show_take_screenshot", &settings->bools.quick_menu_show_take_screenshot, true, quick_menu_show_take_screenshot, false); SETTING_BOOL("quick_menu_show_take_screenshot", &settings->bools.quick_menu_show_take_screenshot, true, quick_menu_show_take_screenshot, false);
SETTING_BOOL("quick_menu_show_save_load_state", &settings->bools.quick_menu_show_save_load_state, true, quick_menu_show_save_load_state, false); SETTING_BOOL("quick_menu_show_save_load_state", &settings->bools.quick_menu_show_save_load_state, true, quick_menu_show_save_load_state, false);
SETTING_BOOL("quick_menu_show_undo_save_load_state", &settings->bools.quick_menu_show_undo_save_load_state, true, quick_menu_show_undo_save_load_state, false); SETTING_BOOL("quick_menu_show_undo_save_load_state", &settings->bools.quick_menu_show_undo_save_load_state, true, quick_menu_show_undo_save_load_state, false);
@ -1484,6 +1498,7 @@ static struct config_float_setting *populate_settings_float(settings_t *settings
SETTING_FLOAT("video_aspect_ratio", &settings->floats.video_aspect_ratio, true, aspect_ratio, false); SETTING_FLOAT("video_aspect_ratio", &settings->floats.video_aspect_ratio, true, aspect_ratio, false);
SETTING_FLOAT("video_scale", &settings->floats.video_scale, false, 0.0f, false); SETTING_FLOAT("video_scale", &settings->floats.video_scale, false, 0.0f, false);
SETTING_FLOAT("crt_video_refresh_rate", &settings->floats.crt_video_refresh_rate, true, crt_refresh_rate, false);
SETTING_FLOAT("video_refresh_rate", &settings->floats.video_refresh_rate, true, refresh_rate, false); SETTING_FLOAT("video_refresh_rate", &settings->floats.video_refresh_rate, true, refresh_rate, false);
SETTING_FLOAT("audio_rate_control_delta", audio_get_float_ptr(AUDIO_ACTION_RATE_CONTROL_DELTA), true, rate_control_delta, false); SETTING_FLOAT("audio_rate_control_delta", audio_get_float_ptr(AUDIO_ACTION_RATE_CONTROL_DELTA), true, rate_control_delta, false);
SETTING_FLOAT("audio_max_timing_skew", &settings->floats.audio_max_timing_skew, true, max_timing_skew, false); SETTING_FLOAT("audio_max_timing_skew", &settings->floats.audio_max_timing_skew, true, max_timing_skew, false);
@ -1517,6 +1532,10 @@ static struct config_uint_setting *populate_settings_uint(settings_t *settings,
unsigned count = 0; unsigned count = 0;
struct config_uint_setting *tmp = (struct config_uint_setting*)malloc((*size + 1) * sizeof(struct config_uint_setting)); struct config_uint_setting *tmp = (struct config_uint_setting*)malloc((*size + 1) * sizeof(struct config_uint_setting));
#ifdef HAVE_NETWORKING
SETTING_UINT("streaming_mode", &settings->uints.streaming_mode, true, STREAMING_MODE_TWITCH, false);
#endif
SETTING_UINT("crt_switch_resolution", &settings->uints.crt_switch_resolution, true, crt_switch_resolution, false);
SETTING_UINT("input_bind_timeout", &settings->uints.input_bind_timeout, true, input_bind_timeout, false); SETTING_UINT("input_bind_timeout", &settings->uints.input_bind_timeout, true, input_bind_timeout, false);
SETTING_UINT("input_bind_hold", &settings->uints.input_bind_hold, true, input_bind_hold, false); SETTING_UINT("input_bind_hold", &settings->uints.input_bind_hold, true, input_bind_hold, false);
SETTING_UINT("input_turbo_period", &settings->uints.input_turbo_period, true, turbo_period, false); SETTING_UINT("input_turbo_period", &settings->uints.input_turbo_period, true, turbo_period, false);
@ -1599,6 +1618,12 @@ static struct config_uint_setting *populate_settings_uint(settings_t *settings,
SETTING_UINT("midi_volume", &settings->uints.midi_volume, true, midi_volume, false); SETTING_UINT("midi_volume", &settings->uints.midi_volume, true, midi_volume, false);
SETTING_UINT("video_stream_port", &settings->uints.video_stream_port, true, RARCH_STREAM_DEFAULT_PORT, false);
SETTING_UINT("video_record_quality", &settings->uints.video_record_quality, true, RECORD_CONFIG_TYPE_RECORDING_LOSSLESS_QUALITY, false);
SETTING_UINT("video_stream_quality", &settings->uints.video_stream_quality, true, RECORD_CONFIG_TYPE_STREAMING_LOW_QUALITY, false);
SETTING_UINT("video_record_scale_factor", &settings->uints.video_record_scale_factor, true, 1, false);
SETTING_UINT("video_stream_scale_factor", &settings->uints.video_stream_scale_factor, true, 1, false);
*size = count; *size = count;
return tmp; return tmp;
@ -1629,6 +1654,7 @@ static struct config_int_setting *populate_settings_int(settings_t *settings, in
#ifdef HAVE_WASAPI #ifdef HAVE_WASAPI
SETTING_INT("audio_wasapi_sh_buffer_length", &settings->ints.audio_wasapi_sh_buffer_length, true, wasapi_sh_buffer_length, false); SETTING_INT("audio_wasapi_sh_buffer_length", &settings->ints.audio_wasapi_sh_buffer_length, true, wasapi_sh_buffer_length, false);
#endif #endif
SETTING_INT("crt_switch_center_adjust", &settings->ints.crt_switch_center_adjust, false, 0 /* TODO */, false);
*size = count; *size = count;
@ -1920,6 +1946,9 @@ static void config_set_defaults(void)
*settings->paths.path_menu_wallpaper = '\0'; *settings->paths.path_menu_wallpaper = '\0';
*settings->paths.path_content_database = '\0'; *settings->paths.path_content_database = '\0';
*settings->paths.path_overlay = '\0'; *settings->paths.path_overlay = '\0';
*settings->paths.path_record_config = '\0';
*settings->paths.path_stream_config = '\0';
*settings->paths.path_stream_url = '\0';
*settings->paths.path_softfilter_plugin = '\0'; *settings->paths.path_softfilter_plugin = '\0';
*settings->arrays.playlist_names = '\0'; *settings->arrays.playlist_names = '\0';
@ -3008,6 +3037,7 @@ static bool config_load_file(const char *path, bool set_defaults,
} }
frontend_driver_set_sustained_performance_mode(settings->bools.sustained_performance_mode); frontend_driver_set_sustained_performance_mode(settings->bools.sustained_performance_mode);
recording_driver_update_streaming_url();
ret = true; ret = true;

View File

@ -53,6 +53,13 @@
var = newvar; \ var = newvar; \
} }
enum crt_switch_type
{
CRT_SWITCH_NONE = 0,
CRT_SWITCH_15KHZ,
CRT_SWITCH_31KHZ
};
enum override_type enum override_type
{ {
OVERRIDE_NONE = 0, OVERRIDE_NONE = 0,
@ -73,6 +80,7 @@ typedef struct settings
bool video_fullscreen; bool video_fullscreen;
bool video_windowed_fullscreen; bool video_windowed_fullscreen;
bool video_vsync; bool video_vsync;
bool video_adaptive_vsync;
bool video_hard_sync; bool video_hard_sync;
bool video_black_frame_insertion; bool video_black_frame_insertion;
bool video_vfilter; bool video_vfilter;
@ -96,7 +104,6 @@ typedef struct settings
bool video_statistics_show; bool video_statistics_show;
bool video_framecount_show; bool video_framecount_show;
bool video_msg_bgcolor_enable; bool video_msg_bgcolor_enable;
bool crt_switch_resolution;
/* Audio */ /* Audio */
bool audio_enable; bool audio_enable;
@ -181,8 +188,12 @@ typedef struct settings
bool quick_menu_show_save_game_overrides; bool quick_menu_show_save_game_overrides;
bool quick_menu_show_save_content_dir_overrides; bool quick_menu_show_save_content_dir_overrides;
bool quick_menu_show_information; bool quick_menu_show_information;
bool quick_menu_show_recording;
bool quick_menu_show_streaming;
bool kiosk_mode_enable; bool kiosk_mode_enable;
bool crt_switch_custom_refresh_enable;
/* Netplay */ /* Netplay */
bool netplay_public_announce; bool netplay_public_announce;
bool netplay_start_as_spectator; bool netplay_start_as_spectator;
@ -289,6 +300,7 @@ typedef struct settings
float video_scale; float video_scale;
float video_aspect_ratio; float video_aspect_ratio;
float video_refresh_rate; float video_refresh_rate;
float crt_video_refresh_rate;
float video_font_size; float video_font_size;
float video_msg_pos_x; float video_msg_pos_x;
float video_msg_pos_y; float video_msg_pos_y;
@ -321,6 +333,7 @@ typedef struct settings
int location_update_interval_distance; int location_update_interval_distance;
int state_slot; int state_slot;
int audio_wasapi_sh_buffer_length; int audio_wasapi_sh_buffer_length;
int crt_switch_center_adjust;
} ints; } ints;
struct struct
@ -359,6 +372,7 @@ typedef struct settings
unsigned video_window_x; unsigned video_window_x;
unsigned video_window_y; unsigned video_window_y;
unsigned video_window_opacity; unsigned video_window_opacity;
unsigned crt_switch_resolution;
unsigned crt_switch_resolution_super; unsigned crt_switch_resolution_super;
unsigned video_monitor_index; unsigned video_monitor_index;
unsigned video_fullscreen_x; unsigned video_fullscreen_x;
@ -373,6 +387,11 @@ typedef struct settings
unsigned video_msg_bgcolor_red; unsigned video_msg_bgcolor_red;
unsigned video_msg_bgcolor_green; unsigned video_msg_bgcolor_green;
unsigned video_msg_bgcolor_blue; unsigned video_msg_bgcolor_blue;
unsigned video_stream_port;
unsigned video_record_quality;
unsigned video_stream_quality;
unsigned video_record_scale_factor;
unsigned video_stream_scale_factor;
unsigned menu_thumbnails; unsigned menu_thumbnails;
unsigned menu_left_thumbnails; unsigned menu_left_thumbnails;
@ -413,6 +432,7 @@ typedef struct settings
unsigned run_ahead_frames; unsigned run_ahead_frames;
unsigned midi_volume; unsigned midi_volume;
unsigned streaming_mode;
} uints; } uints;
struct struct
@ -457,6 +477,9 @@ typedef struct settings
char midi_input[32]; char midi_input[32];
char midi_output[32]; char midi_output[32];
char youtube_stream_key[PATH_MAX_LENGTH];
char twitch_stream_key[PATH_MAX_LENGTH];
} arrays; } arrays;
struct struct
@ -477,6 +500,9 @@ typedef struct settings
char path_cheat_database[PATH_MAX_LENGTH]; char path_cheat_database[PATH_MAX_LENGTH];
char path_content_database[PATH_MAX_LENGTH]; char path_content_database[PATH_MAX_LENGTH];
char path_overlay[PATH_MAX_LENGTH]; char path_overlay[PATH_MAX_LENGTH];
char path_record_config[PATH_MAX_LENGTH];
char path_stream_config[PATH_MAX_LENGTH];
char path_stream_url[PATH_MAX_LENGTH];
char path_menu_wallpaper[PATH_MAX_LENGTH]; char path_menu_wallpaper[PATH_MAX_LENGTH];
char path_audio_dsp_plugin[PATH_MAX_LENGTH]; char path_audio_dsp_plugin[PATH_MAX_LENGTH];
char path_softfilter_plugin[PATH_MAX_LENGTH]; char path_softfilter_plugin[PATH_MAX_LENGTH];
@ -491,7 +517,6 @@ typedef struct settings
char path_shader[PATH_MAX_LENGTH]; char path_shader[PATH_MAX_LENGTH];
char path_font[PATH_MAX_LENGTH]; char path_font[PATH_MAX_LENGTH];
char directory_audio_filter[PATH_MAX_LENGTH]; char directory_audio_filter[PATH_MAX_LENGTH];
char directory_autoconfig[PATH_MAX_LENGTH]; char directory_autoconfig[PATH_MAX_LENGTH];
char directory_video_filter[PATH_MAX_LENGTH]; char directory_video_filter[PATH_MAX_LENGTH];
@ -513,6 +538,7 @@ typedef struct settings
char directory_thumbnails[PATH_MAX_LENGTH]; char directory_thumbnails[PATH_MAX_LENGTH];
char directory_menu_config[PATH_MAX_LENGTH]; char directory_menu_config[PATH_MAX_LENGTH];
char directory_menu_content[PATH_MAX_LENGTH]; char directory_menu_content[PATH_MAX_LENGTH];
char streaming_title[PATH_MAX_LENGTH];
} paths; } paths;
bool modified; bool modified;

View File

@ -43,6 +43,7 @@
#include "verbosity.h" #include "verbosity.h"
#include "gfx/video_driver.h" #include "gfx/video_driver.h"
#include "audio/audio_driver.h" #include "audio/audio_driver.h"
#include "tasks/tasks_internal.h"
#ifdef HAVE_RUNAHEAD #ifdef HAVE_RUNAHEAD
#include "runahead/copy_load_info.h" #include "runahead/copy_load_info.h"
@ -298,6 +299,7 @@ bool core_load_game(retro_ctx_load_content_info_t *load_info)
#endif #endif
content_get_status(&contentless, &is_inited); content_get_status(&contentless, &is_inited);
set_save_state_in_background(false);
if (load_info && load_info->special) if (load_info && load_info->special)
current_core.game_loaded = current_core.retro_load_game_special( current_core.game_loaded = current_core.retro_load_game_special(

View File

@ -24,6 +24,7 @@
#include <errno.h> #include <errno.h>
#include <sys/statvfs.h> #include <sys/statvfs.h>
#include <sys/dirent.h> #include <sys/dirent.h>
#include <sys/iosupport.h>
#include <string.h> #include <string.h>
#include <malloc.h> #include <malloc.h>
#include <stdint.h> #include <stdint.h>

View File

@ -25,21 +25,33 @@
#include "../msg_hash.h" #include "../msg_hash.h"
#ifdef HAVE_NETWORKING
#include "../../network/netplay/netplay.h"
#include "../../network/netplay/netplay_discovery.h"
#include "../../tasks/tasks_internal.h"
#endif
#ifdef HAVE_CHEEVOS
#include "../cheevos/cheevos.h"
#endif
static const char* APPLICATION_ID = "475456035851599874"; static const char* APPLICATION_ID = "475456035851599874";
static int FrustrationLevel = 0; static int FrustrationLevel = 0;
static int64_t start_time = 0; static int64_t start_time = 0;
static int64_t pause_time = 0; static int64_t pause_time = 0;
static int64_t ellapsed_time = 0;
static bool discord_ready = false; static bool discord_ready = false;
static bool in_menu = false;
static unsigned discord_status = 0; static unsigned discord_status = 0;
struct netplay_room *room;
DiscordRichPresence discord_presence; DiscordRichPresence discord_presence;
static void handle_discord_ready(const DiscordUser* connectedUser) static void handle_discord_ready(const DiscordUser* connectedUser)
{ {
RARCH_LOG("[Discord] connected to user %s#%s - %s\n", RARCH_LOG("[Discord] connected to user: %s#%s - avatar id: %s\n",
connectedUser->username, connectedUser->username,
connectedUser->discriminator, connectedUser->discriminator,
connectedUser->userId); connectedUser->userId);
@ -58,6 +70,21 @@ static void handle_discord_error(int errcode, const char* message)
static void handle_discord_join(const char* secret) static void handle_discord_join(const char* secret)
{ {
RARCH_LOG("[Discord] join (%s)\n", secret); RARCH_LOG("[Discord] join (%s)\n", secret);
static struct string_list *list = NULL;
list = string_split(secret, "|");
char tmp_hostname[32];
snprintf(tmp_hostname,
sizeof(tmp_hostname),
"%s|%s", list->elems[0].data, list->elems[1].data);
if (netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_DATA_INITED, NULL))
deinit_netplay();
netplay_driver_ctl(RARCH_NETPLAY_CTL_ENABLE_CLIENT, NULL);
task_push_netplay_crc_scan(atoi(list->elems[3].data),
list->elems[2].data,
tmp_hostname, list->elems[4].data);
} }
static void handle_discord_spectate(const char* secret) static void handle_discord_spectate(const char* secret)
@ -78,19 +105,17 @@ static void handle_discord_join_request(const DiscordUser* request)
void discord_update(enum discord_presence presence) void discord_update(enum discord_presence presence)
{ {
core_info_t *core_info = NULL; core_info_t *core_info = NULL;
bool skip = false;
core_info_get_current_core(&core_info); core_info_get_current_core(&core_info);
if (!discord_ready) if (!discord_ready)
return; return;
if ( if (presence == discord_status)
(discord_status != DISCORD_PRESENCE_MENU) &&
(discord_status == presence))
return; return;
memset(&discord_presence, 0, sizeof(discord_presence)); if (presence == DISCORD_PRESENCE_NONE || presence == DISCORD_PRESENCE_MENU)
memset(&discord_presence, 0, sizeof(discord_presence));
switch (presence) switch (presence)
{ {
@ -99,19 +124,15 @@ void discord_update(enum discord_presence presence)
discord_presence.largeImageKey = "base"; discord_presence.largeImageKey = "base";
discord_presence.largeImageText = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_CORE); discord_presence.largeImageText = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_CORE);
discord_presence.instance = 0; discord_presence.instance = 0;
in_menu = true;
break; break;
case DISCORD_PRESENCE_GAME_PAUSED: case DISCORD_PRESENCE_GAME_PAUSED:
discord_presence.smallImageKey = "paused"; discord_presence.smallImageKey = "paused";
discord_presence.smallImageText = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DISCORD_STATUS_PAUSED); discord_presence.smallImageText = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DISCORD_STATUS_PAUSED);
discord_presence.details = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DISCORD_IN_GAME_PAUSED); discord_presence.details = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DISCORD_IN_GAME_PAUSED);
pause_time = time(0); pause_time = time(0);
skip = true; ellapsed_time = difftime(time(0), start_time);
discord_presence.startTimestamp = pause_time;
if (in_menu) break;
break;
case DISCORD_PRESENCE_GAME: case DISCORD_PRESENCE_GAME:
if (core_info) if (core_info)
{ {
@ -126,7 +147,7 @@ void discord_update(enum discord_presence presence)
if (!label) if (!label)
label = (char *)path_basename(path_get(RARCH_PATH_BASENAME)); label = (char *)path_basename(path_get(RARCH_PATH_BASENAME));
#if 1 #if 0
RARCH_LOG("[Discord] current core: %s\n", system_id); RARCH_LOG("[Discord] current core: %s\n", system_id);
RARCH_LOG("[Discord] current content: %s\n", label); RARCH_LOG("[Discord] current content: %s\n", label);
#endif #endif
@ -135,39 +156,57 @@ void discord_update(enum discord_presence presence)
if (core_info->display_name) if (core_info->display_name)
discord_presence.largeImageText = core_info->display_name; discord_presence.largeImageText = core_info->display_name;
if (in_menu) start_time = time(0);
start_time = time(0); if (pause_time != 0)
else start_time = time(0) - ellapsed_time;
start_time = start_time + difftime(time(0), pause_time);
if (!skip) pause_time = 0;
{ ellapsed_time = 0;
discord_presence.smallImageKey = "playing";
discord_presence.smallImageText = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DISCORD_STATUS_PLAYING); discord_presence.smallImageKey = "playing";
discord_presence.startTimestamp = start_time; discord_presence.smallImageText = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DISCORD_STATUS_PLAYING);
discord_presence.details = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DISCORD_IN_GAME); discord_presence.startTimestamp = start_time;
} discord_presence.details = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DISCORD_IN_GAME);
discord_presence.state = label; discord_presence.state = label;
discord_presence.instance = 0; discord_presence.instance = 0;
} }
in_menu = false;
break; break;
case DISCORD_PRESENCE_NETPLAY_HOSTING: case DISCORD_PRESENCE_NETPLAY_HOSTING:
room = netplay_get_host_room();
if (room->id == 0)
return;
RARCH_LOG("[Discord] netplay room details: id=%d, nick=%s IP=%s port=%d\n",
room->id, room->nickname,
room->host_method == NETPLAY_HOST_METHOD_MITM ? room->mitm_address : room->address,
room->host_method == NETPLAY_HOST_METHOD_MITM ? room->mitm_port : room->port);
char party_id[128];
snprintf(party_id, sizeof(party_id), "%d|%s", room->id, room->nickname);
char join_secret[128];
snprintf(join_secret, sizeof(join_secret), "%s|%d|%s|%u|%s",
room->host_method == NETPLAY_HOST_METHOD_MITM ? room->mitm_address : room->address,
room->host_method == NETPLAY_HOST_METHOD_MITM ? room->mitm_port : room->port,
room->gamename, room->gamecrc, room->corename);
RARCH_LOG("%s\n", join_secret);
discord_presence.joinSecret = strdup(join_secret);
discord_presence.spectateSecret = "SPECSPECSPEC";
discord_presence.partyId = party_id;
discord_presence.partyMax = 0;
discord_presence.partySize = 0;
break;
case DISCORD_PRESENCE_NETPLAY_HOSTING_STOPPED:
case DISCORD_PRESENCE_NETPLAY_CLIENT: case DISCORD_PRESENCE_NETPLAY_CLIENT:
case DISCORD_PRESENCE_CHEEVO_UNLOCKED: default:
/* TODO/FIXME */ discord_presence.joinSecret = NULL;
break; break;
} }
if (in_menu && skip)
return;
RARCH_LOG("[Discord] updating (%d)\n", presence); RARCH_LOG("[Discord] updating (%d)\n", presence);
Discord_UpdatePresence(&discord_presence); Discord_UpdatePresence(&discord_presence);
discord_status = presence; discord_status = presence;
} }
void discord_init(void) void discord_init(void)
@ -197,3 +236,8 @@ void discord_shutdown(void)
Discord_Shutdown(); Discord_Shutdown();
discord_ready = false; discord_ready = false;
} }
void discord_run_callbacks()
{
Discord_RunCallbacks();
}

View File

@ -32,11 +32,13 @@
enum discord_presence enum discord_presence
{ {
DISCORD_PRESENCE_MENU = 0, DISCORD_PRESENCE_NONE = 0,
DISCORD_PRESENCE_MENU,
DISCORD_PRESENCE_GAME, DISCORD_PRESENCE_GAME,
DISCORD_PRESENCE_GAME_PAUSED, DISCORD_PRESENCE_GAME_PAUSED,
DISCORD_PRESENCE_CHEEVO_UNLOCKED, DISCORD_PRESENCE_CHEEVO_UNLOCKED,
DISCORD_PRESENCE_NETPLAY_HOSTING, DISCORD_PRESENCE_NETPLAY_HOSTING,
DISCORD_PRESENCE_NETPLAY_HOSTING_STOPPED,
DISCORD_PRESENCE_NETPLAY_CLIENT DISCORD_PRESENCE_NETPLAY_CLIENT
}; };
@ -51,4 +53,6 @@ void discord_shutdown(void);
void discord_update(enum discord_presence presence); void discord_update(enum discord_presence presence);
void discord_run_callbacks();
#endif /* __RARCH_DISCORD_H */ #endif /* __RARCH_DISCORD_H */

View File

@ -66,6 +66,7 @@
#include "configuration.h" #include "configuration.h"
#include "msg_hash.h" #include "msg_hash.h"
#include "verbosity.h" #include "verbosity.h"
#include "tasks/tasks_internal.h"
#ifdef HAVE_RUNAHEAD #ifdef HAVE_RUNAHEAD
#include "runahead/secondary_core.h" #include "runahead/secondary_core.h"
@ -1041,6 +1042,16 @@ static void core_performance_counter_stop(struct retro_perf_counter *perf)
perf->total += cpu_features_get_perf_counter() - perf->start; perf->total += cpu_features_get_perf_counter() - perf->start;
} }
bool rarch_clear_all_thread_waits(unsigned clear_threads, void *data)
{
if ( clear_threads > 0)
audio_driver_start(false) ;
else
audio_driver_stop() ;
return true ;
}
/** /**
* rarch_environment_cb: * rarch_environment_cb:
* @cmd : Identifier of command. * @cmd : Identifier of command.
@ -1387,6 +1398,16 @@ bool rarch_environment_cb(unsigned cmd, void *data)
break; break;
} }
case RETRO_ENVIRONMENT_SET_SAVE_STATE_IN_BACKGROUND:
{
bool state = *(const bool*)data;
RARCH_LOG("Environ SET_SAVE_STATE_IN_BACKGROUND: %s.\n", state ? "yes" : "no");
set_save_state_in_background(state) ;
break;
}
case RETRO_ENVIRONMENT_GET_LIBRETRO_PATH: case RETRO_ENVIRONMENT_GET_LIBRETRO_PATH:
{ {
const char **path = (const char**)data; const char **path = (const char**)data;
@ -1822,8 +1843,8 @@ bool rarch_environment_cb(unsigned cmd, void *data)
int* result_p = (int*)data; int* result_p = (int*)data;
*result_p = result; *result_p = result;
} }
break;
} }
break;
case RETRO_ENVIRONMENT_GET_MIDI_INTERFACE: case RETRO_ENVIRONMENT_GET_MIDI_INTERFACE:
{ {
@ -1838,8 +1859,21 @@ bool rarch_environment_cb(unsigned cmd, void *data)
midi_interface->write = midi_driver_write; midi_interface->write = midi_driver_write;
midi_interface->flush = midi_driver_flush; midi_interface->flush = midi_driver_flush;
} }
break;
}
case RETRO_ENVIRONMENT_GET_FASTFORWARDING:
{
extern bool runloop_fastmotion;
*(bool *)data = runloop_fastmotion;
break;
}
case RETRO_ENVIRONMENT_GET_CLEAR_ALL_THREAD_WAITS_CB:
{
*(retro_environment_t *)data = rarch_clear_all_thread_waits;
break;
} }
break;
default: default:
RARCH_LOG("Environ UNSUPPORTED (#%u).\n", cmd); RARCH_LOG("Environ UNSUPPORTED (#%u).\n", cmd);

View File

@ -347,9 +347,15 @@ static void frontend_darwin_get_environment_settings(int *argc, char *argv[],
#endif #endif
strlcat(home_dir_buf, "/RetroArch", sizeof(home_dir_buf)); strlcat(home_dir_buf, "/RetroArch", sizeof(home_dir_buf));
#ifdef HAVE_METAL
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_SHADER],
home_dir_buf, "shaders_slang",
sizeof(g_defaults.dirs[DEFAULT_DIR_SHADER]));
#else
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_SHADER], fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_SHADER],
home_dir_buf, "shaders_glsl", home_dir_buf, "shaders_glsl",
sizeof(g_defaults.dirs[DEFAULT_DIR_SHADER])); sizeof(g_defaults.dirs[DEFAULT_DIR_SHADER]));
#endif
#if TARGET_OS_IPHONE #if TARGET_OS_IPHONE
int major, minor; int major, minor;
get_ios_version(&major, &minor); get_ios_version(&major, &minor);

View File

@ -0,0 +1,803 @@
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <ctype.h>
#include <boolean.h>
#include <sys/stat.h>
#include <errno.h>
#include <dirent.h>
#include <file/nbio.h>
#include <formats/rpng.h>
#include <formats/image.h>
#ifdef HAVE_LIBNX
#include <switch.h>
#else
#include <libtransistor/nx.h>
#include <libtransistor/ipc_helpers.h>
#endif
#include <file/file_path.h>
#ifdef HAVE_CONFIG_H
#include "../../config.h"
#endif
#ifndef IS_SALAMANDER
#include <lists/file_list.h>
#endif
#include "../frontend_driver.h"
#include "../../verbosity.h"
#include "../../defaults.h"
#include "../../paths.h"
#include "../../retroarch.h"
#include "../../file_path_special.h"
#include "../../audio/audio_driver.h"
#ifndef IS_SALAMANDER
#ifdef HAVE_MENU
#include "../../menu/menu_driver.h"
#endif
#endif
#ifdef HAVE_LIBNX
#define SD_PREFIX
#include "../../gfx/common/switch_common.h"
#else
#define SD_PREFIX "/sd"
#endif
static enum frontend_fork switch_fork_mode = FRONTEND_FORK_NONE;
static const char *elf_path_cst = "/switch/retroarch_switch.nro";
static uint64_t frontend_switch_get_mem_used(void);
#ifdef HAVE_LIBNX
// Splash
static uint32_t *splashData = NULL;
#endif // HAVE_LIBNX
static void get_first_valid_core(char *path_return)
{
DIR *dir;
struct dirent *ent;
const char *extension = ".nro";
path_return[0] = '\0';
dir = opendir(SD_PREFIX "/retroarch/cores");
if (dir != NULL)
{
while ((ent = readdir(dir)) != NULL)
{
if (ent == NULL)
break;
if (strlen(ent->d_name) > strlen(extension) && !strcmp(ent->d_name + strlen(ent->d_name) - strlen(extension), extension))
{
strcpy(path_return, SD_PREFIX "/retroarch/cores");
strcat(path_return, "/");
strcat(path_return, ent->d_name);
break;
}
}
closedir(dir);
}
}
static void frontend_switch_get_environment_settings(int *argc, char *argv[], void *args, void *params_data)
{
(void)args;
#ifndef IS_SALAMANDER
#if defined(HAVE_LOGGER)
logger_init();
#elif defined(HAVE_FILE_LOGGER)
retro_main_log_file_init(SD_PREFIX "/retroarch-log.txt");
#endif
#endif
fill_pathname_basedir(g_defaults.dirs[DEFAULT_DIR_PORT], SD_PREFIX "/retroarch/retroarch_switch.nro", sizeof(g_defaults.dirs[DEFAULT_DIR_PORT]));
RARCH_LOG("port dir: [%s]\n", g_defaults.dirs[DEFAULT_DIR_PORT]);
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE_ASSETS], g_defaults.dirs[DEFAULT_DIR_PORT],
"downloads", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE_ASSETS]));
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_ASSETS], g_defaults.dirs[DEFAULT_DIR_PORT],
"media", sizeof(g_defaults.dirs[DEFAULT_DIR_ASSETS]));
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE], g_defaults.dirs[DEFAULT_DIR_PORT],
"cores", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE]));
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE_INFO], g_defaults.dirs[DEFAULT_DIR_CORE],
"info", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE_INFO]));
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_SAVESTATE], g_defaults.dirs[DEFAULT_DIR_CORE],
"savestates", sizeof(g_defaults.dirs[DEFAULT_DIR_SAVESTATE]));
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_SRAM], g_defaults.dirs[DEFAULT_DIR_CORE],
"savefiles", sizeof(g_defaults.dirs[DEFAULT_DIR_SRAM]));
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_SYSTEM], g_defaults.dirs[DEFAULT_DIR_CORE],
"system", sizeof(g_defaults.dirs[DEFAULT_DIR_SYSTEM]));
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_PLAYLIST], g_defaults.dirs[DEFAULT_DIR_CORE],
"playlists", sizeof(g_defaults.dirs[DEFAULT_DIR_PLAYLIST]));
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_MENU_CONFIG], g_defaults.dirs[DEFAULT_DIR_PORT],
"config", sizeof(g_defaults.dirs[DEFAULT_DIR_MENU_CONFIG]));
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_REMAP], g_defaults.dirs[DEFAULT_DIR_PORT],
"config/remaps", sizeof(g_defaults.dirs[DEFAULT_DIR_REMAP]));
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_VIDEO_FILTER], g_defaults.dirs[DEFAULT_DIR_PORT],
"filters", sizeof(g_defaults.dirs[DEFAULT_DIR_REMAP]));
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_DATABASE], g_defaults.dirs[DEFAULT_DIR_PORT],
"database/rdb", sizeof(g_defaults.dirs[DEFAULT_DIR_DATABASE]));
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CURSOR], g_defaults.dirs[DEFAULT_DIR_PORT],
"database/cursors", sizeof(g_defaults.dirs[DEFAULT_DIR_CURSOR]));
fill_pathname_join(g_defaults.path.config, g_defaults.dirs[DEFAULT_DIR_PORT],
file_path_str(FILE_PATH_MAIN_CONFIG), sizeof(g_defaults.path.config));
}
extern switch_ctx_data_t *nx_ctx_ptr;
static void frontend_switch_deinit(void *data)
{
(void)data;
#ifdef HAVE_LIBNX
#if defined(SWITCH) && defined(NXLINK)
socketExit();
#endif
// Splash
if (splashData)
{
free(splashData);
splashData = NULL;
}
#ifndef HAVE_OPENGL
gfxExit();
#endif
#endif
}
#ifdef HAVE_LIBNX
static void frontend_switch_exec(const char *path, bool should_load_game)
{
char game_path[PATH_MAX];
const char *arg_data[3];
char error_string[200 + PATH_MAX];
int args = 0;
int error = 0;
game_path[0] = NULL;
arg_data[0] = NULL;
arg_data[args] = elf_path_cst;
arg_data[args + 1] = NULL;
args++;
RARCH_LOG("Attempt to load core: [%s].\n", path);
#ifndef IS_SALAMANDER
if (should_load_game && !path_is_empty(RARCH_PATH_CONTENT))
{
strcpy(game_path, path_get(RARCH_PATH_CONTENT));
arg_data[args] = game_path;
arg_data[args + 1] = NULL;
args++;
RARCH_LOG("content path: [%s].\n", path_get(RARCH_PATH_CONTENT));
}
#endif
if (path && path[0])
{
#ifdef IS_SALAMANDER
struct stat sbuff;
bool file_exists;
file_exists = stat(path, &sbuff) == 0;
if (!file_exists)
{
char core_path[PATH_MAX];
/* find first valid core and load it if the target core doesnt exist */
get_first_valid_core(&core_path[0]);
if (core_path[0] == '\0')
{
/*errorInit(&error_dialog, ERROR_TEXT, CFG_LANGUAGE_EN);
errorText(&error_dialog, "There are no cores installed, install a core to continue.");
errorDisp(&error_dialog);*/
svcExitProcess();
}
}
#endif
char *argBuffer = (char *)malloc(PATH_MAX);
if (should_load_game)
{
snprintf(argBuffer, PATH_MAX, "%s \"%s\"", path, game_path);
}
else
{
snprintf(argBuffer, PATH_MAX, "%s", path);
}
envSetNextLoad(path, argBuffer);
}
}
#ifndef IS_SALAMANDER
static bool frontend_switch_set_fork(enum frontend_fork fork_mode)
{
switch (fork_mode)
{
case FRONTEND_FORK_CORE:
RARCH_LOG("FRONTEND_FORK_CORE\n");
switch_fork_mode = fork_mode;
break;
case FRONTEND_FORK_CORE_WITH_ARGS:
RARCH_LOG("FRONTEND_FORK_CORE_WITH_ARGS\n");
switch_fork_mode = fork_mode;
break;
case FRONTEND_FORK_RESTART:
RARCH_LOG("FRONTEND_FORK_RESTART\n");
/* NOTE: We don't implement Salamander, so just turn
this into FRONTEND_FORK_CORE. */
switch_fork_mode = FRONTEND_FORK_CORE;
break;
case FRONTEND_FORK_NONE:
default:
return false;
}
return true;
}
#endif
static void frontend_switch_exitspawn(char *s, size_t len)
{
bool should_load_game = false;
#ifndef IS_SALAMANDER
if (switch_fork_mode == FRONTEND_FORK_NONE)
return;
switch (switch_fork_mode)
{
case FRONTEND_FORK_CORE_WITH_ARGS:
should_load_game = true;
break;
default:
break;
}
#endif
frontend_switch_exec(s, should_load_game);
}
void argb_to_rgba8(uint32_t *buff, uint32_t height, uint32_t width)
{
// Convert
for (uint32_t h = 0; h < height; h++)
{
for (uint32_t w = 0; w < width; w++)
{
uint32_t offset = (h * width) + w;
uint32_t c = buff[offset];
uint32_t a = (uint32_t)((c & 0xff000000) >> 24);
uint32_t r = (uint32_t)((c & 0x00ff0000) >> 16);
uint32_t g = (uint32_t)((c & 0x0000ff00) >> 8);
uint32_t b = (uint32_t)(c & 0x000000ff);
buff[offset] = RGBA8(r, g, b, a);
}
}
}
void frontend_switch_showsplash()
{
printf("[Splash] Showing splashScreen\n");
if (splashData)
{
uint32_t width, height;
width = height = 0;
uint32_t *frambuffer = (uint32_t *)gfxGetFramebuffer(&width, &height);
gfx_slow_swizzling_blit(frambuffer, splashData, width, height, 0, 0, false);
gfxFlushBuffers();
gfxSwapBuffers();
gfxWaitForVsync();
}
}
// From rpng_test.c
bool rpng_load_image_argb(const char *path, uint32_t **data, unsigned *width, unsigned *height)
{
int retval;
size_t file_len;
bool ret = true;
rpng_t *rpng = NULL;
void *ptr = NULL;
struct nbio_t *handle = (struct nbio_t *)nbio_open(path, NBIO_READ);
if (!handle)
goto end;
nbio_begin_read(handle);
while (!nbio_iterate(handle))
svcSleepThread(3);
ptr = nbio_get_ptr(handle, &file_len);
if (!ptr)
{
ret = false;
goto end;
}
rpng = rpng_alloc();
if (!rpng)
{
ret = false;
goto end;
}
if (!rpng_set_buf_ptr(rpng, (uint8_t *)ptr))
{
ret = false;
goto end;
}
if (!rpng_start(rpng))
{
ret = false;
goto end;
}
while (rpng_iterate_image(rpng))
svcSleepThread(3);
if (!rpng_is_valid(rpng))
{
ret = false;
goto end;
}
do
{
retval = rpng_process_image(rpng, (void **)data, file_len, width, height);
svcSleepThread(3);
} while (retval == IMAGE_PROCESS_NEXT);
if (retval == IMAGE_PROCESS_ERROR || retval == IMAGE_PROCESS_ERROR_END)
ret = false;
end:
if (handle)
nbio_free(handle);
if (rpng)
rpng_free(rpng);
rpng = NULL;
if (!ret)
free(*data);
return ret;
}
int nanosleep(const struct timespec *rqtp, struct timespec *rmtp)
{
svcSleepThread(rqtp->tv_nsec + (rqtp->tv_sec * 1000000000));
return 0;
}
long sysconf(int name)
{
switch (name)
{
case 8:
return 0x1000;
}
return -1;
}
ssize_t readlink(const char *restrict path, char *restrict buf, size_t bufsize)
{
return -1;
}
// Taken from glibc
char *realpath(const char *name, char *resolved)
{
char *rpath, *dest, *extra_buf = NULL;
const char *start, *end, *rpath_limit;
long int path_max;
int num_links = 0;
if (name == NULL)
{
/* As per Single Unix Specification V2 we must return an error if
either parameter is a null pointer. We extend this to allow
the RESOLVED parameter to be NULL in case the we are expected to
allocate the room for the return value. */
return NULL;
}
if (name[0] == '\0')
{
/* As per Single Unix Specification V2 we must return an error if
the name argument points to an empty string. */
return NULL;
}
#ifdef PATH_MAX
path_max = PATH_MAX;
#else
path_max = pathconf(name, _PC_PATH_MAX);
if (path_max <= 0)
path_max = 1024;
#endif
if (resolved == NULL)
{
rpath = malloc(path_max);
if (rpath == NULL)
return NULL;
}
else
rpath = resolved;
rpath_limit = rpath + path_max;
if (name[0] != '/')
{
if (!getcwd(rpath, path_max))
{
rpath[0] = '\0';
goto error;
}
dest = memchr(rpath, '\0', path_max);
}
else
{
rpath[0] = '/';
dest = rpath + 1;
}
for (start = end = name; *start; start = end)
{
int n;
/* Skip sequence of multiple path-separators. */
while (*start == '/')
++start;
/* Find end of path component. */
for (end = start; *end && *end != '/'; ++end)
/* Nothing. */;
if (end - start == 0)
break;
else if (end - start == 1 && start[0] == '.')
/* nothing */;
else if (end - start == 2 && start[0] == '.' && start[1] == '.')
{
/* Back up to previous component, ignore if at root already. */
if (dest > rpath + 1)
while ((--dest)[-1] != '/')
;
}
else
{
size_t new_size;
if (dest[-1] != '/')
*dest++ = '/';
if (dest + (end - start) >= rpath_limit)
{
ptrdiff_t dest_offset = dest - rpath;
char *new_rpath;
if (resolved)
{
if (dest > rpath + 1)
dest--;
*dest = '\0';
goto error;
}
new_size = rpath_limit - rpath;
if (end - start + 1 > path_max)
new_size += end - start + 1;
else
new_size += path_max;
new_rpath = (char *)realloc(rpath, new_size);
if (new_rpath == NULL)
goto error;
rpath = new_rpath;
rpath_limit = rpath + new_size;
dest = rpath + dest_offset;
}
dest = memcpy(dest, start, end - start);
*dest = '\0';
}
}
if (dest > rpath + 1 && dest[-1] == '/')
--dest;
*dest = '\0';
return rpath;
error:
if (resolved == NULL)
free(rpath);
return NULL;
}
#endif // HAVE_LIBNX
static void frontend_switch_shutdown(bool unused)
{
(void)unused;
}
// runloop_get_system_info isnt initialized that early..
extern void retro_get_system_info(struct retro_system_info *info);
static void frontend_switch_init(void *data)
{
(void)data;
#ifdef HAVE_LIBNX
#ifndef HAVE_OPENGL
// Init Resolution before initDefault
gfxInitResolution(1280, 720);
gfxInitDefault();
gfxSetMode(GfxMode_TiledDouble);
gfxConfigureTransform(0);
#endif // HAVE_OPENGL
#ifdef NXLINK
socketInitializeDefault();
nxlinkStdio();
#ifndef IS_SALAMANDER
verbosity_enable();
#endif // IS_SALAMANDER
#endif // NXLINK
rarch_system_info_t *sys_info = runloop_get_system_info();
retro_get_system_info(sys_info);
const char *core_name = NULL;
uint32_t width, height;
width = height = 0;
// Load splash
#ifndef HAVE_OPENGL
if (!splashData)
{
if (sys_info)
{
core_name = sys_info->info.library_name;
char *full_core_splash_path = (char *)malloc(PATH_MAX);
snprintf(full_core_splash_path, PATH_MAX, "/retroarch/rgui/splash/%s.png", core_name);
rpng_load_image_argb((const char *)full_core_splash_path, &splashData, &width, &height);
if (splashData)
{
argb_to_rgba8(splashData, height, width);
frontend_switch_showsplash();
}
else
{
rpng_load_image_argb("/retroarch/rgui/splash/RetroArch.png", &splashData, &width, &height);
if (splashData)
{
argb_to_rgba8(splashData, height, width);
frontend_switch_showsplash();
}
}
free(full_core_splash_path);
}
else
{
rpng_load_image_argb("/retroarch/rgui/splash/RetroArch.png", &splashData, &width, &height);
if (splashData)
{
argb_to_rgba8(splashData, height, width);
frontend_switch_showsplash();
}
}
}
else
{
frontend_switch_showsplash();
}
#endif
#endif // HAVE_LIBNX (splash)
}
static int frontend_switch_get_rating(void)
{
return 1000;
}
enum frontend_architecture frontend_switch_get_architecture(void)
{
return FRONTEND_ARCH_ARMV8;
}
static int frontend_switch_parse_drive_list(void *data, bool load_content)
{
#ifndef IS_SALAMANDER
file_list_t *list = (file_list_t *)data;
enum msg_hash_enums enum_idx = load_content ? MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR : MSG_UNKNOWN;
if (!list)
return -1;
menu_entries_append_enum(list, "/", msg_hash_to_str(MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR),
enum_idx,
FILE_TYPE_DIRECTORY, 0, 0);
#endif
return 0;
}
static uint64_t frontend_switch_get_mem_total(void)
{
uint64_t memoryTotal = 0;
svcGetInfo(&memoryTotal, 6, 0xffff8001, 0); // avaiable
memoryTotal += frontend_switch_get_mem_used();
return memoryTotal;
}
static uint64_t frontend_switch_get_mem_used(void)
{
uint64_t memoryUsed = 0;
svcGetInfo(&memoryUsed, 7, 0xffff8001, 0); // used
return memoryUsed;
}
static enum frontend_powerstate frontend_switch_get_powerstate(int *seconds, int *percent)
{
// This is fine monkaS
return FRONTEND_POWERSTATE_CHARGED;
}
static void frontend_switch_get_os(char *s, size_t len, int *major, int *minor)
{
strlcpy(s, "Horizon OS", len);
#ifdef HAVE_LIBNX
// There is pretty sure a better way, but this will do just fine
if (kernelAbove600())
{
*major = 6;
*minor = 0;
}
else if(kernelAbove500())
{
*major = 5;
*minor = 0;
}else if (kernelAbove400())
{
*major = 4;
*minor = 0;
}
else if (kernelAbove300())
{
*major = 3;
*minor = 0;
}
else if (kernelAbove200())
{
*major = 2;
*minor = 0;
}
else
{
// either 1.0 or > 5.x
*major = 1;
*minor = 0;
}
#else
// defaults in case we error out
*major = 0;
*minor = 0;
char firmware_version[0x100];
result_t r; // used by LIB_ASSERT_OK macros
LIB_ASSERT_OK(fail, sm_init());
ipc_object_t set_sys;
LIB_ASSERT_OK(fail_sm, sm_get_service(&set_sys, "set:sys"));
ipc_request_t rq = ipc_make_request(3);
ipc_buffer_t buffers[] = {
ipc_make_buffer(firmware_version, 0x100, 0x1a),
};
ipc_msg_set_buffers(rq, buffers, buffer_ptrs);
LIB_ASSERT_OK(fail_object, ipc_send(set_sys, &rq, &ipc_default_response_fmt));
int patch;
sscanf(firmware_version + 0x68, "%d.%d.%d", major, minor, &patch);
fail_object:
ipc_close(set_sys);
fail_sm:
sm_finalize();
fail:
return;
#endif
}
static void frontend_switch_get_name(char *s, size_t len)
{
// TODO: Add Mariko at some point
strlcpy(s, "Nintendo Switch", len);
}
frontend_ctx_driver_t frontend_ctx_switch =
{
frontend_switch_get_environment_settings,
frontend_switch_init,
frontend_switch_deinit,
#ifdef HAVE_LIBNX
frontend_switch_exitspawn,
NULL, /* process_args */
frontend_switch_exec,
#ifdef IS_SALAMANDER
NULL,
#else
frontend_switch_set_fork,
#endif
#else // HAVE_LIBNX
NULL,
NULL,
NULL,
NULL,
#endif // HAVE_LIBNX
frontend_switch_shutdown,
frontend_switch_get_name,
frontend_switch_get_os,
frontend_switch_get_rating,
NULL, /* load_content */
frontend_switch_get_architecture,
frontend_switch_get_powerstate,
frontend_switch_parse_drive_list,
frontend_switch_get_mem_total,
frontend_switch_get_mem_used,
NULL, /* install_signal_handler */
NULL, /* get_signal_handler_state */
NULL, /* set_signal_handler_state */
NULL, /* destroy_signal_handler_state */
NULL, /* attach_console */
NULL, /* detach_console */
NULL, /* watch_path_for_changes */
NULL, /* check_for_path_changes */
NULL, /* set_sustained_performance_mode */
"switch",
};

View File

@ -59,6 +59,9 @@ static frontend_ctx_driver_t *frontend_ctx_drivers[] = {
#if defined(_3DS) #if defined(_3DS)
&frontend_ctx_ctr, &frontend_ctx_ctr,
#endif #endif
#if defined(SWITCH) && defined(HAVE_LIBNX)
&frontend_ctx_switch,
#endif
#if defined(_WIN32) && !defined(_XBOX) #if defined(_WIN32) && !defined(_XBOX)
&frontend_ctx_win32, &frontend_ctx_win32,
#endif #endif
@ -67,6 +70,9 @@ static frontend_ctx_driver_t *frontend_ctx_drivers[] = {
#endif #endif
#ifdef DJGPP #ifdef DJGPP
&frontend_ctx_dos, &frontend_ctx_dos,
#endif
#ifdef SWITCH
&frontend_ctx_switch,
#endif #endif
&frontend_ctx_null, &frontend_ctx_null,
NULL NULL
@ -150,6 +156,9 @@ bool frontend_driver_get_core_extension(char *s, size_t len)
#elif defined(__linux__) #elif defined(__linux__)
strlcpy(s, "elf", len); strlcpy(s, "elf", len);
return true; return true;
#elif defined(HAVE_LIBNX)
strlcpy(s, "nro", len);
return true;
#elif defined(_3DS) #elif defined(_3DS)
if (envIsHomebrew()) if (envIsHomebrew())
strlcpy(s, "3dsx", len); strlcpy(s, "3dsx", len);
@ -193,6 +202,9 @@ bool frontend_driver_get_salamander_basename(char *s, size_t len)
#elif defined(_3DS) #elif defined(_3DS)
strlcpy(s, "retroarch.core", len); strlcpy(s, "retroarch.core", len);
return true; return true;
#elif defined(SWITCH)
strlcpy(s, "retroarch_switch.nro", len);
return true;
#else #else
return false; return false;
#endif #endif

View File

@ -121,10 +121,12 @@ extern frontend_ctx_driver_t frontend_ctx_darwin;
extern frontend_ctx_driver_t frontend_ctx_unix; extern frontend_ctx_driver_t frontend_ctx_unix;
extern frontend_ctx_driver_t frontend_ctx_psp; extern frontend_ctx_driver_t frontend_ctx_psp;
extern frontend_ctx_driver_t frontend_ctx_ctr; extern frontend_ctx_driver_t frontend_ctx_ctr;
extern frontend_ctx_driver_t frontend_ctx_switch;
extern frontend_ctx_driver_t frontend_ctx_win32; extern frontend_ctx_driver_t frontend_ctx_win32;
extern frontend_ctx_driver_t frontend_ctx_xenon; extern frontend_ctx_driver_t frontend_ctx_xenon;
extern frontend_ctx_driver_t frontend_ctx_emscripten; extern frontend_ctx_driver_t frontend_ctx_emscripten;
extern frontend_ctx_driver_t frontend_ctx_dos; extern frontend_ctx_driver_t frontend_ctx_dos;
extern frontend_ctx_driver_t frontend_ctx_switch;
extern frontend_ctx_driver_t frontend_ctx_null; extern frontend_ctx_driver_t frontend_ctx_null;
/** /**

View File

@ -171,7 +171,7 @@ void egl_swap_buffers(void *data)
eglSwapBuffers(egl->dpy, egl->surf); eglSwapBuffers(egl->dpy, egl->surf);
} }
void egl_set_swap_interval(egl_ctx_data_t *egl, unsigned interval) void egl_set_swap_interval(egl_ctx_data_t *egl, int interval)
{ {
/* Can be called before initialization. /* Can be called before initialization.
* Some contexts require that swap interval * Some contexts require that swap interval

View File

@ -58,7 +58,7 @@ typedef struct
EGLSurface surf; EGLSurface surf;
EGLDisplay dpy; EGLDisplay dpy;
EGLConfig config; EGLConfig config;
unsigned interval; int interval;
unsigned major; unsigned major;
unsigned minor; unsigned minor;
@ -84,7 +84,7 @@ void egl_bind_hw_render(egl_ctx_data_t *egl, bool enable);
void egl_swap_buffers(void *data); void egl_swap_buffers(void *data);
void egl_set_swap_interval(egl_ctx_data_t *egl, unsigned interval); void egl_set_swap_interval(egl_ctx_data_t *egl, int interval);
void egl_get_video_size(egl_ctx_data_t *egl, unsigned *width, unsigned *height); void egl_get_video_size(egl_ctx_data_t *egl, unsigned *width, unsigned *height);

View File

@ -34,6 +34,9 @@ typedef struct
/*! @brief Specifies whether rendering is synchronized with the display */ /*! @brief Specifies whether rendering is synchronized with the display */
@property (nonatomic, readwrite) bool displaySyncEnabled; @property (nonatomic, readwrite) bool displaySyncEnabled;
/*! @brief captureEnabled allows previous frames to be read */
@property (nonatomic, readwrite) bool captureEnabled;
/*! @brief Returns the command buffer used for pre-render work, /*! @brief Returns the command buffer used for pre-render work,
* such as mip maps and shader effects * such as mip maps and shader effects
* */ * */
@ -52,7 +55,7 @@ typedef struct
- (Texture *)newTexture:(struct texture_image)image filter:(enum texture_filter_type)filter; - (Texture *)newTexture:(struct texture_image)image filter:(enum texture_filter_type)filter;
- (id<MTLTexture>)newTexture:(struct texture_image)image mipmapped:(bool)mipmapped; - (id<MTLTexture>)newTexture:(struct texture_image)image mipmapped:(bool)mipmapped;
- (void)convertFormat:(RPixelFormat)fmt from:(id<MTLBuffer>)src to:(id<MTLTexture>)dst; - (void)convertFormat:(RPixelFormat)fmt from:(id<MTLTexture>)src to:(id<MTLTexture>)dst;
- (id<MTLRenderPipelineState>)getStockShader:(int)index blend:(bool)blend; - (id<MTLRenderPipelineState>)getStockShader:(int)index blend:(bool)blend;
/*! @brief resets the viewport for the main render encoder to the drawable size */ /*! @brief resets the viewport for the main render encoder to the drawable size */
@ -70,4 +73,6 @@ typedef struct
/*! @brief end commits the command buffer */ /*! @brief end commits the command buffer */
- (void)end; - (void)end;
- (bool)readBackBuffer:(uint8_t *)buffer;
@end @end

View File

@ -54,6 +54,9 @@
id<MTLRenderPipelineState> _states[GFX_MAX_SHADERS][2]; id<MTLRenderPipelineState> _states[GFX_MAX_SHADERS][2];
id<MTLRenderPipelineState> _clearState; id<MTLRenderPipelineState> _clearState;
Uniforms _uniforms; Uniforms _uniforms;
bool _captureEnabled;
id<MTLTexture> _backBuffer;
} }
- (instancetype)initWithDevice:(id<MTLDevice>)d - (instancetype)initWithDevice:(id<MTLDevice>)d
@ -65,7 +68,9 @@
_inflightSemaphore = dispatch_semaphore_create(MAX_INFLIGHT); _inflightSemaphore = dispatch_semaphore_create(MAX_INFLIGHT);
_device = d; _device = d;
_layer = layer; _layer = layer;
#if TARGET_OS_OSX
_layer.displaySyncEnabled = YES; _layer.displaySyncEnabled = YES;
#endif
_library = l; _library = l;
_commandQueue = [_device newCommandQueue]; _commandQueue = [_device newCommandQueue];
_clearColor = MTLClearColorMake(0, 0, 0, 1); _clearColor = MTLClearColorMake(0, 0, 0, 1);
@ -127,7 +132,16 @@
- (void)setDisplaySyncEnabled:(bool)displaySyncEnabled - (void)setDisplaySyncEnabled:(bool)displaySyncEnabled
{ {
#if TARGET_OS_OSX
_layer.displaySyncEnabled = displaySyncEnabled; _layer.displaySyncEnabled = displaySyncEnabled;
#endif
}
- (bool)displaySyncEnabled
{
#if TARGET_OS_OSX
return _layer.displaySyncEnabled;
#endif
} }
#pragma mark - shaders #pragma mark - shaders
@ -154,11 +168,6 @@
return _states[index][blend ? 1 : 0]; return _states[index][blend ? 1 : 0];
} }
- (bool)displaySyncEnabled
{
return _layer.displaySyncEnabled;
}
- (MTLVertexDescriptor *)_spriteVertexDescriptor - (MTLVertexDescriptor *)_spriteVertexDescriptor
{ {
MTLVertexDescriptor *vd = [MTLVertexDescriptor new]; MTLVertexDescriptor *vd = [MTLVertexDescriptor new];
@ -376,7 +385,7 @@
{ {
assert(filter >= TEXTURE_FILTER_LINEAR && filter <= TEXTURE_FILTER_MIPMAP_NEAREST); assert(filter >= TEXTURE_FILTER_LINEAR && filter <= TEXTURE_FILTER_MIPMAP_NEAREST);
if (!image.pixels && !image.width && !image.height) if (!image.pixels || !image.width || !image.height)
{ {
/* Create a dummy texture instead. */ /* Create a dummy texture instead. */
#define T0 0xff000000u #define T0 0xff000000u
@ -397,6 +406,7 @@
image.pixels = (uint32_t *)checkerboard; image.pixels = (uint32_t *)checkerboard;
image.width = 8; image.width = 8;
image.height = 8; image.height = 8;
filter = TEXTURE_FILTER_MIPMAP_NEAREST;
} }
BOOL mipmapped = filter == TEXTURE_FILTER_MIPMAP_LINEAR || filter == TEXTURE_FILTER_MIPMAP_NEAREST; BOOL mipmapped = filter == TEXTURE_FILTER_MIPMAP_LINEAR || filter == TEXTURE_FILTER_MIPMAP_NEAREST;
@ -441,13 +451,13 @@
return _drawable; return _drawable;
} }
- (void)convertFormat:(RPixelFormat)fmt from:(id<MTLBuffer>)src to:(id<MTLTexture>)dst - (void)convertFormat:(RPixelFormat)fmt from:(id<MTLTexture>)src to:(id<MTLTexture>)dst
{ {
assert(dst.width * dst.height == src.length / RPixelFormatToBPP(fmt)); assert(src.width == dst.width && src.height == dst.height);
assert(fmt >= 0 && fmt < RPixelFormatCount); assert(fmt >= 0 && fmt < RPixelFormatCount);
Filter *conv = _filters[fmt]; Filter *conv = _filters[fmt];
assert(conv != nil); assert(conv != nil);
[conv apply:self.blitCommandBuffer inBuf:src outTex:dst]; [conv apply:self.blitCommandBuffer in:src out:dst];
} }
- (id<MTLCommandBuffer>)blitCommandBuffer - (id<MTLCommandBuffer>)blitCommandBuffer
@ -463,11 +473,65 @@
[_chain[_currentChain] discard]; [_chain[_currentChain] discard];
} }
- (void)setCaptureEnabled:(bool)captureEnabled
{
if (_captureEnabled == captureEnabled)
return;
_captureEnabled = captureEnabled;
//_layer.framebufferOnly = !captureEnabled;
}
- (bool)captureEnabled
{
return _captureEnabled;
}
- (bool)readBackBuffer:(uint8_t *)buffer
{
if (!_captureEnabled || _backBuffer == nil)
return NO;
if (_backBuffer.pixelFormat != MTLPixelFormatBGRA8Unorm)
{
RARCH_WARN("[Metal]: unexpected pixel format %d\n", _backBuffer.pixelFormat);
return NO;
}
uint8_t *tmp = malloc(_backBuffer.width * _backBuffer.height * 4);
[_backBuffer getBytes:tmp
bytesPerRow:4 * _backBuffer.width
fromRegion:MTLRegionMake2D(0, 0, _backBuffer.width, _backBuffer.height)
mipmapLevel:0];
NSUInteger srcStride = _backBuffer.width * 4;
uint8_t const *src = tmp + (_viewport.y * srcStride);
NSUInteger dstStride = _viewport.width * 3;
uint8_t *dst = buffer + (_viewport.height - 1) * dstStride;
for (int y = _viewport.y; y < _viewport.height; y++, src += srcStride, dst -= dstStride)
{
for (int x = _viewport.x; x < _viewport.width; x++)
{
dst[3 * x + 0] = src[4 * x + 0];
dst[3 * x + 1] = src[4 * x + 1];
dst[3 * x + 2] = src[4 * x + 2];
}
}
free(tmp);
return YES;
}
- (void)begin - (void)begin
{ {
assert(_commandBuffer == nil); assert(_commandBuffer == nil);
dispatch_semaphore_wait(_inflightSemaphore, DISPATCH_TIME_FOREVER); dispatch_semaphore_wait(_inflightSemaphore, DISPATCH_TIME_FOREVER);
_commandBuffer = [_commandQueue commandBuffer]; _commandBuffer = [_commandQueue commandBuffer];
_backBuffer = nil;
} }
- (id<MTLRenderCommandEncoder>)rce - (id<MTLRenderCommandEncoder>)rce
@ -479,6 +543,10 @@
rpd.colorAttachments[0].clearColor = _clearColor; rpd.colorAttachments[0].clearColor = _clearColor;
rpd.colorAttachments[0].loadAction = MTLLoadActionClear; rpd.colorAttachments[0].loadAction = MTLLoadActionClear;
rpd.colorAttachments[0].texture = self.nextDrawable.texture; rpd.colorAttachments[0].texture = self.nextDrawable.texture;
if (_captureEnabled)
{
_backBuffer = self.nextDrawable.texture;
}
_rce = [_commandBuffer renderCommandEncoderWithDescriptor:rpd]; _rce = [_commandBuffer renderCommandEncoderWithDescriptor:rpd];
} }
return _rce; return _rce;
@ -615,6 +683,7 @@ static const NSUInteger kConstantAlignment = 4;
- (void)commitRanges - (void)commitRanges
{ {
#if TARGET_OS_OSX
for (BufferNode *n = _head; n != nil; n = n.next) for (BufferNode *n = _head; n != nil; n = n.next)
{ {
if (n.allocated > 0) if (n.allocated > 0)
@ -622,6 +691,7 @@ static const NSUInteger kConstantAlignment = 4;
[n.src didModifyRange:NSMakeRange(0, n.allocated)]; [n.src didModifyRange:NSMakeRange(0, n.allocated)];
} }
} }
#endif
} }
- (void)discard - (void)discard
@ -635,9 +705,15 @@ static const NSUInteger kConstantAlignment = 4;
{ {
bzero(range, sizeof(*range)); bzero(range, sizeof(*range));
#if TARGET_OS_OSX
MTLResourceOptions opts = MTLResourceStorageModeManaged;
#else
MTLResourceOptions opts = MTLResourceStorageModeShared;
#endif
if (!_head) if (!_head)
{ {
_head = [[BufferNode alloc] initWithBuffer:[_device newBufferWithLength:_blockLen options:MTLResourceStorageModeManaged]]; _head = [[BufferNode alloc] initWithBuffer:[_device newBufferWithLength:_blockLen options:opts]];
_length += _blockLen; _length += _blockLen;
_current = _head; _current = _head;
_offset = 0; _offset = 0;
@ -659,7 +735,7 @@ static const NSUInteger kConstantAlignment = 4;
blockLen = length; blockLen = length;
} }
_current.next = [[BufferNode alloc] initWithBuffer:[_device newBufferWithLength:blockLen options:MTLResourceStorageModeManaged]]; _current.next = [[BufferNode alloc] initWithBuffer:[_device newBufferWithLength:blockLen options:opts]];
if (!_current.next) if (!_current.next)
return NO; return NO;

View File

@ -55,6 +55,12 @@ typedef struct
vector_float2 texCoord METAL_ATTRIBUTE(VertexAttributeTexcoord); vector_float2 texCoord METAL_ATTRIBUTE(VertexAttributeTexcoord);
} Vertex; } Vertex;
typedef struct
{
vector_float4 position;
vector_float2 texCoord;
} VertexSlang;
typedef struct typedef struct
{ {
vector_float4 position METAL_POSITION; vector_float4 position METAL_POSITION;

View File

@ -81,38 +81,32 @@ fragment half4 stock_fragment_color(FontFragmentIn in [[ stage_in ]])
#pragma mark - filter kernels #pragma mark - filter kernels
kernel void convert_bgra4444_to_bgra8888(device uint16_t * in [[ buffer(0) ]], kernel void convert_bgra4444_to_bgra8888(texture2d<ushort, access::read> in [[ texture(0) ]],
texture2d<half, access::write> out [[ texture(0) ]], texture2d<half, access::write> out [[ texture(1) ]],
uint id [[ thread_position_in_grid ]]) uint2 gid [[ thread_position_in_grid ]])
{ {
uint16_t pix = in[id]; ushort pix = in.read(gid).r;
uchar4 pix2 = uchar4( uchar4 pix2 = uchar4(
extract_bits(pix, 4, 4), extract_bits(pix, 4, 4),
extract_bits(pix, 8, 4), extract_bits(pix, 8, 4),
extract_bits(pix, 12, 4), extract_bits(pix, 12, 4),
extract_bits(pix, 0, 4) extract_bits(pix, 0, 4)
); );
uint ypos = id / out.get_width(); out.write(half4(pix2) / 15.0, gid);
uint xpos = id % out.get_width();
out.write(half4(pix2) / 15.0, uint2(xpos, ypos));
} }
kernel void convert_rgb565_to_bgra8888(device uint16_t * in [[ buffer(0) ]], kernel void convert_rgb565_to_bgra8888(texture2d<ushort, access::read> in [[ texture(0) ]],
texture2d<half, access::write> out [[ texture(0) ]], texture2d<half, access::write> out [[ texture(1) ]],
uint id [[ thread_position_in_grid ]]) uint2 gid [[ thread_position_in_grid ]])
{ {
uint16_t pix = in[id]; ushort pix = in.read(gid).r;
uchar4 pix2 = uchar4( uchar4 pix2 = uchar4(
extract_bits(pix, 11, 5), extract_bits(pix, 11, 5),
extract_bits(pix, 5, 6), extract_bits(pix, 5, 6),
extract_bits(pix, 0, 5), extract_bits(pix, 0, 5),
0xf 0xf
); );
uint ypos = id / out.get_width(); out.write(half4(pix2) / half4(0x1f, 0x3f, 0x1f, 0xf), gid);
uint xpos = id % out.get_width();
out.write(half4(pix2) / half4(0x1f, 0x3f, 0x1f, 0xf), uint2(xpos, ypos));
} }

View File

@ -16,8 +16,8 @@
CGRect _frame; CGRect _frame;
NSUInteger _bpp; NSUInteger _bpp;
id<MTLBuffer> _pixels; // frame buffer in _srcFmt id<MTLTexture> _src; // source texture
bool _pixelsDirty; bool _srcDirty;
} }
- (instancetype)initWithDescriptor:(ViewDescriptor *)d context:(Context *)c - (instancetype)initWithDescriptor:(ViewDescriptor *)d context:(Context *)c
@ -53,7 +53,6 @@
_size = size; _size = size;
// create new texture
{ {
MTLTextureDescriptor *td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm MTLTextureDescriptor *td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm
width:(NSUInteger)size.width width:(NSUInteger)size.width
@ -65,8 +64,11 @@
if (_format != RPixelFormatBGRA8Unorm && _format != RPixelFormatBGRX8Unorm) if (_format != RPixelFormatBGRA8Unorm && _format != RPixelFormatBGRX8Unorm)
{ {
_pixels = [_context.device newBufferWithLength:(NSUInteger)(size.width * size.height * 2) MTLTextureDescriptor *td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatR16Uint
options:MTLResourceStorageModeManaged]; width:(NSUInteger)size.width
height:(NSUInteger)size.height
mipmapped:NO];
_src = [_context.device newTextureWithDescriptor:td];
} }
} }
@ -112,11 +114,11 @@
if (_format == RPixelFormatBGRA8Unorm || _format == RPixelFormatBGRX8Unorm) if (_format == RPixelFormatBGRA8Unorm || _format == RPixelFormatBGRX8Unorm)
return; return;
if (!_pixelsDirty) if (!_srcDirty)
return; return;
[_context convertFormat:_format from:_pixels to:_texture]; [_context convertFormat:_format from:_src to:_texture];
_pixelsDirty = NO; _srcDirty = NO;
} }
- (void)drawWithContext:(Context *)ctx - (void)drawWithContext:(Context *)ctx
@ -141,26 +143,10 @@
} }
else else
{ {
void *dst = _pixels.contents; [_src replaceRegion:MTLRegionMake2D(0, 0, (NSUInteger)_size.width, (NSUInteger)_size.height)
size_t len = (size_t)(_bpp * _size.width); mipmapLevel:0 withBytes:src
assert(len <= pitch); // the length can't be larger? bytesPerRow:(NSUInteger)(pitch)];
_srcDirty = YES;
if (len < pitch)
{
for (int i = 0; i < _size.height; i++)
{
memcpy(dst, src, len);
dst += len;
src += pitch;
}
}
else
{
memcpy(dst, src, _pixels.length);
}
[_pixels didModifyRange:NSMakeRange(0, _pixels.length)];
_pixelsDirty = YES;
} }
} }

View File

@ -40,6 +40,7 @@ extern MTLPixelFormat SelectOptimalPixelFormat(MTLPixelFormat fmt);
- (void)setFilteringIndex:(int)index smooth:(bool)smooth; - (void)setFilteringIndex:(int)index smooth:(bool)smooth;
- (BOOL)setShaderFromPath:(NSString *)path; - (BOOL)setShaderFromPath:(NSString *)path;
- (void)updateFrame:(void const *)src pitch:(NSUInteger)pitch; - (void)updateFrame:(void const *)src pitch:(NSUInteger)pitch;
- (bool)readViewport:(uint8_t *)buffer isIdle:(bool)isIdle;
@end @end

View File

@ -299,30 +299,29 @@
settings_t *settings = config_get_ptr(); settings_t *settings = config_get_ptr();
if (settings && settings->bools.video_msg_bgcolor_enable) if (settings && settings->bools.video_msg_bgcolor_enable)
{ {
int msg_width = int msg_width =
font_driver_get_message_width(NULL, msg, (unsigned)strlen(msg), 1.0f); font_driver_get_message_width(NULL, msg, (unsigned)strlen(msg), 1.0f);
float x = video_info->font_msg_pos_x; float x = video_info->font_msg_pos_x;
float y = 1.0f - video_info->font_msg_pos_y; float y = 1.0f - video_info->font_msg_pos_y;
float width = msg_width / (float)_viewport->full_width; float width = msg_width / (float)_viewport->full_width;
float height = float height =
settings->floats.video_font_size / (float)_viewport->full_height; settings->floats.video_font_size / (float)_viewport->full_height;
y -= height; y -= height;
float x2 = 0.005f; /* extend background around text */
float y2 = 0.005f;
float x2 = 0.005f; /* extend background around text */ x -= x2;
float y2 = 0.005f; y -= y2;
width += x2;
height += y2;
x -= x2; float r = settings->uints.video_msg_bgcolor_red / 255.0f;
y -= y2; float g = settings->uints.video_msg_bgcolor_green / 255.0f;
width += x2; float b = settings->uints.video_msg_bgcolor_blue / 255.0f;
height += y2; float a = settings->floats.video_msg_bgcolor_opacity;
float r = settings->uints.video_msg_bgcolor_red / 255.0f;
float g = settings->uints.video_msg_bgcolor_green / 255.0f;
float b = settings->uints.video_msg_bgcolor_blue / 255.0f;
float a = settings->floats.video_msg_bgcolor_opacity;
[_context resetRenderViewport]; [_context resetRenderViewport];
[_context drawQuadX:x y:y w:width h:height r:r g:g b:b a:a]; [_context drawQuadX:x y:y w:width h:height r:r g:g b:b a:a];
} }
@ -332,7 +331,12 @@
- (void)_beginFrame - (void)_beginFrame
{ {
video_viewport_t vp = *_viewport;
video_driver_update_viewport(_viewport, NO, _keepAspect); video_driver_update_viewport(_viewport, NO, _keepAspect);
if (memcmp(&vp, _viewport, sizeof(vp)) != 0)
{
_context.viewport = _viewport;
}
[_context begin]; [_context begin];
[self _updateUniforms]; [self _updateUniforms];
} }
@ -542,12 +546,13 @@ typedef struct MTLALIGN(16)
Context *_context; Context *_context;
id<MTLTexture> _texture; // final render texture id<MTLTexture> _texture; // final render texture
Vertex _v[4]; Vertex _v[4];
VertexSlang _vertex[4];
CGSize _size; // size of view in pixels CGSize _size; // size of view in pixels
CGRect _frame; CGRect _frame;
NSUInteger _bpp; NSUInteger _bpp;
id<MTLBuffer> _pixels; // frame buffer in _srcFmt id<MTLTexture> _src; // src texture
bool _pixelsDirty; bool _srcDirty;
id<MTLSamplerState> _samplers[RARCH_FILTER_MAX][RARCH_WRAP_MAX]; id<MTLSamplerState> _samplers[RARCH_FILTER_MAX][RARCH_WRAP_MAX];
struct video_shader *_shader; struct video_shader *_shader;
@ -583,6 +588,15 @@ typedef struct MTLALIGN(16)
self.size = d.size; self.size = d.size;
self.frame = CGRectMake(0, 0, 1, 1); self.frame = CGRectMake(0, 0, 1, 1);
resize_render_targets = YES; resize_render_targets = YES;
// init slang vertex buffer
VertexSlang v[4] = {
{simd_make_float4(0, 1, 0, 1), simd_make_float2(0, 1)},
{simd_make_float4(1, 1, 0, 1), simd_make_float2(1, 1)},
{simd_make_float4(0, 0, 0, 1), simd_make_float2(0, 0)},
{simd_make_float4(1, 0, 0, 1), simd_make_float2(1, 0)},
};
memcpy(_vertex, v, sizeof(_vertex));
} }
return self; return self;
} }
@ -655,8 +669,11 @@ typedef struct MTLALIGN(16)
if (_format != RPixelFormatBGRA8Unorm && _format != RPixelFormatBGRX8Unorm) if (_format != RPixelFormatBGRA8Unorm && _format != RPixelFormatBGRX8Unorm)
{ {
_pixels = [_context.device newBufferWithLength:(NSUInteger)(size.width * size.height * 2) MTLTextureDescriptor *td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatR16Uint
options:MTLResourceStorageModeManaged]; width:(NSUInteger)size.width
height:(NSUInteger)size.height
mipmapped:NO];
_src = [_context.device newTextureWithDescriptor:td];
} }
} }
@ -702,11 +719,11 @@ typedef struct MTLALIGN(16)
if (_format == RPixelFormatBGRA8Unorm || _format == RPixelFormatBGRX8Unorm) if (_format == RPixelFormatBGRA8Unorm || _format == RPixelFormatBGRX8Unorm)
return; return;
if (!_pixelsDirty) if (!_srcDirty)
return; return;
[_context convertFormat:_format from:_pixels to:_texture]; [_context convertFormat:_format from:_src to:_texture];
_pixelsDirty = NO; _srcDirty = NO;
} }
- (void)_updateHistory - (void)_updateHistory
@ -743,6 +760,24 @@ typedef struct MTLALIGN(16)
} }
} }
- (bool)readViewport:(uint8_t *)buffer isIdle:(bool)isIdle
{
RARCH_LOG("[Metal]: readViewport is_idle = %s\n", isIdle ? "YES" : "NO");
bool enabled = _context.captureEnabled;
if (!enabled)
_context.captureEnabled = YES;
video_driver_cached_frame();
bool res = [_context readBackBuffer:buffer];
if (!enabled)
_context.captureEnabled = NO;
return res;
}
- (void)updateFrame:(void const *)src pitch:(NSUInteger)pitch - (void)updateFrame:(void const *)src pitch:(NSUInteger)pitch
{ {
if (_shader && (_engine.frame.output_size.x != _viewport->width || if (_shader && (_engine.frame.output_size.x != _viewport->width ||
@ -778,26 +813,10 @@ typedef struct MTLALIGN(16)
} }
else else
{ {
void *dst = _pixels.contents; [_src replaceRegion:MTLRegionMake2D(0, 0, (NSUInteger)_size.width, (NSUInteger)_size.height)
size_t len = (size_t)(_bpp * _size.width); mipmapLevel:0 withBytes:src
assert(len <= pitch); // the length can't be larger? bytesPerRow:(NSUInteger)(pitch)];
_srcDirty = YES;
if (len < pitch)
{
for (int i = 0; i < _size.height; i++)
{
memcpy(dst, src, len);
dst += len;
src += pitch;
}
}
else
{
memcpy(dst, src, _pixels.length);
}
[_pixels didModifyRange:NSMakeRange(0, _pixels.length)];
_pixelsDirty = YES;
} }
} }
@ -825,19 +844,6 @@ typedef struct MTLALIGN(16)
init_history = NO; init_history = NO;
} }
typedef struct vertex
{
simd_float4 pos;
simd_float2 tex;
} vertex_t;
static vertex_t vertex_bytes[] = {
{{0, 1, 0, 1}, {0, 1}},
{{1, 1, 0, 1}, {1, 1}},
{{0, 0, 0, 1}, {0, 0}},
{{1, 0, 0, 1}, {1, 0}},
};
- (void)drawWithEncoder:(id<MTLRenderCommandEncoder>)rce - (void)drawWithEncoder:(id<MTLRenderCommandEncoder>)rce
{ {
if (_texture) if (_texture)
@ -951,7 +957,7 @@ static vertex_t vertex_bytes[] = {
[rce setFragmentTextures:textures withRange:NSMakeRange(0, SLANG_NUM_BINDINGS)]; [rce setFragmentTextures:textures withRange:NSMakeRange(0, SLANG_NUM_BINDINGS)];
[rce setFragmentSamplerStates:samplers withRange:NSMakeRange(0, SLANG_NUM_BINDINGS)]; [rce setFragmentSamplerStates:samplers withRange:NSMakeRange(0, SLANG_NUM_BINDINGS)];
[rce setVertexBytes:vertex_bytes length:sizeof(vertex_bytes) atIndex:4]; [rce setVertexBytes:_vertex length:sizeof(_vertex) atIndex:4];
[rce drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4]; [rce drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
if (!backBuffer) if (!backBuffer)
@ -1171,13 +1177,13 @@ static vertex_t vertex_bytes[] = {
@try @try
{ {
MTLVertexDescriptor *vd = [MTLVertexDescriptor new]; MTLVertexDescriptor *vd = [MTLVertexDescriptor new];
vd.attributes[0].offset = offsetof(vertex_t, pos); vd.attributes[0].offset = offsetof(VertexSlang, position);
vd.attributes[0].format = MTLVertexFormatFloat4; vd.attributes[0].format = MTLVertexFormatFloat4;
vd.attributes[0].bufferIndex = 4; vd.attributes[0].bufferIndex = 4;
vd.attributes[1].offset = offsetof(vertex_t, tex); vd.attributes[1].offset = offsetof(VertexSlang, texCoord);
vd.attributes[1].format = MTLVertexFormatFloat2; vd.attributes[1].format = MTLVertexFormatFloat2;
vd.attributes[1].bufferIndex = 4; vd.attributes[1].bufferIndex = 4;
vd.layouts[4].stride = sizeof(vertex_t); vd.layouts[4].stride = sizeof(VertexSlang);
vd.layouts[4].stepFunction = MTLVertexStepFunctionPerVertex; vd.layouts[4].stepFunction = MTLVertexStepFunctionPerVertex;
MTLRenderPipelineDescriptor *psd = [MTLRenderPipelineDescriptor new]; MTLRenderPipelineDescriptor *psd = [MTLRenderPipelineDescriptor new];
@ -1204,7 +1210,7 @@ static vertex_t vertex_bytes[] = {
if (lib == nil) if (lib == nil)
{ {
save_msl = true; save_msl = true;
RARCH_ERR("Metal]: unable to compile vertex shader: %s\n", err.localizedDescription.UTF8String); RARCH_ERR("[Metal]: unable to compile vertex shader: %s\n", err.localizedDescription.UTF8String);
return NO; return NO;
} }
#if DEBUG #if DEBUG
@ -1220,7 +1226,7 @@ static vertex_t vertex_bytes[] = {
if (lib == nil) if (lib == nil)
{ {
save_msl = true; save_msl = true;
RARCH_ERR("Metal]: unable to compile fragment shader: %s\n", err.localizedDescription.UTF8String); RARCH_ERR("[Metal]: unable to compile fragment shader: %s\n", err.localizedDescription.UTF8String);
return NO; return NO;
} }
#if DEBUG #if DEBUG
@ -1234,7 +1240,8 @@ static vertex_t vertex_bytes[] = {
if (err != nil) if (err != nil)
{ {
save_msl = true; save_msl = true;
RARCH_ERR("error creating pipeline state: %s", err.localizedDescription.UTF8String); RARCH_ERR("[Metal]: error creating pipeline state for pass %d: %s\n", i,
err.localizedDescription.UTF8String);
return NO; return NO;
} }
@ -1253,10 +1260,11 @@ static vertex_t vertex_bytes[] = {
{ {
if (save_msl) if (save_msl)
{ {
RARCH_LOG("[Metal]: saving metal shader files\n"); NSString *basePath = [[NSString stringWithUTF8String:shader->pass[i].source.path] stringByDeletingPathExtension];
RARCH_LOG("[Metal]: saving metal shader files to %s\n", basePath.UTF8String);
NSError *err = nil; NSError *err = nil;
NSString *basePath = [[NSString stringWithUTF8String:shader->pass[i].source.path] stringByDeletingPathExtension];
[vs_src writeToFile:[basePath stringByAppendingPathExtension:@"vs.metal"] [vs_src writeToFile:[basePath stringByAppendingPathExtension:@"vs.metal"]
atomically:NO atomically:NO
encoding:NSStringEncodingConversionAllowLossy encoding:NSStringEncodingConversionAllowLossy

View File

@ -0,0 +1,79 @@
#ifndef SWITCH_COMMON_H__
#define SWITCH_COMMON_H__
#include <switch.h>
#include <gfx/scaler/scaler.h>
#ifdef HAVE_EGL
#include "../common/egl_common.h"
#endif
typedef struct
{
bool vsync;
bool rgb32;
bool smooth; // bilinear
unsigned width, height;
unsigned rotation;
struct video_viewport vp;
struct texture_image *overlay;
bool overlay_enabled;
bool in_menu;
struct
{
bool enable;
bool fullscreen;
uint32_t *pixels;
uint32_t width;
uint32_t height;
unsigned tgtw;
unsigned tgth;
struct scaler_ctx scaler;
} menu_texture;
struct
{
uint32_t width;
uint32_t height;
uint32_t x_offset;
} hw_scale;
uint32_t image[1280 * 720];
uint32_t tmp_image[1280 * 720];
u32 cnt;
struct scaler_ctx scaler;
uint32_t last_width;
uint32_t last_height;
bool keep_aspect;
bool should_resize;
bool need_clear;
bool is_threaded;
bool o_size;
uint32_t o_height;
uint32_t o_width;
} switch_video_t;
typedef struct
{
#ifdef HAVE_EGL
egl_ctx_data_t egl;
#endif
struct
{
unsigned short width;
unsigned short height;
} native_window;
bool resize;
unsigned width, height;
float refresh_rate;
} switch_ctx_data_t;
void gfx_slow_swizzling_blit(uint32_t *buffer, uint32_t *image, int w, int h, int tx, int ty, bool blend);
#endif

View File

@ -38,6 +38,14 @@
#include "../../libretro-common/include/retro_math.h" #include "../../libretro-common/include/retro_math.h"
#include "../../libretro-common/include/string/stdstring.h" #include "../../libretro-common/include/string/stdstring.h"
#define VENDOR_ID_AMD 0x1002
#define VENDOR_ID_NV 0x10DE
#define VENDOR_ID_INTEL 0x8086
#ifdef _WIN32
#define VULKAN_EMULATE_MAILBOX
#endif
static dylib_t vulkan_library; static dylib_t vulkan_library;
static VkInstance cached_instance_vk; static VkInstance cached_instance_vk;
static VkDevice cached_device_vk; static VkDevice cached_device_vk;
@ -88,6 +96,7 @@ static VKAPI_ATTR VkBool32 VKAPI_CALL vulkan_debug_cb(
RARCH_ERR("[Vulkan]: Error: %s: %s\n", RARCH_ERR("[Vulkan]: Error: %s: %s\n",
pLayerPrefix, pMessage); pLayerPrefix, pMessage);
} }
#if 0
else if (flags & VK_DEBUG_REPORT_WARNING_BIT_EXT) else if (flags & VK_DEBUG_REPORT_WARNING_BIT_EXT)
{ {
RARCH_WARN("[Vulkan]: Warning: %s: %s\n", RARCH_WARN("[Vulkan]: Warning: %s: %s\n",
@ -103,11 +112,157 @@ static VKAPI_ATTR VkBool32 VKAPI_CALL vulkan_debug_cb(
RARCH_LOG("[Vulkan]: Information: %s: %s\n", RARCH_LOG("[Vulkan]: Information: %s: %s\n",
pLayerPrefix, pMessage); pLayerPrefix, pMessage);
} }
#endif
return VK_FALSE; return VK_FALSE;
} }
#endif #endif
void vulkan_emulated_mailbox_deinit(struct vulkan_emulated_mailbox *mailbox)
{
if (mailbox->thread)
{
slock_lock(mailbox->lock);
mailbox->dead = true;
scond_signal(mailbox->cond);
slock_unlock(mailbox->lock);
sthread_join(mailbox->thread);
}
if (mailbox->lock)
slock_free(mailbox->lock);
if (mailbox->cond)
scond_free(mailbox->cond);
memset(mailbox, 0, sizeof(*mailbox));
}
VkResult vulkan_emulated_mailbox_acquire_next_image(struct vulkan_emulated_mailbox *mailbox,
unsigned *index)
{
VkResult res;
if (mailbox->swapchain == VK_NULL_HANDLE)
return VK_ERROR_OUT_OF_DATE_KHR;
slock_lock(mailbox->lock);
if (!mailbox->has_pending_request)
{
mailbox->request_acquire = true;
scond_signal(mailbox->cond);
}
mailbox->has_pending_request = true;
if (mailbox->acquired)
{
res = mailbox->result;
*index = mailbox->index;
mailbox->has_pending_request = false;
mailbox->acquired = false;
}
else
res = VK_TIMEOUT;
slock_unlock(mailbox->lock);
return res;
}
VkResult vulkan_emulated_mailbox_acquire_next_image_blocking(
struct vulkan_emulated_mailbox *mailbox,
unsigned *index)
{
VkResult res;
if (mailbox->swapchain == VK_NULL_HANDLE)
return VK_ERROR_OUT_OF_DATE_KHR;
slock_lock(mailbox->lock);
if (!mailbox->has_pending_request)
{
mailbox->request_acquire = true;
scond_signal(mailbox->cond);
}
mailbox->has_pending_request = true;
while (!mailbox->acquired)
scond_wait(mailbox->cond, mailbox->lock);
res = mailbox->result;
if (res == VK_SUCCESS)
*index = mailbox->index;
mailbox->has_pending_request = false;
mailbox->acquired = false;
slock_unlock(mailbox->lock);
return res;
}
static void vulkan_emulated_mailbox_loop(void *userdata)
{
VkResult res;
VkFence fence;
VkFenceCreateInfo info = { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO };
struct vulkan_emulated_mailbox *mailbox =
(struct vulkan_emulated_mailbox *)userdata;
vkCreateFence(mailbox->device, &info, NULL, &fence);
for (;;)
{
slock_lock(mailbox->lock);
while (!mailbox->dead && !mailbox->request_acquire)
scond_wait(mailbox->cond, mailbox->lock);
if (mailbox->dead)
{
slock_unlock(mailbox->lock);
break;
}
mailbox->request_acquire = false;
slock_unlock(mailbox->lock);
mailbox->result = vkAcquireNextImageKHR(mailbox->device, mailbox->swapchain, UINT64_MAX,
VK_NULL_HANDLE, fence, &mailbox->index);
if (mailbox->result == VK_SUCCESS)
vkWaitForFences(mailbox->device, 1, &fence, true, UINT64_MAX);
vkResetFences(mailbox->device, 1, &fence);
if (mailbox->result == VK_SUCCESS)
{
slock_lock(mailbox->lock);
mailbox->acquired = true;
scond_signal(mailbox->cond);
slock_unlock(mailbox->lock);
}
}
vkDestroyFence(mailbox->device, fence, NULL);
}
bool vulkan_emulated_mailbox_init(struct vulkan_emulated_mailbox *mailbox,
VkDevice device,
VkSwapchainKHR swapchain)
{
memset(mailbox, 0, sizeof(*mailbox));
mailbox->device = device;
mailbox->swapchain = swapchain;
mailbox->cond = scond_new();
if (!mailbox->cond)
return false;
mailbox->lock = slock_new();
if (!mailbox->lock)
return false;
mailbox->thread = sthread_create(vulkan_emulated_mailbox_loop, mailbox);
if (!mailbox->thread)
return false;
return true;
}
uint32_t vulkan_find_memory_type( uint32_t vulkan_find_memory_type(
const VkPhysicalDeviceMemoryProperties *mem_props, const VkPhysicalDeviceMemoryProperties *mem_props,
uint32_t device_reqs, uint32_t host_reqs) uint32_t device_reqs, uint32_t host_reqs)
@ -184,13 +339,12 @@ void vulkan_copy_staging_to_dynamic(vk_t *vk, VkCommandBuffer cmd,
struct vk_texture *dynamic, struct vk_texture *dynamic,
struct vk_texture *staging) struct vk_texture *staging)
{ {
VkImageCopy region; VkBufferImageCopy region;
retro_assert(dynamic->type == VULKAN_TEXTURE_DYNAMIC); retro_assert(dynamic->type == VULKAN_TEXTURE_DYNAMIC);
retro_assert(staging->type == VULKAN_TEXTURE_STAGING); retro_assert(staging->type == VULKAN_TEXTURE_STAGING);
vulkan_sync_texture_to_gpu(vk, staging); vulkan_sync_texture_to_gpu(vk, staging);
vulkan_transition_texture(vk, cmd, staging);
/* We don't have to sync against previous TRANSFER, /* We don't have to sync against previous TRANSFER,
* since we observed the completion by fences. * since we observed the completion by fences.
@ -208,15 +362,14 @@ void vulkan_copy_staging_to_dynamic(vk_t *vk, VkCommandBuffer cmd,
VK_PIPELINE_STAGE_TRANSFER_BIT); VK_PIPELINE_STAGE_TRANSFER_BIT);
memset(&region, 0, sizeof(region)); memset(&region, 0, sizeof(region));
region.extent.width = dynamic->width; region.imageExtent.width = dynamic->width;
region.extent.height = dynamic->height; region.imageExtent.height = dynamic->height;
region.extent.depth = 1; region.imageExtent.depth = 1;
region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.srcSubresource.layerCount = 1; region.imageSubresource.layerCount = 1;
region.dstSubresource = region.srcSubresource;
vkCmdCopyImage(cmd, vkCmdCopyBufferToImage(cmd,
staging->image, VK_IMAGE_LAYOUT_GENERAL, staging->buffer,
dynamic->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, dynamic->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1, &region); 1, &region);
@ -323,6 +476,7 @@ struct vk_texture vulkan_create_texture(vk_t *vk,
VkSubresourceLayout layout; VkSubresourceLayout layout;
VkDevice device = vk->context->device; VkDevice device = vk->context->device;
VkImageCreateInfo info = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; VkImageCreateInfo info = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
VkBufferCreateInfo buffer_info = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
VkImageViewCreateInfo view = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; VkImageViewCreateInfo view = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
VkMemoryAllocateInfo alloc = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; VkMemoryAllocateInfo alloc = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
VkImageSubresource subresource = { VK_IMAGE_ASPECT_COLOR_BIT }; VkImageSubresource subresource = { VK_IMAGE_ASPECT_COLOR_BIT };
@ -338,6 +492,10 @@ struct vk_texture vulkan_create_texture(vk_t *vk,
info.extent.height = height; info.extent.height = height;
info.extent.depth = 1; info.extent.depth = 1;
info.arrayLayers = 1; info.arrayLayers = 1;
info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
buffer_info.size = width * height * vulkan_format_to_bpp(format);
buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
/* For simplicity, always build mipmaps for /* For simplicity, always build mipmaps for
* static textures, samplers can be used to enable it dynamically. * static textures, samplers can be used to enable it dynamically.
@ -355,7 +513,7 @@ struct vk_texture vulkan_create_texture(vk_t *vk,
if (type == VULKAN_TEXTURE_STREAMED) if (type == VULKAN_TEXTURE_STREAMED)
{ {
VkFormatProperties format_properties; VkFormatProperties format_properties;
VkFormatFeatureFlags required = VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | const VkFormatFeatureFlags required = VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT |
VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT; VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT;
vkGetPhysicalDeviceFormatProperties( vkGetPhysicalDeviceFormatProperties(
@ -396,23 +554,33 @@ struct vk_texture vulkan_create_texture(vk_t *vk,
break; break;
case VULKAN_TEXTURE_STAGING: case VULKAN_TEXTURE_STAGING:
info.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT; buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
info.initialLayout = VK_IMAGE_LAYOUT_GENERAL;
info.tiling = VK_IMAGE_TILING_LINEAR; info.tiling = VK_IMAGE_TILING_LINEAR;
info.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
break; break;
case VULKAN_TEXTURE_READBACK: case VULKAN_TEXTURE_READBACK:
info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT; buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
info.initialLayout = VK_IMAGE_LAYOUT_GENERAL;
info.tiling = VK_IMAGE_TILING_LINEAR; info.tiling = VK_IMAGE_TILING_LINEAR;
info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
break; break;
} }
vkCreateImage(device, &info, NULL, &tex.image); if (type != VULKAN_TEXTURE_STAGING && type != VULKAN_TEXTURE_READBACK)
{
vkCreateImage(device, &info, NULL, &tex.image);
#if 0 #if 0
vulkan_track_alloc(tex.image); vulkan_track_alloc(tex.image);
#endif #endif
vkGetImageMemoryRequirements(device, tex.image, &mem_reqs); vkGetImageMemoryRequirements(device, tex.image, &mem_reqs);
}
else
{
/* Linear staging textures are not guaranteed to be supported,
* use buffers instead. */
vkCreateBuffer(device, &buffer_info, NULL, &tex.buffer);
vkGetBufferMemoryRequirements(device, tex.buffer, &mem_reqs);
}
alloc.allocationSize = mem_reqs.size; alloc.allocationSize = mem_reqs.size;
switch (type) switch (type)
@ -449,13 +617,14 @@ struct vk_texture vulkan_create_texture(vk_t *vk,
{ {
/* Recreate texture but for STAGING this time ... */ /* Recreate texture but for STAGING this time ... */
RARCH_LOG("[Vulkan]: GPU supports linear images as textures, but not DEVICE_LOCAL. Falling back to copy path.\n"); RARCH_LOG("[Vulkan]: GPU supports linear images as textures, but not DEVICE_LOCAL. Falling back to copy path.\n");
type = VULKAN_TEXTURE_STAGING; type = VULKAN_TEXTURE_STAGING;
vkDestroyImage(device, tex.image, NULL); vkDestroyImage(device, tex.image, NULL);
tex.image = NULL;
info.initialLayout = VK_IMAGE_LAYOUT_GENERAL;
info.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT; buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
vkCreateImage(device, &info, NULL, &tex.image); vkCreateBuffer(device, &buffer_info, NULL, &tex.buffer);
vkGetBufferMemoryRequirements(device, tex.buffer, &mem_reqs);
vkGetImageMemoryRequirements(device, tex.image, &mem_reqs);
alloc.allocationSize = mem_reqs.size; alloc.allocationSize = mem_reqs.size;
alloc.memoryTypeIndex = vulkan_find_memory_type_fallback( alloc.memoryTypeIndex = vulkan_find_memory_type_fallback(
@ -478,6 +647,8 @@ struct vk_texture vulkan_create_texture(vk_t *vk,
vulkan_track_dealloc(old->image); vulkan_track_dealloc(old->image);
#endif #endif
} }
if (old && old->buffer != VK_NULL_HANDLE)
vkDestroyBuffer(vk->context->device, old->buffer, NULL);
/* We can pilfer the old memory and move it over to the new texture. */ /* We can pilfer the old memory and move it over to the new texture. */
if (old && if (old &&
@ -507,7 +678,10 @@ struct vk_texture vulkan_create_texture(vk_t *vk,
memset(old, 0, sizeof(*old)); memset(old, 0, sizeof(*old));
} }
vkBindImageMemory(device, tex.image, tex.memory, 0); if (tex.image)
vkBindImageMemory(device, tex.image, tex.memory, 0);
if (tex.buffer)
vkBindBufferMemory(device, tex.buffer, tex.memory, 0);
if (type != VULKAN_TEXTURE_STAGING && type != VULKAN_TEXTURE_READBACK) if (type != VULKAN_TEXTURE_STAGING && type != VULKAN_TEXTURE_READBACK)
{ {
@ -532,8 +706,14 @@ struct vk_texture vulkan_create_texture(vk_t *vk,
else else
tex.view = VK_NULL_HANDLE; tex.view = VK_NULL_HANDLE;
if (info.tiling == VK_IMAGE_TILING_LINEAR) if (tex.image && info.tiling == VK_IMAGE_TILING_LINEAR)
vkGetImageSubresourceLayout(device, tex.image, &subresource, &layout); vkGetImageSubresourceLayout(device, tex.image, &subresource, &layout);
else if (tex.buffer)
{
layout.offset = 0;
layout.size = buffer_info.size;
layout.rowPitch = width * vulkan_format_to_bpp(format);
}
else else
memset(&layout, 0, sizeof(layout)); memset(&layout, 0, sizeof(layout));
@ -568,7 +748,7 @@ struct vk_texture vulkan_create_texture(vk_t *vk,
} }
else if (initial && type == VULKAN_TEXTURE_STATIC) else if (initial && type == VULKAN_TEXTURE_STATIC)
{ {
VkImageCopy region; VkBufferImageCopy region;
VkCommandBuffer staging; VkCommandBuffer staging;
struct vk_texture tmp = vulkan_create_texture(vk, NULL, struct vk_texture tmp = vulkan_create_texture(vk, NULL,
width, height, format, initial, NULL, VULKAN_TEXTURE_STAGING); width, height, format, initial, NULL, VULKAN_TEXTURE_STAGING);
@ -583,12 +763,6 @@ struct vk_texture vulkan_create_texture(vk_t *vk,
vkBeginCommandBuffer(staging, &begin_info); vkBeginCommandBuffer(staging, &begin_info);
vulkan_image_layout_transition(vk, staging, tmp.image,
VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_GENERAL,
VK_ACCESS_HOST_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
VK_PIPELINE_STAGE_HOST_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT);
/* If doing mipmapping on upload, keep in general so we can easily do transfers to /* If doing mipmapping on upload, keep in general so we can easily do transfers to
* and transfers from the images without having to * and transfers from the images without having to
* mess around with lots of extra transitions at per-level granularity. * mess around with lots of extra transitions at per-level granularity.
@ -603,16 +777,14 @@ struct vk_texture vulkan_create_texture(vk_t *vk,
VK_PIPELINE_STAGE_TRANSFER_BIT); VK_PIPELINE_STAGE_TRANSFER_BIT);
memset(&region, 0, sizeof(region)); memset(&region, 0, sizeof(region));
region.extent.width = width; region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.extent.height = height; region.imageSubresource.layerCount = 1;
region.extent.depth = 1; region.imageExtent.width = width;
region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; region.imageExtent.height = height;
region.srcSubresource.layerCount = 1; region.imageExtent.depth = 1;
region.dstSubresource = region.srcSubresource;
vkCmdCopyImage(staging, vkCmdCopyBufferToImage(staging,
tmp.image, tmp.buffer,
VK_IMAGE_LAYOUT_GENERAL,
tex.image, tex.image,
tex.mipmap ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, tex.mipmap ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1, &region); 1, &region);
@ -710,12 +882,18 @@ void vulkan_destroy_texture(
{ {
if (tex->mapped) if (tex->mapped)
vkUnmapMemory(device, tex->memory); vkUnmapMemory(device, tex->memory);
vkFreeMemory(device, tex->memory, NULL);
if (tex->view) if (tex->view)
vkDestroyImageView(device, tex->view, NULL); vkDestroyImageView(device, tex->view, NULL);
vkDestroyImage(device, tex->image, NULL); if (tex->image)
vkDestroyImage(device, tex->image, NULL);
if (tex->buffer)
vkDestroyBuffer(device, tex->buffer, NULL);
if (tex->memory)
vkFreeMemory(device, tex->memory, NULL);
#ifdef VULKAN_DEBUG_TEXTURE_ALLOC #ifdef VULKAN_DEBUG_TEXTURE_ALLOC
vulkan_track_dealloc(tex->image); if (tex->image)
vulkan_track_dealloc(tex->image);
#endif #endif
memset(tex, 0, sizeof(*tex)); memset(tex, 0, sizeof(*tex));
} }
@ -762,6 +940,9 @@ static void vulkan_write_quad_descriptors(
void vulkan_transition_texture(vk_t *vk, VkCommandBuffer cmd, struct vk_texture *texture) void vulkan_transition_texture(vk_t *vk, VkCommandBuffer cmd, struct vk_texture *texture)
{ {
if (!texture->image)
return;
/* Transition to GENERAL layout for linear streamed textures. /* Transition to GENERAL layout for linear streamed textures.
* We're using linear textures here, so only * We're using linear textures here, so only
* GENERAL layout is supported. * GENERAL layout is supported.
@ -782,14 +963,6 @@ void vulkan_transition_texture(vk_t *vk, VkCommandBuffer cmd, struct vk_texture
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
break; break;
case VULKAN_TEXTURE_STAGING:
vulkan_image_layout_transition(vk, cmd, texture->image,
texture->layout, VK_IMAGE_LAYOUT_GENERAL,
VK_ACCESS_HOST_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
VK_PIPELINE_STAGE_HOST_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT);
break;
default: default:
retro_assert(0 && "Attempting to transition invalid texture type.\n"); retro_assert(0 && "Attempting to transition invalid texture type.\n");
break; break;
@ -1550,6 +1723,12 @@ static bool vulkan_context_init_device(gfx_ctx_vulkan_data_t *vk)
vkGetPhysicalDeviceMemoryProperties(vk->context.gpu, vkGetPhysicalDeviceMemoryProperties(vk->context.gpu,
&vk->context.memory_properties); &vk->context.memory_properties);
#ifdef VULKAN_EMULATE_MAILBOX
/* Win32 windowed mode seems to deal just fine with toggling VSync.
* Fullscreen however ... */
vk->emulate_mailbox = vk->fullscreen;
#endif
RARCH_LOG("[Vulkan]: Using GPU: %s\n", vk->context.gpu_properties.deviceName); RARCH_LOG("[Vulkan]: Using GPU: %s\n", vk->context.gpu_properties.deviceName);
if (vk->context.device == VK_NULL_HANDLE) if (vk->context.device == VK_NULL_HANDLE)
@ -2309,12 +2488,14 @@ static void vulkan_destroy_swapchain(gfx_ctx_vulkan_data_t *vk)
{ {
unsigned i; unsigned i;
vulkan_emulated_mailbox_deinit(&vk->mailbox);
if (vk->swapchain != VK_NULL_HANDLE) if (vk->swapchain != VK_NULL_HANDLE)
{ {
vkDeviceWaitIdle(vk->context.device); vkDeviceWaitIdle(vk->context.device);
vkDestroySwapchainKHR(vk->context.device, vk->swapchain, NULL); vkDestroySwapchainKHR(vk->context.device, vk->swapchain, NULL);
memset(vk->context.swapchain_images, 0, sizeof(vk->context.swapchain_images)); memset(vk->context.swapchain_images, 0, sizeof(vk->context.swapchain_images));
vk->swapchain = VK_NULL_HANDLE; vk->swapchain = VK_NULL_HANDLE;
vk->context.has_acquired_swapchain = false;
} }
for (i = 0; i < VULKAN_MAX_SWAPCHAIN_IMAGES; i++) for (i = 0; i < VULKAN_MAX_SWAPCHAIN_IMAGES; i++)
@ -2333,9 +2514,13 @@ static void vulkan_destroy_swapchain(gfx_ctx_vulkan_data_t *vk)
void vulkan_present(gfx_ctx_vulkan_data_t *vk, unsigned index) void vulkan_present(gfx_ctx_vulkan_data_t *vk, unsigned index)
{ {
VkPresentInfoKHR present = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR }; VkPresentInfoKHR present = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR };
VkResult result = VK_SUCCESS; VkResult result = VK_SUCCESS;
VkResult err = VK_SUCCESS; VkResult err = VK_SUCCESS;
if (!vk->context.has_acquired_swapchain)
return;
vk->context.has_acquired_swapchain = false;
/* We're still waiting for a proper swapchain, so just fake it. */ /* We're still waiting for a proper swapchain, so just fake it. */
if (vk->swapchain == VK_NULL_HANDLE) if (vk->swapchain == VK_NULL_HANDLE)
@ -2431,8 +2616,8 @@ static void vulkan_acquire_clear_fences(gfx_ctx_vulkan_data_t *vk)
vkDestroyFence(vk->context.device, vkDestroyFence(vk->context.device,
vk->context.swapchain_fences[i], NULL); vk->context.swapchain_fences[i], NULL);
vk->context.swapchain_fences[i] = VK_NULL_HANDLE; vk->context.swapchain_fences[i] = VK_NULL_HANDLE;
vk->context.swapchain_fences_signalled[i] = false;
} }
vk->context.swapchain_fences_signalled[i] = false;
} }
} }
@ -2449,10 +2634,26 @@ static void vulkan_acquire_wait_fences(gfx_ctx_vulkan_data_t *vk)
if (vk->context.swapchain_fences_signalled[index]) if (vk->context.swapchain_fences_signalled[index])
vkWaitForFences(vk->context.device, 1, next_fence, true, UINT64_MAX); vkWaitForFences(vk->context.device, 1, next_fence, true, UINT64_MAX);
vkResetFences(vk->context.device, 1, next_fence); vkResetFences(vk->context.device, 1, next_fence);
vk->context.swapchain_fences_signalled[index] = false;
} }
else else
vkCreateFence(vk->context.device, &fence_info, NULL, next_fence); vkCreateFence(vk->context.device, &fence_info, NULL, next_fence);
vk->context.swapchain_fences_signalled[index] = false;
}
static void vulkan_create_wait_fences(gfx_ctx_vulkan_data_t *vk)
{
VkFenceCreateInfo fence_info =
{ VK_STRUCTURE_TYPE_FENCE_CREATE_INFO };
unsigned i;
for (i = 0; i < vk->context.num_swapchain_images; i++)
{
if (!vk->context.swapchain_fences[i])
{
vkCreateFence(vk->context.device, &fence_info, NULL,
&vk->context.swapchain_fences[i]);
}
}
} }
void vulkan_acquire_next_image(gfx_ctx_vulkan_data_t *vk) void vulkan_acquire_next_image(gfx_ctx_vulkan_data_t *vk)
@ -2489,22 +2690,47 @@ retry:
} }
} }
vkCreateFence(vk->context.device, &fence_info, NULL, &fence); retro_assert(!vk->context.has_acquired_swapchain);
err = vkAcquireNextImageKHR(vk->context.device, if (vk->emulating_mailbox)
vk->swapchain, UINT64_MAX, {
VK_NULL_HANDLE, fence, &vk->context.current_swapchain_index); /* Non-blocking acquire. If we don't get a swapchain frame right away,
* just skip rendering to the swapchain this frame, similar to what
* MAILBOX would do. */
err = vulkan_emulated_mailbox_acquire_next_image(&vk->mailbox, &vk->context.current_swapchain_index);
fence = VK_NULL_HANDLE;
}
else
{
vkCreateFence(vk->context.device, &fence_info, NULL, &fence);
err = vkAcquireNextImageKHR(vk->context.device,
vk->swapchain, UINT64_MAX,
VK_NULL_HANDLE, fence, &vk->context.current_swapchain_index);
}
if (err == VK_SUCCESS) if (err == VK_SUCCESS)
vkWaitForFences(vk->context.device, 1, &fence, true, UINT64_MAX); {
if (fence != VK_NULL_HANDLE)
vkWaitForFences(vk->context.device, 1, &fence, true, UINT64_MAX);
vk->context.has_acquired_swapchain = true;
}
else
vk->context.has_acquired_swapchain = false;
#ifdef WSI_HARDENING_TEST #ifdef WSI_HARDENING_TEST
trigger_spurious_error_vkresult(&err); trigger_spurious_error_vkresult(&err);
#endif #endif
vkDestroyFence(vk->context.device, fence, NULL); if (fence != VK_NULL_HANDLE)
vkDestroyFence(vk->context.device, fence, NULL);
if (err == VK_ERROR_OUT_OF_DATE_KHR) if (err == VK_NOT_READY || err == VK_TIMEOUT)
{
/* Just pretend we have a swapchain index, round-robin style. */
vk->context.current_swapchain_index =
(vk->context.current_swapchain_index + 1) % vk->context.num_swapchain_images;
}
else if (err == VK_ERROR_OUT_OF_DATE_KHR)
{ {
/* Throw away the old swapchain and try again. */ /* Throw away the old swapchain and try again. */
vulkan_destroy_swapchain(vk); vulkan_destroy_swapchain(vk);
@ -2563,10 +2789,19 @@ bool vulkan_create_swapchain(gfx_ctx_vulkan_data_t *vk,
VkPresentModeKHR swapchain_present_mode = VK_PRESENT_MODE_FIFO_KHR; VkPresentModeKHR swapchain_present_mode = VK_PRESENT_MODE_FIFO_KHR;
settings_t *settings = config_get_ptr(); settings_t *settings = config_get_ptr();
VkCompositeAlphaFlagBitsKHR composite = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; VkCompositeAlphaFlagBitsKHR composite = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
VkResult res;
vkDeviceWaitIdle(vk->context.device); vkDeviceWaitIdle(vk->context.device);
vulkan_acquire_clear_fences(vk); vulkan_acquire_clear_fences(vk);
if (swap_interval == 0 && vk->emulate_mailbox)
{
swap_interval = 1;
vk->emulating_mailbox = true;
}
else
vk->emulating_mailbox = false;
vk->created_new_swapchain = true; vk->created_new_swapchain = true;
if (vk->swapchain != VK_NULL_HANDLE && if (vk->swapchain != VK_NULL_HANDLE &&
!vk->context.invalid_swapchain && !vk->context.invalid_swapchain &&
@ -2576,10 +2811,50 @@ bool vulkan_create_swapchain(gfx_ctx_vulkan_data_t *vk,
{ {
/* Do not bother creating a swapchain redundantly. */ /* Do not bother creating a swapchain redundantly. */
RARCH_LOG("[Vulkan]: Do not need to re-create swapchain.\n"); RARCH_LOG("[Vulkan]: Do not need to re-create swapchain.\n");
vk->created_new_swapchain = false; vulkan_create_wait_fences(vk);
return true;
if (vk->emulating_mailbox && vk->mailbox.swapchain == VK_NULL_HANDLE)
{
vulkan_emulated_mailbox_init(&vk->mailbox, vk->context.device, vk->swapchain);
vk->created_new_swapchain = false;
return true;
}
else if (!vk->emulating_mailbox && vk->mailbox.swapchain != VK_NULL_HANDLE)
{
/* We are tearing down, and entering a state where we are supposed to have
* acquired an image, so block until we have acquired. */
if (!vk->context.has_acquired_swapchain)
{
res = vulkan_emulated_mailbox_acquire_next_image_blocking(
&vk->mailbox,
&vk->context.current_swapchain_index);
}
else
res = VK_SUCCESS;
vulkan_emulated_mailbox_deinit(&vk->mailbox);
if (res == VK_SUCCESS)
{
vk->context.has_acquired_swapchain = true;
vk->created_new_swapchain = false;
return true;
}
else
{
vk->context.has_acquired_swapchain = false;
/* We failed for some reason, so create a new swapchain. */
}
}
else
{
vk->created_new_swapchain = false;
return true;
}
} }
vulkan_emulated_mailbox_deinit(&vk->mailbox);
present_mode_count = 0; present_mode_count = 0;
vkGetPhysicalDeviceSurfacePresentModesKHR( vkGetPhysicalDeviceSurfacePresentModesKHR(
vk->context.gpu, vk->vk_surface, vk->context.gpu, vk->vk_surface,
@ -2814,5 +3089,11 @@ bool vulkan_create_swapchain(gfx_ctx_vulkan_data_t *vk,
/* Force driver to reset swapchain image handles. */ /* Force driver to reset swapchain image handles. */
vk->context.invalid_swapchain = true; vk->context.invalid_swapchain = true;
vk->context.has_acquired_swapchain = false;
vulkan_create_wait_fences(vk);
if (vk->emulating_mailbox)
vulkan_emulated_mailbox_init(&vk->mailbox, vk->context.device, vk->swapchain);
return true; return true;
} }

View File

@ -96,6 +96,7 @@ typedef struct vulkan_context
/* Used by screenshot to get blits with correct colorspace. */ /* Used by screenshot to get blits with correct colorspace. */
bool swapchain_is_srgb; bool swapchain_is_srgb;
bool swap_interval_emulation_lock; bool swap_interval_emulation_lock;
bool has_acquired_swapchain;
unsigned swapchain_width; unsigned swapchain_width;
unsigned swapchain_height; unsigned swapchain_height;
@ -127,13 +128,42 @@ typedef struct vulkan_context
#endif #endif
} vulkan_context_t; } vulkan_context_t;
struct vulkan_emulated_mailbox
{
sthread_t *thread;
VkDevice device;
VkSwapchainKHR swapchain;
slock_t *lock;
scond_t *cond;
unsigned index;
bool acquired;
bool request_acquire;
bool dead;
bool has_pending_request;
VkResult result;
};
bool vulkan_emulated_mailbox_init(struct vulkan_emulated_mailbox *mailbox,
VkDevice device, VkSwapchainKHR swapchain);
void vulkan_emulated_mailbox_deinit(struct vulkan_emulated_mailbox *mailbox);
VkResult vulkan_emulated_mailbox_acquire_next_image(struct vulkan_emulated_mailbox *mailbox, unsigned *index);
VkResult vulkan_emulated_mailbox_acquire_next_image_blocking(struct vulkan_emulated_mailbox *mailbox, unsigned *index);
typedef struct gfx_ctx_vulkan_data typedef struct gfx_ctx_vulkan_data
{ {
bool need_new_swapchain; bool need_new_swapchain;
bool created_new_swapchain; bool created_new_swapchain;
bool emulate_mailbox;
bool emulating_mailbox;
vulkan_context_t context; vulkan_context_t context;
VkSurfaceKHR vk_surface; VkSurfaceKHR vk_surface;
VkSwapchainKHR swapchain; VkSwapchainKHR swapchain;
struct vulkan_emulated_mailbox mailbox;
/* Used to check if we need to use mailbox emulation or not.
* Only relevant on Windows for now. */
bool fullscreen;
} gfx_ctx_vulkan_data_t; } gfx_ctx_vulkan_data_t;
struct vulkan_display_surface_info struct vulkan_display_surface_info
@ -179,6 +209,7 @@ struct vk_texture
VkImage image; VkImage image;
VkImageView view; VkImageView view;
VkDeviceMemory memory; VkDeviceMemory memory;
VkBuffer buffer;
VkFormat format; VkFormat format;

View File

@ -28,14 +28,14 @@ static void null_display_server_destroy(void *data)
(void)data; (void)data;
} }
static bool null_set_window_opacity(void *data, unsigned opacity) static bool null_display_server_set_window_opacity(void *data, unsigned opacity)
{ {
(void)data; (void)data;
(void)opacity; (void)opacity;
return true; return true;
} }
static bool null_set_window_progress(void *data, int progress, bool finished) static bool null_display_server_set_window_progress(void *data, int progress, bool finished)
{ {
(void)data; (void)data;
(void)progress; (void)progress;
@ -46,8 +46,9 @@ static bool null_set_window_progress(void *data, int progress, bool finished)
const video_display_server_t dispserv_null = { const video_display_server_t dispserv_null = {
null_display_server_init, null_display_server_init,
null_display_server_destroy, null_display_server_destroy,
null_set_window_opacity, null_display_server_set_window_opacity,
null_set_window_progress, null_display_server_set_window_progress,
NULL,
NULL, NULL,
NULL, NULL,
"null" "null"

View File

@ -69,6 +69,7 @@ be received by your application before it calls any ITaskbarList3 method.
static unsigned win32_orig_width = 0; static unsigned win32_orig_width = 0;
static unsigned win32_orig_height = 0; static unsigned win32_orig_height = 0;
static unsigned win32_orig_refresh = 0; static unsigned win32_orig_refresh = 0;
static int crt_center = 0;
static void* win32_display_server_init(void) static void* win32_display_server_init(void)
{ {
@ -107,7 +108,7 @@ static void win32_display_server_destroy(void *data)
if (win32_orig_width > 0 && win32_orig_height > 0 ) if (win32_orig_width > 0 && win32_orig_height > 0 )
video_display_server_switch_resolution(win32_orig_width, win32_orig_height, video_display_server_switch_resolution(win32_orig_width, win32_orig_height,
win32_orig_refresh , (float)win32_orig_refresh ); win32_orig_refresh , (float)win32_orig_refresh, crt_center );
#ifdef HAS_TASKBAR_EXT #ifdef HAS_TASKBAR_EXT
if (g_taskbarList && win32_taskbar_is_created()) if (g_taskbarList && win32_taskbar_is_created())
@ -121,7 +122,7 @@ static void win32_display_server_destroy(void *data)
free(dispserv); free(dispserv);
} }
static bool win32_set_window_opacity(void *data, unsigned opacity) static bool win32_display_server_set_window_opacity(void *data, unsigned opacity)
{ {
HWND hwnd = win32_get_window(); HWND hwnd = win32_get_window();
dispserv_win32_t *serv = (dispserv_win32_t*)data; dispserv_win32_t *serv = (dispserv_win32_t*)data;
@ -148,7 +149,7 @@ static bool win32_set_window_opacity(void *data, unsigned opacity)
#endif #endif
} }
static bool win32_set_window_progress(void *data, int progress, bool finished) static bool win32_display_server_set_window_progress(void *data, int progress, bool finished)
{ {
HWND hwnd = win32_get_window(); HWND hwnd = win32_get_window();
dispserv_win32_t *serv = (dispserv_win32_t*)data; dispserv_win32_t *serv = (dispserv_win32_t*)data;
@ -187,7 +188,7 @@ static bool win32_set_window_progress(void *data, int progress, bool finished)
return true; return true;
} }
static bool win32_set_window_decorations(void *data, bool on) static bool win32_display_server_set_window_decorations(void *data, bool on)
{ {
dispserv_win32_t *serv = (dispserv_win32_t*)data; dispserv_win32_t *serv = (dispserv_win32_t*)data;
@ -201,7 +202,7 @@ static bool win32_set_window_decorations(void *data, bool on)
} }
static bool win32_display_server_set_resolution(void *data, static bool win32_display_server_set_resolution(void *data,
unsigned width, unsigned height, int int_hz, float hz) unsigned width, unsigned height, int int_hz, float hz, int center)
{ {
LONG res; LONG res;
DEVMODE curDevmode; DEVMODE curDevmode;
@ -286,10 +287,11 @@ static bool win32_display_server_set_resolution(void *data,
const video_display_server_t dispserv_win32 = { const video_display_server_t dispserv_win32 = {
win32_display_server_init, win32_display_server_init,
win32_display_server_destroy, win32_display_server_destroy,
win32_set_window_opacity, win32_display_server_set_window_opacity,
win32_set_window_progress, win32_display_server_set_window_progress,
win32_set_window_decorations, win32_display_server_set_window_decorations,
win32_display_server_set_resolution, win32_display_server_set_resolution,
NULL, /* get_output_options */
"win32" "win32"
}; };

View File

@ -23,9 +23,14 @@
#include "../video_driver.h" /* needed to set refresh rate in set resolution */ #include "../video_driver.h" /* needed to set refresh rate in set resolution */
#include "../video_crt_switch.h" /* needed to set aspect for low res in linux */ #include "../video_crt_switch.h" /* needed to set aspect for low res in linux */
static char old_mode[150]; static unsigned orig_width = 0;
static char new_mode[150]; static unsigned orig_height = 0;
static bool crt_en = false; static char old_mode[250] = {0};
static char new_mode[250] = {0};
static char xrandr[250] = {0};
static char fbset[150] = {0};
static char output[250] = {0};
static bool crt_en = false;
typedef struct typedef struct
{ {
@ -45,16 +50,35 @@ static void* x11_display_server_init(void)
static void x11_display_server_destroy(void *data) static void x11_display_server_destroy(void *data)
{ {
dispserv_x11_t *dispserv = (dispserv_x11_t*)data; dispserv_x11_t *dispserv = (dispserv_x11_t*)data;
int i = 0;
if (crt_en == true) if (crt_en == true)
system("xrandr -s 704x480"); {
sprintf(output,"xrandr -s %dx%d", orig_width, orig_height);
system(output);
for (i =0; i < 3; i++)
{
sprintf(output,"xrandr --delmode %s%d %s", "VGA",i ,old_mode);
system(output);
sprintf(output,"xrandr --delmode %s-%d %s", "VGA",i ,old_mode);
system(output);
sprintf(output,"xrandr --delmode %s%d %s", "DVI",i ,old_mode);
system(output);
sprintf(output,"xrandr --delmode %s-%d %s", "DVI",i ,old_mode);
system(output);
}
sprintf(output,"xrandr --rmmode %s", old_mode);
system(output);
}
if (dispserv) if (dispserv)
free(dispserv); free(dispserv);
} }
static bool x11_set_window_opacity(void *data, unsigned opacity) static bool x11_display_server_set_window_opacity(void *data, unsigned opacity)
{ {
dispserv_x11_t *serv = (dispserv_x11_t*)data; dispserv_x11_t *serv = (dispserv_x11_t*)data;
Atom net_wm_opacity = XInternAtom(g_x11_dpy, "_NET_WM_WINDOW_OPACITY", False); Atom net_wm_opacity = XInternAtom(g_x11_dpy, "_NET_WM_WINDOW_OPACITY", False);
@ -72,7 +96,7 @@ static bool x11_set_window_opacity(void *data, unsigned opacity)
return true; return true;
} }
static bool x11_set_window_decorations(void *data, bool on) static bool x11_display_server_set_window_decorations(void *data, bool on)
{ {
dispserv_x11_t *serv = (dispserv_x11_t*)data; dispserv_x11_t *serv = (dispserv_x11_t*)data;
@ -83,8 +107,8 @@ static bool x11_set_window_decorations(void *data, bool on)
return true; return true;
} }
static bool x11_set_resolution(void *data, static bool x11_display_server_set_resolution(void *data,
unsigned width, unsigned height, int int_hz, float hz) unsigned width, unsigned height, int int_hz, float hz, int center)
{ {
int i = 0; int i = 0;
int hfp = 0; int hfp = 0;
@ -95,22 +119,41 @@ static bool x11_set_resolution(void *data,
int vbp = 0; int vbp = 0;
int hmax = 0; int hmax = 0;
int vmax = 0; int vmax = 0;
int pdefault = 8;
int pwidth = 0;
float roundw = 0.0f;
float roundh = 0.0f;
float pixel_clock = 0; float pixel_clock = 0;
char xrandr[250];
char fbset[150];
char output[250];
crt_en = true; crt_en = true;
hsp = width*1.16;
/* set core refresh from hz */ /* set core refresh from hz */
video_monitor_set_refresh_rate(hz); video_monitor_set_refresh_rate(hz);
/* following code is the mode line genorator */ /* following code is the mode line genorator */
hfp = width+8; hsp = width*1.140;
hbp = width*1.32; hfp = width*1.055;
pwidth = width;
if (height < 400 && width > 400 )
pwidth = width/2;
roundw = roundf((float)pwidth/(float)height * 100)/100;
if (height > width ) {
roundw = roundf((float)height/(float)width * 100)/100;
}
if (roundw > 1.35)
roundw = 1.25;
if (roundw < 1.20)
roundw = 1.34;
hbp = width*roundw-8;
hmax = hbp; hmax = hbp;
if (height < 241) if (height < 241)
@ -133,22 +176,18 @@ static bool x11_set_resolution(void *data,
{ {
vmax = 285; vmax = 285;
} }
if (height > 240 && height < 260 && hz < 52)
{
vmax = 265;
}
if (height > 250 && height < 260 && hz < 52) if (height > 250 && height < 260 && hz < 52)
{ {
vmax = 313; vmax = 313;
} }
if (height > 260 && height < 300) if (height > 260 && height < 300)
{ {
vmax = 313; vmax = 318;
} }
if (height > 400 && hz > 56) if (height > 400 && hz > 56)
{ {
vmax = 523; vmax = 533;
} }
if (height > 520 && hz < 57) if (height > 520 && hz < 57)
{ {
@ -157,35 +196,27 @@ static bool x11_set_resolution(void *data,
if (height > 300 && hz < 56) if (height > 300 && hz < 56)
{ {
vmax = 627; vmax = 615;
} }
if (height > 500 && hz < 56)
if (hz < 53)
{ {
vfp = height+((vmax-height)*0.38); vmax = 624;
} }
if (hz > 56) if (height > 300)
{ {
vfp = height+((vmax-height)*0.15); pdefault = pdefault*2;
}
if (hz > 53 && hz < 56)
{
vfp = height+((vmax-height)*0.35);
} }
if ( vfp < 1 ) vfp = height+((vmax-height)/2)-pdefault;
{
vfp = height+2;
}
if (height < 300) if (height < 300)
{ {
vsp = vfp+3; /* needs to me 3 for progressive */ vsp = vfp+3; /* needs to me 3 for progressive */
} if (height > 300) }
if (height > 300)
{ {
vsp = vfp+6; /* needs to me 6 for interlaced */ vsp = vfp+6; /* needs to me 6 for interlaced */
} }
vbp = vmax; vbp = vmax;
@ -201,7 +232,7 @@ static bool x11_set_resolution(void *data,
} }
/* above code is the modeline genorator */ /* above code is the modeline genorator */
/* create progressive newmode from modline variables */ /* create interlaced newmode from modline variables */
if (height < 300) if (height < 300)
{ {
snprintf(xrandr, sizeof(xrandr), "xrandr --newmode \"%dx%d_%0.2f\" %lf %d %d %d %d %d %d %d %d -hsync -vsync", width, height, hz, pixel_clock, width, hfp, hsp, hbp, height, vfp, vsp, vbp); snprintf(xrandr, sizeof(xrandr), "xrandr --newmode \"%dx%d_%0.2f\" %lf %d %d %d %d %d %d %d %d -hsync -vsync", width, height, hz, pixel_clock, width, hfp, hsp, hbp, height, vfp, vsp, vbp);
@ -257,18 +288,25 @@ static bool x11_set_resolution(void *data,
/* variable for old mode */ /* variable for old mode */
snprintf(old_mode, sizeof(old_mode), "%s", new_mode); snprintf(old_mode, sizeof(old_mode), "%s", new_mode);
system("xdotool windowactivate $(xdotool search --class RetroArch)"); /* needs xdotool installed. needed to recaputure window. */ system("xdotool windowactivate $(xdotool search --class RetroArch)"); /* needs xdotool installed. needed to recaputure window. */
/* Second run needed as some times it runs to fast to capture first time */ /* Second run needed as some times it runs to fast to capture first time */
return true; return true;
} }
const char *x11_display_server_get_output_options(void)
{
/* TODO/FIXME - hardcoded for now; list should be built up dynamically later */
return "HDMI-0|HDMI-1|HDMI-2|HDMI-3|DVI-0|DVI-1|DVI-2|DVI-3|VGA-0|VGA-1|VGA-2|VGA-3|Config";
}
const video_display_server_t dispserv_x11 = { const video_display_server_t dispserv_x11 = {
x11_display_server_init, x11_display_server_init,
x11_display_server_destroy, x11_display_server_destroy,
x11_set_window_opacity, x11_display_server_set_window_opacity,
NULL, NULL,
x11_set_window_decorations, x11_display_server_set_window_decorations,
x11_set_resolution, /* set_resolution */ x11_display_server_set_resolution,
x11_display_server_get_output_options,
"x11" "x11"
}; };

View File

@ -973,7 +973,7 @@ d3d12_gfx_init(const video_info_t* video, const input_driver_t** input, void** i
{ {
const char* ext = path_get_extension(settings->paths.path_shader); const char* ext = path_get_extension(settings->paths.path_shader);
if (ext && !strcmp(ext, "slangp")) if (ext && string_is_equal(ext, "slangp"))
d3d12_gfx_set_shader(d3d12, RARCH_SHADER_SLANG, settings->paths.path_shader); d3d12_gfx_set_shader(d3d12, RARCH_SHADER_SLANG, settings->paths.path_shader);
} }

View File

@ -1006,13 +1006,15 @@ static bool d3d8_restore(void *data)
static void d3d8_set_nonblock_state(void *data, bool state) static void d3d8_set_nonblock_state(void *data, bool state)
{ {
unsigned interval = state ? 0 : 1; int interval = 0;
d3d8_video_t *d3d = (d3d8_video_t*)data; d3d8_video_t *d3d = (d3d8_video_t*)data;
if (!d3d) if (!d3d)
return; return;
d3d->video_info.vsync = !state; if (!state)
interval = 1;
d3d->video_info.vsync = !state;
#ifdef _XBOX #ifdef _XBOX
d3d8_set_render_state(d3d->dev, D3D8_PRESENTATIONINTERVAL, d3d8_set_render_state(d3d->dev, D3D8_PRESENTATIONINTERVAL,

View File

@ -1059,14 +1059,19 @@ static bool d3d9_restore(void *data)
static void d3d9_set_nonblock_state(void *data, bool state) static void d3d9_set_nonblock_state(void *data, bool state)
{ {
unsigned interval = state ? 0 : 1; int interval = 0;
d3d9_video_t *d3d = (d3d9_video_t*)data; d3d9_video_t *d3d = (d3d9_video_t*)data;
if (!d3d) if (!d3d)
return; return;
if (!state)
interval = 1;
d3d->video_info.vsync = !state; d3d->video_info.vsync = !state;
(void)interval;
#ifdef _XBOX #ifdef _XBOX
d3d9_set_render_state(d3d->dev, d3d9_set_render_state(d3d->dev,
D3D9_PRESENTATIONINTERVAL, D3D9_PRESENTATIONINTERVAL,

View File

@ -952,6 +952,12 @@ static bool gl_frame(void *data, const void *frame,
if (!gl) if (!gl)
return false; return false;
#ifdef HAVE_LIBNX
// Should be called once per frame
if(!appletMainLoop())
return false;
#endif
context_bind_hw_render(false); context_bind_hw_render(false);
if (gl->core_context_in_use && gl->renderchain_driver->bind_vao) if (gl->core_context_in_use && gl->renderchain_driver->bind_vao)
@ -1318,7 +1324,7 @@ static void gl_free(void *data)
static void gl_set_nonblock_state(void *data, bool state) static void gl_set_nonblock_state(void *data, bool state)
{ {
unsigned interval = 0; int interval = 0;
gl_t *gl = (gl_t*)data; gl_t *gl = (gl_t*)data;
settings_t *settings = config_get_ptr(); settings_t *settings = config_get_ptr();
@ -1696,13 +1702,14 @@ static void *gl_init(const video_info_t *video,
{ {
gfx_ctx_mode_t mode; gfx_ctx_mode_t mode;
gfx_ctx_input_t inp; gfx_ctx_input_t inp;
unsigned interval, mip_level;
unsigned full_x, full_y; unsigned full_x, full_y;
video_shader_ctx_filter_t shader_filter; video_shader_ctx_filter_t shader_filter;
video_shader_ctx_info_t shader_info; video_shader_ctx_info_t shader_info;
video_shader_ctx_ident_t ident_info; video_shader_ctx_ident_t ident_info;
settings_t *settings = config_get_ptr(); settings_t *settings = config_get_ptr();
video_shader_ctx_wrap_t wrap_info = {0}; video_shader_ctx_wrap_t wrap_info = {0};
int interval = 0;
unsigned mip_level = 0;
unsigned win_width = 0; unsigned win_width = 0;
unsigned win_height = 0; unsigned win_height = 0;
unsigned temp_width = 0; unsigned temp_width = 0;
@ -1755,6 +1762,10 @@ static void *gl_init(const video_info_t *video,
if (!video_context_driver_set_video_mode(&mode)) if (!video_context_driver_set_video_mode(&mode))
goto error; goto error;
#if !defined(RARCH_CONSOLE) || defined(HAVE_LIBNX)
rglgen_resolve_symbols(ctx_driver->get_proc_address);
#endif
/* Clear out potential error flags in case we use cached context. */ /* Clear out potential error flags in case we use cached context. */
glGetError(); glGetError();
@ -1768,10 +1779,6 @@ static void *gl_init(const video_info_t *video,
if (!string_is_empty(version)) if (!string_is_empty(version))
sscanf(version, "%d.%d", &gl->version_major, &gl->version_minor); sscanf(version, "%d.%d", &gl->version_major, &gl->version_minor);
#ifndef RARCH_CONSOLE
rglgen_resolve_symbols(ctx_driver->get_proc_address);
#endif
hwr = video_driver_get_hw_context(); hwr = video_driver_get_hw_context();
if (hwr->context_type == RETRO_HW_CONTEXT_OPENGL_CORE) if (hwr->context_type == RETRO_HW_CONTEXT_OPENGL_CORE)

View File

@ -892,7 +892,7 @@ static void wiiu_gfx_update_uniform_block(wiiu_video_t *wiiu, int pass, float *u
float *dst = ubo + uniformVars[i].offset; float *dst = ubo + uniformVars[i].offset;
if (!strcmp(id, "OutputSize")) if (string_is_equal(id, "OutputSize"))
{ {
((GX2_vec4 *)dst)->x = wiiu->pass[pass].color_buffer.surface.width; ((GX2_vec4 *)dst)->x = wiiu->pass[pass].color_buffer.surface.width;
((GX2_vec4 *)dst)->y = wiiu->pass[pass].color_buffer.surface.height; ((GX2_vec4 *)dst)->y = wiiu->pass[pass].color_buffer.surface.height;
@ -901,7 +901,7 @@ static void wiiu_gfx_update_uniform_block(wiiu_video_t *wiiu, int pass, float *u
continue; continue;
} }
if (!strcmp(id, "FinalViewportSize")) if (string_is_equal(id, "FinalViewportSize"))
{ {
((GX2_vec4 *)dst)->x = wiiu->vp.width; ((GX2_vec4 *)dst)->x = wiiu->vp.width;
((GX2_vec4 *)dst)->y = wiiu->vp.height; ((GX2_vec4 *)dst)->y = wiiu->vp.height;
@ -910,7 +910,7 @@ static void wiiu_gfx_update_uniform_block(wiiu_video_t *wiiu, int pass, float *u
continue; continue;
} }
if (!strcmp(id, "FrameCount")) if (string_is_equal(id, "FrameCount"))
{ {
*dst = wiiu->shader_preset->pass[pass].frame_count_mod ? *dst = wiiu->shader_preset->pass[pass].frame_count_mod ?
frame_count % wiiu->shader_preset->pass[pass].frame_count_mod : frame_count % wiiu->shader_preset->pass[pass].frame_count_mod :
@ -919,7 +919,7 @@ static void wiiu_gfx_update_uniform_block(wiiu_video_t *wiiu, int pass, float *u
continue; continue;
} }
if (!strcmp(id, "OriginalSize")) if (string_is_equal(id, "OriginalSize"))
{ {
((GX2_vec4 *)dst)->x = wiiu->texture.surface.width; ((GX2_vec4 *)dst)->x = wiiu->texture.surface.width;
((GX2_vec4 *)dst)->y = wiiu->texture.surface.height; ((GX2_vec4 *)dst)->y = wiiu->texture.surface.height;
@ -928,7 +928,7 @@ static void wiiu_gfx_update_uniform_block(wiiu_video_t *wiiu, int pass, float *u
continue; continue;
} }
if (!strcmp(id, "SourceSize")) if (string_is_equal(id, "SourceSize"))
{ {
GX2Surface *source = (pass > 0) ? &wiiu->pass[pass - 1].texture.surface : &wiiu->texture.surface; GX2Surface *source = (pass > 0) ? &wiiu->pass[pass - 1].texture.surface : &wiiu->texture.surface;
((GX2_vec4 *)dst)->x = source->width; ((GX2_vec4 *)dst)->x = source->width;
@ -995,7 +995,7 @@ static void wiiu_gfx_update_uniform_block(wiiu_video_t *wiiu, int pass, float *u
} }
} }
if (!strcmp(id, "MVP")) if (string_is_equal(id, "MVP"))
{ {
memcpy(dst, wiiu->ubo_mvp, sizeof(*wiiu->ubo_mvp)); memcpy(dst, wiiu->ubo_mvp, sizeof(*wiiu->ubo_mvp));
continue; continue;
@ -1004,7 +1004,7 @@ static void wiiu_gfx_update_uniform_block(wiiu_video_t *wiiu, int pass, float *u
for (int k = 0; k < wiiu->shader_preset->num_parameters; k++) for (int k = 0; k < wiiu->shader_preset->num_parameters; k++)
{ {
if (!strcmp(id, wiiu->shader_preset->parameters[k].id)) if (string_is_equal(id, wiiu->shader_preset->parameters[k].id))
{ {
*dst = wiiu->shader_preset->parameters[k].current; *dst = wiiu->shader_preset->parameters[k].current;
*(u32 *)dst = __builtin_bswap32(*(u32 *)dst); *(u32 *)dst = __builtin_bswap32(*(u32 *)dst);
@ -1174,7 +1174,7 @@ static bool wiiu_gfx_frame(void *data, const void *frame,
for (int j = 0; j < wiiu->pass[i].gfd->ps->samplerVarCount; j++) for (int j = 0; j < wiiu->pass[i].gfd->ps->samplerVarCount; j++)
{ {
if (!strcmp(wiiu->pass[i].gfd->ps->samplerVars[j].name, "Source")) if (string_is_equal(wiiu->pass[i].gfd->ps->samplerVars[j].name, "Source"))
{ {
GX2SetPixelTexture(texture, wiiu->pass[i].gfd->ps->samplerVars[j].location); GX2SetPixelTexture(texture, wiiu->pass[i].gfd->ps->samplerVars[j].location);
GX2SetPixelSampler(wiiu->shader_preset->pass[i].filter ? GX2SetPixelSampler(wiiu->shader_preset->pass[i].filter ?
@ -1184,7 +1184,7 @@ static bool wiiu_gfx_frame(void *data, const void *frame,
continue; continue;
} }
if (!strcmp(wiiu->pass[i].gfd->ps->samplerVars[j].name, "Original")) if (string_is_equal(wiiu->pass[i].gfd->ps->samplerVars[j].name, "Original"))
{ {
GX2SetPixelTexture(&wiiu->texture, wiiu->pass[i].gfd->ps->samplerVars[j].location); GX2SetPixelTexture(&wiiu->texture, wiiu->pass[i].gfd->ps->samplerVars[j].location);
GX2SetPixelSampler(wiiu->shader_preset->pass[0].filter ? GX2SetPixelSampler(wiiu->shader_preset->pass[0].filter ?
@ -1245,7 +1245,7 @@ static bool wiiu_gfx_frame(void *data, const void *frame,
for (int k = 0; k < wiiu->shader_preset->luts; k++) for (int k = 0; k < wiiu->shader_preset->luts; k++)
{ {
if (wiiu->luts[k].surface.image if (wiiu->luts[k].surface.image
&& !strcmp(wiiu->pass[i].gfd->ps->samplerVars[j].name, wiiu->shader_preset->lut[k].id)) && string_is_equal(wiiu->pass[i].gfd->ps->samplerVars[j].name, wiiu->shader_preset->lut[k].id))
{ {
GX2SetPixelTexture(&wiiu->luts[k], wiiu->pass[i].gfd->ps->samplerVars[j].location); GX2SetPixelTexture(&wiiu->luts[k], wiiu->pass[i].gfd->ps->samplerVars[j].location);
GX2SetPixelSampler(wiiu->shader_preset->lut[k].filter ? GX2SetPixelSampler(wiiu->shader_preset->lut[k].filter ?

View File

@ -163,7 +163,8 @@ static void metal_viewport_info(void *data, struct video_viewport *vp)
static bool metal_read_viewport(void *data, uint8_t *buffer, bool is_idle) static bool metal_read_viewport(void *data, uint8_t *buffer, bool is_idle)
{ {
return true; MetalDriver *md = (__bridge MetalDriver *)data;
return [md.frameView readViewport:buffer isIdle:is_idle];
} }
static uintptr_t metal_load_texture(void *video_data, void *data, static uintptr_t metal_load_texture(void *video_data, void *data,

View File

@ -1,3 +1,19 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2018 - misson20000
* Copyright (C) 2018 - m4xw
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <malloc.h> #include <malloc.h>
@ -146,41 +162,41 @@ static bool switch_frame(void *data, const void *frame,
centerx = (1280-tgtw)/2; centerx = (1280-tgtw)/2;
centery = (720-tgth)/2; centery = (720-tgth)/2;
// clear image to black /* clear image to black */
for(y = 0; y < 720; y++) for(y = 0; y < 720; y++)
{ {
for(x = 0; x < 1280; x++) for(x = 0; x < 1280; x++)
{
sw->image[y*1280+x] = 0xFF000000; sw->image[y*1280+x] = 0xFF000000;
}
} }
if(width > 0 && height > 0) { if(width > 0 && height > 0)
{
if(sw->last_width != width || if(sw->last_width != width ||
sw->last_height != height) sw->last_height != height)
{ {
scaler_ctx_gen_reset(&sw->scaler); scaler_ctx_gen_reset(&sw->scaler);
sw->scaler.in_width = width; sw->scaler.in_width = width;
sw->scaler.in_height = height; sw->scaler.in_height = height;
sw->scaler.in_stride = pitch; sw->scaler.in_stride = pitch;
sw->scaler.in_fmt = sw->rgb32 ? SCALER_FMT_ARGB8888 : SCALER_FMT_RGB565; sw->scaler.in_fmt = sw->rgb32 ? SCALER_FMT_ARGB8888 : SCALER_FMT_RGB565;
sw->scaler.out_width = tgtw; sw->scaler.out_width = tgtw;
sw->scaler.out_height = tgth; sw->scaler.out_height = tgth;
sw->scaler.out_stride = 1280 * sizeof(uint32_t); sw->scaler.out_stride = 1280 * sizeof(uint32_t);
sw->scaler.out_fmt = SCALER_FMT_ABGR8888; sw->scaler.out_fmt = SCALER_FMT_ABGR8888;
sw->scaler.scaler_type = SCALER_TYPE_POINT; sw->scaler.scaler_type = SCALER_TYPE_POINT;
if(!scaler_ctx_gen_filter(&sw->scaler)) { if(!scaler_ctx_gen_filter(&sw->scaler))
RARCH_ERR("failed to generate scaler for main image\n"); {
return false; RARCH_ERR("failed to generate scaler for main image\n");
} return false;
}
sw->last_width = width; sw->last_width = width;
sw->last_height = height; sw->last_height = height;
} }
scaler_ctx_scale(&sw->scaler, sw->image + (centery * 1280) + centerx, frame); scaler_ctx_scale(&sw->scaler, sw->image + (centery * 1280) + centerx, frame);
} }
@ -213,10 +229,8 @@ static bool switch_frame(void *data, const void *frame,
&video_info->osd_stat_params; &video_info->osd_stat_params;
if (osd_params) if (osd_params)
{
font_driver_render_msg(video_info, NULL, video_info->stat_text, font_driver_render_msg(video_info, NULL, video_info->stat_text,
(const struct font_params*)&video_info->osd_stat_params); (const struct font_params*)&video_info->osd_stat_params);
}
} }
#endif #endif
@ -232,14 +246,12 @@ static bool switch_frame(void *data, const void *frame,
RARCH_LOG("message: %s\n", msg); RARCH_LOG("message: %s\n", msg);
r = surface_dequeue_buffer(&sw->surface, &out_buffer); r = surface_dequeue_buffer(&sw->surface, &out_buffer);
if(r != RESULT_OK) { if(r != RESULT_OK)
return true; // just skip the frame return true; /* just skip the frame */
}
r = surface_wait_buffer(&sw->surface); r = surface_wait_buffer(&sw->surface);
if(r != RESULT_OK) { if(r != RESULT_OK)
return true; return true;
}
gfx_slow_swizzling_blit(out_buffer, sw->image, 1280, 720, 0, 0); gfx_slow_swizzling_blit(out_buffer, sw->image, 1280, 720, 0, 0);
r = surface_queue_buffer(&sw->surface); r = surface_queue_buffer(&sw->surface);
@ -333,6 +345,8 @@ static void switch_set_texture_frame(
sw->menu_texture.width != width || sw->menu_texture.width != width ||
sw->menu_texture.height != height) sw->menu_texture.height != height)
{ {
struct scaler_ctx *sctx;
int xsf, ysf, sf;
if (sw->menu_texture.pixels) if (sw->menu_texture.pixels)
free(sw->menu_texture.pixels); free(sw->menu_texture.pixels);
@ -343,32 +357,32 @@ static void switch_set_texture_frame(
return; return;
} }
int xsf = 1280 / width; xsf = 1280 / width;
int ysf = 720 / height; ysf = 720 / height;
int sf = xsf; sf = xsf;
if (ysf < sf) if (ysf < sf)
sf = ysf; sf = ysf;
sw->menu_texture.width = width; sw->menu_texture.width = width;
sw->menu_texture.height = height; sw->menu_texture.height = height;
sw->menu_texture.tgtw = width * sf; sw->menu_texture.tgtw = width * sf;
sw->menu_texture.tgth = height * sf; sw->menu_texture.tgth = height * sf;
struct scaler_ctx *sctx = &sw->menu_texture.scaler; sctx = &sw->menu_texture.scaler;
scaler_ctx_gen_reset(sctx); scaler_ctx_gen_reset(sctx);
sctx->in_width = width; sctx->in_width = width;
sctx->in_height = height; sctx->in_height = height;
sctx->in_stride = width * (rgb32 ? 4 : 2); sctx->in_stride = width * (rgb32 ? 4 : 2);
sctx->in_fmt = rgb32 ? SCALER_FMT_ARGB8888 : SCALER_FMT_RGB565; sctx->in_fmt = rgb32 ? SCALER_FMT_ARGB8888 : SCALER_FMT_RGB565;
sctx->out_width = sw->menu_texture.tgtw; sctx->out_width = sw->menu_texture.tgtw;
sctx->out_height = sw->menu_texture.tgth; sctx->out_height = sw->menu_texture.tgth;
sctx->out_stride = 1280 * 4; sctx->out_stride = 1280 * 4;
sctx->out_fmt = SCALER_FMT_ABGR8888; sctx->out_fmt = SCALER_FMT_ABGR8888;
sctx->scaler_type = SCALER_TYPE_POINT; sctx->scaler_type = SCALER_TYPE_POINT;
if (!scaler_ctx_gen_filter(sctx)) if (!scaler_ctx_gen_filter(sctx))
{ {
@ -383,6 +397,9 @@ static void switch_set_texture_frame(
static void switch_set_texture_enable(void *data, bool enable, bool full_screen) static void switch_set_texture_enable(void *data, bool enable, bool full_screen)
{ {
switch_video_t *sw = data; switch_video_t *sw = data;
if (!sw)
return;
sw->menu_texture.enable = enable; sw->menu_texture.enable = enable;
sw->menu_texture.fullscreen = full_screen; sw->menu_texture.fullscreen = full_screen;
} }
@ -441,3 +458,5 @@ video_driver_t video_switch = {
#endif #endif
switch_get_poke_interface, switch_get_poke_interface,
}; };
/* vim: set ts=3 sw=3 */

775
gfx/drivers/switch_nx_gfx.c Normal file
View File

@ -0,0 +1,775 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2018 - misson20000
* Copyright (C) 2018 - m4xw
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <retro_inline.h>
#include <retro_math.h>
#include <formats/image.h>
#include <formats/image.h>
#include <gfx/scaler/scaler.h>
#include <gfx/scaler/pixconv.h>
#include <gfx/video_frame.h>
#include <switch.h>
#ifdef HAVE_CONFIG_H
#include "../../config.h"
#endif
#ifdef HAVE_MENU
#include "../../menu/menu_driver.h"
#endif
#include "../font_driver.h"
#include "../../configuration.h"
#include "../../command.h"
#include "../../driver.h"
#include "../../retroarch.h"
#include "../../verbosity.h"
#include "../common/switch_common.h"
#ifndef HAVE_THREADS
#include "../../tasks/tasks_internal.h"
#endif
#ifdef HAVE_NXRGUI
extern uint32_t *nx_backgroundImage;
/* Temp Overlay - kill it with fire */
extern uint32_t *tmp_overlay;
#endif
/* (C) libtransistor */
static int pdep(uint32_t mask, uint32_t value)
{
uint32_t out = 0;
for (int shift = 0; shift < 32; shift++)
{
uint32_t bit = 1u << shift;
if (mask & bit)
{
if (value & 1)
out |= bit;
value >>= 1;
}
}
return out;
}
static uint32_t swizzle_x(uint32_t v) { return pdep(~0x7B4u, v); }
static uint32_t swizzle_y(uint32_t v) { return pdep(0x7B4, v); }
void gfx_slow_swizzling_blit(uint32_t *buffer, uint32_t *image, int w, int h, int tx, int ty, bool blend)
{
uint32_t *dest = buffer;
uint32_t *src = image;
int x0 = tx;
int y0 = ty;
int x1 = x0 + w;
int y1 = y0 + h;
const uint32_t tile_height = 128;
const uint32_t padded_width = 128 * 10;
/* we're doing this in pixels - should just shift the swizzles instead */
uint32_t offs_x0 = swizzle_x(x0);
uint32_t offs_y = swizzle_y(y0);
uint32_t x_mask = swizzle_x(~0u);
uint32_t y_mask = swizzle_y(~0u);
uint32_t incr_y = swizzle_x(padded_width);
/* step offs_x0 to the right row of tiles */
offs_x0 += incr_y * (y0 / tile_height);
uint32_t x, y;
for (y = y0; y < y1; y++)
{
uint32_t *dest_line = dest + offs_y;
uint32_t offs_x = offs_x0;
for (x = x0; x < x1; x++)
{
uint32_t pixel = *src++;
if (blend) /* supercheap masking */
{
uint32_t dst = dest_line[offs_x];
uint8_t src_a = ((pixel & 0xFF000000) >> 24);
if (src_a > 0)
pixel &= 0x00FFFFFF;
else
pixel = dst;
}
dest_line[offs_x] = pixel;
offs_x = (offs_x - x_mask) & x_mask;
}
offs_y = (offs_y - y_mask) & y_mask;
if (!offs_y)
offs_x0 += incr_y; /* wrap into next tile row */
}
}
/* needed to clear surface completely as hw scaling doesn't always scale to full resoution perflectly */
static void clear_screen(switch_video_t *sw)
{
uint32_t *out_buffer = NULL;
gfxConfigureResolution(sw->vp.full_width, sw->vp.full_height);
out_buffer = (uint32_t *)gfxGetFramebuffer(NULL, NULL);
memset(out_buffer, 0, gfxGetFramebufferSize());
gfxFlushBuffers();
gfxSwapBuffers();
}
static void *switch_init(const video_info_t *video,
const input_driver_t **input, void **input_data)
{
void *switchinput = NULL;
switch_video_t *sw = (switch_video_t *)calloc(1, sizeof(*sw));
if (!sw)
return NULL;
printf("loading switch gfx driver, width: %d, height: %d threaded: %d smooth %d\n", video->width, video->height, video->is_threaded, video->smooth);
sw->vp.x = 0;
sw->vp.y = 0;
sw->vp.width = sw->o_width = video->width;
sw->vp.height = sw->o_height = video->height;
sw->overlay_enabled = false;
sw->overlay = NULL;
sw->in_menu = false;
sw->vp.full_width = 1280;
sw->vp.full_height = 720;
/* Sanity check */
sw->vp.width = MIN(sw->vp.width, sw->vp.full_width);
sw->vp.height = MIN(sw->vp.height, sw->vp.full_height);
sw->vsync = video->vsync;
sw->rgb32 = video->rgb32;
sw->keep_aspect = true;
sw->should_resize = true;
sw->o_size = true;
sw->is_threaded = video->is_threaded;
sw->smooth = video->smooth;
sw->menu_texture.enable = false;
/* Autoselect driver */
if (input && input_data)
{
settings_t *settings = config_get_ptr();
switchinput = input_switch.init(settings->arrays.input_joypad_driver);
*input = switchinput ? &input_switch : NULL;
*input_data = switchinput;
}
font_driver_init_osd(sw, false,
video->is_threaded,
FONT_DRIVER_RENDER_SWITCH);
clear_screen(sw);
return sw;
}
static void switch_update_viewport(switch_video_t *sw,
video_frame_info_t *video_info)
{
settings_t *settings = config_get_ptr();
int x = 0;
int y = 0;
float desired_aspect = 0.0f;
float width = sw->vp.full_width;
float height = sw->vp.full_height;
if (sw->o_size)
{
width = sw->o_width;
height = sw->o_height;
sw->vp.x = (int)(((float)sw->vp.full_width - width)) / 2;
sw->vp.y = (int)(((float)sw->vp.full_height - height)) / 2;
sw->vp.width = width;
sw->vp.height = height;
return;
}
desired_aspect = video_driver_get_aspect_ratio();
/* We crash if >1.0f */
printf("[Video] Aspect: %f\n", desired_aspect);
/*if (desired_aspect > 1.8f)
desired_aspect = 1.7778f;
if (desired_aspect < 1.2f && desired_aspect != 0.0f)
desired_aspect = 1.0f;*/
if (settings->bools.video_scale_integer)
{
video_viewport_get_scaled_integer(&sw->vp, sw->vp.full_width, sw->vp.full_height, desired_aspect, sw->keep_aspect);
}
else if (sw->keep_aspect)
{
#if defined(HAVE_MENU)
if (settings->uints.video_aspect_ratio_idx == ASPECT_RATIO_CUSTOM)
{
sw->vp.x = sw->vp.y = 0;
sw->vp.width = width;
sw->vp.height = height;
}
else
#endif
{
float delta;
float device_aspect = ((float)sw->vp.full_width) / sw->vp.full_height;
if (fabsf(device_aspect - desired_aspect) < 0.0001f)
{
/*
* If the aspect ratios of screen and desired aspect
* ratio are sufficiently equal (floating point stuff),
* assume they are actually equal.
*/
}
else if (device_aspect > desired_aspect)
{
delta = (desired_aspect / device_aspect - 1.0f) / 2.0f + 0.5f;
x = (int)roundf(width * (0.5f - delta));
width = (unsigned)roundf(2.0f * width * delta);
}
else
{
delta = (device_aspect / desired_aspect - 1.0f) / 2.0f + 0.5f;
y = (int)roundf(height * (0.5f - delta));
height = (unsigned)roundf(2.0f * height * delta);
}
}
sw->vp.x = x;
sw->vp.y = y;
sw->vp.width = width;
sw->vp.height = height;
}
else
{
sw->vp.x = sw->vp.y = 0;
sw->vp.width = width;
sw->vp.height = height;
}
}
static void switch_set_aspect_ratio(void *data, unsigned aspect_ratio_idx)
{
switch_video_t *sw = (switch_video_t *)data;
if (!sw)
return;
sw->keep_aspect = true;
sw->o_size = false;
settings_t *settings = config_get_ptr();
switch (aspect_ratio_idx)
{
case ASPECT_RATIO_SQUARE:
video_driver_set_viewport_square_pixel();
break;
case ASPECT_RATIO_CORE:
video_driver_set_viewport_core();
sw->o_size = true;
sw->keep_aspect = false;
break;
case ASPECT_RATIO_CONFIG:
video_driver_set_viewport_config();
break;
case ASPECT_RATIO_CUSTOM:
if (settings->bools.video_scale_integer)
{
video_driver_set_viewport_core();
sw->o_size = true;
sw->keep_aspect = false;
}
break;
default:
break;
}
video_driver_set_aspect_ratio_value(aspectratio_lut[aspect_ratio_idx].value);
sw->should_resize = true;
}
static bool switch_frame(void *data, const void *frame,
unsigned width, unsigned height,
uint64_t frame_count, unsigned pitch,
const char *msg, video_frame_info_t *video_info)
{
switch_video_t *sw = data;
uint32_t *out_buffer = NULL;
bool ffwd_mode = video_info->input_driver_nonblock_state;
if (!frame)
return true;
if (ffwd_mode && !sw->is_threaded)
{
/* render every 4th frame when in ffwd mode and not threaded */
if ((frame_count % 4) != 0)
return true;
}
if (sw->should_resize || width != sw->last_width || height != sw->last_height)
{
printf("[Video] Requesting new size: width %i height %i\n", width, height);
printf("[Video] fw: %i fh: %i w: %i h: %i x: %i y: %i\n", sw->vp.full_width, sw->vp.full_height, sw->vp.width, sw->vp.height, sw->vp.x, sw->vp.y);
switch_update_viewport(sw, video_info);
printf("[Video] fw: %i fh: %i w: %i h: %i x: %i y: %i\n", sw->vp.full_width, sw->vp.full_height, sw->vp.width, sw->vp.height, sw->vp.x, sw->vp.y);
/* Sanity check */
sw->vp.width = MIN(sw->vp.width, sw->vp.full_width);
sw->vp.height = MIN(sw->vp.height, sw->vp.full_height);
scaler_ctx_gen_reset(&sw->scaler);
sw->scaler.in_width = width;
sw->scaler.in_height = height;
sw->scaler.in_stride = pitch;
sw->scaler.in_fmt = sw->rgb32 ? SCALER_FMT_ARGB8888 : SCALER_FMT_RGB565;
if (!sw->smooth)
{
sw->scaler.out_width = sw->vp.width;
sw->scaler.out_height = sw->vp.height;
sw->scaler.out_stride = sw->vp.full_width * sizeof(uint32_t);
}
else
{
sw->scaler.out_width = width;
sw->scaler.out_height = height;
sw->scaler.out_stride = width * sizeof(uint32_t);
float screen_ratio = (float)sw->vp.full_width / sw->vp.full_height;
float tgt_ratio = (float)sw->vp.width / sw->vp.height;
sw->hw_scale.width = ceil(screen_ratio / tgt_ratio * sw->scaler.out_width);
sw->hw_scale.height = sw->scaler.out_height;
sw->hw_scale.x_offset = ceil((sw->hw_scale.width - sw->scaler.out_width) / 2.0);
if (!video_info->menu_is_alive)
{
clear_screen(sw);
gfxConfigureResolution(sw->hw_scale.width, sw->hw_scale.height);
}
}
sw->scaler.out_fmt = SCALER_FMT_ABGR8888;
sw->scaler.scaler_type = SCALER_TYPE_POINT;
if (!scaler_ctx_gen_filter(&sw->scaler))
{
printf("failed to generate scaler for main image\n");
return false;
}
sw->last_width = width;
sw->last_height = height;
sw->should_resize = false;
}
out_buffer = (uint32_t *)gfxGetFramebuffer(NULL, NULL);
if (sw->in_menu && !video_info->menu_is_alive && sw->smooth)
{
memset(out_buffer, 0, sw->vp.full_width * sw->vp.full_height * 4);
gfxConfigureResolution(sw->hw_scale.width, sw->hw_scale.height);
}
sw->in_menu = video_info->menu_is_alive;
if (sw->menu_texture.enable)
{
menu_driver_frame(video_info);
if (sw->menu_texture.pixels)
{
#ifdef HAVE_NXRGUI
gfx_slow_swizzling_blit(out_buffer, nx_backgroundImage, sw->vp.full_width, sw->vp.full_height, 0, 0, false);
#else
memset(out_buffer, 0, gfxGetFramebufferSize());
#endif
scaler_ctx_scale(&sw->menu_texture.scaler, sw->tmp_image + ((sw->vp.full_height - sw->menu_texture.tgth) / 2) * sw->vp.full_width + ((sw->vp.full_width - sw->menu_texture.tgtw) / 2), sw->menu_texture.pixels);
gfx_slow_swizzling_blit(out_buffer, sw->tmp_image, sw->vp.full_width, sw->vp.full_height, 0, 0, true);
}
}
else if (sw->smooth) /* bilinear */
{
int w, h;
unsigned x, y;
struct scaler_ctx *ctx = &sw->scaler;
scaler_ctx_scale_direct(ctx, sw->image, frame);
w = sw->scaler.out_width;
h = sw->scaler.out_height;
for (y = 0; y < h; y++)
for (x = 0; x < w; x++)
out_buffer[gfxGetFramebufferDisplayOffset(x + sw->hw_scale.x_offset, y)] = sw->image[y * w + x];
}
else
{
struct scaler_ctx *ctx = &sw->scaler;
scaler_ctx_scale(ctx, sw->image + (sw->vp.y * sw->vp.full_width) + sw->vp.x, frame);
gfx_slow_swizzling_blit(out_buffer, sw->image, sw->vp.full_width, sw->vp.full_height, 0, 0, false);
#ifdef HAVE_NXRGUI
if (tmp_overlay)
gfx_slow_swizzling_blit(out_buffer, tmp_overlay, sw->vp.full_width, sw->vp.full_height, 0, 0, true);
#endif
}
if (video_info->statistics_show && !sw->smooth)
{
struct font_params *osd_params = (struct font_params *)&video_info->osd_stat_params;
if (osd_params)
font_driver_render_msg(video_info, NULL, video_info->stat_text,
(const struct font_params *)&video_info->osd_stat_params);
}
if (msg)
font_driver_render_msg(video_info, NULL, msg, NULL);
gfxFlushBuffers();
gfxSwapBuffers();
if (sw->vsync)
gfxWaitForVsync();
return true;
}
static void switch_set_nonblock_state(void *data, bool toggle)
{
switch_video_t *sw = data;
sw->vsync = !toggle;
}
static bool switch_alive(void *data)
{
(void)data;
return true;
}
static bool switch_focus(void *data)
{
(void)data;
return true;
}
static bool switch_suppress_screensaver(void *data, bool enable)
{
(void)data;
(void)enable;
return false;
}
static bool switch_has_windowed(void *data)
{
(void)data;
return false;
}
static void switch_free(void *data)
{
switch_video_t *sw = data;
if (sw->menu_texture.pixels)
free(sw->menu_texture.pixels);
free(sw);
}
static bool switch_set_shader(void *data,
enum rarch_shader_type type, const char *path)
{
(void)data;
(void)type;
(void)path;
return false;
}
static void switch_set_rotation(void *data, unsigned rotation)
{
switch_video_t *sw = data;
if (!sw)
return;
sw->rotation = rotation;
}
static void switch_viewport_info(void *data, struct video_viewport *vp)
{
switch_video_t *sw = data;
*vp = sw->vp;
}
static bool switch_read_viewport(void *data, uint8_t *buffer, bool is_idle)
{
(void)data;
(void)buffer;
return true;
}
static void switch_set_texture_frame(
void *data, const void *frame, bool rgb32,
unsigned width, unsigned height, float alpha)
{
switch_video_t *sw = data;
size_t sz = width * height * (rgb32 ? 4 : 2);
if (!sw->menu_texture.pixels ||
sw->menu_texture.width != width ||
sw->menu_texture.height != height)
{
int xsf, ysf, sf;
struct scaler_ctx *sctx = NULL;
if (sw->menu_texture.pixels)
realloc(sw->menu_texture.pixels, sz);
else
sw->menu_texture.pixels = malloc(sz);
if (!sw->menu_texture.pixels)
{
printf("failed to allocate buffer for menu texture\n");
return;
}
xsf = 1280 / width;
ysf = 720 / height;
sf = xsf;
if (ysf < sf)
sf = ysf;
sw->menu_texture.width = width;
sw->menu_texture.height = height;
sw->menu_texture.tgtw = width * sf;
sw->menu_texture.tgth = height * sf;
sctx = &sw->menu_texture.scaler;
scaler_ctx_gen_reset(sctx);
sctx->in_width = width;
sctx->in_height = height;
sctx->in_stride = width * (rgb32 ? 4 : 2);
sctx->in_fmt = rgb32 ? SCALER_FMT_ARGB8888 : SCALER_FMT_RGB565;
sctx->out_width = sw->menu_texture.tgtw;
sctx->out_height = sw->menu_texture.tgth;
sctx->out_stride = 1280 * 4;
sctx->out_fmt = SCALER_FMT_ABGR8888;
sctx->scaler_type = SCALER_TYPE_POINT;
if (!scaler_ctx_gen_filter(sctx))
{
printf("failed to generate scaler for menu texture\n");
return;
}
}
memcpy(sw->menu_texture.pixels, frame, sz);
}
static void switch_apply_state_changes(void *data)
{
(void)data;
}
static void switch_set_texture_enable(void *data, bool enable, bool full_screen)
{
switch_video_t *sw = data;
if (!sw->menu_texture.enable && enable)
gfxConfigureResolution(sw->vp.full_width, sw->vp.full_height);
else if (!enable && sw->menu_texture.enable && sw->smooth)
{
clear_screen(sw);
gfxConfigureResolution(sw->hw_scale.width, sw->hw_scale.height);
}
sw->menu_texture.enable = enable;
sw->menu_texture.fullscreen = full_screen;
}
static void switch_set_osd_msg(void *data,
video_frame_info_t *video_info,
const char *msg,
const void *params, void *font)
{
switch_video_t *sw = (switch_video_t *)data;
if (sw)
font_driver_render_msg(video_info, font, msg, params);
}
#ifdef HAVE_OVERLAY
static void switch_overlay_enable(void *data, bool state)
{
printf("[Video] Enabled Overlay\n");
switch_video_t *swa = (switch_video_t *)data;
if (!swa)
return;
swa->overlay_enabled = state;
}
static bool switch_overlay_load(void *data,
const void *image_data, unsigned num_images)
{
switch_video_t *swa = (switch_video_t *)data;
struct texture_image *images = (struct texture_image *)image_data;
if (!swa)
return false;
swa->overlay = images;
swa->overlay_enabled = true;
return true;
}
static void switch_overlay_tex_geom(void *data,
unsigned idx, float x, float y, float w, float h)
{
switch_video_t *swa = (switch_video_t *)data;
if (!swa)
return;
}
static void switch_overlay_vertex_geom(void *data,
unsigned idx, float x, float y, float w, float h)
{
switch_video_t *swa = (switch_video_t *)data;
if (!swa)
return;
}
static void switch_overlay_full_screen(void *data, bool enable)
{
(void)data;
(void)enable;
}
static void switch_overlay_set_alpha(void *data, unsigned idx, float mod)
{
(void)data;
(void)idx;
(void)mod;
}
static const video_overlay_interface_t switch_overlay = {
switch_overlay_enable,
switch_overlay_load,
switch_overlay_tex_geom,
switch_overlay_vertex_geom,
switch_overlay_full_screen,
switch_overlay_set_alpha,
};
void switch_overlay_interface(void *data, const video_overlay_interface_t **iface)
{
switch_video_t *swa = (switch_video_t *)data;
if (!swa)
return;
*iface = &switch_overlay;
}
#endif
static const video_poke_interface_t switch_poke_interface = {
NULL, /* get_flags */
NULL, /* set_coords */
NULL, /* set_mvp */
NULL, /* load_texture */
NULL, /* unload_texture */
NULL, /* set_video_mode */
NULL, /* get_refresh_rate */
NULL, /* set_filtering */
NULL, /* get_video_output_size */
NULL, /* get_video_output_prev */
NULL, /* get_video_output_next */
NULL, /* get_current_framebuffer */
NULL, /* get_proc_address */
switch_set_aspect_ratio, /* set_aspect_ratio */
switch_apply_state_changes, /* apply_state_changes */
switch_set_texture_frame,
switch_set_texture_enable,
switch_set_osd_msg,
NULL, /* show_mouse */
NULL, /* grab_mouse_toggle */
NULL, /* get_current_shader */
NULL, /* get_current_software_framebuffer */
NULL, /* get_hw_render_interface */
};
static void switch_get_poke_interface(void *data,
const video_poke_interface_t **iface)
{
(void)data;
*iface = &switch_poke_interface;
}
video_driver_t video_switch = {
switch_init,
switch_frame,
switch_set_nonblock_state,
switch_alive,
switch_focus,
switch_suppress_screensaver,
switch_has_windowed,
switch_set_shader,
switch_free,
"switch",
NULL, /* set_viewport */
switch_set_rotation,
switch_viewport_info,
switch_read_viewport,
NULL, /* read_frame_raw */
#ifdef HAVE_OVERLAY
switch_overlay_interface, /* switch_overlay_interface */
#endif
switch_get_poke_interface,
};
/* vim: set ts=3 sw=3 */

View File

@ -77,7 +77,7 @@ static PFNVGCREATEEGLIMAGETARGETKHRPROC pvgCreateEGLImageTargetKHR;
static void vg_set_nonblock_state(void *data, bool state) static void vg_set_nonblock_state(void *data, bool state)
{ {
unsigned interval = state ? 0 : 1; int interval = state ? 0 : 1;
video_context_driver_swap_interval(&interval); video_context_driver_swap_interval(&interval);
} }
@ -97,13 +97,14 @@ static void *vg_init(const video_info_t *video,
gfx_ctx_mode_t mode; gfx_ctx_mode_t mode;
gfx_ctx_input_t inp; gfx_ctx_input_t inp;
gfx_ctx_aspect_t aspect_data; gfx_ctx_aspect_t aspect_data;
unsigned interval;
unsigned temp_width = 0, temp_height = 0;
unsigned win_width, win_height; unsigned win_width, win_height;
VGfloat clearColor[4] = {0, 0, 0, 1}; VGfloat clearColor[4] = {0, 0, 0, 1};
settings_t *settings = config_get_ptr(); int interval = 0;
vg_t *vg = (vg_t*)calloc(1, sizeof(vg_t)); unsigned temp_width = 0;
const gfx_ctx_driver_t *ctx = video_context_driver_init_first( unsigned temp_height = 0;
settings_t *settings = config_get_ptr();
vg_t *vg = (vg_t*)calloc(1, sizeof(vg_t));
const gfx_ctx_driver_t *ctx = video_context_driver_init_first(
vg, settings->arrays.video_context_driver, vg, settings->arrays.video_context_driver,
GFX_CTX_OPENVG_API, 0, 0, false); GFX_CTX_OPENVG_API, 0, 0, false);

View File

@ -854,6 +854,8 @@ static void vulkan_init_static_resources(vk_t *vk)
uint32_t blank[4 * 4]; uint32_t blank[4 * 4];
VkCommandPoolCreateInfo pool_info = { VkCommandPoolCreateInfo pool_info = {
VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO }; VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO };
pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
/* Create the pipeline cache. */ /* Create the pipeline cache. */
VkPipelineCacheCreateInfo cache = { VkPipelineCacheCreateInfo cache = {
VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO }; VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO };
@ -1092,8 +1094,8 @@ static void vulkan_init_readback(vk_t *vk)
* not initialized yet. * not initialized yet.
*/ */
settings_t *settings = config_get_ptr(); settings_t *settings = config_get_ptr();
bool *recording_enabled = recording_is_enabled(); bool recording_enabled = recording_is_enabled();
vk->readback.streamed = settings->bools.video_gpu_record && *recording_enabled; vk->readback.streamed = settings->bools.video_gpu_record && recording_enabled;
if (!vk->readback.streamed) if (!vk->readback.streamed)
return; return;
@ -1119,10 +1121,10 @@ static void *vulkan_init(const video_info_t *video,
{ {
gfx_ctx_mode_t mode; gfx_ctx_mode_t mode;
gfx_ctx_input_t inp; gfx_ctx_input_t inp;
unsigned interval;
unsigned full_x, full_y; unsigned full_x, full_y;
unsigned win_width; unsigned win_width;
unsigned win_height; unsigned win_height;
int interval = 0;
unsigned temp_width = 0; unsigned temp_width = 0;
unsigned temp_height = 0; unsigned temp_height = 0;
const gfx_ctx_driver_t *ctx_driver = NULL; const gfx_ctx_driver_t *ctx_driver = NULL;
@ -1257,7 +1259,7 @@ static void vulkan_check_swapchain(vk_t *vk)
static void vulkan_set_nonblock_state(void *data, bool state) static void vulkan_set_nonblock_state(void *data, bool state)
{ {
unsigned interval; int interval = 0;
vk_t *vk = (vk_t*)data; vk_t *vk = (vk_t*)data;
settings_t *settings = config_get_ptr(); settings_t *settings = config_get_ptr();
@ -1266,7 +1268,9 @@ static void vulkan_set_nonblock_state(void *data, bool state)
RARCH_LOG("[Vulkan]: VSync => %s\n", state ? "off" : "on"); RARCH_LOG("[Vulkan]: VSync => %s\n", state ? "off" : "on");
interval = state ? 0 : settings->uints.video_swap_interval; if (!state)
interval = settings->uints.video_swap_interval;
video_context_driver_swap_interval(&interval); video_context_driver_swap_interval(&interval);
/* Changing vsync might require recreating the swapchain, which means new VkImages /* Changing vsync might require recreating the swapchain, which means new VkImages
@ -1509,23 +1513,22 @@ static void vulkan_set_viewport(void *data, unsigned viewport_width,
static void vulkan_readback(vk_t *vk) static void vulkan_readback(vk_t *vk)
{ {
VkImageCopy region; VkBufferImageCopy region;
struct vk_texture *staging; struct vk_texture *staging;
struct video_viewport vp; struct video_viewport vp;
VkMemoryBarrier barrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER };
vulkan_viewport_info(vk, &vp); vulkan_viewport_info(vk, &vp);
memset(&region, 0, sizeof(region)); memset(&region, 0, sizeof(region));
region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.srcSubresource.layerCount = 1; region.imageSubresource.layerCount = 1;
region.dstSubresource = region.srcSubresource; region.imageOffset.x = vp.x;
region.imageOffset.y = vp.y;
region.imageExtent.width = vp.width;
region.imageExtent.height = vp.height;
region.imageExtent.depth = 1;
region.srcOffset.x = vp.x; /* FIXME: We won't actually get format conversion with vkCmdCopyImageToBuffer, so have to check
region.srcOffset.y = vp.y;
region.extent.width = vp.width;
region.extent.height = vp.height;
region.extent.depth = 1;
/* FIXME: We won't actually get format conversion with vkCmdCopyImage, so have to check
* properly for this. BGRA seems to be the default for all swapchains. */ * properly for this. BGRA seems to be the default for all swapchains. */
if (vk->context->swapchain_format != VK_FORMAT_B8G8R8A8_UNORM) if (vk->context->swapchain_format != VK_FORMAT_B8G8R8A8_UNORM)
RARCH_WARN("[Vulkan]: Backbuffer is not BGRA8888, readbacks might not work properly.\n"); RARCH_WARN("[Vulkan]: Backbuffer is not BGRA8888, readbacks might not work properly.\n");
@ -1537,25 +1540,18 @@ static void vulkan_readback(vk_t *vk)
VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_B8G8R8A8_UNORM,
NULL, NULL, VULKAN_TEXTURE_READBACK); NULL, NULL, VULKAN_TEXTURE_READBACK);
/* Go through the long-winded dance of remapping image layouts. */ vkCmdCopyImageToBuffer(vk->cmd, vk->chain->backbuffer.image,
vulkan_image_layout_transition(vk, vk->cmd, staging->image,
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL,
0, VK_ACCESS_TRANSFER_WRITE_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT);
vkCmdCopyImage(vk->cmd, vk->chain->backbuffer.image,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
staging->image, staging->buffer,
VK_IMAGE_LAYOUT_GENERAL,
1, &region); 1, &region);
/* Make the data visible to host. */ /* Make the data visible to host. */
vulkan_image_layout_transition(vk, vk->cmd, staging->image, barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL, barrier.dstAccessMask = VK_ACCESS_HOST_READ_BIT;
VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, vkCmdPipelineBarrier(vk->cmd,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_HOST_BIT); VK_PIPELINE_STAGE_HOST_BIT, 0,
1, &barrier, 0, NULL, 0, NULL);
} }
static void vulkan_inject_black_frame(vk_t *vk, video_frame_info_t *video_info) static void vulkan_inject_black_frame(vk_t *vk, video_frame_info_t *video_info)
@ -1784,7 +1780,7 @@ static bool vulkan_frame(void *data, const void *frame,
(vulkan_filter_chain_t*)vk->filter_chain, (vulkan_filter_chain_t*)vk->filter_chain,
vk->cmd, &vk->vk_vp); vk->cmd, &vk->vk_vp);
/* Render to backbuffer. */ /* Render to backbuffer. */
if (chain->backbuffer.image != VK_NULL_HANDLE) if (chain->backbuffer.image != VK_NULL_HANDLE && vk->context->has_acquired_swapchain)
{ {
rp_info.renderPass = vk->render_pass; rp_info.renderPass = vk->render_pass;
rp_info.framebuffer = chain->backbuffer.framebuffer; rp_info.framebuffer = chain->backbuffer.framebuffer;
@ -1880,6 +1876,7 @@ static bool vulkan_frame(void *data, const void *frame,
vulkan_filter_chain_end_frame((vulkan_filter_chain_t*)vk->filter_chain, vk->cmd); vulkan_filter_chain_end_frame((vulkan_filter_chain_t*)vk->filter_chain, vk->cmd);
if (chain->backbuffer.image != VK_NULL_HANDLE && if (chain->backbuffer.image != VK_NULL_HANDLE &&
vk->context->has_acquired_swapchain &&
(vk->readback.pending || vk->readback.streamed)) (vk->readback.pending || vk->readback.streamed))
{ {
/* We cannot safely read back from an image which /* We cannot safely read back from an image which
@ -1911,7 +1908,8 @@ static bool vulkan_frame(void *data, const void *frame,
vk->readback.pending = false; vk->readback.pending = false;
} }
else if (chain->backbuffer.image != VK_NULL_HANDLE) else if (chain->backbuffer.image != VK_NULL_HANDLE &&
vk->context->has_acquired_swapchain)
{ {
/* Prepare backbuffer for presentation. */ /* Prepare backbuffer for presentation. */
vulkan_image_layout_transition(vk, vk->cmd, vulkan_image_layout_transition(vk, vk->cmd,
@ -1971,8 +1969,11 @@ static bool vulkan_frame(void *data, const void *frame,
submit_info.signalSemaphoreCount = 0; submit_info.signalSemaphoreCount = 0;
if (vk->context->swapchain_semaphores[frame_index] != VK_NULL_HANDLE) if (vk->context->swapchain_semaphores[frame_index] != VK_NULL_HANDLE &&
vk->context->has_acquired_swapchain)
{
signal_semaphores[submit_info.signalSemaphoreCount++] = vk->context->swapchain_semaphores[frame_index]; signal_semaphores[submit_info.signalSemaphoreCount++] = vk->context->swapchain_semaphores[frame_index];
}
if (vk->hw.signal_semaphore != VK_NULL_HANDLE) if (vk->hw.signal_semaphore != VK_NULL_HANDLE)
{ {
@ -2014,7 +2015,9 @@ static bool vulkan_frame(void *data, const void *frame,
/* Disable BFI during fast forward, slow-motion, /* Disable BFI during fast forward, slow-motion,
* and pause to prevent flicker. */ * and pause to prevent flicker. */
if ( if (
video_info->black_frame_insertion chain->backbuffer.image != VK_NULL_HANDLE
&& vk->context->has_acquired_swapchain
&& video_info->black_frame_insertion
&& !video_info->input_driver_nonblock_state && !video_info->input_driver_nonblock_state
&& !video_info->runloop_is_slowmotion && !video_info->runloop_is_slowmotion
&& !video_info->runloop_is_paused) && !video_info->runloop_is_paused)
@ -2023,7 +2026,8 @@ static bool vulkan_frame(void *data, const void *frame,
} }
/* Vulkan doesn't directly support swap_interval > 1, so we fake it by duping out more frames. */ /* Vulkan doesn't directly support swap_interval > 1, so we fake it by duping out more frames. */
if (vk->context->swap_interval > 1 && !vk->context->swap_interval_emulation_lock) if ( vk->context->swap_interval > 1
&& !vk->context->swap_interval_emulation_lock)
{ {
unsigned i; unsigned i;
vk->context->swap_interval_emulation_lock = true; vk->context->swap_interval_emulation_lock = true;

View File

@ -60,7 +60,7 @@ typedef struct
gfx_ctx_vulkan_data_t vk; gfx_ctx_vulkan_data_t vk;
unsigned width; unsigned width;
unsigned height; unsigned height;
unsigned swap_interval; int swap_interval;
#endif #endif
} android_ctx_data_t; } android_ctx_data_t;
@ -507,7 +507,7 @@ static void android_gfx_ctx_swap_buffers(void *data, void *data2)
} }
} }
static void android_gfx_ctx_set_swap_interval(void *data, unsigned swap_interval) static void android_gfx_ctx_set_swap_interval(void *data, int swap_interval)
{ {
android_ctx_data_t *and = (android_ctx_data_t*)data; android_ctx_data_t *and = (android_ctx_data_t*)data;

View File

@ -63,7 +63,7 @@ typedef struct gfx_ctx_cgl_data
int width, height; int width, height;
} gfx_ctx_cgl_data_t; } gfx_ctx_cgl_data_t;
static void gfx_ctx_cgl_swap_interval(void *data, unsigned interval) static void gfx_ctx_cgl_swap_interval(void *data, int interval)
{ {
gfx_ctx_cgl_data_t *cgl = (gfx_ctx_cgl_data_t*)data; gfx_ctx_cgl_data_t *cgl = (gfx_ctx_cgl_data_t*)data;
GLint params = interval; GLint params = interval;

View File

@ -81,7 +81,7 @@ typedef struct gfx_ctx_drm_data
egl_ctx_data_t egl; egl_ctx_data_t egl;
#endif #endif
int fd; int fd;
unsigned interval; int interval;
unsigned fb_width; unsigned fb_width;
unsigned fb_height; unsigned fb_height;
@ -134,7 +134,7 @@ error:
return NULL; return NULL;
} }
static void gfx_ctx_drm_swap_interval(void *data, unsigned interval) static void gfx_ctx_drm_swap_interval(void *data, int interval)
{ {
gfx_ctx_drm_data_t *drm = (gfx_ctx_drm_data_t*)data; gfx_ctx_drm_data_t *drm = (gfx_ctx_drm_data_t*)data;
drm->interval = interval; drm->interval = interval;

View File

@ -49,14 +49,14 @@ static int emscripten_initial_width;
static int emscripten_initial_height; static int emscripten_initial_height;
static enum gfx_ctx_api emscripten_api = GFX_CTX_NONE; static enum gfx_ctx_api emscripten_api = GFX_CTX_NONE;
static void gfx_ctx_emscripten_swap_interval(void *data, unsigned interval) static void gfx_ctx_emscripten_swap_interval(void *data, int interval)
{ {
(void)data; (void)data;
if (interval == 0) if (interval == 0)
emscripten_set_main_loop_timing(EM_TIMING_SETIMMEDIATE, 0); emscripten_set_main_loop_timing(EM_TIMING_SETIMMEDIATE, 0);
else else
emscripten_set_main_loop_timing(EM_TIMING_RAF, (int)interval); emscripten_set_main_loop_timing(EM_TIMING_RAF, interval);
} }
static void gfx_ctx_emscripten_get_canvas_size(int *width, int *height) static void gfx_ctx_emscripten_get_canvas_size(int *width, int *height)

View File

@ -45,7 +45,7 @@ static HDC win32_gdi_hdc;
static unsigned win32_gdi_major = 0; static unsigned win32_gdi_major = 0;
static unsigned win32_gdi_minor = 0; static unsigned win32_gdi_minor = 0;
static unsigned win32_gdi_interval = 0; static int win32_gdi_interval = 0;
static enum gfx_ctx_api win32_gdi_api = GFX_CTX_NONE; static enum gfx_ctx_api win32_gdi_api = GFX_CTX_NONE;
typedef struct gfx_ctx_gdi_data typedef struct gfx_ctx_gdi_data
@ -308,7 +308,7 @@ static void gfx_ctx_gdi_show_mouse(void *data, bool state)
win32_show_cursor(state); win32_show_cursor(state);
} }
static void gfx_ctx_gdi_swap_interval(void *data, unsigned interval) static void gfx_ctx_gdi_swap_interval(void *data, int interval)
{ {
(void)data; (void)data;
(void)interval; (void)interval;

View File

@ -18,7 +18,7 @@
#include "../video_driver.h" #include "../video_driver.h"
static void gfx_ctx_null_swap_interval(void *data, unsigned interval) static void gfx_ctx_null_swap_interval(void *data, int interval)
{ {
(void)data; (void)data;
(void)interval; (void)interval;

View File

@ -23,7 +23,7 @@
typedef struct typedef struct
{ {
gfx_ctx_vulkan_data_t vk; gfx_ctx_vulkan_data_t vk;
unsigned swap_interval; int swap_interval;
unsigned width; unsigned width;
unsigned height; unsigned height;
} khr_display_ctx_data_t; } khr_display_ctx_data_t;
@ -99,7 +99,8 @@ static bool gfx_ctx_khr_display_set_resize(void *data,
khr->width = width; khr->width = width;
khr->height = height; khr->height = height;
if (!vulkan_create_swapchain(&khr->vk, khr->width, khr->height, khr->swap_interval)) if (!vulkan_create_swapchain(&khr->vk, khr->width, khr->height,
khr->swap_interval))
{ {
RARCH_ERR("[Vulkan]: Failed to update swapchain.\n"); RARCH_ERR("[Vulkan]: Failed to update swapchain.\n");
return false; return false;
@ -189,9 +190,11 @@ static bool gfx_ctx_khr_display_suppress_screensaver(void *data, bool enable)
return false; return false;
} }
static void gfx_ctx_khr_display_set_swap_interval(void *data, unsigned swap_interval) static void gfx_ctx_khr_display_set_swap_interval(void *data,
int swap_interval)
{ {
khr_display_ctx_data_t *khr = (khr_display_ctx_data_t*)data; khr_display_ctx_data_t *khr = (khr_display_ctx_data_t*)data;
if (khr->swap_interval != swap_interval) if (khr->swap_interval != swap_interval)
{ {
khr->swap_interval = swap_interval; khr->swap_interval = swap_interval;

View File

@ -88,7 +88,7 @@ typedef struct cocoa_ctx_data
bool core_hw_context_enable; bool core_hw_context_enable;
#ifdef HAVE_VULKAN #ifdef HAVE_VULKAN
gfx_ctx_vulkan_data_t vk; gfx_ctx_vulkan_data_t vk;
unsigned swap_interval; int swap_interval;
#endif #endif
unsigned width; unsigned width;
unsigned height; unsigned height;
@ -299,7 +299,8 @@ static void *cocoagl_gfx_ctx_init(video_frame_info_t *video_info, void *video_dr
{ {
#if defined(HAVE_COCOATOUCH) #if defined(HAVE_COCOATOUCH)
case GFX_CTX_OPENGL_ES_API: case GFX_CTX_OPENGL_ES_API:
[apple_platform setViewType:APPLE_VIEW_TYPE_OPENGL_ES]; // setViewType is not (yet?) defined for iOS
// [apple_platform setViewType:APPLE_VIEW_TYPE_OPENGL_ES];
break; break;
#elif defined(HAVE_COCOA) #elif defined(HAVE_COCOA)
case GFX_CTX_OPENGL_API: case GFX_CTX_OPENGL_API:
@ -360,7 +361,7 @@ static bool cocoagl_gfx_ctx_bind_api(void *data, enum gfx_ctx_api api, unsigned
return true; return true;
} }
static void cocoagl_gfx_ctx_swap_interval(void *data, unsigned interval) static void cocoagl_gfx_ctx_swap_interval(void *data, int interval)
{ {
#ifdef HAVE_VULKAN #ifdef HAVE_VULKAN
cocoa_ctx_data_t *cocoa_ctx = (cocoa_ctx_data_t*)data; cocoa_ctx_data_t *cocoa_ctx = (cocoa_ctx_data_t*)data;
@ -373,10 +374,10 @@ static void cocoagl_gfx_ctx_swap_interval(void *data, unsigned interval)
{ {
#if defined(HAVE_COCOATOUCH) // < No way to disable Vsync on iOS? #if defined(HAVE_COCOATOUCH) // < No way to disable Vsync on iOS?
// Just skip presents so fast forward still works. // Just skip presents so fast forward still works.
g_is_syncing = interval ? true : false; g_is_syncing = interval ? true : false;
g_fast_forward_skips = interval ? 0 : 3; g_fast_forward_skips = interval ? 0 : 3;
#elif defined(HAVE_COCOA) #elif defined(HAVE_COCOA)
GLint value = interval ? 1 : 0; GLint value = interval ? 1 : 0;
[g_context setValues:&value forParameter:NSOpenGLCPSwapInterval]; [g_context setValues:&value forParameter:NSOpenGLCPSwapInterval];
#endif #endif
break; break;
@ -491,9 +492,10 @@ static bool cocoagl_gfx_ctx_set_video_mode(void *data,
case GFX_CTX_VULKAN_API: case GFX_CTX_VULKAN_API:
#ifdef HAVE_VULKAN #ifdef HAVE_VULKAN
RARCH_LOG("[macOS]: Native window size: %u x %u.\n", cocoa_ctx->width, cocoa_ctx->height); RARCH_LOG("[macOS]: Native window size: %u x %u.\n", cocoa_ctx->width, cocoa_ctx->height);
if (!vulkan_surface_create(&cocoa_ctx->vk, VULKAN_WSI_MVK_MACOS, NULL, if (!vulkan_surface_create(&cocoa_ctx->vk,
(BRIDGE void *)g_view, cocoa_ctx->width, cocoa_ctx->height, VULKAN_WSI_MVK_MACOS, NULL,
cocoa_ctx->swap_interval)) (BRIDGE void *)g_view, cocoa_ctx->width, cocoa_ctx->height,
cocoa_ctx->swap_interval))
{ {
RARCH_ERR("[macOS]: Failed to create surface.\n"); RARCH_ERR("[macOS]: Failed to create surface.\n");
return false; return false;
@ -772,7 +774,8 @@ static bool cocoagl_gfx_ctx_set_resize(void *data, unsigned width, unsigned heig
cocoa_ctx->width = width; cocoa_ctx->width = width;
cocoa_ctx->height = height; cocoa_ctx->height = height;
if (vulkan_create_swapchain(&cocoa_ctx->vk, width, height, cocoa_ctx->swap_interval)) if (vulkan_create_swapchain(&cocoa_ctx->vk,
width, height, cocoa_ctx->swap_interval))
{ {
cocoa_ctx->vk.context.invalid_swapchain = true; cocoa_ctx->vk.context.invalid_swapchain = true;
if (cocoa_ctx->vk.created_new_swapchain) if (cocoa_ctx->vk.created_new_swapchain)

View File

@ -244,7 +244,8 @@ static bool gfx_ctx_mali_fbdev_suppress_screensaver(void *data, bool enable)
return false; return false;
} }
static void gfx_ctx_mali_fbdev_set_swap_interval(void *data, unsigned swap_interval) static void gfx_ctx_mali_fbdev_set_swap_interval(void *data,
int swap_interval)
{ {
mali_ctx_data_t *mali = (mali_ctx_data_t*)data; mali_ctx_data_t *mali = (mali_ctx_data_t*)data;

View File

@ -225,7 +225,7 @@ static void gfx_ctx_opendingux_swap_buffers(void *data, void *data2)
} }
static void gfx_ctx_opendingux_set_swap_interval( static void gfx_ctx_opendingux_set_swap_interval(
void *data, unsigned swap_interval) void *data, int swap_interval)
{ {
opendingux_ctx_data_t *viv = (opendingux_ctx_data_t*)data; opendingux_ctx_data_t *viv = (opendingux_ctx_data_t*)data;

View File

@ -240,7 +240,7 @@ static bool osmesa_ctx_bind_api(void *data,
return true; return true;
} }
static void osmesa_ctx_swap_interval(void *data, unsigned interval) static void osmesa_ctx_swap_interval(void *data, int interval)
{ {
(void)data; (void)data;
(void)interval; (void)interval;

View File

@ -140,10 +140,10 @@ static void gfx_ctx_ps3_get_available_resolutions(void)
global->console.screen.resolutions.check = true; global->console.screen.resolutions.check = true;
} }
static void gfx_ctx_ps3_set_swap_interval(void *data, unsigned interval) static void gfx_ctx_ps3_set_swap_interval(void *data, int interval)
{ {
#if defined(HAVE_PSGL) #if defined(HAVE_PSGL)
if (interval) if (interval == 1)
glEnable(GL_VSYNC_SCE); glEnable(GL_VSYNC_SCE);
else else
glDisable(GL_VSYNC_SCE); glDisable(GL_VSYNC_SCE);

View File

@ -417,7 +417,7 @@ dpi_fallback:
return true; return true;
} }
static void gfx_ctx_qnx_set_swap_interval(void *data, unsigned swap_interval) static void gfx_ctx_qnx_set_swap_interval(void *data, int swap_interval)
{ {
qnx_ctx_data_t *qnx = (qnx_ctx_data_t*)data; qnx_ctx_data_t *qnx = (qnx_ctx_data_t*)data;

View File

@ -156,7 +156,7 @@ static bool sdl_ctx_bind_api(void *data,
return true; return true;
} }
static void sdl_ctx_swap_interval(void *data, unsigned interval) static void sdl_ctx_swap_interval(void *data, int interval)
{ {
(void)data; (void)data;
#ifdef HAVE_SDL2 #ifdef HAVE_SDL2

View File

@ -151,7 +151,7 @@ static void gfx_ctx_sixel_show_mouse(void *data, bool state)
(void)data; (void)data;
} }
static void gfx_ctx_sixel_swap_interval(void *data, unsigned interval) static void gfx_ctx_sixel_swap_interval(void *data, int interval)
{ {
(void)data; (void)data;
(void)interval; (void)interval;

View File

@ -0,0 +1,287 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
* Copyright (C) 2018 - M4xw
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#ifdef HAVE_CONFIG_H
#include "../../config.h"
#endif
#include <switch.h>
#include "../common/switch_common.h"
#include "../../frontend/frontend_driver.h"
static enum gfx_ctx_api ctx_nx_api = GFX_CTX_OPENGL_API;
switch_ctx_data_t *nx_ctx_ptr = NULL;
void switch_ctx_destroy(void *data)
{
switch_ctx_data_t *ctx_nx = (switch_ctx_data_t *)data;
if (ctx_nx)
{
#ifdef HAVE_EGL
egl_destroy(&ctx_nx->egl);
#endif
ctx_nx->resize = false;
free(ctx_nx);
}
}
static void switch_ctx_get_video_size(void *data,
unsigned *width, unsigned *height)
{
switch_ctx_data_t *ctx_nx = (switch_ctx_data_t *)data;
*width = 1280;
*height = 720;
}
static void *switch_ctx_init(video_frame_info_t *video_info, void *video_driver)
{
#ifdef HAVE_EGL
EGLint n;
EGLint major, minor;
static const EGLint attribs[] = {
EGL_BLUE_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_RED_SIZE, 8,
EGL_ALPHA_SIZE, 8,
EGL_NONE};
#endif
switch_ctx_data_t *ctx_nx = (switch_ctx_data_t *)calloc(1, sizeof(*ctx_nx));
if (!ctx_nx)
return NULL;
nx_ctx_ptr = ctx_nx;
//setenv("MESA_NO_ERROR", "1", 1);
// Uncomment below to enable Mesa logging:
//setenv("EGL_LOG_LEVEL", "debug", 1);
//setenv("MESA_VERBOSE", "all", 1);
//setenv("NOUVEAU_MESA_DEBUG", "1", 1);
// Uncomment below to enable shader debugging in Nouveau:
//setenv("NV50_PROG_OPTIMIZE", "0", 1);
//setenv("NV50_PROG_DEBUG", "1", 1);
//setenv("NV50_PROG_CHIPSET", "0x120", 1);
#ifdef HAVE_EGL
if (!egl_init_context(&ctx_nx->egl, EGL_NONE, EGL_DEFAULT_DISPLAY,
&major, &minor, &n, attribs))
{
egl_report_error();
goto error;
}
#endif
return ctx_nx;
error:
printf("[NXGL]: EGL error: %d.\n", eglGetError());
switch_ctx_destroy(video_driver);
return NULL;
}
static void switch_ctx_check_window(void *data, bool *quit,
bool *resize, unsigned *width, unsigned *height, bool is_shutdown)
{
unsigned new_width, new_height;
switch_ctx_get_video_size(data, &new_width, &new_height);
if (new_width != *width || new_height != *height)
{
*width = new_width;
*height = new_height;
*resize = true;
}
*quit = (bool)false;
}
static bool switch_ctx_set_video_mode(void *data,
video_frame_info_t *video_info,
unsigned width, unsigned height,
bool fullscreen)
{
// Create an EGL rendering context
static const EGLint contextAttributeList[] =
{
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE};
switch_ctx_data_t *ctx_nx = (switch_ctx_data_t *)data;
ctx_nx->width = 1280;
ctx_nx->height = 720;
ctx_nx->native_window.width = ctx_nx->width;
ctx_nx->native_window.height = ctx_nx->height;
ctx_nx->refresh_rate = 60;
#ifdef HAVE_EGL
if (!egl_create_context(&ctx_nx->egl, contextAttributeList))
{
egl_report_error();
goto error;
}
#endif
#ifdef HAVE_EGL
if (!egl_create_surface(&ctx_nx->egl, &ctx_nx->native_window))
goto error;
#endif
return true;
error:
printf("[ctx_nx]: EGL error: %d.\n", eglGetError());
switch_ctx_destroy(data);
return false;
}
static void switch_ctx_input_driver(void *data,
const char *name,
const input_driver_t **input, void **input_data)
{
*input = NULL;
*input_data = NULL;
}
static enum gfx_ctx_api switch_ctx_get_api(void *data)
{
return ctx_nx_api;
}
static bool switch_ctx_bind_api(void *data,
enum gfx_ctx_api api, unsigned major, unsigned minor)
{
(void)data;
ctx_nx_api = api;
if (api == GFX_CTX_OPENGL_API)
if (eglBindAPI(EGL_OPENGL_API) != EGL_FALSE)
return true;
return false;
}
static bool switch_ctx_has_focus(void *data)
{
(void)data;
return true;
}
static bool switch_ctx_suppress_screensaver(void *data, bool enable)
{
(void)data;
(void)enable;
return false;
}
static void switch_ctx_set_swap_interval(void *data,
int swap_interval)
{
switch_ctx_data_t *ctx_nx = (switch_ctx_data_t *)data;
#ifdef HAVE_EGL
egl_set_swap_interval(&ctx_nx->egl, swap_interval);
#endif
}
static void switch_ctx_swap_buffers(void *data, void *data2)
{
switch_ctx_data_t *ctx_nx = (switch_ctx_data_t *)data;
#ifdef HAVE_EGL
egl_swap_buffers(&ctx_nx->egl);
#endif
}
static gfx_ctx_proc_t switch_ctx_get_proc_address(const char *symbol)
{
#ifdef HAVE_EGL
return egl_get_proc_address(symbol);
#endif
}
static void switch_ctx_bind_hw_render(void *data, bool enable)
{
switch_ctx_data_t *ctx_nx = (switch_ctx_data_t *)data;
#ifdef HAVE_EGL
egl_bind_hw_render(&ctx_nx->egl, enable);
#endif
}
static uint32_t switch_ctx_get_flags(void *data)
{
uint32_t flags = 0;
BIT32_SET(flags, GFX_CTX_FLAGS_NONE);
return flags;
}
static void switch_ctx_set_flags(void *data, uint32_t flags)
{
(void)data;
}
static float switch_ctx_get_refresh_rate(void *data)
{
switch_ctx_data_t *ctx_nx = (switch_ctx_data_t *)data;
return ctx_nx->refresh_rate;
}
const gfx_ctx_driver_t switch_ctx = {
switch_ctx_init,
switch_ctx_destroy,
switch_ctx_get_api,
switch_ctx_bind_api,
switch_ctx_set_swap_interval,
switch_ctx_set_video_mode,
switch_ctx_get_video_size,
switch_ctx_get_refresh_rate,
NULL, /* get_video_output_size */
NULL, /* get_video_output_prev */
NULL, /* get_video_output_next */
NULL, /* get_metrics */
NULL,
NULL, /* update_title */
switch_ctx_check_window,
NULL, /* set_resize */
switch_ctx_has_focus,
switch_ctx_suppress_screensaver,
NULL, /* has_windowed */
switch_ctx_swap_buffers,
switch_ctx_input_driver,
switch_ctx_get_proc_address,
NULL,
NULL,
NULL,
"switch",
switch_ctx_get_flags,
switch_ctx_set_flags,
switch_ctx_bind_hw_render,
NULL,
NULL};

View File

@ -330,7 +330,7 @@ error:
return NULL; return NULL;
} }
static void gfx_ctx_vc_set_swap_interval(void *data, unsigned swap_interval) static void gfx_ctx_vc_set_swap_interval(void *data, int swap_interval)
{ {
#ifdef HAVE_EGL #ifdef HAVE_EGL
vc_ctx_data_t *vc = (vc_ctx_data_t*)data; vc_ctx_data_t *vc = (vc_ctx_data_t*)data;

View File

@ -221,7 +221,7 @@ static bool gfx_ctx_vivante_suppress_screensaver(void *data, bool enable)
return false; return false;
} }
static void gfx_ctx_vivante_set_swap_interval(void *data, unsigned swap_interval) static void gfx_ctx_vivante_set_swap_interval(void *data, int swap_interval)
{ {
vivante_ctx_data_t *viv = (vivante_ctx_data_t*)data; vivante_ctx_data_t *viv = (vivante_ctx_data_t*)data;

View File

@ -86,7 +86,7 @@ typedef struct gfx_ctx_wayland_data
struct wl_touch *wl_touch; struct wl_touch *wl_touch;
struct wl_seat *seat; struct wl_seat *seat;
struct wl_shm *shm; struct wl_shm *shm;
unsigned swap_interval; int swap_interval;
bool core_hw_context_enable; bool core_hw_context_enable;
unsigned buffer_scale; unsigned buffer_scale;
@ -1232,7 +1232,7 @@ static void gfx_ctx_wl_destroy(void *data)
free(wl); free(wl);
} }
static void gfx_ctx_wl_set_swap_interval(void *data, unsigned swap_interval) static void gfx_ctx_wl_set_swap_interval(void *data, int swap_interval)
{ {
gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data; gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;

View File

@ -47,6 +47,7 @@
#ifdef HAVE_OPENGL #ifdef HAVE_OPENGL
#include "../common/gl_common.h" #include "../common/gl_common.h"
#include <gfx/gl_capabilities.h>
#endif #endif
#ifdef HAVE_VULKAN #ifdef HAVE_VULKAN
@ -90,6 +91,7 @@ static HGLRC win32_hw_hrc;
static HDC win32_hdc; static HDC win32_hdc;
static bool win32_use_hw_ctx = false; static bool win32_use_hw_ctx = false;
static bool win32_core_hw_context_enable = false; static bool win32_core_hw_context_enable = false;
static bool wgl_adaptive_vsync = false;
#ifdef HAVE_VULKAN #ifdef HAVE_VULKAN
static gfx_ctx_vulkan_data_t win32_vk; static gfx_ctx_vulkan_data_t win32_vk;
@ -97,13 +99,45 @@ static gfx_ctx_vulkan_data_t win32_vk;
static unsigned win32_major = 0; static unsigned win32_major = 0;
static unsigned win32_minor = 0; static unsigned win32_minor = 0;
static unsigned win32_interval = 0; static int win32_interval = 0;
static enum gfx_ctx_api win32_api = GFX_CTX_NONE; static enum gfx_ctx_api win32_api = GFX_CTX_NONE;
#ifdef HAVE_DYNAMIC #ifdef HAVE_DYNAMIC
static dylib_t dll_handle = NULL; /* Handle to OpenGL32.dll */ static dylib_t dll_handle = NULL; /* Handle to OpenGL32.dll */
#endif #endif
#ifdef HAVE_OPENGL
static bool wgl_has_extension(const char *extension, const char *extensions)
{
const char *start = NULL;
const char *terminator = NULL;
const char *where = strchr(extension, ' ');
if (where || *extension == '\0')
return false;
if (!extensions)
return false;
start = extensions;
for (;;)
{
where = strstr(start, extension);
if (!where)
break;
terminator = where + strlen(extension);
if (where == start || *(where - 1) == ' ')
if (*terminator == ' ' || *terminator == '\0')
return true;
start = terminator;
}
return false;
}
#endif
typedef struct gfx_ctx_cgl_data typedef struct gfx_ctx_cgl_data
{ {
void *empty; void *empty;
@ -263,6 +297,23 @@ static void create_gl_context(HWND hwnd, bool *quit)
else else
RARCH_ERR("[WGL]: wglCreateContextAttribsARB not supported.\n"); RARCH_ERR("[WGL]: wglCreateContextAttribsARB not supported.\n");
} }
{
const char *(WINAPI * wglGetExtensionsStringARB) (HDC) = 0;
const char *extensions = NULL;
wglGetExtensionsStringARB = (const char *(WINAPI *) (HDC))
gfx_ctx_wgl_get_proc_address("wglGetExtensionsStringARB");
if (wglGetExtensionsStringARB)
extensions = wglGetExtensionsStringARB(win32_hdc);
RARCH_LOG("[WGL] extensions: %s\n", extensions);
if (wgl_has_extension("WGL_EXT_swap_control_tear", extensions))
{
RARCH_LOG("[WGL]: Adaptive VSync supported.\n");
wgl_adaptive_vsync = true;
}
}
} }
#endif #endif
@ -308,7 +359,7 @@ void create_graphics_context(HWND hwnd, bool *quit)
void *dinput_wgl; void *dinput_wgl;
static void gfx_ctx_wgl_swap_interval(void *data, unsigned interval) static void gfx_ctx_wgl_swap_interval(void *data, int interval)
{ {
(void)data; (void)data;
@ -322,7 +373,7 @@ static void gfx_ctx_wgl_swap_interval(void *data, unsigned interval)
if (!p_swap_interval) if (!p_swap_interval)
return; return;
RARCH_LOG("[WGL]: wglSwapInterval(%u)\n", win32_interval); RARCH_LOG("[WGL]: wglSwapInterval(%i)\n", win32_interval);
if (!p_swap_interval(win32_interval)) if (!p_swap_interval(win32_interval))
RARCH_WARN("[WGL]: wglSwapInterval() failed.\n"); RARCH_WARN("[WGL]: wglSwapInterval() failed.\n");
#endif #endif
@ -479,6 +530,7 @@ static void *gfx_ctx_wgl_init(video_frame_info_t *video_info, void *video_driver
dll_handle = dylib_load("OpenGL32.dll"); dll_handle = dylib_load("OpenGL32.dll");
#endif #endif
win32_window_reset(); win32_window_reset();
win32_monitor_init(); win32_monitor_init();
@ -572,6 +624,7 @@ static void gfx_ctx_wgl_destroy(void *data)
if (wgl) if (wgl)
free(wgl); free(wgl);
wgl_adaptive_vsync = false;
win32_core_hw_context_enable = false; win32_core_hw_context_enable = false;
g_win32_inited = false; g_win32_inited = false;
win32_major = 0; win32_major = 0;
@ -584,6 +637,10 @@ static bool gfx_ctx_wgl_set_video_mode(void *data,
unsigned width, unsigned height, unsigned width, unsigned height,
bool fullscreen) bool fullscreen)
{ {
#ifdef HAVE_VULKAN
win32_vk.fullscreen = fullscreen;
#endif
if (!win32_set_video_mode(NULL, width, height, fullscreen)) if (!win32_set_video_mode(NULL, width, height, fullscreen))
{ {
RARCH_ERR("[WGL]: win32_set_video_mode failed.\n"); RARCH_ERR("[WGL]: win32_set_video_mode failed.\n");
@ -721,22 +778,51 @@ static void *gfx_ctx_wgl_get_context_data(void *data)
static uint32_t gfx_ctx_wgl_get_flags(void *data) static uint32_t gfx_ctx_wgl_get_flags(void *data)
{ {
uint32_t flags = 0; uint32_t flags = 0;
if (win32_core_hw_context_enable)
BIT32_SET(flags, GFX_CTX_FLAGS_NONE);
switch (win32_api)
{ {
BIT32_SET(flags, GFX_CTX_FLAGS_GL_CORE_CONTEXT); case GFX_CTX_OPENGL_API:
} if (wgl_adaptive_vsync)
else {
{ BIT32_SET(flags, GFX_CTX_FLAGS_ADAPTIVE_VSYNC);
BIT32_SET(flags, GFX_CTX_FLAGS_NONE); }
if (win32_core_hw_context_enable)
{
BIT32_SET(flags, GFX_CTX_FLAGS_GL_CORE_CONTEXT);
}
break;
case GFX_CTX_NONE:
default:
break;
} }
return flags; return flags;
} }
static void gfx_ctx_wgl_set_flags(void *data, uint32_t flags) static void gfx_ctx_wgl_set_flags(void *data, uint32_t flags)
{ {
if (BIT32_GET(flags, GFX_CTX_FLAGS_GL_CORE_CONTEXT)) switch (win32_api)
win32_core_hw_context_enable = true; {
case GFX_CTX_OPENGL_API:
#ifdef HAVE_OPENGL
if (BIT32_GET(flags, GFX_CTX_FLAGS_ADAPTIVE_VSYNC))
{
wgl_adaptive_vsync = true;
}
if (BIT32_GET(flags, GFX_CTX_FLAGS_GL_CORE_CONTEXT))
win32_core_hw_context_enable = true;
#endif
break;
case GFX_CTX_NONE:
default:
break;
}
} }
static void gfx_ctx_wgl_get_video_output_size(void *data, static void gfx_ctx_wgl_get_video_output_size(void *data,

View File

@ -25,6 +25,7 @@
#ifdef HAVE_OPENGL #ifdef HAVE_OPENGL
#include <GL/glx.h> #include <GL/glx.h>
#include <gfx/gl_capabilities.h>
#ifndef GLX_SAMPLE_BUFFERS #ifndef GLX_SAMPLE_BUFFERS
#define GLX_SAMPLE_BUFFERS 100000 #define GLX_SAMPLE_BUFFERS 100000
@ -100,13 +101,14 @@ typedef struct gfx_ctx_x_data
unsigned swap_mode; unsigned swap_mode;
#endif #endif
unsigned g_interval; int g_interval;
#ifdef HAVE_VULKAN #ifdef HAVE_VULKAN
gfx_ctx_vulkan_data_t vk; gfx_ctx_vulkan_data_t vk;
#endif #endif
} gfx_ctx_x_data_t; } gfx_ctx_x_data_t;
static bool x_adaptive_vsync = false;
static bool x_enable_msaa = false; static bool x_enable_msaa = false;
static unsigned g_major = 0; static unsigned g_major = 0;
static unsigned g_minor = 0; static unsigned g_minor = 0;
@ -124,51 +126,25 @@ typedef struct Hints
} Hints; } Hints;
/* We use long because X11 wants 32-bit pixels for 32-bit systems and 64 for 64... */ /* We use long because X11 wants 32-bit pixels for 32-bit systems and 64 for 64... */
/* ARGB*/
static const unsigned long retroarch_icon_data[] = { static const unsigned long retroarch_icon_data[] = {
16, 16, 16, 16,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,0x00000000,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0x00000000,0x00000000,0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0x00000000,0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0x00000000,0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,0xff333333,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xff333333,0x00000000,0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0x00000000,0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,0xff333333,0xfff2f2f2,0xff333333,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xff333333,0xfff2f2f2,0xff333333,0x00000000,0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,0xff333333,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xff333333,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xff333333,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xff333333,0x00000000,0x00000000,
0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000,0xff333333,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xff333333,0x00000000,0x00000000,
0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000,0xff333333,0xff333333,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xff333333,0xff333333,0x00000000,0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0x00000000,0xff333333,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xff333333,0x00000000,0x00000000,
0xffffffff, 0xffffffff, 0x000000ff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0x00000000,0x00000000,
0xffffffff, 0xffffffff, 0x000000ff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0x00000000,0x00000000,
0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0x000000ff, 0xffffffff, 0x00000000,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0x00000000,0x00000000,
0xffffffff, 0x000000ff, 0xffffffff, 0xffffffff, 0xffffffff, 0x000000ff, 0x00000000,0x00000000,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0x00000000,0x00000000,0x00000000,
0xffffffff, 0xffffffff, 0x000000ff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000
0x00000000, 0xffffffff, 0x000000ff, 0xffffffff, 0x000000ff, 0x000000ff,
0x000000ff, 0x000000ff, 0x000000ff, 0x000000ff, 0x000000ff, 0xffffffff,
0x000000ff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0xffffffff,
0x000000ff, 0x000000ff, 0x000000ff, 0xffffffff, 0x000000ff, 0x000000ff,
0x000000ff, 0xffffffff, 0x000000ff, 0x000000ff, 0x000000ff, 0xffffffff,
0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0x000000ff, 0x000000ff,
0x000000ff, 0x000000ff, 0x000000ff, 0x000000ff, 0x000000ff, 0x000000ff,
0x000000ff, 0x000000ff, 0x000000ff, 0xffffffff, 0x00000000, 0x00000000,
0x00000000, 0xffffffff, 0xffffffff, 0x000000ff, 0x000000ff, 0x000000ff,
0x000000ff, 0x000000ff, 0x000000ff, 0x000000ff, 0x000000ff, 0x000000ff,
0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0xffffffff, 0xffffffff, 0x000000ff, 0xffffffff, 0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff, 0x000000ff, 0xffffffff, 0xffffffff, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0x000000ff,
0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0xffffffff,
0xffffffff, 0x000000ff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000
}; };
#ifdef HAVE_OPENGL #ifdef HAVE_OPENGL
@ -320,7 +296,7 @@ static void gfx_ctx_x_destroy(void *data)
free(data); free(data);
} }
static void gfx_ctx_x_swap_interval(void *data, unsigned interval) static void gfx_ctx_x_swap_interval(void *data, int interval)
{ {
gfx_ctx_x_data_t *x = (gfx_ctx_x_data_t*)data; gfx_ctx_x_data_t *x = (gfx_ctx_x_data_t*)data;
@ -333,18 +309,18 @@ static void gfx_ctx_x_swap_interval(void *data, unsigned interval)
if (g_pglSwapIntervalEXT) if (g_pglSwapIntervalEXT)
{ {
RARCH_LOG("[GLX]: glXSwapIntervalEXT(%u)\n", x->g_interval); RARCH_LOG("[GLX]: glXSwapIntervalEXT(%i)\n", x->g_interval);
g_pglSwapIntervalEXT(g_x11_dpy, x->g_glx_win, x->g_interval); g_pglSwapIntervalEXT(g_x11_dpy, x->g_glx_win, x->g_interval);
} }
else if (g_pglSwapInterval) else if (g_pglSwapInterval)
{ {
RARCH_LOG("[GLX]: glXSwapInterval(%u)\n", x->g_interval); RARCH_LOG("[GLX]: glXSwapInterval(%i)\n", x->g_interval);
if (g_pglSwapInterval(x->g_interval) != 0) if (g_pglSwapInterval(x->g_interval) != 0)
RARCH_WARN("[GLX]: glXSwapInterval() failed.\n"); RARCH_WARN("[GLX]: glXSwapInterval() failed.\n");
} }
else if (g_pglSwapIntervalSGI) else if (g_pglSwapIntervalSGI)
{ {
RARCH_LOG("[GLX]: glXSwapIntervalSGI(%u)\n", x->g_interval); RARCH_LOG("[GLX]: glXSwapIntervalSGI(%i)\n", x->g_interval);
if (g_pglSwapIntervalSGI(x->g_interval) != 0) if (g_pglSwapIntervalSGI(x->g_interval) != 0)
RARCH_WARN("[GLX]: glXSwapIntervalSGI() failed.\n"); RARCH_WARN("[GLX]: glXSwapIntervalSGI() failed.\n");
} }
@ -581,6 +557,11 @@ static void *gfx_ctx_x_init(video_frame_info_t *video_info, void *data)
{ {
case GFX_CTX_OPENGL_API: case GFX_CTX_OPENGL_API:
#ifdef HAVE_OPENGL #ifdef HAVE_OPENGL
if (GLXExtensionSupported(g_x11_dpy, "GLX_EXT_swap_control_tear"))
{
RARCH_LOG("[GLX]: GLX_EXT_swap_control_tear supported.\n");
x_adaptive_vsync = true;
}
if (GLXExtensionSupported(g_x11_dpy, "GLX_OML_sync_control") && if (GLXExtensionSupported(g_x11_dpy, "GLX_OML_sync_control") &&
GLXExtensionSupported(g_x11_dpy, "GLX_MESA_swap_control") GLXExtensionSupported(g_x11_dpy, "GLX_MESA_swap_control")
) )
@ -1167,30 +1148,56 @@ static void *gfx_ctx_x_get_context_data(void *data)
static uint32_t gfx_ctx_x_get_flags(void *data) static uint32_t gfx_ctx_x_get_flags(void *data)
{ {
uint32_t flags = 0; uint32_t flags = 0;
gfx_ctx_x_data_t *x = (gfx_ctx_x_data_t*)data; gfx_ctx_x_data_t *x = (gfx_ctx_x_data_t*)data;
if (x->core_hw_context_enable || x->g_core_es)
BIT32_SET(flags, GFX_CTX_FLAGS_NONE);
switch (x_api)
{ {
BIT32_SET(flags, GFX_CTX_FLAGS_GL_CORE_CONTEXT); case GFX_CTX_OPENGL_API:
} case GFX_CTX_OPENGL_ES_API:
else if (x_adaptive_vsync)
{ {
BIT32_SET(flags, GFX_CTX_FLAGS_NONE); BIT32_SET(flags, GFX_CTX_FLAGS_ADAPTIVE_VSYNC);
} }
if (x_enable_msaa)
{ if (x->core_hw_context_enable || x->g_core_es)
BIT32_SET(flags, GFX_CTX_FLAGS_MULTISAMPLING); {
BIT32_SET(flags, GFX_CTX_FLAGS_GL_CORE_CONTEXT);
}
if (x_enable_msaa)
{
BIT32_SET(flags, GFX_CTX_FLAGS_MULTISAMPLING);
}
break;
case GFX_CTX_NONE:
default:
break;
} }
return flags; return flags;
} }
static void gfx_ctx_x_set_flags(void *data, uint32_t flags) static void gfx_ctx_x_set_flags(void *data, uint32_t flags)
{ {
gfx_ctx_x_data_t *x = (gfx_ctx_x_data_t*)data; gfx_ctx_x_data_t *x = (gfx_ctx_x_data_t*)data;
if (BIT32_GET(flags, GFX_CTX_FLAGS_GL_CORE_CONTEXT))
x->core_hw_context_enable = true; switch (x_api)
if (BIT32_GET(flags, GFX_CTX_FLAGS_MULTISAMPLING)) {
x_enable_msaa = true; case GFX_CTX_OPENGL_API:
case GFX_CTX_OPENGL_ES_API:
if (BIT32_GET(flags, GFX_CTX_FLAGS_ADAPTIVE_VSYNC))
x_adaptive_vsync = true;
if (BIT32_GET(flags, GFX_CTX_FLAGS_GL_CORE_CONTEXT))
x->core_hw_context_enable = true;
if (BIT32_GET(flags, GFX_CTX_FLAGS_MULTISAMPLING))
x_enable_msaa = true;
break;
case GFX_CTX_NONE:
default:
break;
}
} }
static void gfx_ctx_x_make_current(bool release) static void gfx_ctx_x_make_current(bool release)

View File

@ -255,7 +255,7 @@ static EGLint *xegl_fill_attribs(xegl_ctx_data_t *xegl, EGLint *attr)
} }
/* forward declaration */ /* forward declaration */
static void gfx_ctx_xegl_set_swap_interval(void *data, unsigned swap_interval); static void gfx_ctx_xegl_set_swap_interval(void *data, int swap_interval);
static bool gfx_ctx_xegl_set_video_mode(void *data, static bool gfx_ctx_xegl_set_video_mode(void *data,
video_frame_info_t *video_info, video_frame_info_t *video_info,
@ -561,7 +561,7 @@ static void gfx_ctx_xegl_bind_hw_render(void *data, bool enable)
} }
} }
static void gfx_ctx_xegl_set_swap_interval(void *data, unsigned swap_interval) static void gfx_ctx_xegl_set_swap_interval(void *data, int swap_interval)
{ {
xegl_ctx_data_t *xegl = (xegl_ctx_data_t*)data; xegl_ctx_data_t *xegl = (xegl_ctx_data_t*)data;

View File

@ -0,0 +1,350 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2018 - lifajucejo
* Copyright (C) 2018 - m4xw
* Copyright (C) 2018 - natinusala
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <math.h>
#include <encodings/utf.h>
#include <retro_math.h>
#include "../font_driver.h"
#include "../video_driver.h"
#include "../../verbosity.h"
#include "../common/switch_common.h"
typedef struct
{
struct font_atlas *atlas;
const font_renderer_driver_t *font_driver;
void *font_data;
} switch_font_t;
static void *switch_font_init_font(void *data, const char *font_path,
float font_size, bool is_threaded)
{
switch_font_t *font = (switch_font_t *)calloc(1, sizeof(switch_font_t));
if (!font)
return NULL;
if (!font_renderer_create_default((const void **)&font->font_driver,
&font->font_data, font_path, font_size))
{
RARCH_WARN("Couldn't initialize font renderer.\n");
free(font);
return NULL;
}
font->atlas = font->font_driver->get_atlas(font->font_data);
RARCH_LOG("Switch font driver initialized with backend %s\n", font->font_driver->ident);
return font;
}
static void switch_font_free_font(void *data, bool is_threaded)
{
switch_font_t *font = (switch_font_t *)data;
if (!font)
return;
if (font->font_driver && font->font_data)
font->font_driver->free(font->font_data);
free(font);
}
static int switch_font_get_message_width(void *data, const char *msg,
unsigned msg_len, float scale)
{
switch_font_t *font = (switch_font_t *)data;
unsigned i;
int delta_x = 0;
if (!font)
return 0;
for (i = 0; i < msg_len; i++)
{
const char *msg_tmp = &msg[i];
unsigned code = utf8_walk(&msg_tmp);
unsigned skip = msg_tmp - &msg[i];
if (skip > 1)
i += skip - 1;
const struct font_glyph *glyph =
font->font_driver->get_glyph(font->font_data, code);
if (!glyph) /* Do something smarter here ... */
glyph = font->font_driver->get_glyph(font->font_data, '?');
if (!glyph)
continue;
delta_x += glyph->advance_x;
}
return delta_x * scale;
}
static void switch_font_render_line(
video_frame_info_t *video_info,
switch_font_t *font, const char *msg, unsigned msg_len,
float scale, const unsigned int color, float pos_x,
float pos_y, unsigned text_align)
{
int delta_x = 0;
int delta_y = 0;
unsigned fbWidth = 0;
unsigned fbHeight = 0;
uint32_t *out_buffer = (uint32_t *)gfxGetFramebuffer(&fbWidth, &fbHeight);
if (out_buffer)
{
int x = roundf(pos_x * fbWidth);
int y = roundf((1.0f - pos_y) * fbHeight);
switch (text_align)
{
case TEXT_ALIGN_RIGHT:
x -= switch_font_get_message_width(font, msg, msg_len, scale);
break;
case TEXT_ALIGN_CENTER:
x -= switch_font_get_message_width(font, msg, msg_len, scale) / 2;
break;
}
for (int i = 0; i < msg_len; i++)
{
int off_x, off_y, tex_x, tex_y, width, height;
const char *msg_tmp = &msg[i];
unsigned code = utf8_walk(&msg_tmp);
unsigned skip = msg_tmp - &msg[i];
if (skip > 1)
i += skip - 1;
const struct font_glyph *glyph =
font->font_driver->get_glyph(font->font_data, code);
if (!glyph) /* Do something smarter here ... */
glyph = font->font_driver->get_glyph(font->font_data, '?');
if (!glyph)
continue;
off_x = x + glyph->draw_offset_x + delta_x;
off_y = y + glyph->draw_offset_y + delta_y;
width = glyph->width;
height = glyph->height;
tex_x = glyph->atlas_offset_x;
tex_y = glyph->atlas_offset_y;
for (int y = tex_y; y < tex_y + height; y++)
{
uint8_t *row = &font->atlas->buffer[y * font->atlas->width];
for (int x = tex_x; x < tex_x + width; x++)
{
if (!row[x])
continue;
int x1 = off_x + (x - tex_x);
int y1 = off_y + (y - tex_y);
if (x1 < fbWidth && y1 < fbHeight)
out_buffer[gfxGetFramebufferDisplayOffset(x1, y1)] = color;
}
}
delta_x += glyph->advance_x;
delta_y += glyph->advance_y;
}
}
}
#define AVG_GLPYH_LIMIT 140
static void switch_font_render_message(
video_frame_info_t *video_info,
switch_font_t *font, const char *msg, float scale,
const unsigned int color, float pos_x, float pos_y,
unsigned text_align)
{
int lines = 0;
float line_height;
if (!msg || !*msg)
return;
/* If the font height is not supported just draw as usual */
if (!font->font_driver->get_line_height)
{
int msgLen = strlen(msg);
if (msgLen <= AVG_GLPYH_LIMIT)
{
switch_font_render_line(video_info, font, msg, strlen(msg),
scale, color, pos_x, pos_y, text_align);
}
return;
}
line_height = scale / font->font_driver->get_line_height(font->font_data);
for (;;)
{
const char *delim = strchr(msg, '\n');
/* Draw the line */
if (delim)
{
unsigned msg_len = delim - msg;
if (msg_len <= AVG_GLPYH_LIMIT)
{
switch_font_render_line(video_info, font, msg, msg_len,
scale, color, pos_x, pos_y - (float)lines * line_height,
text_align);
}
msg += msg_len + 1;
lines++;
}
else
{
unsigned msg_len = strlen(msg);
if (msg_len <= AVG_GLPYH_LIMIT)
{
switch_font_render_line(video_info, font, msg, msg_len,
scale, color, pos_x, pos_y - (float)lines * line_height,
text_align);
}
break;
}
}
}
static void switch_font_render_msg(
video_frame_info_t *video_info,
void *data, const char *msg,
const struct font_params *params)
{
float x, y, scale, drop_mod, drop_alpha;
int drop_x, drop_y;
unsigned max_glyphs;
enum text_alignment text_align;
unsigned color, color_dark, r, g, b,
alpha, r_dark, g_dark, b_dark, alpha_dark;
switch_font_t *font = (switch_font_t *)data;
unsigned width = video_info->width;
unsigned height = video_info->height;
if (!font || !msg || msg && !*msg)
return;
if (params)
{
x = params->x;
y = params->y;
scale = params->scale;
text_align = params->text_align;
drop_x = params->drop_x;
drop_y = params->drop_y;
drop_mod = params->drop_mod;
drop_alpha = params->drop_alpha;
r = FONT_COLOR_GET_RED(params->color);
g = FONT_COLOR_GET_GREEN(params->color);
b = FONT_COLOR_GET_BLUE(params->color);
alpha = FONT_COLOR_GET_ALPHA(params->color);
color = params->color;
}
else
{
x = 0.0f;
y = 0.0f;
scale = 1.0f;
text_align = TEXT_ALIGN_LEFT;
r = (video_info->font_msg_color_r * 255);
g = (video_info->font_msg_color_g * 255);
b = (video_info->font_msg_color_b * 255);
alpha = 255;
color = COLOR_ABGR(r, g, b, alpha);
drop_x = -2;
drop_y = -2;
drop_mod = 0.3f;
drop_alpha = 1.0f;
}
max_glyphs = strlen(msg);
/*if (drop_x || drop_y)
max_glyphs *= 2;
if (drop_x || drop_y)
{
r_dark = r * drop_mod;
g_dark = g * drop_mod;
b_dark = b * drop_mod;
alpha_dark = alpha * drop_alpha;
color_dark = COLOR_ABGR(r_dark, g_dark, b_dark, alpha_dark);
switch_font_render_message(video_info, font, msg, scale, color_dark,
x + scale * drop_x / width, y +
scale * drop_y / height, text_align);
}*/
switch_font_render_message(video_info, font, msg, scale,
color, x, y, text_align);
}
static const struct font_glyph *switch_font_get_glyph(
void *data, uint32_t code)
{
switch_font_t *font = (switch_font_t *)data;
if (!font || !font->font_driver)
return NULL;
if (!font->font_driver->ident)
return NULL;
return font->font_driver->get_glyph((void *)font->font_driver, code);
}
static void switch_font_bind_block(void *data, void *userdata)
{
(void)data;
}
font_renderer_t switch_font =
{
switch_font_init_font,
switch_font_free_font,
switch_font_render_msg,
"switchfont",
switch_font_get_glyph,
switch_font_bind_block,
NULL, /* flush_block */
switch_font_get_message_width,
};

View File

@ -524,6 +524,37 @@ static bool ctr_font_init_first(
} }
#endif #endif
#ifdef HAVE_LIBNX
static const font_renderer_t *switch_font_backends[] = {
&switch_font,
NULL
};
static bool switch_font_init_first(
const void **font_driver, void **font_handle,
void *video_data, const char *font_path,
float font_size, bool is_threaded)
{
unsigned i;
for (i = 0; switch_font_backends[i]; i++)
{
void *data = switch_font_backends[i]->init(
video_data, font_path, font_size,
is_threaded);
if (!data)
continue;
*font_driver = switch_font_backends[i];
*font_handle = data;
return true;
}
return false;
}
#endif
#ifdef WIIU #ifdef WIIU
static const font_renderer_t *wiiu_font_backends[] = { static const font_renderer_t *wiiu_font_backends[] = {
&wiiu_font, &wiiu_font,
@ -630,6 +661,11 @@ static bool font_init_first(
return sixel_font_init_first(font_driver, font_handle, return sixel_font_init_first(font_driver, font_handle,
video_data, font_path, font_size, is_threaded); video_data, font_path, font_size, is_threaded);
#endif #endif
#ifdef HAVE_LIBNX
case FONT_DRIVER_RENDER_SWITCH:
return switch_font_init_first(font_driver, font_handle,
video_data, font_path, font_size, is_threaded);
#endif
#if defined(_WIN32) && !defined(_XBOX) #if defined(_WIN32) && !defined(_XBOX)
case FONT_DRIVER_RENDER_GDI: case FONT_DRIVER_RENDER_GDI:
return gdi_font_init_first(font_driver, font_handle, return gdi_font_init_first(font_driver, font_handle,

View File

@ -173,6 +173,7 @@ extern font_renderer_t caca_font;
extern font_renderer_t gdi_font; extern font_renderer_t gdi_font;
extern font_renderer_t vga_font; extern font_renderer_t vga_font;
extern font_renderer_t sixel_font; extern font_renderer_t sixel_font;
extern font_renderer_t switch_font;
extern font_renderer_driver_t stb_font_renderer; extern font_renderer_driver_t stb_font_renderer;
extern font_renderer_driver_t stb_unicode_font_renderer; extern font_renderer_driver_t stb_unicode_font_renderer;

View File

@ -31,6 +31,7 @@ static unsigned ra_tmp_height = 0;
static unsigned ra_set_core_hz = 0; static unsigned ra_set_core_hz = 0;
static unsigned orig_width = 0; static unsigned orig_width = 0;
static unsigned orig_height = 0; static unsigned orig_height = 0;
static int crt_center_adjust = 0;
static bool first_run = true; static bool first_run = true;
@ -53,12 +54,25 @@ static void switch_crt_hz(void)
if (ra_core_hz == ra_tmp_core_hz) if (ra_core_hz == ra_tmp_core_hz)
return; return;
/* set hz float to an int for windows switching */ /* set hz float to an int for windows switching */
if (ra_core_hz < 53) if (ra_core_hz < 100)
ra_set_core_hz = 50; {
if (ra_core_hz >= 53 && ra_core_hz < 57) if (ra_core_hz < 53)
ra_set_core_hz = 55; ra_set_core_hz = 50;
if (ra_core_hz >= 57) if (ra_core_hz >= 53 && ra_core_hz < 57)
ra_set_core_hz = 60; ra_set_core_hz = 55;
if (ra_core_hz >= 57)
ra_set_core_hz = 60;
}
if (ra_core_hz > 100)
{
if (ra_core_hz < 106)
ra_set_core_hz = 120;
if (ra_core_hz >= 106 && ra_core_hz < 114)
ra_set_core_hz = 110;
if (ra_core_hz >= 114)
ra_set_core_hz = 120;
}
video_monitor_set_refresh_rate(ra_set_core_hz); video_monitor_set_refresh_rate(ra_set_core_hz);
@ -78,7 +92,7 @@ static void switch_res_crt(unsigned width, unsigned height)
if (height > 100) if (height > 100)
{ {
video_display_server_switch_resolution(width, height, video_display_server_switch_resolution(width, height,
ra_set_core_hz, ra_core_hz); ra_set_core_hz, ra_core_hz, crt_center_adjust);
video_driver_apply_state_changes(); video_driver_apply_state_changes();
} }
} }
@ -100,13 +114,13 @@ static void crt_screen_setup_aspect(unsigned width, unsigned height)
crt_aspect_ratio_switch(width, height); crt_aspect_ratio_switch(width, height);
} }
if (height < 191 && height != 144) if (height < 200 && height != 144)
{ {
crt_aspect_ratio_switch(width, height); crt_aspect_ratio_switch(width, height);
height = 200; height = 200;
} }
if (height > 191) if (height > 200)
crt_aspect_ratio_switch(width, height); crt_aspect_ratio_switch(width, height);
if (height == 144 && ra_set_core_hz == 50) if (height == 144 && ra_set_core_hz == 50)
@ -149,13 +163,23 @@ static void crt_screen_setup_aspect(unsigned width, unsigned height)
} }
void crt_switch_res_core(unsigned width, unsigned height, float hz) void crt_switch_res_core(unsigned width, unsigned height, float hz, unsigned crt_mode, int crt_switch_center_adjust)
{ {
/* ra_core_hz float passed from within /* ra_core_hz float passed from within
* void video_driver_monitor_adjust_system_rates(void) */ * void video_driver_monitor_adjust_system_rates(void) */
ra_core_width = width; ra_core_width = width;
ra_core_height = height; ra_core_height = height;
ra_core_hz = hz; ra_core_hz = hz;
crt_center_adjust = crt_switch_center_adjust;
if (crt_mode == 2)
{
if (hz > 53)
ra_core_hz = hz*2;
if (hz <= 53)
ra_core_hz = 120.0f;
}
crt_check_first_run(); crt_check_first_run();

View File

@ -27,7 +27,7 @@
RETRO_BEGIN_DECLS RETRO_BEGIN_DECLS
void crt_switch_res_core(unsigned width, unsigned height, float hz); void crt_switch_res_core(unsigned width, unsigned height, float hz, unsigned crt_mode, int crt_switch_center_adjust);
void crt_aspect_ratio_switch(unsigned width, unsigned height); void crt_aspect_ratio_switch(unsigned width, unsigned height);

View File

@ -96,7 +96,8 @@ enum font_driver_render_api
FONT_DRIVER_RENDER_CACA, FONT_DRIVER_RENDER_CACA,
FONT_DRIVER_RENDER_SIXEL, FONT_DRIVER_RENDER_SIXEL,
FONT_DRIVER_RENDER_GDI, FONT_DRIVER_RENDER_GDI,
FONT_DRIVER_RENDER_VGA FONT_DRIVER_RENDER_VGA,
FONT_DRIVER_RENDER_SWITCH
}; };
enum text_alignment enum text_alignment

View File

@ -84,9 +84,16 @@ bool video_display_server_set_window_decorations(bool on)
bool video_display_server_switch_resolution(unsigned width, unsigned height, bool video_display_server_switch_resolution(unsigned width, unsigned height,
int int_hz, float hz) int int_hz, float hz, int center)
{ {
if (current_display_server && current_display_server->switch_resolution) if (current_display_server && current_display_server->switch_resolution)
return current_display_server->switch_resolution(current_display_server_data, width, height, int_hz, hz); return current_display_server->switch_resolution(current_display_server_data, width, height, int_hz, hz, center);
return false; return false;
} }
const char *video_display_server_get_output_options(void)
{
if (current_display_server && current_display_server->get_output_options)
return current_display_server->get_output_options(current_display_server_data);
return NULL;
}

View File

@ -31,7 +31,8 @@ typedef struct video_display_server
bool (*set_window_progress)(void *data, int progress, bool finished); bool (*set_window_progress)(void *data, int progress, bool finished);
bool (*set_window_decorations)(void *data, bool on); bool (*set_window_decorations)(void *data, bool on);
bool (*switch_resolution)(void *data, unsigned width, bool (*switch_resolution)(void *data, unsigned width,
unsigned height, int int_hz, float hz); unsigned height, int int_hz, float hz, int center);
const char *(*get_output_options)(void *data);
const char *ident; const char *ident;
} video_display_server_t; } video_display_server_t;
@ -47,7 +48,9 @@ bool video_display_server_set_window_decorations(bool on);
bool video_display_server_switch_resolution( bool video_display_server_switch_resolution(
unsigned width, unsigned height, unsigned width, unsigned height,
int int_hz, float hz); int int_hz, float hz, int center);
const char *video_display_server_get_output_options(void);
extern const video_display_server_t dispserv_win32; extern const video_display_server_t dispserv_win32;
extern const video_display_server_t dispserv_x11; extern const video_display_server_t dispserv_x11;

View File

@ -354,6 +354,9 @@ static const video_driver_t *video_drivers[] = {
}; };
static const gfx_ctx_driver_t *gfx_ctx_drivers[] = { static const gfx_ctx_driver_t *gfx_ctx_drivers[] = {
#if defined(HAVE_LIBNX) && defined(HAVE_OPENGL)
&switch_ctx,
#endif
#if defined(__CELLOS_LV2__) #if defined(__CELLOS_LV2__)
&gfx_ctx_ps3, &gfx_ctx_ps3,
#endif #endif
@ -2643,7 +2646,7 @@ void video_driver_frame(const void *data, unsigned width,
width = 3840; width = 3840;
if (video_info.crt_switch_resolution_super == 1920) if (video_info.crt_switch_resolution_super == 1920)
width = 1920; width = 1920;
crt_switch_res_core(width, height, video_driver_core_hz); crt_switch_res_core(width, height, video_driver_core_hz, video_info.crt_switch_resolution, video_info.crt_switch_center_adjust);
} }
else if (!video_info.crt_switch_resolution) else if (!video_info.crt_switch_resolution)
video_driver_crt_switching_active = false; video_driver_crt_switching_active = false;
@ -2741,8 +2744,9 @@ void video_driver_build_info(video_frame_info_t *video_info)
settings = config_get_ptr(); settings = config_get_ptr();
custom_vp = &settings->video_viewport_custom; custom_vp = &settings->video_viewport_custom;
video_info->refresh_rate = settings->floats.video_refresh_rate; video_info->refresh_rate = settings->floats.video_refresh_rate;
video_info->crt_switch_resolution = settings->bools.crt_switch_resolution; video_info->crt_switch_resolution = settings->uints.crt_switch_resolution;
video_info->crt_switch_resolution_super = settings->uints.crt_switch_resolution_super; video_info->crt_switch_resolution_super = settings->uints.crt_switch_resolution_super;
video_info->crt_switch_center_adjust = settings->ints.crt_switch_center_adjust;
video_info->black_frame_insertion = settings->bools.video_black_frame_insertion; video_info->black_frame_insertion = settings->bools.video_black_frame_insertion;
video_info->hard_sync = settings->bools.video_hard_sync; video_info->hard_sync = settings->bools.video_hard_sync;
video_info->hard_sync_frames = settings->uints.video_hard_sync_frames; video_info->hard_sync_frames = settings->uints.video_hard_sync_frames;
@ -3028,7 +3032,7 @@ static const gfx_ctx_driver_t *video_context_driver_init(
ctx->bind_hw_render(ctx_data, ctx->bind_hw_render(ctx_data,
video_info.shared_context && hw_render_ctx); video_info.shared_context && hw_render_ctx);
video_context_driver_set_data(ctx_data); video_context_data = ctx_data;
return ctx; return ctx;
} }
@ -3202,11 +3206,18 @@ bool video_context_driver_get_video_output_size(gfx_ctx_size_t *size_data)
return true; return true;
} }
bool video_context_driver_swap_interval(unsigned *interval) bool video_context_driver_swap_interval(int *interval)
{ {
gfx_ctx_flags_t flags;
int current_interval = *interval;
settings_t *settings = config_get_ptr();
bool adaptive_vsync_enabled = video_driver_get_all_flags(&flags, GFX_CTX_FLAGS_ADAPTIVE_VSYNC) && settings->bools.video_adaptive_vsync;
if (!current_video_context.swap_interval) if (!current_video_context.swap_interval)
return false; return false;
current_video_context.swap_interval(video_context_data, *interval); if (adaptive_vsync_enabled && current_interval == 1)
current_interval = -1;
current_video_context.swap_interval(video_context_data, current_interval);
return true; return true;
} }
@ -3232,14 +3243,26 @@ bool video_context_driver_get_metrics(gfx_ctx_metrics_t *metrics)
bool video_context_driver_get_refresh_rate(float *refresh_rate) bool video_context_driver_get_refresh_rate(float *refresh_rate)
{ {
float refresh_holder = 0;
if (!current_video_context.get_refresh_rate || !refresh_rate) if (!current_video_context.get_refresh_rate || !refresh_rate)
return false; return false;
if (!video_context_data) if (!video_context_data)
return false; return false;
if (refresh_rate) if (!video_driver_crt_switching_active)
*refresh_rate = if (refresh_rate)
current_video_context.get_refresh_rate(video_context_data); *refresh_rate =
current_video_context.get_refresh_rate(video_context_data);
if (video_driver_crt_switching_active)
{
if (refresh_rate)
refresh_holder =
current_video_context.get_refresh_rate(video_context_data);
if (refresh_holder != video_driver_core_hz) /* Fix for incorrect interlace detsction -- HARD SET VSNC TO REQUIRED REFRESH FOR CRT*/
*refresh_rate = video_driver_core_hz;
}
return true; return true;
} }
@ -3317,22 +3340,9 @@ bool video_context_driver_show_mouse(bool *bool_data)
return true; return true;
} }
void video_context_driver_set_data(void *data) static bool video_context_driver_get_flags(gfx_ctx_flags_t *flags)
{ {
video_context_data = data; if (!current_video_context.get_flags)
}
bool video_driver_get_flags(gfx_ctx_flags_t *flags)
{
if (!flags || !video_driver_poke || !video_driver_poke->get_flags)
return false;
flags->flags = video_driver_poke->get_flags(video_driver_data);
return true;
}
bool video_context_driver_get_flags(gfx_ctx_flags_t *flags)
{
if (!flags || !current_video_context.get_flags)
return false; return false;
if (deferred_video_context_driver_set_flags) if (deferred_video_context_driver_set_flags)
@ -3346,6 +3356,34 @@ bool video_context_driver_get_flags(gfx_ctx_flags_t *flags)
return true; return true;
} }
static bool video_driver_get_flags(gfx_ctx_flags_t *flags)
{
if (!video_driver_poke || !video_driver_poke->get_flags)
return false;
flags->flags = video_driver_poke->get_flags(video_driver_data);
return true;
}
bool video_driver_get_all_flags(gfx_ctx_flags_t *flags, enum display_flags flag)
{
if (!flags)
return false;
if (video_driver_get_flags(flags))
if (BIT32_GET(flags->flags, flag))
return true;
flags->flags = 0;
if (video_context_driver_get_flags(flags))
if (BIT32_GET(flags->flags, flag))
return true;
return false;
}
bool video_context_driver_set_flags(gfx_ctx_flags_t *flags) bool video_context_driver_set_flags(gfx_ctx_flags_t *flags)
{ {
if (!flags) if (!flags)

View File

@ -118,7 +118,8 @@ enum display_flags
GFX_CTX_FLAGS_CUSTOMIZABLE_SWAPCHAIN_IMAGES, GFX_CTX_FLAGS_CUSTOMIZABLE_SWAPCHAIN_IMAGES,
GFX_CTX_FLAGS_HARD_SYNC, GFX_CTX_FLAGS_HARD_SYNC,
GFX_CTX_FLAGS_BLACK_FRAME_INSERTION, GFX_CTX_FLAGS_BLACK_FRAME_INSERTION,
GFX_CTX_FLAGS_MENU_FRAME_FILTERING GFX_CTX_FLAGS_MENU_FRAME_FILTERING,
GFX_CTX_FLAGS_ADAPTIVE_VSYNC
}; };
enum shader_uniform_type enum shader_uniform_type
@ -354,7 +355,7 @@ typedef struct video_info
*/ */
unsigned height; unsigned height;
unsigned swap_interval; int swap_interval;
#ifdef GEKKO #ifdef GEKKO
bool vfilter; bool vfilter;
@ -408,7 +409,6 @@ typedef struct video_frame_info
bool black_frame_insertion; bool black_frame_insertion;
bool hard_sync; bool hard_sync;
bool fps_show; bool fps_show;
bool crt_switch_resolution;
bool statistics_show; bool statistics_show;
bool framecount_show; bool framecount_show;
bool scale_integer; bool scale_integer;
@ -429,11 +429,13 @@ typedef struct video_frame_info
int custom_vp_x; int custom_vp_x;
int custom_vp_y; int custom_vp_y;
int crt_switch_center_adjust;
unsigned hard_sync_frames; unsigned hard_sync_frames;
unsigned aspect_ratio_idx; unsigned aspect_ratio_idx;
unsigned max_swapchain_images; unsigned max_swapchain_images;
unsigned monitor_index; unsigned monitor_index;
unsigned crt_switch_resolution;
unsigned crt_switch_resolution_super; unsigned crt_switch_resolution_super;
unsigned width; unsigned width;
unsigned height; unsigned height;
@ -522,7 +524,7 @@ typedef struct gfx_ctx_driver
unsigned major, unsigned minor); unsigned major, unsigned minor);
/* Sets the swap interval. */ /* Sets the swap interval. */
void (*swap_interval)(void *data, unsigned); void (*swap_interval)(void *data, int);
/* Sets video mode. Creates a window, etc. */ /* Sets video mode. Creates a window, etc. */
bool (*set_video_mode)(void*, video_frame_info_t *video_info, unsigned, unsigned, bool); bool (*set_video_mode)(void*, video_frame_info_t *video_info, unsigned, unsigned, bool);
@ -1166,7 +1168,7 @@ void video_context_driver_destroy(void);
bool video_context_driver_get_video_output_size(gfx_ctx_size_t *size_data); bool video_context_driver_get_video_output_size(gfx_ctx_size_t *size_data);
bool video_context_driver_swap_interval(unsigned *interval); bool video_context_driver_swap_interval(int *interval);
bool video_context_driver_get_proc_address(gfx_ctx_proc_address_t *proc); bool video_context_driver_get_proc_address(gfx_ctx_proc_address_t *proc);
@ -1184,12 +1186,6 @@ bool video_context_driver_get_context_data(void *data);
bool video_context_driver_show_mouse(bool *bool_data); bool video_context_driver_show_mouse(bool *bool_data);
void video_context_driver_set_data(void *data);
bool video_driver_get_flags(gfx_ctx_flags_t *flags);
bool video_context_driver_get_flags(gfx_ctx_flags_t *flags);
bool video_context_driver_set_flags(gfx_ctx_flags_t *flags); bool video_context_driver_set_flags(gfx_ctx_flags_t *flags);
bool video_context_driver_get_metrics(gfx_ctx_metrics_t *metrics); bool video_context_driver_get_metrics(gfx_ctx_metrics_t *metrics);
@ -1248,6 +1244,9 @@ bool video_driver_started_fullscreen(void);
bool video_driver_is_threaded(void); bool video_driver_is_threaded(void);
bool video_driver_get_all_flags(gfx_ctx_flags_t *flags,
enum display_flags flag);
extern video_driver_t video_gl; extern video_driver_t video_gl;
extern video_driver_t video_vulkan; extern video_driver_t video_vulkan;
extern video_driver_t video_metal; extern video_driver_t video_metal;
@ -1299,6 +1298,7 @@ extern const gfx_ctx_driver_t gfx_ctx_opendingux_fbdev;
extern const gfx_ctx_driver_t gfx_ctx_khr_display; extern const gfx_ctx_driver_t gfx_ctx_khr_display;
extern const gfx_ctx_driver_t gfx_ctx_gdi; extern const gfx_ctx_driver_t gfx_ctx_gdi;
extern const gfx_ctx_driver_t gfx_ctx_sixel; extern const gfx_ctx_driver_t gfx_ctx_sixel;
extern const gfx_ctx_driver_t switch_ctx;
extern const gfx_ctx_driver_t gfx_ctx_null; extern const gfx_ctx_driver_t gfx_ctx_null;

View File

@ -940,6 +940,8 @@ FRONTEND
#include "../frontend/drivers/platform_psp.c" #include "../frontend/drivers/platform_psp.c"
#elif defined(_3DS) #elif defined(_3DS)
#include "../frontend/drivers/platform_ctr.c" #include "../frontend/drivers/platform_ctr.c"
#elif defined(SWITCH) && defined(HAVE_LIBNX)
#include "../frontend/drivers/platform_switch.c"
#elif defined(XENON) #elif defined(XENON)
#include "../frontend/drivers/platform_xenon.c" #include "../frontend/drivers/platform_xenon.c"
#elif defined(__QNX__) #elif defined(__QNX__)
@ -1135,7 +1137,6 @@ MENU
#ifdef HAVE_MENU #ifdef HAVE_MENU
#include "../menu/menu_driver.c" #include "../menu/menu_driver.c"
#include "../menu/menu_input.c" #include "../menu/menu_input.c"
#include "../menu/menu_event.c"
#include "../menu/menu_entries.c" #include "../menu/menu_entries.c"
#include "../menu/menu_setting.c" #include "../menu/menu_setting.c"
#include "../menu/menu_cbs.c" #include "../menu/menu_cbs.c"
@ -1215,6 +1216,10 @@ MENU
#include "../menu/drivers_display/menu_display_wiiu.c" #include "../menu/drivers_display/menu_display_wiiu.c"
#endif #endif
#if defined(HAVE_LIBNX)
#include "../menu/drivers_display/menu_display_switch.c"
#endif
#ifdef HAVE_CACA #ifdef HAVE_CACA
#include "../menu/drivers_display/menu_display_caca.c" #include "../menu/drivers_display/menu_display_caca.c"
#endif #endif
@ -1421,6 +1426,7 @@ HTTP SERVER
SSL SSL
============================================================ */ ============================================================ */
#if defined(HAVE_SSL) #if defined(HAVE_SSL)
#if defined(HAVE_NETWORKING)
#include "../deps/mbedtls/aes.c" #include "../deps/mbedtls/aes.c"
#include "../deps/mbedtls/aesni.c" #include "../deps/mbedtls/aesni.c"
#include "../deps/mbedtls/arc4.c" #include "../deps/mbedtls/arc4.c"
@ -1497,3 +1503,4 @@ SSL
#include "../libretro-common/net/net_socket_ssl.c" #include "../libretro-common/net/net_socket_ssl.c"
#endif #endif
#endif

View File

@ -33,7 +33,9 @@ MENU
UI UI
============================================================ */ ============================================================ */
#if defined(HAVE_QT) #if defined(HAVE_QT)
#ifndef __APPLE__
#define HAVE_MAIN /* also requires defining in frontend.c */ #define HAVE_MAIN /* also requires defining in frontend.c */
#endif
#include "../ui/drivers/ui_qt.cpp" #include "../ui/drivers/ui_qt.cpp"
#include "../ui/drivers/qt/ui_qt_window.cpp" #include "../ui/drivers/qt/ui_qt_window.cpp"
@ -53,6 +55,15 @@ UI
#include "../ui/drivers/qt/thumbnaildownload.cpp" #include "../ui/drivers/qt/thumbnaildownload.cpp"
#include "../ui/drivers/qt/thumbnailpackdownload.cpp" #include "../ui/drivers/qt/thumbnailpackdownload.cpp"
#include "../ui/drivers/qt/playlistthumbnaildownload.cpp" #include "../ui/drivers/qt/playlistthumbnaildownload.cpp"
#include "../ui/drivers/moc_ui_qt.cpp"
#include "../ui/drivers/qt/moc_coreinfodialog.cpp"
#include "../ui/drivers/qt/moc_coreoptionsdialog.cpp"
#include "../ui/drivers/qt/moc_filedropwidget.cpp"
#include "../ui/drivers/qt/moc_flowlayout.cpp"
#include "../ui/drivers/qt/moc_playlistentrydialog.cpp"
#include "../ui/drivers/qt/moc_shaderparamsdialog.cpp"
#include "../ui/drivers/qt/moc_ui_qt_load_core_window.cpp"
#include "../ui/drivers/qt/moc_viewoptionsdialog.cpp"
#endif #endif
/*============================================================ /*============================================================

View File

@ -9,6 +9,10 @@
#include "../../config.h" #include "../../config.h"
#endif #endif
#ifdef HAVE_LIBNX
#include <switch.h>
#endif
#include "../input_driver.h" #include "../input_driver.h"
#define MAX_PADS 10 #define MAX_PADS 10

View File

@ -2,8 +2,13 @@
#include "../../config.h" #include "../../config.h"
#endif #endif
#include<libtransistor/nx.h> #ifdef HAVE_LIBNX
#include <switch.h>
#else
#include <libtransistor/nx.h>
#endif
#include "../configuration.h"
#include "../input_driver.h" #include "../input_driver.h"
#include "../../tasks/tasks_internal.h" #include "../../tasks/tasks_internal.h"
@ -13,10 +18,20 @@
#include "string.h" #include "string.h"
#ifdef HAVE_LIBNX
#ifndef MAX_PADS
#define MAX_PADS 8
#endif
#else
#ifndef MAX_PADS #ifndef MAX_PADS
#define MAX_PADS 10 #define MAX_PADS 10
#endif #endif
#endif
static uint16_t pad_state[MAX_PADS]; static uint16_t pad_state[MAX_PADS];
static int16_t analog_state[MAX_PADS][2][2]; static int16_t analog_state[MAX_PADS][2][2];
extern uint64_t lifecycle_state; extern uint64_t lifecycle_state;
@ -40,10 +55,16 @@ static void switch_joypad_autodetect_add(unsigned autoconf_pad)
static bool switch_joypad_init(void *data) static bool switch_joypad_init(void *data)
{ {
#ifdef HAVE_LIBNX
unsigned i;
hidScanInput();
for (i = 0; i < MAX_PADS; i++)
switch_joypad_autodetect_add(i);
#else
hid_init(); hid_init();
switch_joypad_autodetect_add(0); switch_joypad_autodetect_add(0);
switch_joypad_autodetect_add(1); switch_joypad_autodetect_add(1);
#endif
return true; return true;
} }
@ -126,27 +147,80 @@ static bool switch_joypad_query_pad(unsigned pad)
static void switch_joypad_destroy(void) static void switch_joypad_destroy(void)
{ {
#ifndef HAVE_LIBNX
hid_finalize(); hid_finalize();
#endif
} }
#ifdef HAVE_LIBNX
int lastMode = 0; // 0 = handheld, 1 = whatever
static void switch_joypad_poll(void) static void switch_joypad_poll(void)
{ {
settings_t *settings = config_get_ptr();
hidScanInput();
// TODO: Options via menu
/*if (settings->bools.split_joycon && !hidGetHandheldMode())
{
if (lastMode != 1)
{
RARCH_LOG("[HID] Enable Split Joycon!\n");
hidSetNpadJoyAssignmentModeSingleByDefault(CONTROLLER_PLAYER_1);
hidSetNpadJoyAssignmentModeSingleByDefault(CONTROLLER_PLAYER_2);
lastMode = 1;
}
}
else
{
if (lastMode != 0)
{
RARCH_LOG("[HID] Disable Split Joycon!\n");
hidSetNpadJoyAssignmentModeDual(CONTROLLER_PLAYER_1);
hidSetNpadJoyAssignmentModeDual(CONTROLLER_PLAYER_2);
lastMode = 0;
}
}*/
for (int i = 0; i < MAX_PADS; i++)
{
HidControllerID target = (i == 0) ? CONTROLLER_P1_AUTO : i;
pad_state[i] = hidKeysDown(target) | hidKeysHeld(target);
JoystickPosition joyPositionLeft, joyPositionRight;
hidJoystickRead(&joyPositionLeft, target, JOYSTICK_LEFT);
hidJoystickRead(&joyPositionRight, target, JOYSTICK_RIGHT);
analog_state[i][RETRO_DEVICE_INDEX_ANALOG_LEFT][RETRO_DEVICE_ID_ANALOG_X] = joyPositionLeft.dx;
analog_state[i][RETRO_DEVICE_INDEX_ANALOG_LEFT][RETRO_DEVICE_ID_ANALOG_Y] = -joyPositionLeft.dy;
analog_state[i][RETRO_DEVICE_INDEX_ANALOG_RIGHT][RETRO_DEVICE_ID_ANALOG_X] = joyPositionRight.dx;
analog_state[i][RETRO_DEVICE_INDEX_ANALOG_RIGHT][RETRO_DEVICE_ID_ANALOG_Y] = -joyPositionRight.dy;
}
}
#else
static void switch_joypad_poll(void)
{
int16_t lsx, lsy, rsx, rsy;
hid_controller_t *controllers = hid_get_shared_memory()->controllers; hid_controller_t *controllers = hid_get_shared_memory()->controllers;
hid_controller_t *cont = &controllers[0]; hid_controller_t *cont = &controllers[0];
hid_controller_state_entry_t ent = cont->main.entries[cont->main.latest_idx]; hid_controller_state_entry_t ent = cont->main.entries[cont->main.latest_idx];
hid_controller_state_entry_t ent8 = (cont+8)->main.entries[(cont+8)->main.latest_idx]; hid_controller_state_entry_t ent8 = (cont+8)->main.entries[(cont+8)->main.latest_idx];
pad_state[0] = ent.button_state | ent8.button_state; pad_state[0] = ent.button_state | ent8.button_state;
int16_t lsx, lsy, rsx, rsy; lsx = ent.left_stick_x;
lsx = ent.left_stick_x; lsy = ent.left_stick_y;
lsy = ent.left_stick_y; rsx = ent.right_stick_x;
rsx = ent.right_stick_x; rsy = ent.right_stick_y;
rsy = ent.right_stick_y;
if(ent8.left_stick_x != 0 || ent8.left_stick_y != 0) { // handheld overrides player 1 if (ent8.left_stick_x != 0 || ent8.left_stick_y != 0)
{
/* handheld overrides player 1 */
lsx = ent8.left_stick_x; lsx = ent8.left_stick_x;
lsy = ent8.left_stick_y; lsy = ent8.left_stick_y;
} }
if(ent8.right_stick_x != 0 || ent8.right_stick_y != 0) { // handheld overrides player 1
if (ent8.right_stick_x != 0 || ent8.right_stick_y != 0)
{
/* handheld overrides player 1 */
rsx = ent8.right_stick_x; rsx = ent8.right_stick_x;
rsy = ent8.right_stick_y; rsy = ent8.right_stick_y;
} }
@ -156,6 +230,7 @@ static void switch_joypad_poll(void)
analog_state[0][RETRO_DEVICE_INDEX_ANALOG_RIGHT][RETRO_DEVICE_ID_ANALOG_X] = rsx; analog_state[0][RETRO_DEVICE_INDEX_ANALOG_RIGHT][RETRO_DEVICE_ID_ANALOG_X] = rsx;
analog_state[0][RETRO_DEVICE_INDEX_ANALOG_RIGHT][RETRO_DEVICE_ID_ANALOG_Y] = -rsy; analog_state[0][RETRO_DEVICE_INDEX_ANALOG_RIGHT][RETRO_DEVICE_ID_ANALOG_Y] = -rsy;
} }
#endif
input_device_driver_t switch_joypad = { input_device_driver_t switch_joypad = {
switch_joypad_init, switch_joypad_init,

View File

@ -668,7 +668,7 @@ const char* const input_builtin_autoconfs[] =
#ifdef __CELLOS_LV2__ #ifdef __CELLOS_LV2__
DECL_AUTOCONF_DEVICE("SixAxis Controller", "ps3", PS3INPUT_DEFAULT_BINDS), DECL_AUTOCONF_DEVICE("SixAxis Controller", "ps3", PS3INPUT_DEFAULT_BINDS),
#endif #endif
#ifdef __SWITCH__ #if defined(__SWITCH__) || defined(SWITCH)
DECL_AUTOCONF_DEVICE("Switch Controller", "switch", SWITCH_DEFAULT_BINDS), DECL_AUTOCONF_DEVICE("Switch Controller", "switch", SWITCH_DEFAULT_BINDS),
#endif #endif
#ifdef EMSCRIPTEN #ifdef EMSCRIPTEN

View File

@ -28,7 +28,7 @@ RETRO_BEGIN_DECLS
#define MAX_INPUT_DEVICES 16 #define MAX_INPUT_DEVICES 16
#define RARCH_MAX_KEYS 136 #define RARCH_MAX_KEYS 137
#define RARCH_FIRST_CUSTOM_BIND 16 #define RARCH_FIRST_CUSTOM_BIND 16
#define RARCH_FIRST_LIGHTGUN_BIND RARCH_ANALOG_BIND_LIST_END #define RARCH_FIRST_LIGHTGUN_BIND RARCH_ANALOG_BIND_LIST_END
@ -85,7 +85,7 @@ enum
RARCH_STATE_SLOT_PLUS, RARCH_STATE_SLOT_PLUS,
RARCH_STATE_SLOT_MINUS, RARCH_STATE_SLOT_MINUS,
RARCH_REWIND, RARCH_REWIND,
RARCH_MOVIE_RECORD_TOGGLE, RARCH_BSV_RECORD_TOGGLE,
RARCH_PAUSE_TOGGLE, RARCH_PAUSE_TOGGLE,
RARCH_FRAMEADVANCE, RARCH_FRAMEADVANCE,
RARCH_RESET, RARCH_RESET,
@ -111,6 +111,9 @@ enum
RARCH_MENU_TOGGLE, RARCH_MENU_TOGGLE,
RARCH_RECORDING_TOGGLE,
RARCH_STREAMING_TOGGLE,
RARCH_BIND_LIST_END, RARCH_BIND_LIST_END,
RARCH_BIND_LIST_END_NULL RARCH_BIND_LIST_END_NULL
}; };

View File

@ -325,7 +325,7 @@ const struct input_bind_map input_config_bind_map[RARCH_BIND_LIST_END_NULL] = {
DECLARE_META_BIND(2, state_slot_increase, RARCH_STATE_SLOT_PLUS, MENU_ENUM_LABEL_VALUE_INPUT_META_STATE_SLOT_PLUS), DECLARE_META_BIND(2, state_slot_increase, RARCH_STATE_SLOT_PLUS, MENU_ENUM_LABEL_VALUE_INPUT_META_STATE_SLOT_PLUS),
DECLARE_META_BIND(2, state_slot_decrease, RARCH_STATE_SLOT_MINUS, MENU_ENUM_LABEL_VALUE_INPUT_META_STATE_SLOT_MINUS), DECLARE_META_BIND(2, state_slot_decrease, RARCH_STATE_SLOT_MINUS, MENU_ENUM_LABEL_VALUE_INPUT_META_STATE_SLOT_MINUS),
DECLARE_META_BIND(1, rewind, RARCH_REWIND, MENU_ENUM_LABEL_VALUE_INPUT_META_REWIND), DECLARE_META_BIND(1, rewind, RARCH_REWIND, MENU_ENUM_LABEL_VALUE_INPUT_META_REWIND),
DECLARE_META_BIND(2, movie_record_toggle, RARCH_MOVIE_RECORD_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_MOVIE_RECORD_TOGGLE), DECLARE_META_BIND(2, movie_record_toggle, RARCH_BSV_RECORD_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_BSV_RECORD_TOGGLE),
DECLARE_META_BIND(2, pause_toggle, RARCH_PAUSE_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_PAUSE_TOGGLE), DECLARE_META_BIND(2, pause_toggle, RARCH_PAUSE_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_PAUSE_TOGGLE),
DECLARE_META_BIND(2, frame_advance, RARCH_FRAMEADVANCE, MENU_ENUM_LABEL_VALUE_INPUT_META_FRAMEADVANCE), DECLARE_META_BIND(2, frame_advance, RARCH_FRAMEADVANCE, MENU_ENUM_LABEL_VALUE_INPUT_META_FRAMEADVANCE),
DECLARE_META_BIND(2, reset, RARCH_RESET, MENU_ENUM_LABEL_VALUE_INPUT_META_RESET), DECLARE_META_BIND(2, reset, RARCH_RESET, MENU_ENUM_LABEL_VALUE_INPUT_META_RESET),
@ -351,6 +351,8 @@ const struct input_bind_map input_config_bind_map[RARCH_BIND_LIST_END_NULL] = {
#ifdef HAVE_MENU #ifdef HAVE_MENU
DECLARE_META_BIND(1, menu_toggle, RARCH_MENU_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_MENU_TOGGLE), DECLARE_META_BIND(1, menu_toggle, RARCH_MENU_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_MENU_TOGGLE),
#endif #endif
DECLARE_META_BIND(2, recording_toggle, RARCH_RECORDING_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_RECORDING_TOGGLE),
DECLARE_META_BIND(2, streaming_toggle, RARCH_STREAMING_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_STREAMING_TOGGLE),
}; };

View File

@ -288,6 +288,7 @@ const struct input_key_map input_config_key_map[] = {
{ "euro", RETROK_EURO }, { "euro", RETROK_EURO },
{ "undo", RETROK_UNDO }, { "undo", RETROK_UNDO },
{ "clear", RETROK_CLEAR }, { "clear", RETROK_CLEAR },
{ "oem102", RETROK_OEM_102 },
{ "nul", RETROK_UNKNOWN }, { "nul", RETROK_UNKNOWN },
{ NULL, RETROK_UNKNOWN }, { NULL, RETROK_UNKNOWN },
@ -328,7 +329,7 @@ const struct rarch_key_map rarch_key_map_sdl[] = {
{ SDLK_9, RETROK_9 }, { SDLK_9, RETROK_9 },
{ SDLK_COLON, RETROK_COLON }, { SDLK_COLON, RETROK_COLON },
{ SDLK_SEMICOLON, RETROK_SEMICOLON }, { SDLK_SEMICOLON, RETROK_SEMICOLON },
{ SDLK_LESS, RETROK_LESS }, { SDLK_LESS, RETROK_OEM_102 },
{ SDLK_EQUALS, RETROK_EQUALS }, { SDLK_EQUALS, RETROK_EQUALS },
{ SDLK_GREATER, RETROK_GREATER }, { SDLK_GREATER, RETROK_GREATER },
{ SDLK_QUESTION, RETROK_QUESTION }, { SDLK_QUESTION, RETROK_QUESTION },
@ -574,6 +575,7 @@ const struct rarch_key_map rarch_key_map_dinput[] = {
{ DIK_SCROLL, RETROK_SCROLLOCK }, { DIK_SCROLL, RETROK_SCROLLOCK },
{ DIK_CAPSLOCK, RETROK_CAPSLOCK }, { DIK_CAPSLOCK, RETROK_CAPSLOCK },
{ DIK_NUMLOCK, RETROK_NUMLOCK }, { DIK_NUMLOCK, RETROK_NUMLOCK },
{ DIK_OEM_102, RETROK_OEM_102 },
{ 0, RETROK_UNKNOWN }, { 0, RETROK_UNKNOWN },
}; };
#endif #endif
@ -831,6 +833,7 @@ const struct rarch_key_map rarch_key_map_x11[] = {
/*{ ?, RETROK_POWER },*/ /*{ ?, RETROK_POWER },*/
{ XK_EuroSign, RETROK_EURO }, { XK_EuroSign, RETROK_EURO },
{ XK_Undo, RETROK_UNDO }, { XK_Undo, RETROK_UNDO },
/*{ ?, RETROK_OEM_102 },*/
/* FIXME(shizeeg): RetroArch can't handle these buttons atm. /* FIXME(shizeeg): RetroArch can't handle these buttons atm.
* Do we really need RETROK_KP_INSERT, RETROK_KP_END, * Do we really need RETROK_KP_INSERT, RETROK_KP_END,
* RETROK_KP_DOWN, RETROK_KP_PAGEDOWN ??? * RETROK_KP_DOWN, RETROK_KP_PAGEDOWN ???
@ -986,6 +989,7 @@ const struct rarch_key_map rarch_key_map_linux[] = {
{ KEY_EURO, RETROK_EURO }, { KEY_EURO, RETROK_EURO },
#endif #endif
{ KEY_UNDO, RETROK_UNDO }, { KEY_UNDO, RETROK_UNDO },
/*{ ?, RETROK_OEM_102 },*/
{ 0, RETROK_UNKNOWN }, { 0, RETROK_UNKNOWN },
}; };
#endif #endif
@ -1320,6 +1324,7 @@ const struct rarch_key_map rarch_key_map_apple_hid[] = {
/* { ?, RETROK_POWER }, */ /* { ?, RETROK_POWER }, */
/* { ?, RETROK_EURO }, */ /* { ?, RETROK_EURO }, */
/* { ?, RETROK_UNDO }, */ /* { ?, RETROK_UNDO }, */
{ KEY_NonUSBackslash, RETROK_OEM_102 },
{ 0, RETROK_UNKNOWN } { 0, RETROK_UNKNOWN }
}; };
#endif #endif

View File

@ -221,15 +221,19 @@ void input_mapper_poll(input_mapper_t *handle)
else else
{ {
int invert = 1; int invert = 1;
unsigned remap_axis_bind = remap_axis - RARCH_FIRST_CUSTOM_BIND;
if ( (k % 2 == 0 && remap_axis % 2 != 0) || if ( (k % 2 == 0 && remap_axis % 2 != 0) ||
(k % 2 != 0 && remap_axis % 2 == 0) (k % 2 != 0 && remap_axis % 2 == 0)
) )
invert = -1; invert = -1;
handle->analog_value[i][ if (remap_axis_bind < sizeof(handle->analog_value[i]))
remap_axis - RARCH_FIRST_CUSTOM_BIND] = {
current_axis_value * invert; handle->analog_value[i][
remap_axis_bind] =
current_axis_value * invert;
}
#if 0 #if 0
RARCH_LOG("axis %d(%d) remapped to axis %d val %d\n", RARCH_LOG("axis %d(%d) remapped to axis %d val %d\n",
j, k, j, k,

View File

@ -233,17 +233,17 @@ static void input_overlay_set_vertex_geom(input_overlay_t *ol)
ol->active->mod_x, ol->active->mod_y, ol->active->mod_x, ol->active->mod_y,
ol->active->mod_w, ol->active->mod_h); ol->active->mod_w, ol->active->mod_h);
for (i = 0; i < ol->active->size; i++) if (ol->iface->vertex_geom)
{ for (i = 0; i < ol->active->size; i++)
struct overlay_desc *desc = &ol->active->descs[i]; {
struct overlay_desc *desc = &ol->active->descs[i];
if (!desc->image.pixels) if (!desc->image.pixels)
continue; continue;
if (ol->iface->vertex_geom)
ol->iface->vertex_geom(ol->iface_data, desc->image_index, ol->iface->vertex_geom(ol->iface_data, desc->image_index,
desc->mod_x, desc->mod_y, desc->mod_w, desc->mod_h); desc->mod_x, desc->mod_y, desc->mod_w, desc->mod_h);
} }
} }
/** /**

Some files were not shown because too many files have changed in this diff Show More