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/modules/
pkg/apple/build/
ui/drivers/qt/moc_*
ui/drivers/moc_*
obj-unix/
.vagrant/
@ -151,3 +154,12 @@ retroarch.js.mem
# only ignore .js files in the repo root
/*.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:
- 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
addons:
# Install a more recent gcc than the default
apt:
packages:
- g++-7
sources:
- ubuntu-toolchain-r-test
- compiler: clang
addons:
# Install a more recent clang than the default
@ -37,7 +44,17 @@ matrix:
- os: osx
osx_image: xcode9.3
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:
- ./configure
@ -60,6 +77,9 @@ addons:
- libsdl-mixer1.2-dev
- libsdl-ttf2.0-dev
- libusb-1.0-0-dev
- qt5-default
- qt5-qmake
- qtbase5-dev-tools
coverity_scan:
project:
name: "RetroArch"

View File

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

View File

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

View File

@ -193,7 +193,7 @@ else ifeq ($(libogc_platform), 1)
RARCH_CONSOLE = 1
ifeq ($(platform), wii)
HAVE_LANGEXTRA := 1
#HAVE_LANGEXTRA := 1
HAVE_WIIUSB_HID := 1
HAVE_RARCH_EXEC := 1
HAVE_RSOUND := 1
@ -250,7 +250,7 @@ else ifeq ($(platform), psp1)
HAVE_RBMP := 1
HAVE_RTGA := 1
HAVE_KERNEL_PRX := 1
HAVE_LANGEXTRA := 1
#HAVE_LANGEXTRA := 1
RARCH_CONSOLE = 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)),)
$(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
include $(LIBTRANSISTOR_HOME)/libtransistor.mk
@ -52,7 +52,7 @@ LIBDIRS := -L.
TARGETS := $(TARGET).nro
CFLAGS += $(INCDIRS) $(DEFINES) -Wunused-command-line-argument
CFLAGS += $(INCDIRS) $(DEFINES) -Wno-unused-command-line-argument -Werror-implicit-function-declaration
all: $(TARGETS)

View File

@ -125,6 +125,7 @@ static const audio_driver_t *audio_drivers[] = {
#endif
#ifdef SWITCH
&audio_switch,
&audio_switch_thread,
#endif
&audio_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_dsp;
extern audio_driver_t audio_switch;
extern audio_driver_t audio_switch_thread;
extern audio_driver_t audio_rwebaudio;
extern audio_driver_t audio_null;

View File

@ -1,4 +1,6 @@
/* 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-
@ -17,12 +19,16 @@
#include <malloc.h>
#include <stdint.h>
#include<libtransistor/nx.h>
#include<libtransistor/alloc_pages.h>
#include "switch_audio_compat.h"
#include "../audio_driver.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 max_num_samples = sample_rate;
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
{
audio_output_t output;
handle_t event;
audio_output_buffer_t buffers[3];
audio_output_buffer_t *current_buffer;
bool blocking;
bool is_paused;
uint64_t last_append;
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;
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)
{
size_t to_write = size;
switch_audio_t *swa = (switch_audio_t*) data;
#if 0
RARCH_LOG("write %ld samples\n", size/sizeof(uint16_t));
#endif
if (!swa)
return -1;
if (!swa->current_buffer)
{
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");
return -1;
}
#if 0
RARCH_LOG("got buffer, num %d, ptr %p\n", num, swa->current_buffer);
#endif
if (num < 1)
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)
{
uint32_t num;
uint32_t handle_idx;
uint32_t handle_idx = 0;
num = 0;
#ifdef HAVE_LIBNX
if (audoutWaitPlayFinish(&swa->current_buffer, &num, U64_MAX) != 0) { }
#else
svcWaitSynchronization(&handle_idx, &swa->event, 1, 33333333);
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;
#endif
}
} else {
}
else
/* no buffer, nonblocking... */
return 0;
}
}
swa->current_buffer->data_size = 0;
}
if (to_write > sample_buffer_size - swa->current_buffer->data_size)
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 = 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);
#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->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 (r != RESULT_OK)
{
RARCH_ERR("failed to append buffer: 0x%x\n", r);
return -1;
}
if (switch_audio_ipc_output_append_buffer(swa, swa->current_buffer) != 0)
return -1;
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();
return to_write;
@ -125,25 +149,32 @@ static bool switch_audio_stop(void *data)
if (!swa)
return false;
if(!swa->is_paused) {
if(audio_ipc_output_stop(&swa->output) != RESULT_OK)
/* TODO/FIXME - fix libnx codepath */
#ifndef HAVE_LIBNX
if (!swa->is_paused)
if (switch_audio_ipc_output_stop(swa) != 0)
return false;
}
swa->is_paused = true;
#endif
return true;
}
static bool switch_audio_start(void *data, bool is_shutdown)
{
switch_audio_t *swa = (switch_audio_t*) data;
if (!swa)
return false;
if(swa->is_paused) {
if (audio_ipc_output_start(&swa->output) != RESULT_OK)
/* TODO/FIXME - fix libnx codepath */
#ifndef HAVE_LIBNX
if (swa->is_paused)
if (switch_audio_ipc_output_start(swa) != 0)
return false;
}
swa->is_paused = false;
#endif
return true;
}
@ -159,8 +190,22 @@ static void switch_audio_free(void *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_finalize();
#endif
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,
unsigned rate, unsigned latency,
unsigned block_frames,
unsigned *new_rate)
unsigned rate, unsigned latency,
unsigned block_frames,
unsigned *new_rate)
{
result_t r;
unsigned i;
char names[8][0x20];
uint32_t num_names = 0;
@ -202,12 +246,14 @@ static void *switch_audio_init(const char *device,
if (!swa)
return NULL;
r = audio_ipc_init();
if (r != RESULT_OK)
if (switch_audio_ipc_init() != 0)
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;
if (num_names != 1)
@ -216,7 +262,7 @@ static void *switch_audio_init(const char *device,
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;
if (swa->output.sample_rate != sample_rate)
@ -239,51 +285,70 @@ static void *switch_audio_init(const char *device,
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;
#endif
swa->blocking = block_frames;
*new_rate = swa->output.sample_rate;
for(i = 0; i < 3; i++)
for (i = 0; i < BUFFER_COUNT; i++)
{
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].buffer_size = switch_audio_buffer_size(NULL);
swa->buffers[i].data_size = switch_audio_data_size();
if(swa->buffers[i].sample_data == NULL)
#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].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)
goto fail_audio_output;
if (audio_ipc_output_append_buffer(&swa->output, &swa->buffers[i]) != RESULT_OK)
#endif
if (switch_audio_ipc_output_append_buffer(swa, &swa->buffers[i]) != 0)
goto fail_audio_output;
}
#ifdef HAVE_LIBNX
*new_rate = audoutGetSampleRate();
#else
*new_rate = swa->output.sample_rate;
#endif
swa->current_buffer = NULL;
swa->latency = latency;
swa->last_append = svcGetSystemTick();
swa->blocking = block_frames;
swa->is_paused = true;
RARCH_LOG("[Audio]: Audio initialized\n");
return swa;
fail_audio_output:
/* TODO/FIXME - fix libnx codepath */
#ifndef HAVE_LIBNX
audio_ipc_output_close(&swa->output);
#endif
fail_audio_ipc:
/* TODO/FIXME - fix libnx codepath */
#ifndef HAVE_LIBNX
audio_ipc_finalize();
#endif
fail:
free(swa);
if (swa)
free(swa);
return NULL;
}
static size_t switch_audio_buffer_size(void *data)
{
(void) data;
return sample_buffer_size;
}
audio_driver_t audio_switch = {
switch_audio_init,
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];
} video4linux_t;
static int xioctl(int fd, int request, void *args)
static int xioctl(int fd, unsigned long request, void *args)
{
int r;
@ -91,7 +91,7 @@ static bool init_mmap(void *data)
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
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)
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.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");
return false;
@ -152,7 +152,7 @@ static bool init_device(void *data)
struct v4l2_cropcap cropcap = {0};
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)
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;
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.c = cropcap.defrect;
@ -190,7 +190,7 @@ static bool init_device(void *data)
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
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");
return false;
@ -248,7 +248,7 @@ static bool v4l_start(void *data)
buf.memory = V4L2_MEMORY_MMAP;
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");
return false;
@ -364,7 +364,7 @@ static bool preprocess_image(void *data)
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
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)
{
@ -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);
if (xioctl(v4l->fd, (uint8_t)VIDIOC_QBUF, &buf) == -1)
if (xioctl(v4l->fd, VIDIOC_QBUF, &buf) == -1)
RARCH_ERR("[V4L2]: VIDIOC_QBUF\n");
return true;

View File

@ -162,6 +162,7 @@ static cheevos_locals_t cheevos_locals =
bool cheevos_loaded = false;
bool cheevos_hardcore_active = false;
bool cheevos_hardcore_paused = false;
bool cheevos_state_loaded_flag = false;
int cheats_are_enabled = 0;
int cheats_were_enabled = 0;
@ -947,6 +948,9 @@ bool cheevos_toggle_hardcore_mode(void)
const char *msg = msg_hash_to_str(
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
* savestate previusly loaded. */
command_event(CMD_EVENT_RESET, NULL);
@ -1503,16 +1507,11 @@ found:
memcpy((void*)&coro->header, coro->data,
sizeof(coro->header));
if ( coro->header.id[0] != 'N'
|| coro->header.id[1] != 'E'
|| coro->header.id[2] != 'S'
|| coro->header.id[3] != 0x1a)
{
coro->gameid = 0;
CORO_RET();
}
if (coro->header.id[0] == 'N'
&& coro->header.id[1] == 'E'
&& coro->header.id[2] == 'S'
&& coro->header.id[3] == 0x1a)
{
size_t romsize = 256;
/* from FCEU core - compute size using the cart mapper */
@ -1528,24 +1527,64 @@ found:
* we use FCEU_read. */
coro->round = mapper != 53 && mapper != 198 && mapper != 228;
coro->bytes = coro->round ? romsize : coro->header.rom_size;
}
/* from FCEU core - check if Trainer included in ROM data */
MD5_Init(&coro->md5);
coro->offset = sizeof(coro->header) + (coro->header.rom_type & 4
coro->offset = sizeof(coro->header) + (coro->header.rom_type & 4
? sizeof(coro->header) : 0);
coro->count = 0x4000 * coro->bytes;
CORO_GOSUB(EVAL_MD5);
/* from FCEU core - check if Trainer included in ROM data */
MD5_Init(&coro->md5);
coro->count = 0x4000 * coro->bytes;
CORO_GOSUB(EVAL_MD5);
if (coro->count < 0x4000 * coro->bytes)
{
coro->offset = 0xff;
coro->count = 0x4000 * coro->bytes - coro->count;
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);
CORO_GOTO(GET_GAMEID);
/* Fall back to headerless hashing
* 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

View File

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

View File

@ -98,6 +98,8 @@
#define DEFAULT_NETWORK_CMD_PORT 55355
#define STDIN_BUF_SIZE 4096
extern bool discord_is_inited;
enum cmd_source_t
{
CMD_NONE = 0,
@ -159,7 +161,7 @@ static const struct cmd_map map[] = {
{ "STATE_SLOT_PLUS", RARCH_STATE_SLOT_PLUS },
{ "STATE_SLOT_MINUS", RARCH_STATE_SLOT_MINUS },
{ "REWIND", RARCH_REWIND },
{ "MOVIE_RECORD_TOGGLE", RARCH_MOVIE_RECORD_TOGGLE },
{ "BSV_RECORD_TOGGLE", RARCH_BSV_RECORD_TOGGLE },
{ "PAUSE_TOGGLE", RARCH_PAUSE_TOGGLE },
{ "FRAMEADVANCE", RARCH_FRAMEADVANCE },
{ "RESET", RARCH_RESET },
@ -182,6 +184,8 @@ static const struct cmd_map map[] = {
{ "UI_COMPANION_TOGGLE", RARCH_UI_COMPANION_TOGGLE },
{ "GAME_FOCUS_TOGGLE", RARCH_GAME_FOCUS_TOGGLE },
{ "MENU_TOGGLE", RARCH_MENU_TOGGLE },
{ "RECORDING_TOGGLE", RARCH_RECORDING_TOGGLE },
{ "STREAMING_TOGGLE", RARCH_STREAMING_TOGGLE },
{ "MENU_UP", RETRO_DEVICE_ID_JOYPAD_UP },
{ "MENU_DOWN", RETRO_DEVICE_ID_JOYPAD_DOWN },
{ "MENU_LEFT", RETRO_DEVICE_ID_JOYPAD_LEFT },
@ -1656,6 +1660,9 @@ static bool command_event_main_state(unsigned cmd)
case CMD_EVENT_LOAD_STATE:
if (content_load_state(state_path, false, false))
{
#ifdef HAVE_CHEEVOS
cheevos_state_loaded_flag = true;
#endif
ret = true;
#ifdef HAVE_NETWORKING
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 *data,
playlist_t *playlist,
const char *path,
const char *label,
const char *core_path,
const char *core_name)
{
playlist_t *playlist = (playlist_t*)data;
if (!playlist)
return;
@ -1732,7 +1737,7 @@ void command_playlist_push_write(
}
void command_playlist_update_write(
void *data,
playlist_t *plist,
size_t idx,
const char *path,
const char *label,
@ -1741,7 +1746,6 @@ void command_playlist_update_write(
const char *crc32,
const char *db_name)
{
playlist_t *plist = (playlist_t*)data;
playlist_t *playlist = plist ? plist : playlist_get_cached();
if (!playlist)
@ -1777,11 +1781,6 @@ bool command_event(enum event_command cmd, void *data)
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:
#if defined(GEKKO)
{
@ -1842,6 +1841,7 @@ bool command_event(enum event_command cmd, void *data)
case CMD_EVENT_LOAD_CORE:
{
bool success = command_event(CMD_EVENT_LOAD_CORE_PERSIST, NULL);
(void)success;
#ifndef HAVE_DYNAMIC
command_event(CMD_EVENT_QUIT, NULL);
@ -1888,6 +1888,10 @@ bool command_event(enum event_command cmd, void *data)
command_event_init_controllers();
break;
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));
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();
if (!rarch_ctl(RARCH_CTL_IS_DUMMY_CORE, NULL))
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;
case CMD_EVENT_QUIT:
@ -2145,13 +2158,22 @@ TODO: Add a setting for these tweaks */
video_driver_gpu_record_deinit();
break;
case CMD_EVENT_RECORD_DEINIT:
if (!recording_deinit())
return false;
{
recording_set_state(false);
streaming_set_state(false);
if (!recording_deinit())
return false;
}
break;
case CMD_EVENT_RECORD_INIT:
command_event(CMD_EVENT_HISTORY_DEINIT, NULL);
if (!recording_init())
return false;
{
recording_set_state(true);
if (!recording_init())
{
command_event(CMD_EVENT_RECORD_DEINIT, NULL);
return false;
}
}
break;
case CMD_EVENT_HISTORY_DEINIT:
if (g_defaults.content_history)

View File

@ -27,6 +27,8 @@
#include "config.h"
#endif
#include "playlist.h"
RETRO_BEGIN_DECLS
typedef struct command command_t;
@ -155,7 +157,6 @@ enum event_command
CMD_EVENT_MENU_PAUSE_LIBRETRO,
/* Toggles menu on/off. */
CMD_EVENT_MENU_TOGGLE,
CMD_EVENT_MENU_REFRESH,
/* Applies shader changes. */
CMD_EVENT_SHADERS_APPLY_CHANGES,
/* 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);
void command_playlist_push_write(
void *data,
playlist_t *playlist,
const char *path,
const char *label,
const char *core_path,
const char *core_name);
void command_playlist_update_write(
void *data,
playlist_t *playlist,
size_t idx,
const char *path,
const char *label,

View File

@ -20,6 +20,7 @@
#include <boolean.h>
#include <audio/audio_resampler.h>
#include "configuration.h"
#include "gfx/video_defines.h"
#include "input/input_driver.h"
@ -67,15 +68,15 @@ static bool bundle_assets_extract_enable = false;
static bool materialui_icons_enable = true;
#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_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 bool def_playlist_entry_remove = true;
static const bool def_playlist_entry_rename = true;
static const unsigned int def_user_language = 0;
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)
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 bool adaptive_vsync = false;
/* Attempts to hard-synchronize CPU and GPU.
* Can reduce latency at cost of performance. */
static const bool hard_sync = false;
@ -229,7 +232,7 @@ static const float aspect_ratio = DEFAULT_ASPECT_RATIO;
/* 1:1 PAR */
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;
#elif defined(PSP)
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 quick_menu_show_take_screenshot = 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_add_to_favorites = true;
static bool quick_menu_show_options = true;
static bool quick_menu_show_controls = true;
static bool quick_menu_show_cheats = true;
static bool quick_menu_show_shaders = true;
static bool quick_menu_show_information = true;
static bool quick_menu_show_take_screenshot = 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_add_to_favorites = true;
static bool quick_menu_show_options = true;
static bool quick_menu_show_controls = true;
static bool quick_menu_show_cheats = true;
static bool quick_menu_show_shaders = 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_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,
* disable VSync, and leave this at its default. */
#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
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
/* 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
#endif
#ifndef RARCH_STREAM_DEFAULT_PORT
#define RARCH_STREAM_DEFAULT_PORT 56400
#endif
/* KEYBINDS, JOYPAD */
/* 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_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_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_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 },
@ -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_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_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
{ 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 },
@ -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_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_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_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 },
@ -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_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_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
};

View File

@ -54,6 +54,8 @@
#include "../list_special.h"
#include "record/record_driver.h"
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 */
"~", "#", "%", "&", "*", "{", "}", "\\", ":", "[", "]", "?", "/", "|", "\'", "\"",
@ -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_input", settings->arrays.midi_input, true, midi_input, 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;
return tmp;
@ -1160,6 +1164,12 @@ static struct config_path_setting *populate_settings_path(settings_t *settings,
SETTING_PATH("input_overlay",
settings->paths.path_overlay, false, NULL, true);
#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",
settings->paths.path_font, false, NULL, true);
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));
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("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);
@ -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("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_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_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("pause_nonactive", &settings->bools.pause_nonactive, true, pause_nonactive, 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_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("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_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);
@ -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_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("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);
@ -1517,6 +1532,10 @@ static struct config_uint_setting *populate_settings_uint(settings_t *settings,
unsigned count = 0;
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_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);
@ -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("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;
return tmp;
@ -1629,6 +1654,7 @@ static struct config_int_setting *populate_settings_int(settings_t *settings, in
#ifdef HAVE_WASAPI
SETTING_INT("audio_wasapi_sh_buffer_length", &settings->ints.audio_wasapi_sh_buffer_length, true, wasapi_sh_buffer_length, false);
#endif
SETTING_INT("crt_switch_center_adjust", &settings->ints.crt_switch_center_adjust, false, 0 /* TODO */, false);
*size = count;
@ -1920,6 +1946,9 @@ static void config_set_defaults(void)
*settings->paths.path_menu_wallpaper = '\0';
*settings->paths.path_content_database = '\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->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);
recording_driver_update_streaming_url();
ret = true;

View File

@ -53,6 +53,13 @@
var = newvar; \
}
enum crt_switch_type
{
CRT_SWITCH_NONE = 0,
CRT_SWITCH_15KHZ,
CRT_SWITCH_31KHZ
};
enum override_type
{
OVERRIDE_NONE = 0,
@ -73,6 +80,7 @@ typedef struct settings
bool video_fullscreen;
bool video_windowed_fullscreen;
bool video_vsync;
bool video_adaptive_vsync;
bool video_hard_sync;
bool video_black_frame_insertion;
bool video_vfilter;
@ -96,7 +104,6 @@ typedef struct settings
bool video_statistics_show;
bool video_framecount_show;
bool video_msg_bgcolor_enable;
bool crt_switch_resolution;
/* Audio */
bool audio_enable;
@ -181,8 +188,12 @@ typedef struct settings
bool quick_menu_show_save_game_overrides;
bool quick_menu_show_save_content_dir_overrides;
bool quick_menu_show_information;
bool quick_menu_show_recording;
bool quick_menu_show_streaming;
bool kiosk_mode_enable;
bool crt_switch_custom_refresh_enable;
/* Netplay */
bool netplay_public_announce;
bool netplay_start_as_spectator;
@ -289,6 +300,7 @@ typedef struct settings
float video_scale;
float video_aspect_ratio;
float video_refresh_rate;
float crt_video_refresh_rate;
float video_font_size;
float video_msg_pos_x;
float video_msg_pos_y;
@ -321,6 +333,7 @@ typedef struct settings
int location_update_interval_distance;
int state_slot;
int audio_wasapi_sh_buffer_length;
int crt_switch_center_adjust;
} ints;
struct
@ -359,6 +372,7 @@ typedef struct settings
unsigned video_window_x;
unsigned video_window_y;
unsigned video_window_opacity;
unsigned crt_switch_resolution;
unsigned crt_switch_resolution_super;
unsigned video_monitor_index;
unsigned video_fullscreen_x;
@ -373,6 +387,11 @@ typedef struct settings
unsigned video_msg_bgcolor_red;
unsigned video_msg_bgcolor_green;
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_left_thumbnails;
@ -413,6 +432,7 @@ typedef struct settings
unsigned run_ahead_frames;
unsigned midi_volume;
unsigned streaming_mode;
} uints;
struct
@ -457,6 +477,9 @@ typedef struct settings
char midi_input[32];
char midi_output[32];
char youtube_stream_key[PATH_MAX_LENGTH];
char twitch_stream_key[PATH_MAX_LENGTH];
} arrays;
struct
@ -477,6 +500,9 @@ typedef struct settings
char path_cheat_database[PATH_MAX_LENGTH];
char path_content_database[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_audio_dsp_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_font[PATH_MAX_LENGTH];
char directory_audio_filter[PATH_MAX_LENGTH];
char directory_autoconfig[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_menu_config[PATH_MAX_LENGTH];
char directory_menu_content[PATH_MAX_LENGTH];
char streaming_title[PATH_MAX_LENGTH];
} paths;
bool modified;

View File

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

View File

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

View File

@ -25,21 +25,33 @@
#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 int FrustrationLevel = 0;
static int64_t start_time = 0;
static int64_t pause_time = 0;
static int64_t ellapsed_time = 0;
static bool discord_ready = false;
static bool in_menu = false;
static unsigned discord_status = 0;
struct netplay_room *room;
DiscordRichPresence discord_presence;
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->discriminator,
connectedUser->userId);
@ -58,6 +70,21 @@ static void handle_discord_error(int errcode, const char* message)
static void handle_discord_join(const char* 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)
@ -78,19 +105,17 @@ static void handle_discord_join_request(const DiscordUser* request)
void discord_update(enum discord_presence presence)
{
core_info_t *core_info = NULL;
bool skip = false;
core_info_get_current_core(&core_info);
if (!discord_ready)
return;
if (
(discord_status != DISCORD_PRESENCE_MENU) &&
(discord_status == presence))
if (presence == discord_status)
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)
{
@ -99,19 +124,15 @@ void discord_update(enum discord_presence presence)
discord_presence.largeImageKey = "base";
discord_presence.largeImageText = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_CORE);
discord_presence.instance = 0;
in_menu = true;
break;
case DISCORD_PRESENCE_GAME_PAUSED:
discord_presence.smallImageKey = "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);
pause_time = time(0);
skip = true;
if (in_menu)
break;
ellapsed_time = difftime(time(0), start_time);
discord_presence.startTimestamp = pause_time;
break;
case DISCORD_PRESENCE_GAME:
if (core_info)
{
@ -126,7 +147,7 @@ void discord_update(enum discord_presence presence)
if (!label)
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 content: %s\n", label);
#endif
@ -135,39 +156,57 @@ void discord_update(enum discord_presence presence)
if (core_info->display_name)
discord_presence.largeImageText = core_info->display_name;
if (in_menu)
start_time = time(0);
else
start_time = start_time + difftime(time(0), pause_time);
start_time = time(0);
if (pause_time != 0)
start_time = time(0) - ellapsed_time;
if (!skip)
{
discord_presence.smallImageKey = "playing";
discord_presence.smallImageText = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DISCORD_STATUS_PLAYING);
discord_presence.startTimestamp = start_time;
discord_presence.details = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DISCORD_IN_GAME);
}
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.startTimestamp = start_time;
discord_presence.details = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DISCORD_IN_GAME);
discord_presence.state = label;
discord_presence.instance = 0;
}
in_menu = false;
break;
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_CHEEVO_UNLOCKED:
/* TODO/FIXME */
default:
discord_presence.joinSecret = NULL;
break;
}
if (in_menu && skip)
return;
RARCH_LOG("[Discord] updating (%d)\n", presence);
Discord_UpdatePresence(&discord_presence);
discord_status = presence;
discord_status = presence;
}
void discord_init(void)
@ -197,3 +236,8 @@ void discord_shutdown(void)
Discord_Shutdown();
discord_ready = false;
}
void discord_run_callbacks()
{
Discord_RunCallbacks();
}

View File

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

View File

@ -66,6 +66,7 @@
#include "configuration.h"
#include "msg_hash.h"
#include "verbosity.h"
#include "tasks/tasks_internal.h"
#ifdef HAVE_RUNAHEAD
#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;
}
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:
* @cmd : Identifier of command.
@ -1387,6 +1398,16 @@ bool rarch_environment_cb(unsigned cmd, void *data)
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:
{
const char **path = (const char**)data;
@ -1822,8 +1843,8 @@ bool rarch_environment_cb(unsigned cmd, void *data)
int* result_p = (int*)data;
*result_p = result;
}
break;
}
break;
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->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:
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
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],
home_dir_buf, "shaders_glsl",
sizeof(g_defaults.dirs[DEFAULT_DIR_SHADER]));
#endif
#if TARGET_OS_IPHONE
int 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)
&frontend_ctx_ctr,
#endif
#if defined(SWITCH) && defined(HAVE_LIBNX)
&frontend_ctx_switch,
#endif
#if defined(_WIN32) && !defined(_XBOX)
&frontend_ctx_win32,
#endif
@ -67,6 +70,9 @@ static frontend_ctx_driver_t *frontend_ctx_drivers[] = {
#endif
#ifdef DJGPP
&frontend_ctx_dos,
#endif
#ifdef SWITCH
&frontend_ctx_switch,
#endif
&frontend_ctx_null,
NULL
@ -150,6 +156,9 @@ bool frontend_driver_get_core_extension(char *s, size_t len)
#elif defined(__linux__)
strlcpy(s, "elf", len);
return true;
#elif defined(HAVE_LIBNX)
strlcpy(s, "nro", len);
return true;
#elif defined(_3DS)
if (envIsHomebrew())
strlcpy(s, "3dsx", len);
@ -193,6 +202,9 @@ bool frontend_driver_get_salamander_basename(char *s, size_t len)
#elif defined(_3DS)
strlcpy(s, "retroarch.core", len);
return true;
#elif defined(SWITCH)
strlcpy(s, "retroarch_switch.nro", len);
return true;
#else
return false;
#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_psp;
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_xenon;
extern frontend_ctx_driver_t frontend_ctx_emscripten;
extern frontend_ctx_driver_t frontend_ctx_dos;
extern frontend_ctx_driver_t frontend_ctx_switch;
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);
}
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.
* Some contexts require that swap interval

View File

@ -58,7 +58,7 @@ typedef struct
EGLSurface surf;
EGLDisplay dpy;
EGLConfig config;
unsigned interval;
int interval;
unsigned major;
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_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);

View File

@ -34,6 +34,9 @@ typedef struct
/*! @brief Specifies whether rendering is synchronized with the display */
@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,
* 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;
- (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;
/*! @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 */
- (void)end;
- (bool)readBackBuffer:(uint8_t *)buffer;
@end

View File

@ -54,6 +54,9 @@
id<MTLRenderPipelineState> _states[GFX_MAX_SHADERS][2];
id<MTLRenderPipelineState> _clearState;
Uniforms _uniforms;
bool _captureEnabled;
id<MTLTexture> _backBuffer;
}
- (instancetype)initWithDevice:(id<MTLDevice>)d
@ -65,7 +68,9 @@
_inflightSemaphore = dispatch_semaphore_create(MAX_INFLIGHT);
_device = d;
_layer = layer;
#if TARGET_OS_OSX
_layer.displaySyncEnabled = YES;
#endif
_library = l;
_commandQueue = [_device newCommandQueue];
_clearColor = MTLClearColorMake(0, 0, 0, 1);
@ -127,7 +132,16 @@
- (void)setDisplaySyncEnabled:(bool)displaySyncEnabled
{
#if TARGET_OS_OSX
_layer.displaySyncEnabled = displaySyncEnabled;
#endif
}
- (bool)displaySyncEnabled
{
#if TARGET_OS_OSX
return _layer.displaySyncEnabled;
#endif
}
#pragma mark - shaders
@ -154,11 +168,6 @@
return _states[index][blend ? 1 : 0];
}
- (bool)displaySyncEnabled
{
return _layer.displaySyncEnabled;
}
- (MTLVertexDescriptor *)_spriteVertexDescriptor
{
MTLVertexDescriptor *vd = [MTLVertexDescriptor new];
@ -376,7 +385,7 @@
{
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. */
#define T0 0xff000000u
@ -397,6 +406,7 @@
image.pixels = (uint32_t *)checkerboard;
image.width = 8;
image.height = 8;
filter = TEXTURE_FILTER_MIPMAP_NEAREST;
}
BOOL mipmapped = filter == TEXTURE_FILTER_MIPMAP_LINEAR || filter == TEXTURE_FILTER_MIPMAP_NEAREST;
@ -441,13 +451,13 @@
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);
Filter *conv = _filters[fmt];
assert(conv != nil);
[conv apply:self.blitCommandBuffer inBuf:src outTex:dst];
[conv apply:self.blitCommandBuffer in:src out:dst];
}
- (id<MTLCommandBuffer>)blitCommandBuffer
@ -463,11 +473,65 @@
[_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
{
assert(_commandBuffer == nil);
dispatch_semaphore_wait(_inflightSemaphore, DISPATCH_TIME_FOREVER);
_commandBuffer = [_commandQueue commandBuffer];
_backBuffer = nil;
}
- (id<MTLRenderCommandEncoder>)rce
@ -479,6 +543,10 @@
rpd.colorAttachments[0].clearColor = _clearColor;
rpd.colorAttachments[0].loadAction = MTLLoadActionClear;
rpd.colorAttachments[0].texture = self.nextDrawable.texture;
if (_captureEnabled)
{
_backBuffer = self.nextDrawable.texture;
}
_rce = [_commandBuffer renderCommandEncoderWithDescriptor:rpd];
}
return _rce;
@ -615,6 +683,7 @@ static const NSUInteger kConstantAlignment = 4;
- (void)commitRanges
{
#if TARGET_OS_OSX
for (BufferNode *n = _head; n != nil; n = n.next)
{
if (n.allocated > 0)
@ -622,6 +691,7 @@ static const NSUInteger kConstantAlignment = 4;
[n.src didModifyRange:NSMakeRange(0, n.allocated)];
}
}
#endif
}
- (void)discard
@ -634,10 +704,16 @@ static const NSUInteger kConstantAlignment = 4;
- (bool)allocRange:(BufferRange *)range length:(NSUInteger)length
{
bzero(range, sizeof(*range));
#if TARGET_OS_OSX
MTLResourceOptions opts = MTLResourceStorageModeManaged;
#else
MTLResourceOptions opts = MTLResourceStorageModeShared;
#endif
if (!_head)
{
_head = [[BufferNode alloc] initWithBuffer:[_device newBufferWithLength:_blockLen options:MTLResourceStorageModeManaged]];
_head = [[BufferNode alloc] initWithBuffer:[_device newBufferWithLength:_blockLen options:opts]];
_length += _blockLen;
_current = _head;
_offset = 0;
@ -659,7 +735,7 @@ static const NSUInteger kConstantAlignment = 4;
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)
return NO;

View File

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

View File

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

View File

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

View File

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

View File

@ -299,30 +299,29 @@
settings_t *settings = config_get_ptr();
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);
float x = video_info->font_msg_pos_x;
float y = 1.0f - video_info->font_msg_pos_y;
float width = msg_width / (float)_viewport->full_width;
float height =
float x = video_info->font_msg_pos_x;
float y = 1.0f - video_info->font_msg_pos_y;
float width = msg_width / (float)_viewport->full_width;
float height =
settings->floats.video_font_size / (float)_viewport->full_height;
y -= height;
float x2 = 0.005f; /* extend background around text */
float y2 = 0.005f;
x -= x2;
y -= y2;
width += x2;
height += y2;
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;
float x2 = 0.005f; /* extend background around text */
float y2 = 0.005f;
x -= x2;
y -= y2;
width += x2;
height += y2;
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 drawQuadX:x y:y w:width h:height r:r g:g b:b a:a];
}
@ -332,7 +331,12 @@
- (void)_beginFrame
{
video_viewport_t vp = *_viewport;
video_driver_update_viewport(_viewport, NO, _keepAspect);
if (memcmp(&vp, _viewport, sizeof(vp)) != 0)
{
_context.viewport = _viewport;
}
[_context begin];
[self _updateUniforms];
}
@ -542,12 +546,13 @@ typedef struct MTLALIGN(16)
Context *_context;
id<MTLTexture> _texture; // final render texture
Vertex _v[4];
VertexSlang _vertex[4];
CGSize _size; // size of view in pixels
CGRect _frame;
NSUInteger _bpp;
id<MTLBuffer> _pixels; // frame buffer in _srcFmt
bool _pixelsDirty;
id<MTLTexture> _src; // src texture
bool _srcDirty;
id<MTLSamplerState> _samplers[RARCH_FILTER_MAX][RARCH_WRAP_MAX];
struct video_shader *_shader;
@ -583,6 +588,15 @@ typedef struct MTLALIGN(16)
self.size = d.size;
self.frame = CGRectMake(0, 0, 1, 1);
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;
}
@ -655,8 +669,11 @@ typedef struct MTLALIGN(16)
if (_format != RPixelFormatBGRA8Unorm && _format != RPixelFormatBGRX8Unorm)
{
_pixels = [_context.device newBufferWithLength:(NSUInteger)(size.width * size.height * 2)
options:MTLResourceStorageModeManaged];
MTLTextureDescriptor *td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatR16Uint
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)
return;
if (!_pixelsDirty)
if (!_srcDirty)
return;
[_context convertFormat:_format from:_pixels to:_texture];
_pixelsDirty = NO;
[_context convertFormat:_format from:_src to:_texture];
_srcDirty = NO;
}
- (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
{
if (_shader && (_engine.frame.output_size.x != _viewport->width ||
@ -778,26 +813,10 @@ typedef struct MTLALIGN(16)
}
else
{
void *dst = _pixels.contents;
size_t len = (size_t)(_bpp * _size.width);
assert(len <= pitch); // the length can't be larger?
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;
[_src replaceRegion:MTLRegionMake2D(0, 0, (NSUInteger)_size.width, (NSUInteger)_size.height)
mipmapLevel:0 withBytes:src
bytesPerRow:(NSUInteger)(pitch)];
_srcDirty = YES;
}
}
@ -825,19 +844,6 @@ typedef struct MTLALIGN(16)
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
{
if (_texture)
@ -951,7 +957,7 @@ static vertex_t vertex_bytes[] = {
[rce setFragmentTextures:textures 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];
if (!backBuffer)
@ -1171,13 +1177,13 @@ static vertex_t vertex_bytes[] = {
@try
{
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].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].bufferIndex = 4;
vd.layouts[4].stride = sizeof(vertex_t);
vd.layouts[4].stride = sizeof(VertexSlang);
vd.layouts[4].stepFunction = MTLVertexStepFunctionPerVertex;
MTLRenderPipelineDescriptor *psd = [MTLRenderPipelineDescriptor new];
@ -1204,7 +1210,7 @@ static vertex_t vertex_bytes[] = {
if (lib == nil)
{
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;
}
#if DEBUG
@ -1220,7 +1226,7 @@ static vertex_t vertex_bytes[] = {
if (lib == nil)
{
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;
}
#if DEBUG
@ -1234,7 +1240,8 @@ static vertex_t vertex_bytes[] = {
if (err != nil)
{
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;
}
@ -1253,10 +1260,11 @@ static vertex_t vertex_bytes[] = {
{
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;
NSString *basePath = [[NSString stringWithUTF8String:shader->pass[i].source.path] stringByDeletingPathExtension];
[vs_src writeToFile:[basePath stringByAppendingPathExtension:@"vs.metal"]
atomically:NO
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/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 VkInstance cached_instance_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",
pLayerPrefix, pMessage);
}
#if 0
else if (flags & VK_DEBUG_REPORT_WARNING_BIT_EXT)
{
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",
pLayerPrefix, pMessage);
}
#endif
return VK_FALSE;
}
#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(
const VkPhysicalDeviceMemoryProperties *mem_props,
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 *staging)
{
VkImageCopy region;
VkBufferImageCopy region;
retro_assert(dynamic->type == VULKAN_TEXTURE_DYNAMIC);
retro_assert(staging->type == VULKAN_TEXTURE_STAGING);
vulkan_sync_texture_to_gpu(vk, staging);
vulkan_transition_texture(vk, cmd, staging);
/* We don't have to sync against previous TRANSFER,
* 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);
memset(&region, 0, sizeof(region));
region.extent.width = dynamic->width;
region.extent.height = dynamic->height;
region.extent.depth = 1;
region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.srcSubresource.layerCount = 1;
region.dstSubresource = region.srcSubresource;
region.imageExtent.width = dynamic->width;
region.imageExtent.height = dynamic->height;
region.imageExtent.depth = 1;
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.layerCount = 1;
vkCmdCopyImage(cmd,
staging->image, VK_IMAGE_LAYOUT_GENERAL,
vkCmdCopyBufferToImage(cmd,
staging->buffer,
dynamic->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1, &region);
@ -323,6 +476,7 @@ struct vk_texture vulkan_create_texture(vk_t *vk,
VkSubresourceLayout layout;
VkDevice device = vk->context->device;
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 };
VkMemoryAllocateInfo alloc = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
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.depth = 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
* 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)
{
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;
vkGetPhysicalDeviceFormatProperties(
@ -396,23 +554,33 @@ struct vk_texture vulkan_create_texture(vk_t *vk,
break;
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.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
break;
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.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
break;
}
vkCreateImage(device, &info, NULL, &tex.image);
if (type != VULKAN_TEXTURE_STAGING && type != VULKAN_TEXTURE_READBACK)
{
vkCreateImage(device, &info, NULL, &tex.image);
#if 0
vulkan_track_alloc(tex.image);
vulkan_track_alloc(tex.image);
#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;
switch (type)
@ -449,13 +617,14 @@ struct vk_texture vulkan_create_texture(vk_t *vk,
{
/* 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");
type = VULKAN_TEXTURE_STAGING;
type = VULKAN_TEXTURE_STAGING;
vkDestroyImage(device, tex.image, NULL);
tex.image = NULL;
info.initialLayout = VK_IMAGE_LAYOUT_GENERAL;
info.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
vkCreateImage(device, &info, NULL, &tex.image);
vkGetImageMemoryRequirements(device, tex.image, &mem_reqs);
buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
vkCreateBuffer(device, &buffer_info, NULL, &tex.buffer);
vkGetBufferMemoryRequirements(device, tex.buffer, &mem_reqs);
alloc.allocationSize = mem_reqs.size;
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);
#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. */
if (old &&
@ -507,7 +678,10 @@ struct vk_texture vulkan_create_texture(vk_t *vk,
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)
{
@ -532,8 +706,14 @@ struct vk_texture vulkan_create_texture(vk_t *vk,
else
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);
else if (tex.buffer)
{
layout.offset = 0;
layout.size = buffer_info.size;
layout.rowPitch = width * vulkan_format_to_bpp(format);
}
else
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)
{
VkImageCopy region;
VkBufferImageCopy region;
VkCommandBuffer staging;
struct vk_texture tmp = vulkan_create_texture(vk, NULL,
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);
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
* and transfers from the images without having to
* 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);
memset(&region, 0, sizeof(region));
region.extent.width = width;
region.extent.height = height;
region.extent.depth = 1;
region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.srcSubresource.layerCount = 1;
region.dstSubresource = region.srcSubresource;
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.layerCount = 1;
region.imageExtent.width = width;
region.imageExtent.height = height;
region.imageExtent.depth = 1;
vkCmdCopyImage(staging,
tmp.image,
VK_IMAGE_LAYOUT_GENERAL,
vkCmdCopyBufferToImage(staging,
tmp.buffer,
tex.image,
tex.mipmap ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1, &region);
@ -710,12 +882,18 @@ void vulkan_destroy_texture(
{
if (tex->mapped)
vkUnmapMemory(device, tex->memory);
vkFreeMemory(device, tex->memory, NULL);
if (tex->view)
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
vulkan_track_dealloc(tex->image);
if (tex->image)
vulkan_track_dealloc(tex->image);
#endif
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)
{
if (!texture->image)
return;
/* Transition to GENERAL layout for linear streamed textures.
* We're using linear textures here, so only
* 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);
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:
retro_assert(0 && "Attempting to transition invalid texture type.\n");
break;
@ -1550,6 +1723,12 @@ static bool vulkan_context_init_device(gfx_ctx_vulkan_data_t *vk)
vkGetPhysicalDeviceMemoryProperties(vk->context.gpu,
&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);
if (vk->context.device == VK_NULL_HANDLE)
@ -2309,12 +2488,14 @@ static void vulkan_destroy_swapchain(gfx_ctx_vulkan_data_t *vk)
{
unsigned i;
vulkan_emulated_mailbox_deinit(&vk->mailbox);
if (vk->swapchain != VK_NULL_HANDLE)
{
vkDeviceWaitIdle(vk->context.device);
vkDestroySwapchainKHR(vk->context.device, vk->swapchain, NULL);
memset(vk->context.swapchain_images, 0, sizeof(vk->context.swapchain_images));
vk->swapchain = VK_NULL_HANDLE;
vk->context.has_acquired_swapchain = false;
}
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)
{
VkPresentInfoKHR present = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR };
VkResult result = VK_SUCCESS;
VkResult err = VK_SUCCESS;
VkPresentInfoKHR present = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR };
VkResult result = 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. */
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,
vk->context.swapchain_fences[i], NULL);
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])
vkWaitForFences(vk->context.device, 1, next_fence, true, UINT64_MAX);
vkResetFences(vk->context.device, 1, next_fence);
vk->context.swapchain_fences_signalled[index] = false;
}
else
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)
@ -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,
vk->swapchain, UINT64_MAX,
VK_NULL_HANDLE, fence, &vk->context.current_swapchain_index);
if (vk->emulating_mailbox)
{
/* 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)
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
trigger_spurious_error_vkresult(&err);
#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. */
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;
settings_t *settings = config_get_ptr();
VkCompositeAlphaFlagBitsKHR composite = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
VkResult res;
vkDeviceWaitIdle(vk->context.device);
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;
if (vk->swapchain != VK_NULL_HANDLE &&
!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. */
RARCH_LOG("[Vulkan]: Do not need to re-create swapchain.\n");
vk->created_new_swapchain = false;
return true;
vulkan_create_wait_fences(vk);
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;
vkGetPhysicalDeviceSurfacePresentModesKHR(
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. */
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;
}

View File

@ -96,6 +96,7 @@ typedef struct vulkan_context
/* Used by screenshot to get blits with correct colorspace. */
bool swapchain_is_srgb;
bool swap_interval_emulation_lock;
bool has_acquired_swapchain;
unsigned swapchain_width;
unsigned swapchain_height;
@ -127,13 +128,42 @@ typedef struct vulkan_context
#endif
} 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
{
bool need_new_swapchain;
bool created_new_swapchain;
bool emulate_mailbox;
bool emulating_mailbox;
vulkan_context_t context;
VkSurfaceKHR vk_surface;
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;
struct vulkan_display_surface_info
@ -179,6 +209,7 @@ struct vk_texture
VkImage image;
VkImageView view;
VkDeviceMemory memory;
VkBuffer buffer;
VkFormat format;

View File

@ -28,14 +28,14 @@ static void null_display_server_destroy(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)opacity;
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)progress;
@ -46,8 +46,9 @@ static bool null_set_window_progress(void *data, int progress, bool finished)
const video_display_server_t dispserv_null = {
null_display_server_init,
null_display_server_destroy,
null_set_window_opacity,
null_set_window_progress,
null_display_server_set_window_opacity,
null_display_server_set_window_progress,
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_height = 0;
static unsigned win32_orig_refresh = 0;
static int crt_center = 0;
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 )
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
if (g_taskbarList && win32_taskbar_is_created())
@ -121,7 +122,7 @@ static void win32_display_server_destroy(void *data)
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();
dispserv_win32_t *serv = (dispserv_win32_t*)data;
@ -148,7 +149,7 @@ static bool win32_set_window_opacity(void *data, unsigned opacity)
#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();
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;
}
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;
@ -201,7 +202,7 @@ static bool win32_set_window_decorations(void *data, bool on)
}
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;
DEVMODE curDevmode;
@ -286,10 +287,11 @@ static bool win32_display_server_set_resolution(void *data,
const video_display_server_t dispserv_win32 = {
win32_display_server_init,
win32_display_server_destroy,
win32_set_window_opacity,
win32_set_window_progress,
win32_set_window_decorations,
win32_display_server_set_window_opacity,
win32_display_server_set_window_progress,
win32_display_server_set_window_decorations,
win32_display_server_set_resolution,
NULL, /* get_output_options */
"win32"
};

View File

@ -23,9 +23,14 @@
#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 */
static char old_mode[150];
static char new_mode[150];
static bool crt_en = false;
static unsigned orig_width = 0;
static unsigned orig_height = 0;
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
{
@ -45,16 +50,35 @@ static void* x11_display_server_init(void)
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)
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)
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;
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;
}
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;
@ -83,8 +107,8 @@ static bool x11_set_window_decorations(void *data, bool on)
return true;
}
static bool x11_set_resolution(void *data,
unsigned width, unsigned height, int int_hz, float hz)
static bool x11_display_server_set_resolution(void *data,
unsigned width, unsigned height, int int_hz, float hz, int center)
{
int i = 0;
int hfp = 0;
@ -95,22 +119,41 @@ static bool x11_set_resolution(void *data,
int vbp = 0;
int hmax = 0;
int vmax = 0;
int pdefault = 8;
int pwidth = 0;
float roundw = 0.0f;
float roundh = 0.0f;
float pixel_clock = 0;
char xrandr[250];
char fbset[150];
char output[250];
crt_en = true;
hsp = width*1.16;
/* set core refresh from hz */
video_monitor_set_refresh_rate(hz);
/* following code is the mode line genorator */
/* following code is the mode line genorator */
hfp = width+8;
hbp = width*1.32;
hsp = width*1.140;
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;
if (height < 241)
@ -133,22 +176,18 @@ static bool x11_set_resolution(void *data,
{
vmax = 285;
}
if (height > 240 && height < 260 && hz < 52)
{
vmax = 265;
}
if (height > 250 && height < 260 && hz < 52)
{
vmax = 313;
}
if (height > 260 && height < 300)
{
vmax = 313;
vmax = 318;
}
if (height > 400 && hz > 56)
{
vmax = 523;
vmax = 533;
}
if (height > 520 && hz < 57)
{
@ -157,35 +196,27 @@ static bool x11_set_resolution(void *data,
if (height > 300 && hz < 56)
{
vmax = 627;
vmax = 615;
}
if (hz < 53)
{
vfp = height+((vmax-height)*0.38);
if (height > 500 && hz < 56)
{
vmax = 624;
}
if (hz > 56)
{
vfp = height+((vmax-height)*0.15);
}
if (hz > 53 && hz < 56)
{
vfp = height+((vmax-height)*0.35);
if (height > 300)
{
pdefault = pdefault*2;
}
if ( vfp < 1 )
{
vfp = height+2;
}
vfp = height+((vmax-height)/2)-pdefault;
if (height < 300)
{
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;
@ -201,7 +232,7 @@ static bool x11_set_resolution(void *data,
}
/* above code is the modeline genorator */
/* create progressive newmode from modline variables */
/* create interlaced newmode from modline variables */
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);
@ -257,18 +288,25 @@ static bool x11_set_resolution(void *data,
/* variable for old mode */
snprintf(old_mode, sizeof(old_mode), "%s", new_mode);
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;
}
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 = {
x11_display_server_init,
x11_display_server_destroy,
x11_set_window_opacity,
x11_display_server_set_window_opacity,
NULL,
x11_set_window_decorations,
x11_set_resolution, /* set_resolution */
x11_display_server_set_window_decorations,
x11_display_server_set_resolution,
x11_display_server_get_output_options,
"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);
if (ext && !strcmp(ext, "slangp"))
if (ext && string_is_equal(ext, "slangp"))
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)
{
unsigned interval = state ? 0 : 1;
int interval = 0;
d3d8_video_t *d3d = (d3d8_video_t*)data;
if (!d3d)
return;
d3d->video_info.vsync = !state;
if (!state)
interval = 1;
d3d->video_info.vsync = !state;
#ifdef _XBOX
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)
{
unsigned interval = state ? 0 : 1;
int interval = 0;
d3d9_video_t *d3d = (d3d9_video_t*)data;
if (!d3d)
return;
if (!state)
interval = 1;
d3d->video_info.vsync = !state;
(void)interval;
#ifdef _XBOX
d3d9_set_render_state(d3d->dev,
D3D9_PRESENTATIONINTERVAL,

View File

@ -952,6 +952,12 @@ static bool gl_frame(void *data, const void *frame,
if (!gl)
return false;
#ifdef HAVE_LIBNX
// Should be called once per frame
if(!appletMainLoop())
return false;
#endif
context_bind_hw_render(false);
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)
{
unsigned interval = 0;
int interval = 0;
gl_t *gl = (gl_t*)data;
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_input_t inp;
unsigned interval, mip_level;
unsigned full_x, full_y;
video_shader_ctx_filter_t shader_filter;
video_shader_ctx_info_t shader_info;
video_shader_ctx_ident_t ident_info;
settings_t *settings = config_get_ptr();
video_shader_ctx_wrap_t wrap_info = {0};
int interval = 0;
unsigned mip_level = 0;
unsigned win_width = 0;
unsigned win_height = 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))
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. */
glGetError();
@ -1768,10 +1779,6 @@ static void *gl_init(const video_info_t *video,
if (!string_is_empty(version))
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();
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;
if (!strcmp(id, "OutputSize"))
if (string_is_equal(id, "OutputSize"))
{
((GX2_vec4 *)dst)->x = wiiu->pass[pass].color_buffer.surface.width;
((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;
}
if (!strcmp(id, "FinalViewportSize"))
if (string_is_equal(id, "FinalViewportSize"))
{
((GX2_vec4 *)dst)->x = wiiu->vp.width;
((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;
}
if (!strcmp(id, "FrameCount"))
if (string_is_equal(id, "FrameCount"))
{
*dst = 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;
}
if (!strcmp(id, "OriginalSize"))
if (string_is_equal(id, "OriginalSize"))
{
((GX2_vec4 *)dst)->x = wiiu->texture.surface.width;
((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;
}
if (!strcmp(id, "SourceSize"))
if (string_is_equal(id, "SourceSize"))
{
GX2Surface *source = (pass > 0) ? &wiiu->pass[pass - 1].texture.surface : &wiiu->texture.surface;
((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));
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++)
{
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;
*(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++)
{
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);
GX2SetPixelSampler(wiiu->shader_preset->pass[i].filter ?
@ -1184,7 +1184,7 @@ static bool wiiu_gfx_frame(void *data, const void *frame,
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);
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++)
{
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);
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)
{
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,

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 <string.h>
#include <malloc.h>
@ -146,41 +162,41 @@ static bool switch_frame(void *data, const void *frame,
centerx = (1280-tgtw)/2;
centery = (720-tgth)/2;
// clear image to black
/* clear image to black */
for(y = 0; y < 720; y++)
{
for(x = 0; x < 1280; x++)
{
sw->image[y*1280+x] = 0xFF000000;
}
}
if(width > 0 && height > 0) {
if(width > 0 && height > 0)
{
if(sw->last_width != width ||
sw->last_height != 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;
sw->scaler.out_width = tgtw;
sw->scaler.out_height = tgth;
sw->scaler.out_stride = 1280 * sizeof(uint32_t);
sw->scaler.out_fmt = SCALER_FMT_ABGR8888;
sw->scaler.scaler_type = SCALER_TYPE_POINT;
if(!scaler_ctx_gen_filter(&sw->scaler)) {
RARCH_ERR("failed to generate scaler for main image\n");
return false;
}
{
scaler_ctx_gen_reset(&sw->scaler);
sw->last_width = width;
sw->last_height = height;
}
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;
sw->scaler.out_width = tgtw;
sw->scaler.out_height = tgth;
sw->scaler.out_stride = 1280 * sizeof(uint32_t);
sw->scaler.out_fmt = SCALER_FMT_ABGR8888;
sw->scaler.scaler_type = SCALER_TYPE_POINT;
if(!scaler_ctx_gen_filter(&sw->scaler))
{
RARCH_ERR("failed to generate scaler for main image\n");
return false;
}
sw->last_width = width;
sw->last_height = height;
}
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;
if (osd_params)
{
font_driver_render_msg(video_info, NULL, video_info->stat_text,
(const struct font_params*)&video_info->osd_stat_params);
}
}
#endif
@ -232,14 +246,12 @@ static bool switch_frame(void *data, const void *frame,
RARCH_LOG("message: %s\n", msg);
r = surface_dequeue_buffer(&sw->surface, &out_buffer);
if(r != RESULT_OK) {
return true; // just skip the frame
}
if(r != RESULT_OK)
return true; /* just skip the frame */
r = surface_wait_buffer(&sw->surface);
if(r != RESULT_OK) {
if(r != RESULT_OK)
return true;
}
gfx_slow_swizzling_blit(out_buffer, sw->image, 1280, 720, 0, 0);
r = surface_queue_buffer(&sw->surface);
@ -333,6 +345,8 @@ static void switch_set_texture_frame(
sw->menu_texture.width != width ||
sw->menu_texture.height != height)
{
struct scaler_ctx *sctx;
int xsf, ysf, sf;
if (sw->menu_texture.pixels)
free(sw->menu_texture.pixels);
@ -343,32 +357,32 @@ static void switch_set_texture_frame(
return;
}
int xsf = 1280 / width;
int ysf = 720 / height;
int sf = xsf;
xsf = 1280 / width;
ysf = 720 / height;
sf = xsf;
if (ysf < sf)
sf = ysf;
sw->menu_texture.width = width;
sw->menu_texture.width = width;
sw->menu_texture.height = height;
sw->menu_texture.tgtw = width * sf;
sw->menu_texture.tgth = height * sf;
sw->menu_texture.tgtw = width * 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);
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->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->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;
sctx->scaler_type = SCALER_TYPE_POINT;
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)
{
switch_video_t *sw = data;
if (!sw)
return;
sw->menu_texture.enable = enable;
sw->menu_texture.fullscreen = full_screen;
}
@ -441,3 +458,5 @@ video_driver_t video_switch = {
#endif
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)
{
unsigned interval = state ? 0 : 1;
int interval = state ? 0 : 1;
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_input_t inp;
gfx_ctx_aspect_t aspect_data;
unsigned interval;
unsigned temp_width = 0, temp_height = 0;
unsigned win_width, win_height;
VGfloat clearColor[4] = {0, 0, 0, 1};
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(
VGfloat clearColor[4] = {0, 0, 0, 1};
int interval = 0;
unsigned temp_width = 0;
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,
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];
VkCommandPoolCreateInfo pool_info = {
VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO };
pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
/* Create the pipeline cache. */
VkPipelineCacheCreateInfo cache = {
VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO };
@ -1092,8 +1094,8 @@ static void vulkan_init_readback(vk_t *vk)
* not initialized yet.
*/
settings_t *settings = config_get_ptr();
bool *recording_enabled = recording_is_enabled();
vk->readback.streamed = settings->bools.video_gpu_record && *recording_enabled;
bool recording_enabled = recording_is_enabled();
vk->readback.streamed = settings->bools.video_gpu_record && recording_enabled;
if (!vk->readback.streamed)
return;
@ -1119,10 +1121,10 @@ static void *vulkan_init(const video_info_t *video,
{
gfx_ctx_mode_t mode;
gfx_ctx_input_t inp;
unsigned interval;
unsigned full_x, full_y;
unsigned win_width;
unsigned win_height;
int interval = 0;
unsigned temp_width = 0;
unsigned temp_height = 0;
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)
{
unsigned interval;
int interval = 0;
vk_t *vk = (vk_t*)data;
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");
interval = state ? 0 : settings->uints.video_swap_interval;
if (!state)
interval = settings->uints.video_swap_interval;
video_context_driver_swap_interval(&interval);
/* 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)
{
VkImageCopy region;
VkBufferImageCopy region;
struct vk_texture *staging;
struct video_viewport vp;
VkMemoryBarrier barrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER };
vulkan_viewport_info(vk, &vp);
memset(&region, 0, sizeof(region));
region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.srcSubresource.layerCount = 1;
region.dstSubresource = region.srcSubresource;
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.layerCount = 1;
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;
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
/* FIXME: We won't actually get format conversion with vkCmdCopyImageToBuffer, so have to check
* properly for this. BGRA seems to be the default for all swapchains. */
if (vk->context->swapchain_format != VK_FORMAT_B8G8R8A8_UNORM)
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,
NULL, NULL, VULKAN_TEXTURE_READBACK);
/* Go through the long-winded dance of remapping image layouts. */
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,
vkCmdCopyImageToBuffer(vk->cmd, vk->chain->backbuffer.image,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
staging->image,
VK_IMAGE_LAYOUT_GENERAL,
staging->buffer,
1, &region);
/* Make the data visible to host. */
vulkan_image_layout_transition(vk, vk->cmd, staging->image,
VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL,
VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT,
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_HOST_READ_BIT;
vkCmdPipelineBarrier(vk->cmd,
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)
@ -1784,7 +1780,7 @@ static bool vulkan_frame(void *data, const void *frame,
(vulkan_filter_chain_t*)vk->filter_chain,
vk->cmd, &vk->vk_vp);
/* 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.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);
if (chain->backbuffer.image != VK_NULL_HANDLE &&
vk->context->has_acquired_swapchain &&
(vk->readback.pending || vk->readback.streamed))
{
/* 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;
}
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. */
vulkan_image_layout_transition(vk, vk->cmd,
@ -1971,8 +1969,11 @@ static bool vulkan_frame(void *data, const void *frame,
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];
}
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,
* and pause to prevent flicker. */
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->runloop_is_slowmotion
&& !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. */
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;
vk->context->swap_interval_emulation_lock = true;

View File

@ -60,7 +60,7 @@ typedef struct
gfx_ctx_vulkan_data_t vk;
unsigned width;
unsigned height;
unsigned swap_interval;
int swap_interval;
#endif
} 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;

View File

@ -63,7 +63,7 @@ typedef struct gfx_ctx_cgl_data
int width, height;
} 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;
GLint params = interval;

View File

@ -81,7 +81,7 @@ typedef struct gfx_ctx_drm_data
egl_ctx_data_t egl;
#endif
int fd;
unsigned interval;
int interval;
unsigned fb_width;
unsigned fb_height;
@ -134,7 +134,7 @@ error:
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;
drm->interval = interval;

View File

@ -49,14 +49,14 @@ static int emscripten_initial_width;
static int emscripten_initial_height;
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;
if (interval == 0)
emscripten_set_main_loop_timing(EM_TIMING_SETIMMEDIATE, 0);
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)

View File

@ -45,7 +45,7 @@ static HDC win32_gdi_hdc;
static unsigned win32_gdi_major = 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;
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);
}
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)interval;

View File

@ -18,7 +18,7 @@
#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)interval;

View File

@ -23,7 +23,7 @@
typedef struct
{
gfx_ctx_vulkan_data_t vk;
unsigned swap_interval;
int swap_interval;
unsigned width;
unsigned height;
} khr_display_ctx_data_t;
@ -99,7 +99,8 @@ static bool gfx_ctx_khr_display_set_resize(void *data,
khr->width = width;
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");
return false;
@ -189,9 +190,11 @@ static bool gfx_ctx_khr_display_suppress_screensaver(void *data, bool enable)
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;
if (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;
#ifdef HAVE_VULKAN
gfx_ctx_vulkan_data_t vk;
unsigned swap_interval;
int swap_interval;
#endif
unsigned width;
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)
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;
#elif defined(HAVE_COCOA)
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;
}
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
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?
// 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;
#elif defined(HAVE_COCOA)
GLint value = interval ? 1 : 0;
GLint value = interval ? 1 : 0;
[g_context setValues:&value forParameter:NSOpenGLCPSwapInterval];
#endif
break;
@ -491,9 +492,10 @@ static bool cocoagl_gfx_ctx_set_video_mode(void *data,
case GFX_CTX_VULKAN_API:
#ifdef HAVE_VULKAN
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,
(BRIDGE void *)g_view, cocoa_ctx->width, cocoa_ctx->height,
cocoa_ctx->swap_interval))
if (!vulkan_surface_create(&cocoa_ctx->vk,
VULKAN_WSI_MVK_MACOS, NULL,
(BRIDGE void *)g_view, cocoa_ctx->width, cocoa_ctx->height,
cocoa_ctx->swap_interval))
{
RARCH_ERR("[macOS]: Failed to create surface.\n");
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->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;
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;
}
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;

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(
void *data, unsigned swap_interval)
void *data, int swap_interval)
{
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;
}
static void osmesa_ctx_swap_interval(void *data, unsigned interval)
static void osmesa_ctx_swap_interval(void *data, int interval)
{
(void)data;
(void)interval;

View File

@ -140,10 +140,10 @@ static void gfx_ctx_ps3_get_available_resolutions(void)
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 (interval)
if (interval == 1)
glEnable(GL_VSYNC_SCE);
else
glDisable(GL_VSYNC_SCE);

View File

@ -417,7 +417,7 @@ dpi_fallback:
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;

View File

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

View File

@ -151,7 +151,7 @@ static void gfx_ctx_sixel_show_mouse(void *data, bool state)
(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)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;
}
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
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;
}
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;

View File

@ -86,7 +86,7 @@ typedef struct gfx_ctx_wayland_data
struct wl_touch *wl_touch;
struct wl_seat *seat;
struct wl_shm *shm;
unsigned swap_interval;
int swap_interval;
bool core_hw_context_enable;
unsigned buffer_scale;
@ -1232,7 +1232,7 @@ static void gfx_ctx_wl_destroy(void *data)
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;

View File

@ -47,6 +47,7 @@
#ifdef HAVE_OPENGL
#include "../common/gl_common.h"
#include <gfx/gl_capabilities.h>
#endif
#ifdef HAVE_VULKAN
@ -90,6 +91,7 @@ static HGLRC win32_hw_hrc;
static HDC win32_hdc;
static bool win32_use_hw_ctx = false;
static bool win32_core_hw_context_enable = false;
static bool wgl_adaptive_vsync = false;
#ifdef HAVE_VULKAN
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_minor = 0;
static unsigned win32_interval = 0;
static int win32_interval = 0;
static enum gfx_ctx_api win32_api = GFX_CTX_NONE;
#ifdef HAVE_DYNAMIC
static dylib_t dll_handle = NULL; /* Handle to OpenGL32.dll */
#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
{
void *empty;
@ -263,6 +297,23 @@ static void create_gl_context(HWND hwnd, bool *quit)
else
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
@ -308,7 +359,7 @@ void create_graphics_context(HWND hwnd, bool *quit)
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;
@ -322,7 +373,7 @@ static void gfx_ctx_wgl_swap_interval(void *data, unsigned interval)
if (!p_swap_interval)
return;
RARCH_LOG("[WGL]: wglSwapInterval(%u)\n", win32_interval);
RARCH_LOG("[WGL]: wglSwapInterval(%i)\n", win32_interval);
if (!p_swap_interval(win32_interval))
RARCH_WARN("[WGL]: wglSwapInterval() failed.\n");
#endif
@ -478,6 +529,7 @@ static void *gfx_ctx_wgl_init(video_frame_info_t *video_info, void *video_driver
#ifdef HAVE_DYNAMIC
dll_handle = dylib_load("OpenGL32.dll");
#endif
win32_window_reset();
win32_monitor_init();
@ -572,6 +624,7 @@ static void gfx_ctx_wgl_destroy(void *data)
if (wgl)
free(wgl);
wgl_adaptive_vsync = false;
win32_core_hw_context_enable = false;
g_win32_inited = false;
win32_major = 0;
@ -584,6 +637,10 @@ static bool gfx_ctx_wgl_set_video_mode(void *data,
unsigned width, unsigned height,
bool fullscreen)
{
#ifdef HAVE_VULKAN
win32_vk.fullscreen = fullscreen;
#endif
if (!win32_set_video_mode(NULL, width, height, fullscreen))
{
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)
{
uint32_t flags = 0;
if (win32_core_hw_context_enable)
uint32_t flags = 0;
BIT32_SET(flags, GFX_CTX_FLAGS_NONE);
switch (win32_api)
{
BIT32_SET(flags, GFX_CTX_FLAGS_GL_CORE_CONTEXT);
}
else
{
BIT32_SET(flags, GFX_CTX_FLAGS_NONE);
case GFX_CTX_OPENGL_API:
if (wgl_adaptive_vsync)
{
BIT32_SET(flags, GFX_CTX_FLAGS_ADAPTIVE_VSYNC);
}
if (win32_core_hw_context_enable)
{
BIT32_SET(flags, GFX_CTX_FLAGS_GL_CORE_CONTEXT);
}
break;
case GFX_CTX_NONE:
default:
break;
}
return flags;
}
static void gfx_ctx_wgl_set_flags(void *data, uint32_t flags)
{
if (BIT32_GET(flags, GFX_CTX_FLAGS_GL_CORE_CONTEXT))
win32_core_hw_context_enable = true;
switch (win32_api)
{
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,

View File

@ -25,6 +25,7 @@
#ifdef HAVE_OPENGL
#include <GL/glx.h>
#include <gfx/gl_capabilities.h>
#ifndef GLX_SAMPLE_BUFFERS
#define GLX_SAMPLE_BUFFERS 100000
@ -100,13 +101,14 @@ typedef struct gfx_ctx_x_data
unsigned swap_mode;
#endif
unsigned g_interval;
int g_interval;
#ifdef HAVE_VULKAN
gfx_ctx_vulkan_data_t vk;
#endif
} gfx_ctx_x_data_t;
static bool x_adaptive_vsync = false;
static bool x_enable_msaa = false;
static unsigned g_major = 0;
static unsigned g_minor = 0;
@ -124,51 +126,25 @@ typedef struct Hints
} Hints;
/* 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[] = {
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,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff,
0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff,
0xffffffff, 0xffffffff, 0x000000ff, 0xffffffff, 0xffffffff, 0x00000000,
0xffffffff, 0xffffffff, 0x000000ff, 0xffffffff, 0xffffffff, 0xffffffff,
0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0x000000ff, 0xffffffff,
0xffffffff, 0x000000ff, 0xffffffff, 0xffffffff, 0xffffffff, 0x000000ff,
0xffffffff, 0xffffffff, 0x000000ff, 0xffffffff, 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
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,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0x00000000,0x00000000,
0x00000000,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0x00000000,0x00000000,
0x00000000,0xff333333,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xff333333,0x00000000,0x00000000,
0x00000000,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0x00000000,0x00000000,
0x00000000,0xff333333,0xfff2f2f2,0xff333333,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xff333333,0xfff2f2f2,0xff333333,0x00000000,0x00000000,
0x00000000,0xff333333,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xff333333,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xff333333,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xff333333,0x00000000,0x00000000,
0x00000000,0xff333333,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xff333333,0x00000000,0x00000000,
0x00000000,0xff333333,0xff333333,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xff333333,0xff333333,0x00000000,0x00000000,
0x00000000,0xff333333,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xff333333,0x00000000,0x00000000,
0x00000000,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0x00000000,0x00000000,
0x00000000,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0x00000000,0x00000000,
0x00000000,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,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,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000
};
#ifdef HAVE_OPENGL
@ -320,7 +296,7 @@ static void gfx_ctx_x_destroy(void *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;
@ -333,18 +309,18 @@ static void gfx_ctx_x_swap_interval(void *data, unsigned interval)
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);
}
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)
RARCH_WARN("[GLX]: glXSwapInterval() failed.\n");
}
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)
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:
#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") &&
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)
{
uint32_t flags = 0;
uint32_t flags = 0;
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);
}
else
{
BIT32_SET(flags, GFX_CTX_FLAGS_NONE);
}
if (x_enable_msaa)
{
BIT32_SET(flags, GFX_CTX_FLAGS_MULTISAMPLING);
case GFX_CTX_OPENGL_API:
case GFX_CTX_OPENGL_ES_API:
if (x_adaptive_vsync)
{
BIT32_SET(flags, GFX_CTX_FLAGS_ADAPTIVE_VSYNC);
}
if (x->core_hw_context_enable || x->g_core_es)
{
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;
}
static void gfx_ctx_x_set_flags(void *data, uint32_t flags)
{
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;
if (BIT32_GET(flags, GFX_CTX_FLAGS_MULTISAMPLING))
x_enable_msaa = true;
switch (x_api)
{
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)

View File

@ -255,7 +255,7 @@ static EGLint *xegl_fill_attribs(xegl_ctx_data_t *xegl, EGLint *attr)
}
/* 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,
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;

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
#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
static const font_renderer_t *wiiu_font_backends[] = {
&wiiu_font,
@ -630,6 +661,11 @@ static bool font_init_first(
return sixel_font_init_first(font_driver, font_handle,
video_data, font_path, font_size, is_threaded);
#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)
case FONT_DRIVER_RENDER_GDI:
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 vga_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_unicode_font_renderer;

View File

@ -31,6 +31,7 @@ static unsigned ra_tmp_height = 0;
static unsigned ra_set_core_hz = 0;
static unsigned orig_width = 0;
static unsigned orig_height = 0;
static int crt_center_adjust = 0;
static bool first_run = true;
@ -53,12 +54,25 @@ static void switch_crt_hz(void)
if (ra_core_hz == ra_tmp_core_hz)
return;
/* set hz float to an int for windows switching */
if (ra_core_hz < 53)
ra_set_core_hz = 50;
if (ra_core_hz >= 53 && ra_core_hz < 57)
ra_set_core_hz = 55;
if (ra_core_hz >= 57)
ra_set_core_hz = 60;
if (ra_core_hz < 100)
{
if (ra_core_hz < 53)
ra_set_core_hz = 50;
if (ra_core_hz >= 53 && ra_core_hz < 57)
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);
@ -78,7 +92,7 @@ static void switch_res_crt(unsigned width, unsigned height)
if (height > 100)
{
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();
}
}
@ -100,13 +114,13 @@ static void crt_screen_setup_aspect(unsigned width, unsigned height)
crt_aspect_ratio_switch(width, height);
}
if (height < 191 && height != 144)
if (height < 200 && height != 144)
{
crt_aspect_ratio_switch(width, height);
height = 200;
}
if (height > 191)
if (height > 200)
crt_aspect_ratio_switch(width, height);
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
* void video_driver_monitor_adjust_system_rates(void) */
ra_core_width = width;
ra_core_height = height;
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();

View File

@ -27,7 +27,7 @@
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);

View File

@ -96,7 +96,8 @@ enum font_driver_render_api
FONT_DRIVER_RENDER_CACA,
FONT_DRIVER_RENDER_SIXEL,
FONT_DRIVER_RENDER_GDI,
FONT_DRIVER_RENDER_VGA
FONT_DRIVER_RENDER_VGA,
FONT_DRIVER_RENDER_SWITCH
};
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,
int int_hz, float hz)
int int_hz, float hz, int center)
{
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;
}
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_decorations)(void *data, bool on);
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;
} video_display_server_t;
@ -47,7 +48,9 @@ bool video_display_server_set_window_decorations(bool on);
bool video_display_server_switch_resolution(
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_x11;

View File

@ -354,6 +354,9 @@ static const video_driver_t *video_drivers[] = {
};
static const gfx_ctx_driver_t *gfx_ctx_drivers[] = {
#if defined(HAVE_LIBNX) && defined(HAVE_OPENGL)
&switch_ctx,
#endif
#if defined(__CELLOS_LV2__)
&gfx_ctx_ps3,
#endif
@ -2643,7 +2646,7 @@ void video_driver_frame(const void *data, unsigned width,
width = 3840;
if (video_info.crt_switch_resolution_super == 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)
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();
custom_vp = &settings->video_viewport_custom;
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_center_adjust = settings->ints.crt_switch_center_adjust;
video_info->black_frame_insertion = settings->bools.video_black_frame_insertion;
video_info->hard_sync = settings->bools.video_hard_sync;
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,
video_info.shared_context && hw_render_ctx);
video_context_driver_set_data(ctx_data);
video_context_data = ctx_data;
return ctx;
}
@ -3202,11 +3206,18 @@ bool video_context_driver_get_video_output_size(gfx_ctx_size_t *size_data)
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)
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;
}
@ -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)
{
float refresh_holder = 0;
if (!current_video_context.get_refresh_rate || !refresh_rate)
return false;
if (!video_context_data)
return false;
if (refresh_rate)
*refresh_rate =
current_video_context.get_refresh_rate(video_context_data);
if (!video_driver_crt_switching_active)
if (refresh_rate)
*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;
}
@ -3317,22 +3340,9 @@ bool video_context_driver_show_mouse(bool *bool_data)
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;
}
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)
if (!current_video_context.get_flags)
return false;
if (deferred_video_context_driver_set_flags)
@ -3346,6 +3356,34 @@ bool video_context_driver_get_flags(gfx_ctx_flags_t *flags)
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)
{
if (!flags)

View File

@ -118,7 +118,8 @@ enum display_flags
GFX_CTX_FLAGS_CUSTOMIZABLE_SWAPCHAIN_IMAGES,
GFX_CTX_FLAGS_HARD_SYNC,
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
@ -354,7 +355,7 @@ typedef struct video_info
*/
unsigned height;
unsigned swap_interval;
int swap_interval;
#ifdef GEKKO
bool vfilter;
@ -408,7 +409,6 @@ typedef struct video_frame_info
bool black_frame_insertion;
bool hard_sync;
bool fps_show;
bool crt_switch_resolution;
bool statistics_show;
bool framecount_show;
bool scale_integer;
@ -429,11 +429,13 @@ typedef struct video_frame_info
int custom_vp_x;
int custom_vp_y;
int crt_switch_center_adjust;
unsigned hard_sync_frames;
unsigned aspect_ratio_idx;
unsigned max_swapchain_images;
unsigned monitor_index;
unsigned crt_switch_resolution;
unsigned crt_switch_resolution_super;
unsigned width;
unsigned height;
@ -522,7 +524,7 @@ typedef struct gfx_ctx_driver
unsigned major, unsigned minor);
/* Sets the swap interval. */
void (*swap_interval)(void *data, unsigned);
void (*swap_interval)(void *data, int);
/* Sets video mode. Creates a window, etc. */
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_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);
@ -1184,12 +1186,6 @@ bool video_context_driver_get_context_data(void *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_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_get_all_flags(gfx_ctx_flags_t *flags,
enum display_flags flag);
extern video_driver_t video_gl;
extern video_driver_t video_vulkan;
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_gdi;
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;

View File

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

View File

@ -33,7 +33,9 @@ MENU
UI
============================================================ */
#if defined(HAVE_QT)
#ifndef __APPLE__
#define HAVE_MAIN /* also requires defining in frontend.c */
#endif
#include "../ui/drivers/ui_qt.cpp"
#include "../ui/drivers/qt/ui_qt_window.cpp"
@ -53,6 +55,15 @@ UI
#include "../ui/drivers/qt/thumbnaildownload.cpp"
#include "../ui/drivers/qt/thumbnailpackdownload.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
/*============================================================

View File

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

View File

@ -2,8 +2,13 @@
#include "../../config.h"
#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 "../../tasks/tasks_internal.h"
@ -13,10 +18,20 @@
#include "string.h"
#ifdef HAVE_LIBNX
#ifndef MAX_PADS
#define MAX_PADS 8
#endif
#else
#ifndef MAX_PADS
#define MAX_PADS 10
#endif
#endif
static uint16_t pad_state[MAX_PADS];
static int16_t analog_state[MAX_PADS][2][2];
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)
{
#ifdef HAVE_LIBNX
unsigned i;
hidScanInput();
for (i = 0; i < MAX_PADS; i++)
switch_joypad_autodetect_add(i);
#else
hid_init();
switch_joypad_autodetect_add(0);
switch_joypad_autodetect_add(1);
#endif
return true;
}
@ -126,27 +147,80 @@ static bool switch_joypad_query_pad(unsigned pad)
static void switch_joypad_destroy(void)
{
#ifndef HAVE_LIBNX
hid_finalize();
#endif
}
#ifdef HAVE_LIBNX
int lastMode = 0; // 0 = handheld, 1 = whatever
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 *cont = &controllers[0];
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];
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;
lsy = ent.left_stick_y;
rsx = ent.right_stick_x;
rsy = ent.right_stick_y;
if(ent8.left_stick_x != 0 || ent8.left_stick_y != 0) { // handheld overrides player 1
lsx = ent.left_stick_x;
lsy = ent.left_stick_y;
rsx = ent.right_stick_x;
rsy = ent.right_stick_y;
if (ent8.left_stick_x != 0 || ent8.left_stick_y != 0)
{
/* handheld overrides player 1 */
lsx = ent8.left_stick_x;
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;
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_Y] = -rsy;
}
#endif
input_device_driver_t switch_joypad = {
switch_joypad_init,

View File

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

View File

@ -28,7 +28,7 @@ RETRO_BEGIN_DECLS
#define MAX_INPUT_DEVICES 16
#define RARCH_MAX_KEYS 136
#define RARCH_MAX_KEYS 137
#define RARCH_FIRST_CUSTOM_BIND 16
#define RARCH_FIRST_LIGHTGUN_BIND RARCH_ANALOG_BIND_LIST_END
@ -85,7 +85,7 @@ enum
RARCH_STATE_SLOT_PLUS,
RARCH_STATE_SLOT_MINUS,
RARCH_REWIND,
RARCH_MOVIE_RECORD_TOGGLE,
RARCH_BSV_RECORD_TOGGLE,
RARCH_PAUSE_TOGGLE,
RARCH_FRAMEADVANCE,
RARCH_RESET,
@ -111,6 +111,9 @@ enum
RARCH_MENU_TOGGLE,
RARCH_RECORDING_TOGGLE,
RARCH_STREAMING_TOGGLE,
RARCH_BIND_LIST_END,
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_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(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, 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),
@ -351,6 +351,8 @@ const struct input_bind_map input_config_bind_map[RARCH_BIND_LIST_END_NULL] = {
#ifdef HAVE_MENU
DECLARE_META_BIND(1, menu_toggle, RARCH_MENU_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_MENU_TOGGLE),
#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 },
{ "undo", RETROK_UNDO },
{ "clear", RETROK_CLEAR },
{ "oem102", RETROK_OEM_102 },
{ "nul", RETROK_UNKNOWN },
{ NULL, RETROK_UNKNOWN },
@ -328,7 +329,7 @@ const struct rarch_key_map rarch_key_map_sdl[] = {
{ SDLK_9, RETROK_9 },
{ SDLK_COLON, RETROK_COLON },
{ SDLK_SEMICOLON, RETROK_SEMICOLON },
{ SDLK_LESS, RETROK_LESS },
{ SDLK_LESS, RETROK_OEM_102 },
{ SDLK_EQUALS, RETROK_EQUALS },
{ SDLK_GREATER, RETROK_GREATER },
{ SDLK_QUESTION, RETROK_QUESTION },
@ -574,6 +575,7 @@ const struct rarch_key_map rarch_key_map_dinput[] = {
{ DIK_SCROLL, RETROK_SCROLLOCK },
{ DIK_CAPSLOCK, RETROK_CAPSLOCK },
{ DIK_NUMLOCK, RETROK_NUMLOCK },
{ DIK_OEM_102, RETROK_OEM_102 },
{ 0, RETROK_UNKNOWN },
};
#endif
@ -831,6 +833,7 @@ const struct rarch_key_map rarch_key_map_x11[] = {
/*{ ?, RETROK_POWER },*/
{ XK_EuroSign, RETROK_EURO },
{ XK_Undo, RETROK_UNDO },
/*{ ?, RETROK_OEM_102 },*/
/* FIXME(shizeeg): RetroArch can't handle these buttons atm.
* Do we really need RETROK_KP_INSERT, RETROK_KP_END,
* RETROK_KP_DOWN, RETROK_KP_PAGEDOWN ???
@ -986,6 +989,7 @@ const struct rarch_key_map rarch_key_map_linux[] = {
{ KEY_EURO, RETROK_EURO },
#endif
{ KEY_UNDO, RETROK_UNDO },
/*{ ?, RETROK_OEM_102 },*/
{ 0, RETROK_UNKNOWN },
};
#endif
@ -1320,6 +1324,7 @@ const struct rarch_key_map rarch_key_map_apple_hid[] = {
/* { ?, RETROK_POWER }, */
/* { ?, RETROK_EURO }, */
/* { ?, RETROK_UNDO }, */
{ KEY_NonUSBackslash, RETROK_OEM_102 },
{ 0, RETROK_UNKNOWN }
};
#endif

View File

@ -117,7 +117,7 @@ void input_mapper_poll(input_mapper_t *handle)
input_get_state_for_port(settings, i, &current_input);
for (j = 0; j < RARCH_CUSTOM_BIND_LIST_END; j++)
{
unsigned remap_button =
unsigned remap_button =
settings->uints.input_keymapper_ids[i][j];
bool remap_valid = remap_button != RETROK_UNKNOWN;
@ -137,7 +137,7 @@ void input_mapper_poll(input_mapper_t *handle)
0, 0, RETRO_DEVICE_KEYBOARD);
key_event[j] = true;
}
/* key_event tracks if a key is pressed for ANY PLAYER, so we must check
/* key_event tracks if a key is pressed for ANY PLAYER, so we must check
if the key was used by any player before releasing */
else if (!key_event[j])
{
@ -152,10 +152,10 @@ void input_mapper_poll(input_mapper_t *handle)
/* gamepad remapping */
case RETRO_DEVICE_JOYPAD:
case RETRO_DEVICE_ANALOG:
/* this loop iterates on all users and all buttons,
* and checks if a pressed button is assigned to any
* other button than the default one, then it sets
* the bit on the mapper input bitmap, later on the
/* this loop iterates on all users and all buttons,
* and checks if a pressed button is assigned to any
* other button than the default one, then it sets
* the bit on the mapper input bitmap, later on the
* original input is cleared in input_state */
BIT256_CLEAR_ALL(handle->buttons[i]);
BIT256_CLEAR_ALL_PTR(&current_input);
@ -194,7 +194,7 @@ void input_mapper_poll(input_mapper_t *handle)
invert = -1;
handle->analog_value[i][
remap_button - RARCH_FIRST_CUSTOM_BIND] =
remap_button - RARCH_FIRST_CUSTOM_BIND] =
32767 * invert;
}
}
@ -204,7 +204,7 @@ void input_mapper_poll(input_mapper_t *handle)
{
unsigned k = j + RARCH_FIRST_CUSTOM_BIND;
int16_t current_axis_value = current_input.analogs[j];
unsigned remap_axis =
unsigned remap_axis =
settings->uints.input_remap_ids[i][k];
if (
@ -213,7 +213,7 @@ void input_mapper_poll(input_mapper_t *handle)
(remap_axis != RARCH_UNMAPPED)
))
{
if (remap_axis < RARCH_FIRST_CUSTOM_BIND &&
if (remap_axis < RARCH_FIRST_CUSTOM_BIND &&
abs(current_axis_value) > *input_driver_get_float(INPUT_ACTION_AXIS_THRESHOLD) * 32767)
{
BIT256_SET(handle->buttons[i], remap_axis);
@ -221,18 +221,22 @@ void input_mapper_poll(input_mapper_t *handle)
else
{
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)
)
invert = -1;
handle->analog_value[i][
remap_axis - RARCH_FIRST_CUSTOM_BIND] =
current_axis_value * invert;
if (remap_axis_bind < sizeof(handle->analog_value[i]))
{
handle->analog_value[i][
remap_axis_bind] =
current_axis_value * invert;
}
#if 0
RARCH_LOG("axis %d(%d) remapped to axis %d val %d\n",
j, k,
j, k,
remap_axis - RARCH_FIRST_CUSTOM_BIND,
current_axis_value);
#endif

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_w, ol->active->mod_h);
for (i = 0; i < ol->active->size; i++)
{
struct overlay_desc *desc = &ol->active->descs[i];
if (ol->iface->vertex_geom)
for (i = 0; i < ol->active->size; i++)
{
struct overlay_desc *desc = &ol->active->descs[i];
if (!desc->image.pixels)
continue;
if (!desc->image.pixels)
continue;
if (ol->iface->vertex_geom)
ol->iface->vertex_geom(ol->iface_data, desc->image_index,
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