mirror of
https://github.com/libretro/RetroArch.git
synced 2025-02-17 07:58:55 +00:00
Merge branch 'master' into integrate_rcheevos
This commit is contained in:
commit
d67aa83f48
12
.gitignore
vendored
12
.gitignore
vendored
@ -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
|
||||
|
22
.travis.yml
22
.travis.yml
@ -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"
|
||||
|
7
.vscode/settings.json
vendored
7
.vscode/settings.json
vendored
@ -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,
|
||||
}
|
28
CHANGES.md
28
CHANGES.md
@ -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.
|
||||
|
24
Makefile.apple
Normal file
24
Makefile.apple
Normal 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 '$*=$($*)'
|
@ -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)
|
||||
|
@ -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
246
Makefile.libnx
Normal 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
|
||||
#---------------------------------------------------------------------------------------
|
@ -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)
|
||||
|
||||
|
@ -125,6 +125,7 @@ static const audio_driver_t *audio_drivers[] = {
|
||||
#endif
|
||||
#ifdef SWITCH
|
||||
&audio_switch,
|
||||
&audio_switch_thread,
|
||||
#endif
|
||||
&audio_null,
|
||||
NULL,
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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,
|
||||
|
93
audio/drivers/switch_audio_compat.h
Normal file
93
audio/drivers/switch_audio_compat.h
Normal 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
|
448
audio/drivers/switch_thread_audio.c
Normal file
448
audio/drivers/switch_thread_audio.c
Normal 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 */
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
54
command.c
54
command.c
@ -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)
|
||||
|
@ -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,
|
||||
|
47
config.def.h
47
config.def.h
@ -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)
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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(
|
||||
|
1
deps/libiosuhax/iosuhax_devoptab.c
vendored
1
deps/libiosuhax/iosuhax_devoptab.c
vendored
@ -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>
|
||||
|
@ -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();
|
||||
}
|
@ -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 */
|
||||
|
38
dynamic.c
38
dynamic.c
@ -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);
|
||||
|
@ -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);
|
||||
|
803
frontend/drivers/platform_switch.c
Normal file
803
frontend/drivers/platform_switch.c
Normal 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",
|
||||
};
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
79
gfx/common/switch_common.h
Normal file
79
gfx/common/switch_common.h
Normal 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
|
@ -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(®ion, 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, ®ion);
|
||||
|
||||
@ -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(®ion, 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, ®ion);
|
||||
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
};
|
||||
|
||||
|
@ -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"
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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 ?
|
||||
|
@ -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,
|
||||
|
@ -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
775
gfx/drivers/switch_nx_gfx.c
Normal 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 */
|
@ -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);
|
||||
|
||||
|
@ -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(®ion, 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, ®ion);
|
||||
|
||||
/* 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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
287
gfx/drivers_context/switch_ctx.c
Normal file
287
gfx/drivers_context/switch_ctx.c
Normal 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};
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
||||
|
350
gfx/drivers_font/switch_font.c
Normal file
350
gfx/drivers_font/switch_font.c
Normal 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,
|
||||
};
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
/*============================================================
|
||||
|
@ -9,6 +9,10 @@
|
||||
#include "../../config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LIBNX
|
||||
#include <switch.h>
|
||||
#endif
|
||||
|
||||
#include "../input_driver.h"
|
||||
|
||||
#define MAX_PADS 10
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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),
|
||||
};
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -117,7 +117,7 @@ void input_mapper_poll(input_mapper_t *handle)
|
||||
input_get_state_for_port(settings, i, ¤t_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(¤t_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
|
||||
|
@ -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
Loading…
x
Reference in New Issue
Block a user