Compare commits

...

157 Commits

Author SHA1 Message Date
Kripesh Adhikari
bf9df10259
audio/hwopus-decoder: fix glitchy playback (#110)
* audio/hwopus-decoder: fix offset for pointer to wavebuf start
* audio/hwopus-decoder: change to 4 wavebufs and reduce size of wavebufs
2024-10-26 15:36:59 +01:00
Dave Murphy
5e36dc2902
time: display local time 2024-10-18 19:30:12 +01:00
Dave Murphy
399aa26c6e
templates/sysmodule: add force_debug_prod. Closes #109 2024-10-18 18:46:00 +01:00
Vicki Pfau
b41c4957e7
vibration: Improve vibration example usability (#107)
This update moves the frequency controls to D-Pad and lets you turn the values
down, as well as displaying the current values on screen.
2024-10-16 19:57:56 +01:00
Vicki Pfau
d1b2175d58
power: Update charger enum (#108) 2024-10-16 19:57:38 +01:00
Dave Murphy
4f6e700642
sdl2-demo: use acceleration 2024-01-20 02:21:36 +00:00
Narr the Reg
455ad6732c nfc: Update example 2023-07-04 23:17:34 +02:00
yellows8
fbbe90507d
app-playstats: Updated for latest libnx, etc. 2023-03-08 16:36:36 -05:00
yellows8
454610b266
Updated hdls example for passing workmem. 2023-02-23 10:09:09 -05:00
TurtleP
713404797b
Delete various copy constructors and assignment operators (#102)
Prevents common C++ programming mistakes
2023-01-04 21:30:48 +01:00
yellows8
73960d720c
app-playstats: Updated for 15.0.0. 2022-10-16 12:56:12 -04:00
scturtle
0947425fc5 graphics/deko3d: fix and simplify rbtree code 2022-09-14 19:16:35 +02:00
Jeremy S. Postelnek
b55cb17676
account: add code to satisfy multiple accounts (#97)
* add code to satisfy multiple accounts
2022-04-09 16:17:54 -04:00
yellows8
950e99a1ab
Updated hdls example for 13.0.0. 2021-09-17 12:42:06 -04:00
yellows8
f03b9864ce
Improved IMAGE_DISPLAY handling in simplegfx. 2021-09-13 13:25:26 -04:00
Ben Kallus
5980937bdf ChargerType -> PsmChargerType 2021-07-08 23:01:23 +02:00
yellows8
e423a0ab0e
audio/playtone: KEY -> HidNpadButton, which were missed previously. 2021-02-15 12:29:32 -05:00
yellows8
78b1ed515e
Updated for libnx applet changes. 2020-12-15 19:17:30 -05:00
yellows8
ce7c9a99ea
network/ldn: Updated button text. 2020-12-10 23:57:35 -05:00
yellows8
84be02950a
Added network/lp2p. 2020-12-10 23:52:53 -05:00
yellows8
a5f53afed0
hid/hdls: Use HiddbgNpadButton. 2020-12-08 15:39:02 -05:00
yellows8
b924d358ec
hid/read-controls: Updated comment. 2020-12-08 14:59:11 -05:00
yellows8
62885bbbff
hid/hdls: Updated for hid-refactor. 2020-12-08 13:54:25 -05:00
yellows8
ac702280bc
hid/read-controls: Updated for hid-refactor. 2020-12-08 13:52:19 -05:00
yellows8
40ec713452
hid/abstracted-pad: Updated for hid-refactor. 2020-12-08 12:56:11 -05:00
yellows8
07863f0585
hid/touch-screen: Updated for hid-refactor. 2020-12-08 10:36:50 -05:00
yellows8
a6ea828801
hid/irsensor: Updated for hid-refactor. 2020-12-08 01:03:19 -05:00
yellows8
00a0bb3899
hid/vibration: Minor adjustments. 2020-12-08 00:33:18 -05:00
yellows8
29484f5c06
hid/sevensixaxis: Updated for hid-refactor. 2020-12-08 00:23:52 -05:00
yellows8
5a42b4766b
hid/ring-con: Updated for hid-refactor. 2020-12-08 00:00:16 -05:00
yellows8
2b0d5062a5
colors/notification-led: Updated for hid-refactor. 2020-12-07 23:44:31 -05:00
fincs
1e59bd0699
Update all non-HID examples for hid-refactor 2020-12-02 14:39:00 +01:00
fincs
4272e286cc
hid/vibration: update for hid-refactor 2020-12-01 21:51:39 +01:00
fincs
8487e07653
hid/sixaxis: update for hid-refactor 2020-12-01 21:46:58 +01:00
fincs
11fa984774
templates/application: Update for hid-refactor API 2020-12-01 21:36:02 +01:00
fincs
f252e65ebd
Update & clean up the sysmodule template 2020-10-31 19:25:14 +01:00
fincs
847bb129e9
Use diagAbortWithResult instead of fatalThrow 2020-10-31 19:13:24 +01:00
Dave Murphy
fc98393d50 Add targets per Makefile, allowing make -j to work 2020-10-03 16:00:22 +01:00
Vicki Pfau
9db0cfd567 Add power example 2020-09-28 13:08:31 +02:00
Dave Murphy
c21700136e
remove stray printf 2020-07-07 02:54:53 +01:00
Dave Murphy
179e9a847b sdl2-audio: include path is set to SDL2 by pkg-config. Closes #79 2020-06-26 09:58:25 +01:00
Dave Murphy
a6de2da842 sdl2-audio: rely on pkg-config for includes & libs 2020-06-26 09:58:25 +01:00
Nichole Mattera
cb01bf3c37
Fixed SDL2 demo to not render text on every frame. (#81) 2020-06-26 09:41:59 +01:00
AdaLovegirls
ea27fe123d
Added sdl2-ttf example to sdl2-demo (#80) 2020-06-25 12:31:06 +01:00
yellows8
73b50688ab
Added -lm to the swkbd example Makefile. 2020-06-14 00:08:11 -04:00
yellows8
4437fb814f
Added a comment for the swkbd example. 2020-06-11 22:56:04 -04:00
yellows8
1392e6cad0
Added hid/sevensixaxis example. 2020-06-01 12:16:15 -04:00
fincs
7cce42673e
sysmodule template: set __nx_fs_num_sessions to 1 2020-05-28 18:36:37 +02:00
Dave Murphy
a0ce16d0f7 add sdl2 demo using romfs for data 2020-05-26 23:49:58 +01:00
yellows8
25cdf12ab0
ldn: Use fixed-size buffer for sendto(), and make sure the data from recvfrom() is NUL-terminated. 2020-05-24 15:50:22 -04:00
yellows8
52bdce1eb0
ldn: Various adjustments/fixes, and added a printf regarding D-Pad buttons. 2020-05-24 12:54:33 -04:00
yellows8
1f30cbcd17
Added ldn example. 2020-05-24 12:43:05 -04:00
yellows8
edb1506560 Added curl example. 2020-05-11 22:04:19 +02:00
fincs
1a4cb0662a
deko example framework: Include <unistd.h> for close() 2020-05-06 00:57:24 +02:00
yellows8
0175f8020b
Updated the app-playstats example for latest libnx, due to [10.0.0+], and a minor other adjustment. 2020-04-17 14:20:49 -04:00
fincs
d8e5cd4d2b
Use nxlinkStdioForDebug in deko_examples in order to avoid collisions with the console 2020-04-17 16:49:38 +02:00
HookedBehemoth
794a26392b update for libnx pl init change 2020-04-15 18:18:33 +02:00
yellows8
c68fc5e826
Updated the nfc example for latest libnx. 2020-04-07 12:45:06 -04:00
fincs
bbd8727804
Add deko3d GPU-rendered console example 2020-04-04 13:41:18 +02:00
fincs
36dff0db63
Import deko3d examples 2020-04-04 13:41:18 +02:00
fincs
554d75d499
Add basic deko3d C example (rendering a triangle) 2020-04-04 13:41:05 +02:00
yellows8
a34aba3b72
Fixed issue with the fs/save example where the FsSaveDataInfoReader was not closed when a save was found successfully. 2020-04-03 00:23:04 -04:00
yellows8
948ce48e58
Updated the irsensor example for latest libnx, and various improvements. 2020-04-02 18:50:27 -04:00
yellows8
2e7d0c198e
Minor adjustment for the ring-con example. 2020-03-24 22:23:58 -04:00
yellows8
2ee7aff9e4
Added hid/ring-con example. (#76)
* Added hid/ring-con example.
2020-03-24 22:19:30 -04:00
fincs
f82d955dfe
U64_MAX -> UINT64_MAX 2020-03-16 16:22:27 +01:00
Bruno Macabeus
d279271037 doc: update templates/sysmodule readme 2020-01-17 11:44:52 -05:00
yellows8
9da8e4a9f4
Updated vibration example for latest libnx / various adjustments. 2020-01-12 12:27:17 -05:00
yellows8
f133405219
Updated save example for latest libnx. 2019-12-10 18:48:34 -05:00
yellows8
25d9880c3b
Updated examples to pass ServiceType to the relevant *Initialize() funcs, as required by latest libnx. 2019-11-18 10:56:41 -05:00
yellows8
b9df83b93d
Added alarm-notifications example. 2019-11-17 18:26:53 -05:00
yellows8
afb3d2132f
Updated fs/save for latest libnx. 2019-11-16 16:12:43 -05:00
yellows8
147a5e2fc4
Updated fs/save for latest libnx. 2019-11-13 14:49:20 -05:00
yellows8
86a1af14fc
usb/usbhs: Updated for using an actual mass-storage cmd and reading the reply, instead of just reading from an endpoint (which would hang). Various improvements + comments improvements. 2019-10-30 17:20:23 -04:00
yellows8
d952469c56
Updated fs/save example for latest libnx + minor changes. 2019-10-30 12:05:06 -04:00
yellows8
89bfe5608d
Updated for latest libnx, and use official naming instead of 'title'. 2019-10-30 11:31:37 -04:00
yellows8
c3c4c4f07c
Updated for latest libnx (fatalSimple -> fatalThrow). 2019-10-25 18:51:09 -04:00
fincs
c1524e86d6
Update for latest libnx changes 2019-10-21 21:46:28 +02:00
yellows8
9b44df4132
Updated notification-led example for using hidsysSetNotificationLedPatternWithTimeout. 2019-10-19 19:53:56 -04:00
yellows8
188b41ff9e
Updated threadCreate() usage in the misc examples for latest libnx. 2019-10-19 19:02:43 -04:00
yellows8
68a9002b4a
Updated app-playstats for latest libnx, and updated for using more cmds etc. 2019-10-18 16:56:58 -04:00
yellows8
868da01cd5
Updated nfc example for latest libnx. 2019-10-14 17:22:30 -04:00
yellows8
a0b4ee1229
Updated nfc example for latest libnx. 2019-10-13 19:40:03 -04:00
yellows8
5363a04d9b
Updated nfc example for latest libnx, and minor other changes. 2019-10-13 13:42:18 -04:00
yellows8
da994a8a69
Updated hid/notification-led for latest libnx. 2019-10-11 15:37:32 -04:00
yellows8
707d65171e
Updated fs/save for latest libnx. 2019-10-08 18:16:35 -04:00
yellows8
3fc12d7765
Updated the account example for latest libnx. 2019-10-08 15:45:14 -04:00
yellows8
b8cf089964
Updated the get_system_language example for using the SetLanguage enum from latest libnx. 2019-10-06 18:56:37 -04:00
yellows8
25a7b2adaa
Minor print fix for the vrmode example. 2019-10-01 21:19:52 -04:00
yellows8
9d11f70f3c
Updated the hid/abstracted-pad and hid/hdls examples for latest libnx, this includes hdls support for [9.0.0+]. Adjusted buttons handling in hdls example, and print NpadInterfaceType/PowerInfo. 2019-09-14 11:17:58 -04:00
yellows8
c24895c52f
Added deprecated warning to the hid/abstracted-pad example. 2019-09-11 16:13:54 -04:00
yellows8
d1d712702a
Added graphics/simplegfx_moviemaker. 2019-08-28 18:46:58 -04:00
yellows8
927256ce99
Use accountGetPreselectedUser for getting the userID in the account and fs/save examples. 2019-07-29 19:13:50 -04:00
yellows8
f89c1572bc
Added applet/screenshot-overlay example. 2019-07-14 17:45:08 -04:00
yellows8
c53781e6e4
Removed a comment from the app-playstats example. 2019-07-12 19:45:15 -04:00
yellows8
b2e214321b
Added pdmqry usage to the applet/app-playstats example. 2019-07-09 21:44:55 -04:00
yellows8
d459edb90d
Updated fs/save for latest libnx. 2019-07-09 12:31:26 -04:00
yellows8
fee28d299f
Updated irsensor example. 2019-07-01 22:16:58 -04:00
yellows8
98b90baa84
Updated app-playstats example. 2019-06-30 10:47:07 -04:00
yellows8
28bae570fe
Added applet/app-playstats example. 2019-06-29 20:50:13 -04:00
yellows8
a6b6b7be06
Added hid/abstracted-pad example. 2019-06-28 21:56:54 -04:00
yellows8
5d34eb6476
Updated hdls example, for controller type handling. 2019-06-24 21:56:21 -04:00
jonyluke
b957923561 Sdl2 audio example (#53) 2019-06-17 17:06:25 +02:00
switchjsdev
9b5f5d2ada Update .gitignore
templates/sysmodule generate nsp and npdm files
2019-06-16 21:29:57 +02:00
fincs
4e888f77ed Correct key names in hid/read-controls 2019-06-16 21:25:29 +02:00
fincs
0c0e739814 Avoid legal nitpicking with explicit licensing terms (fix #57) 2019-06-16 21:16:44 +02:00
yellows8
7d45e89416
In the hdls example, set the batteryCharge and added a comment. 2019-06-08 21:56:01 -04:00
yellows8
b074eb7a4f
Added hid/hdls example. 2019-06-06 20:52:48 -04:00
yellows8
d5016747de
Updated swkbd example for latest libnx, and use swkbdInlineSetDecidedCancelCallback. 2019-06-04 19:18:56 -04:00
CTCaer
6027f34bfd [hid] Notification led example update and add effects (#54)
* [hid] Notification led example update and add effects
2019-04-27 18:11:25 -04:00
yellows8
1d7a5f04df
Updated notification-led example for latest libnx. 2019-04-26 21:51:19 -04:00
yellows8
452002158c
Added notification-led example. 2019-04-26 12:41:32 -04:00
yellows8
8e78780d9c
Added vrmode example. 2019-04-22 15:50:57 -04:00
yellows8
a355ddbc00
Added applet/light-sensor example. 2019-04-20 16:14:15 -04:00
yellows8
9b7b9e7268
Updated swkbd example comments. 2019-04-09 18:26:58 -04:00
fincs
d6db1e45c9 Update ARCH to -march=armv8-a+crc+crypto 2019-04-05 13:20:32 +02:00
yellows8
3267713676
Added pctlauth example. 2019-04-02 15:56:28 -04:00
fincs
373eb9c1f2 Remove newlib long double math workaround 2019-03-29 19:25:06 +01:00
yellows8
641a096d47
Added error-applet example. 2019-03-28 20:39:35 -04:00
fincs
f07f848dfd Consolidate all makefiles with changes from the template 2019-03-26 13:52:19 +01:00
fincs
e42e391bc1 Consolidate application/sysmodule template makefiles, see details:
- Removed old EXEFS_SRC handling
- Removed old nso/pfs0 handling
- Added new ExeFS PFS0 (.nsp) handling, with autogenerated npdm from json
  (disabled if no such json file exists)
2019-03-26 13:48:33 +01:00
yellows8
98172389b2 Updated comments for web example. 2019-03-22 20:52:10 -04:00
yellows8
64ca65a3cd Fixed usbhs example for 7.0.0+. 2019-03-21 11:22:44 -04:00
plutoo
a1dfd11769 Add barrier example (#50)
* Add barrier example
2019-03-10 12:22:36 -04:00
yellows8
b5d5d15985 Updated web example for latest libnx, it now uses Web instead of WebWifi. 2019-03-07 11:16:03 -05:00
yellows8
3a5c170c48 Moved web_wifi/ to web/. 2019-03-06 22:01:39 -05:00
averne
e654fde8f9 Add nfc example. (#45)
* Add nfc example.
2019-02-17 20:06:20 -05:00
yellows8
e0bfaf2329 Added swkbd-inline to the swkbd example. Removed a comment. 2019-02-17 16:53:48 -05:00
fincs
a591ef394c Fix library template 2019-02-16 17:01:33 +01:00
Samuel P
f3e9dce94e add web_wifi example (#44)
* add web_wifi example
2019-02-05 13:10:25 -05:00
Jakob Dietrich
a6f0e59e4c Fix pool partition in sysmodule sample
Games won't launch with the npdm generated with `pool_partition` being 0.
2019-01-18 17:46:43 -05:00
Jakob Dietrich
bc7b810f80 Fix boot2.flag filename 2019-01-13 14:05:40 -05:00
Cpasjuste
bf8ac646b0 add sdl2 graphics example (#36) 2019-01-07 19:12:39 +00:00
fincs
0ea76cf3ed Replace usage of old gfx API with new NWindow/Framebuffer API. Clean up some of the examples that used mixed console + custom sw rendering. 2018-12-25 16:58:57 +01:00
fincs
6590041f87 OpenGL examples: use NWindow instead of old gfx API 2018-12-25 16:58:57 +01:00
yellows8
2a6e86f1ea Added applet/libapplets/software-keyboard/. 2018-12-24 20:48:07 -05:00
fincs
f0d7771a3a Update and fix usermode event/timer examples 2018-12-15 18:46:13 +01:00
plutooo
86f28347cf Add user sync primitive examples 2018-12-13 00:10:38 +01:00
fincs
f97ccaa9e0 OpenGL examples: make eglChooseConfig attribute list forwards-compatible 2018-12-07 00:01:25 +01:00
yellows8
4c836fd796 Added usbhs example. 2018-11-30 20:21:19 -05:00
yellows8
70e1e46a82
Updated sysmodule README for latest atmo. 2018-11-30 16:32:52 -05:00
yellows8
ef37c94ddd
Updated usb README regarding .h. 2018-11-28 17:36:31 -05:00
yellows8
7df4b57389 Added sysmodule template. 2018-11-27 19:37:40 -05:00
yellows8
888f86ab36 Added exception-handler example. 2018-11-14 22:01:30 -05:00
yellows8
87a69934f6 Added hwopus example. 2018-11-06 18:31:23 -05:00
yellows8
b8cd670b53 Added applet/lockexit example. 2018-10-25 13:00:11 -04:00
yellows8
159631cb56 Removed an old line in applet/recording. 2018-10-22 13:46:13 -04:00
yellows8
377b8ab820 Added applet/recording example. 2018-10-22 13:43:55 -04:00
yellows8
f614d498b3 Added hid/colors example. 2018-10-16 22:48:15 -04:00
yellows8
16d98071b2 Removed usbds example and added usb/README.md. 2018-10-12 12:03:37 -04:00
Vicki Pfau
79e5635e14 Add SixAxis example 2018-10-12 11:40:12 -04:00
fincs
696b163d0a Add opengl/gpu_console example 2018-10-06 17:06:22 +02:00
fincs
4894fcf472 Update console examples to use the new console API. Improved documentation of application template. 2018-10-06 16:50:57 +02:00
yellows8
4c0e6cf201 Updated localtime comment in the time example. 2018-10-05 19:50:58 -04:00
fincs
5c2187ca44 OpenGL examples: use eglMakeCurrent to unbind the context/surfaces in deinitEgl 2018-09-26 20:12:25 +02:00
yellows8
0dd20a470d
Removed outdated text from README. 2018-09-23 16:52:13 -04:00
Dave Murphy
9334a40b31 use default gnu++14 instead of explicit gnu++11 2018-09-19 02:10:58 +02:00
197 changed files with 21536 additions and 1127 deletions

4
.gitignore vendored
View File

@ -5,4 +5,8 @@ build
*.pfs0
*.nacp
*.nro
*.nsp
*.npdm
*.bz2
*.dksh
*~

View File

@ -1,13 +1,19 @@
MAKEFILES := $(shell find . -mindepth 2 -name Makefile)
MAKEFILES := $(subst ./,,$(shell find . -mindepth 2 -name Makefile))
TARGETS := $(dir $(MAKEFILES))
DATESTRING := $(shell date +%Y)$(shell date +%m)$(shell date +%d)
all:
@for i in $(MAKEFILES); do $(MAKE) -C `dirname $$i` || exit 1; done;
all: $(TARGETS)
.PHONY: $(TARGETS)
$(TARGETS):
@$(MAKE) -C $@
clean:
@rm -f *.bz2
@for i in $(MAKEFILES); do $(MAKE) -C `dirname $$i` clean || exit 1; done;
@for i in $(TARGETS); do $(MAKE) -C $$i clean || exit 1; done;
dist: clean
@tar -cvjf switch-examples-$(DATESTRING).tar.bz2 *

View File

@ -2,3 +2,4 @@
Examples for Switch using devkitA64 and libnx.
<p align="center"><a href="http://creativecommons.org/publicdomain/mark/1.0/"><img src="http://i.creativecommons.org/p/mark/1.0/88x31.png" alt="Public Domain Mark"></a></p>

View File

@ -15,7 +15,6 @@ include $(DEVKITPRO)/libnx/switch_rules
# 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.
@ -29,26 +28,33 @@ include $(DEVKITPRO)/libnx/switch_rules
# - <Project name>.jpg
# - icon.jpg
# - <libnx folder>/default_icon.jpg
#
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
# If not set, it attempts to use one of the following (in this order):
# - <Project name>.json
# - config.json
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
# NACP building is skipped as well.
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := source
DATA := data
INCLUDES := include
EXEFS_SRC := exefs_src
#ROMFS := romfs
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -g -Wall -O2 -ffunction-sections \
$(ARCH) $(DEFINES)
CFLAGS += $(INCLUDE) -D__SWITCH__
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
@ -107,7 +113,18 @@ export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC)
ifeq ($(strip $(CONFIG_JSON)),)
jsons := $(wildcard *.json)
ifneq (,$(findstring $(TARGET).json,$(jsons)))
export APP_JSON := $(TOPDIR)/$(TARGET).json
else
ifneq (,$(findstring config.json,$(jsons)))
export APP_JSON := $(TOPDIR)/config.json
endif
endif
else
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
endif
ifeq ($(strip $(ICON)),)
icons := $(wildcard *.jpg)
@ -150,7 +167,11 @@ $(BUILD):
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) $(TARGET).pfs0 $(TARGET).nso $(TARGET).nro $(TARGET).nacp $(TARGET).elf
ifeq ($(strip $(APP_JSON)),)
@rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
else
@rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
endif
#---------------------------------------------------------------------------------
@ -162,11 +183,9 @@ DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
all : $(OUTPUT).pfs0 $(OUTPUT).nro
ifeq ($(strip $(APP_JSON)),)
$(OUTPUT).pfs0 : $(OUTPUT).nso
$(OUTPUT).nso : $(OUTPUT).elf
all : $(OUTPUT).nro
ifeq ($(strip $(NO_NACP)),)
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp
@ -174,6 +193,16 @@ else
$(OUTPUT).nro : $(OUTPUT).elf
endif
else
all : $(OUTPUT).nsp
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
$(OUTPUT).nso : $(OUTPUT).elf
endif
$(OUTPUT).elf : $(OFILES)
$(OFILES_SRC) : $(HFILES_BIN)

View File

@ -3,44 +3,55 @@
#include <switch.h>
//This example shows how to get info for the current user account. See libnx acc.h.
//This example shows how to get info for the preselected user account. See libnx acc.h.
int main(int argc, char **argv)
{
Result rc=0;
u128 userID=0;
bool account_selected=0;
AccountUid userID={0};
AccountProfile profile;
AccountUserData userdata;
AccountProfileBase profilebase;
char username[0x21];
char nickname[0x21];
gfxInitDefault();
consoleInit(NULL);
// Configure our supported input layout: a single player with standard controller styles
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
// Initialize the default gamepad (which reads handheld mode inputs as well as the first connected controller)
PadState pad;
padInitializeDefault(&pad);
memset(&userdata, 0, sizeof(userdata));
memset(&profilebase, 0, sizeof(profilebase));
rc = accountInitialize();
rc = accountInitialize(AccountServiceType_Application);
if (R_FAILED(rc)) {
printf("accountInitialize() failed: 0x%x\n", rc);
}
if (R_SUCCEEDED(rc)) {
rc = accountGetActiveUser(&userID, &account_selected);
rc = accountGetPreselectedUser(&userID);
if (R_FAILED(rc)) {
printf("accountGetActiveUser() failed: 0x%x\n", rc);
}
else if(!account_selected) {
printf("No user is currently selected.\n");
rc = -1;
printf("accountGetPreselectedUser() failed: 0x%x, using pselShowUserSelector..\n", rc);
/* Create player selection UI settings */
PselUserSelectionSettings settings;
memset(&settings, 0, sizeof(settings));
rc = pselShowUserSelector(&userID, &settings);
if (R_FAILED(rc)) {
printf("pselShowUserSelector() failed: 0x%x\n", rc);
}
}
if (R_SUCCEEDED(rc)) {
printf("Current userID: 0x%lx 0x%lx\n", (u64)(userID>>64), (u64)userID);
printf("Current userID: 0x%lx 0x%lx\n", userID.uid[1], userID.uid[0]);
rc = accountGetProfile(&profile, userID);
@ -57,10 +68,10 @@ int main(int argc, char **argv)
}
if (R_SUCCEEDED(rc)) {
memset(username, 0, sizeof(username));
strncpy(username, profilebase.username, sizeof(username)-1);//Even though profilebase.username usually has a NUL-terminator, don't assume it does for safety.
memset(nickname, 0, sizeof(nickname));
strncpy(nickname, profilebase.nickname, sizeof(nickname)-1);//Copy the nickname elsewhere to make sure it's NUL-terminated.
printf("Username: %s\n", username);//Note that the print-console doesn't support UTF-8. The username is UTF-8, so this will only display properly if there isn't any non-ASCII characters. To display it properly, a print method which supports UTF-8 should be used instead.
printf("Nickname: %s\n", nickname);//Note that the print-console doesn't support UTF-8. The nickname is UTF-8, so this will only display properly if there isn't any non-ASCII characters. To display it properly, a print method which supports UTF-8 should be used instead.
//You can also use accountProfileGetImageSize()/accountProfileLoadImage() to load the icon for use with a JPEG library, for displaying the user profile icon. See libnx acc.h.
}
@ -74,19 +85,17 @@ int main(int argc, char **argv)
// Main loop
while(appletMainLoop())
{
//Scan all the inputs. This should be done once for each frame
hidScanInput();
// Scan the gamepad. This should be done once for each frame
padUpdate(&pad);
//hidKeysDown returns information about which buttons have been just pressed (and they weren't in the previous frame)
u64 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
// padGetButtonsDown returns the set of buttons that have been newly pressed in this frame compared to the previous one
u64 kDown = padGetButtonsDown(&pad);
if (kDown & KEY_PLUS) break; // break in order to return to hbmenu
if (kDown & HidNpadButton_Plus) break; // break in order to return to hbmenu
gfxFlushBuffers();
gfxSwapBuffers();
consoleUpdate(NULL);
}
gfxExit();
consoleExit(NULL);
return 0;
}

View File

@ -0,0 +1,222 @@
#---------------------------------------------------------------------------------
.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 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
# 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
#
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
# If not set, it attempts to use one of the following (in this order):
# - <Project name>.json
# - config.json
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
# NACP building is skipped as well.
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := source
DATA := data
INCLUDES := include
#ROMFS := romfs
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -g -Wall -O2 -ffunction-sections \
$(ARCH) $(DEFINES)
CFLAGS += $(INCLUDE) -D__SWITCH__
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
LIBS := -lnx
#---------------------------------------------------------------------------------
# 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 := $(CURDIR)/$(TARGET)
export TOPDIR := $(CURDIR)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD)
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_BIN := $(addsuffix .o,$(BINFILES))
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
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)
ifeq ($(strip $(CONFIG_JSON)),)
jsons := $(wildcard *.json)
ifneq (,$(findstring $(TARGET).json,$(jsons)))
export APP_JSON := $(TOPDIR)/$(TARGET).json
else
ifneq (,$(findstring config.json,$(jsons)))
export APP_JSON := $(TOPDIR)/config.json
endif
endif
else
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
endif
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
.PHONY: $(BUILD) clean all
#---------------------------------------------------------------------------------
all: $(BUILD)
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
ifeq ($(strip $(APP_JSON)),)
@rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
else
@rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
endif
#---------------------------------------------------------------------------------
else
.PHONY: all
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
ifeq ($(strip $(APP_JSON)),)
all : $(OUTPUT).nro
ifeq ($(strip $(NO_NACP)),)
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp
else
$(OUTPUT).nro : $(OUTPUT).elf
endif
else
all : $(OUTPUT).nsp
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
$(OUTPUT).nso : $(OUTPUT).elf
endif
$(OUTPUT).elf : $(OFILES)
$(OFILES_SRC) : $(HFILES_BIN)
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o %_bin.h : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

View File

@ -0,0 +1,188 @@
// Include the most common headers from the C standard library
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
// Include the main libnx system header, for Switch development
#include <switch.h>
// This example shows how to use Alarm Notifications, see libnx notif.h.
// This must be run under a host Application. To avoid conflicts with official apps, normally you should not run under a host Application where the official Application also uses Alarm Notifications.
// Parse the ApplicationParameter and verify that it belongs to the current hb app. You can store any data you want in the ApplicationParameter.
bool parse_app_param(u8 *outdata, size_t outdata_size, u64 out_size, u8 *app_param_header, size_t app_param_header_size) {
printf("out_size=0x%lx\n", out_size);
if (out_size != outdata_size) // Adjust this check if needed.
printf("out_size is invalid, ignoring data since it doesn't belong to the current app.\n");
else {
if(memcmp(outdata, app_param_header, app_param_header_size)==0) {
printf("data: %s\n", (char*)&outdata[0x8]);
return true;
}
else
printf("ApplicationParameter header mismatch, ignoring data since it doesn't belong to the current app.\n");
}
return false;
}
// Main program entrypoint
int main(int argc, char* argv[])
{
// This example uses a text console, as a simple way to output text to the screen.
// If you want to write a software-rendered graphics application,
// take a look at the graphics/simplegfx example, which uses the libnx Framebuffer API instead.
// If on the other hand you want to write an OpenGL based application,
// take a look at the graphics/opengl set of examples, which uses EGL instead.
consoleInit(NULL);
// Configure our supported input layout: a single player with standard controller styles
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
// Initialize the default gamepad (which reads handheld mode inputs as well as the first connected controller)
PadState pad;
padInitializeDefault(&pad);
printf("alarm-notifications example\n");
Result rc=0, rc2=0;
bool tmpflag=0;
u16 alarm_setting_id=0;
s32 total_out=0;
NotifAlarmSetting alarm_setting;
NotifAlarmSetting alarm_settings[NOTIF_MAX_ALARMS];
Event alarmevent={0};
// To avoid conflicts with other hb apps, use some sort of header to verify that the ApplicationParameter belongs to the current hb app. Replace this with your own data. Note that official apps don't do this.
// Note that ApplicationParameter is optional, see notif.h - you can disable/remove code using it you want.
u8 app_param_header[0x8] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
u8 app_param[0x20]={0};
u8 outdata[0x21]; // Since we're handling this as a string, make sure it's NUL-terminated.
memcpy(app_param, app_param_header, sizeof(app_param_header));
strncpy((char*)&app_param[0x8], "app param", sizeof(app_param)-9); // This example stores a string in the app_param - replace this with whatever data you want.
rc = notifInitialize(NotifServiceType_Application);
if (R_FAILED(rc)) printf("notifInitialize(): 0x%x\n", rc);
if (R_SUCCEEDED(rc)) { // Some official apps don't use this. See libnx notif.h for this.
rc = notifGetNotificationSystemEvent(&alarmevent);
printf("notifGetNotificationSystemEvent(): 0x%x\n", rc);
}
if (R_SUCCEEDED(rc)) {
printf("Press A to register the Alarm.\n");
printf("Press B to list the Alarms.\n");
printf("Press X to delete the registered Alarm.\n");
}
printf("Press + to exit.\n");
// Main loop
while (appletMainLoop())
{
// Scan the gamepad. This should be done once for each frame
padUpdate(&pad);
// padGetButtonsDown returns the set of buttons that have been
// newly pressed in this frame compared to the previous one
u64 kDown = padGetButtonsDown(&pad);
if (kDown & HidNpadButton_Plus)
break; // break in order to return to hbmenu
if (R_SUCCEEDED(rc) && R_SUCCEEDED(eventWait(&alarmevent, 0))) { // Some official apps don't use this. See libnx notif.h for this.
u64 out_size=0;
memset(outdata, 0, sizeof(outdata));
rc = notifTryPopNotifiedApplicationParameter(outdata, sizeof(outdata)-1, &out_size);
printf("notifTryPopNotifiedApplicationParameter(): 0x%x\n", rc);
if (R_SUCCEEDED(rc)) {
parse_app_param(outdata, sizeof(outdata)-1, out_size, app_param_header, sizeof(app_param_header));
}
}
if (R_SUCCEEDED(rc)) {
if (kDown & HidNpadButton_A) {
// Setup an alarm with {current local-time} + {2 minutes}. You can use any weekday/time you want.
// See libnx notif.h for more notifAlarmSetting*() funcs.
notifAlarmSettingCreate(&alarm_setting);
time_t unixTime = time(NULL) + 60*2;
struct tm* timeStruct = localtime((const time_t *)&unixTime);
u32 day = timeStruct->tm_wday;
s32 hour = timeStruct->tm_hour;
s32 minute = timeStruct->tm_min;
printf("Using the following schedule setting: weekday = %u, %02d:%02d.\n", day, hour, minute);
// You can also use this multiple times if you want for multiple days - this example only uses it once for a single day.
rc = notifAlarmSettingEnable(&alarm_setting, day, hour, minute);
printf("notifAlarmSettingEnable(): 0x%x\n", rc);
if (R_SUCCEEDED(rc)) {
rc = notifRegisterAlarmSetting(&alarm_setting_id, &alarm_setting, app_param, sizeof(app_param));
printf("notifRegisterAlarmSetting(): 0x%x\n", rc);
if (R_SUCCEEDED(rc)) printf("alarm_setting_id = 0x%x\n", alarm_setting_id);
}
}
else if (kDown & HidNpadButton_B) {
// List the Alarms.
total_out=0;
memset(alarm_settings, 0, sizeof(alarm_settings));
rc = notifListAlarmSettings(alarm_settings, NOTIF_MAX_ALARMS, &total_out);
printf("notifListAlarmSettings(): 0x%x\n", rc);
if (R_SUCCEEDED(rc)) {
printf("total_out: %d\n", total_out);
for (s32 alarmi=0; alarmi<total_out; alarmi++) {
printf("[%d]: muted=%d.\n", alarmi, alarm_settings[alarmi].muted);
// Verify that the Alarm belongs to the current app via the ApplicationParameter / parse the data from there.
u32 actual_size=0;
memset(outdata, 0, sizeof(outdata));
rc2 = notifLoadApplicationParameter(alarm_settings[alarmi].alarm_setting_id, outdata, sizeof(outdata)-1, &actual_size);
printf("notifLoadApplicationParameter(): 0x%x\n", rc2);
if (R_FAILED(rc2) || !parse_app_param(outdata, sizeof(outdata)-1, actual_size, app_param_header, sizeof(app_param_header))) continue;
// If you want to update an AlarmSetting/ApplicationParameter from the alarm-listing, you can use this. notifAlarmSetting*() funcs can be used to update it if wanted.
// rc2 = notifUpdateAlarmSetting(&alarm_settings[alarmi], outdata, sizeof(outdata)-1);
// printf("notifUpdateAlarmSetting(): 0x%x\n", rc2);
// Print the schedule settings.
for (u32 day_of_week=0; day_of_week<7; day_of_week++) {
rc2 = notifAlarmSettingIsEnabled(&alarm_settings[alarmi], day_of_week, &tmpflag);
if (R_SUCCEEDED(rc2) && tmpflag) {
NotifAlarmTime alarmtime={0};
rc2 = notifAlarmSettingGet(&alarm_settings[alarmi], day_of_week, &alarmtime);
if (R_SUCCEEDED(rc2)) {
printf("weekday %d = %02d:%02d. ", day_of_week, alarmtime.hour, alarmtime.minute);
}
}
}
printf("\n");
}
}
}
else if ((kDown & HidNpadButton_X) && alarm_setting_id) {
// Delete the AlarmSetting which was registered with the HidNpadButton_A block.
// If wanted, you can also use this with alarm_settings[alarmi].alarm_setting_id with the output from notifListAlarmSettings.
rc = notifDeleteAlarmSetting(alarm_setting_id);
printf("notifDeleteAlarmSetting(): 0x%x\n", rc);
}
}
// Update the console, sending a new frame to the display
consoleUpdate(NULL);
}
eventClose(&alarmevent);
notifExit();
// Deinitialize and clean up resources used by the console (important!)
consoleExit(NULL);
return 0;
}

View File

@ -15,7 +15,6 @@ include $(DEVKITPRO)/libnx/switch_rules
# 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.
@ -29,26 +28,33 @@ include $(DEVKITPRO)/libnx/switch_rules
# - <Project name>.jpg
# - icon.jpg
# - <libnx folder>/default_icon.jpg
#
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
# If not set, it attempts to use one of the following (in this order):
# - <Project name>.json
# - config.json
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
# NACP building is skipped as well.
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := source
DATA := data
INCLUDES := include
EXEFS_SRC := exefs_src
#ROMFS := romfs
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -g -Wall -O2 -ffunction-sections \
$(ARCH) $(DEFINES)
CFLAGS += $(INCLUDE) -D__SWITCH__
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
@ -107,7 +113,18 @@ export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC)
ifeq ($(strip $(CONFIG_JSON)),)
jsons := $(wildcard *.json)
ifneq (,$(findstring $(TARGET).json,$(jsons)))
export APP_JSON := $(TOPDIR)/$(TARGET).json
else
ifneq (,$(findstring config.json,$(jsons)))
export APP_JSON := $(TOPDIR)/config.json
endif
endif
else
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
endif
ifeq ($(strip $(ICON)),)
icons := $(wildcard *.jpg)
@ -150,7 +167,11 @@ $(BUILD):
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) $(TARGET).pfs0 $(TARGET).nso $(TARGET).nro $(TARGET).nacp $(TARGET).elf
ifeq ($(strip $(APP_JSON)),)
@rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
else
@rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
endif
#---------------------------------------------------------------------------------
@ -162,11 +183,9 @@ DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
all : $(OUTPUT).pfs0 $(OUTPUT).nro
ifeq ($(strip $(APP_JSON)),)
$(OUTPUT).pfs0 : $(OUTPUT).nso
$(OUTPUT).nso : $(OUTPUT).elf
all : $(OUTPUT).nro
ifeq ($(strip $(NO_NACP)),)
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp
@ -174,6 +193,16 @@ else
$(OUTPUT).nro : $(OUTPUT).elf
endif
else
all : $(OUTPUT).nsp
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
$(OUTPUT).nso : $(OUTPUT).elf
endif
$(OUTPUT).elf : $(OFILES)
$(OFILES_SRC) : $(HFILES_BIN)

View File

@ -4,22 +4,28 @@
#include <switch.h>
//This example shows how to get NsApplicationControlData for a title, which contains nacp/icon. See libnx ns.h.
//This example shows how to get NsApplicationControlData for an application, which contains nacp/icon. See libnx ns.h.
int main(int argc, char **argv)
{
Result rc=0;
u64 titleID=0x01007ef00011e000;//titleID for use with nsGetApplicationControlData, in this case BOTW.
u64 application_id=0x01007ef00011e000;//ApplicationId for use with nsGetApplicationControlData, in this case BOTW.
NsApplicationControlData *buf=NULL;
size_t outsize=0;
u64 outsize=0;
NacpLanguageEntry *langentry = NULL;
char name[0x201];
gfxInitDefault();
consoleInit(NULL);
// Configure our supported input layout: a single player with standard controller styles
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
// Initialize the default gamepad (which reads handheld mode inputs as well as the first connected controller)
PadState pad;
padInitializeDefault(&pad);
buf = (NsApplicationControlData*)malloc(sizeof(NsApplicationControlData));
if (buf==NULL) {
rc = MAKERESULT(Module_Libnx, LibnxError_OutOfMemory);
@ -37,7 +43,7 @@ int main(int argc, char **argv)
}
if (R_SUCCEEDED(rc)) {
rc = nsGetApplicationControlData(1, titleID, buf, sizeof(NsApplicationControlData), &outsize);
rc = nsGetApplicationControlData(NsApplicationControlSource_Storage, application_id, buf, sizeof(NsApplicationControlData), &outsize);
if (R_FAILED(rc)) {
printf("nsGetApplicationControlData() failed: 0x%x\n", rc);
}
@ -70,19 +76,17 @@ int main(int argc, char **argv)
// Main loop
while(appletMainLoop())
{
//Scan all the inputs. This should be done once for each frame
hidScanInput();
// Scan the gamepad. This should be done once for each frame
padUpdate(&pad);
//hidKeysDown returns information about which buttons have been just pressed (and they weren't in the previous frame)
u64 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
// padGetButtonsDown returns the set of buttons that have been newly pressed in this frame compared to the previous one
u64 kDown = padGetButtonsDown(&pad);
if (kDown & KEY_PLUS) break; // break in order to return to hbmenu
if (kDown & HidNpadButton_Plus) break; // break in order to return to hbmenu
gfxFlushBuffers();
gfxSwapBuffers();
consoleUpdate(NULL);
}
gfxExit();
consoleExit(NULL);
return 0;
}

View File

@ -0,0 +1,222 @@
#---------------------------------------------------------------------------------
.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 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
# 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
#
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
# If not set, it attempts to use one of the following (in this order):
# - <Project name>.json
# - config.json
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
# NACP building is skipped as well.
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := source
DATA := data
INCLUDES := include
#ROMFS := romfs
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -g -Wall -O2 -ffunction-sections \
$(ARCH) $(DEFINES)
CFLAGS += $(INCLUDE) -D__SWITCH__
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
LIBS := -lnx
#---------------------------------------------------------------------------------
# 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 := $(CURDIR)/$(TARGET)
export TOPDIR := $(CURDIR)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD)
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_BIN := $(addsuffix .o,$(BINFILES))
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
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)
ifeq ($(strip $(CONFIG_JSON)),)
jsons := $(wildcard *.json)
ifneq (,$(findstring $(TARGET).json,$(jsons)))
export APP_JSON := $(TOPDIR)/$(TARGET).json
else
ifneq (,$(findstring config.json,$(jsons)))
export APP_JSON := $(TOPDIR)/config.json
endif
endif
else
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
endif
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
.PHONY: $(BUILD) clean all
#---------------------------------------------------------------------------------
all: $(BUILD)
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
ifeq ($(strip $(APP_JSON)),)
@rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
else
@rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
endif
#---------------------------------------------------------------------------------
else
.PHONY: all
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
ifeq ($(strip $(APP_JSON)),)
all : $(OUTPUT).nro
ifeq ($(strip $(NO_NACP)),)
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp
else
$(OUTPUT).nro : $(OUTPUT).elf
endif
else
all : $(OUTPUT).nsp
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
$(OUTPUT).nso : $(OUTPUT).elf
endif
$(OUTPUT).elf : $(OFILES)
$(OFILES_SRC) : $(HFILES_BIN)
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o %_bin.h : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

View File

@ -0,0 +1,161 @@
// Include the most common headers from the C standard library
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
// Include the main libnx system header, for Switch development
#include <switch.h>
// This example shows how to use applet to get playstats for applications. See also libnx applet.h. See applet.h for the requirements for using this.
// This also shows how to use pdmqry, see also libnx pdm.h.
/// Main program entrypoint
int main(int argc, char* argv[])
{
// This example uses a text console, as a simple way to output text to the screen.
// If you want to write a software-rendered graphics application,
// take a look at the graphics/simplegfx example, which uses the libnx Framebuffer API instead.
// If on the other hand you want to write an OpenGL based application,
// take a look at the graphics/opengl set of examples, which uses EGL instead.
consoleInit(NULL);
// Configure our supported input layout: a single player with standard controller styles
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
// Initialize the default gamepad (which reads handheld mode inputs as well as the first connected controller)
PadState pad;
padInitializeDefault(&pad);
printf("application play-stats example\n");
Result rc=0;
PdmApplicationPlayStatistics stats[1];
PdmAppletEvent events[1];
u64 application_ids[1] = {0x010021B002EEA000}; // Change this to the ApplicationId of the current-process / the ApplicationId you want to use.
s32 total_out;
s32 i;
bool initflag=0;
// Only needed when using the cmds which require the Uid.
AccountUid preselected_uid={0};
rc = accountInitialize(AccountServiceType_Application);
if (R_SUCCEEDED(rc)) {
rc = accountGetPreselectedUser(&preselected_uid);
accountExit();
}
if (R_FAILED(rc)) printf("Failed to get user: 0x%x\n", rc);
// Not needed if you just want to use the applet cmds.
if (R_SUCCEEDED(rc)) {
rc = pdmqryInitialize();
if (R_FAILED(rc)) printf("pdmqryInitialize(): 0x%x\n", rc);
if (R_SUCCEEDED(rc)) initflag = true;
}
printf("Press A to get playstats.\n");
if (initflag) printf("Press X to use pdmqry.\n");
printf("Press + to exit.\n");
// Main loop
while (appletMainLoop())
{
// Scan the gamepad. This should be done once for each frame
padUpdate(&pad);
// padGetButtonsDown returns the set of buttons that have been
// newly pressed in this frame compared to the previous one
u64 kDown = padGetButtonsDown(&pad);
if (kDown & HidNpadButton_Plus)
break; // break in order to return to hbmenu
if (kDown & HidNpadButton_A) {
// Use appletQueryApplicationPlayStatisticsByUid if you want playstats for a specific userID.
memset(stats, 0, sizeof(stats));
total_out = 0;
if (R_SUCCEEDED(rc)) {
rc = appletQueryApplicationPlayStatistics(stats, application_ids, sizeof(application_ids)/sizeof(u64), &total_out);
printf("appletQueryApplicationPlayStatistics(): 0x%x\n", rc);
//rc = appletQueryApplicationPlayStatisticsByUid(preselected_uid, stats, application_ids, sizeof(application_ids)/sizeof(u64), &total_out);
//printf("appletQueryApplicationPlayStatisticsByUid(): 0x%x\n", rc);
}
if (R_SUCCEEDED(rc)) {
printf("total_out: %d\n", total_out);
for (i=0; i<total_out; i++) {
printf("%d: ", i);
printf("application_id = 0x%08lX, playtime = %lu (%lu seconds), total_launches = %lu\n", stats[i].application_id, stats[i].playtime, stats[i].playtime / 1000000000UL, stats[i].total_launches);
}
}
}
if (initflag && (kDown & HidNpadButton_X)) {
memset(events, 0, sizeof(events));
total_out = 0;
// Get PdmAppletEvents.
rc = pdmqryQueryAppletEvent(false, 0, events, sizeof(events)/sizeof(PdmAppletEvent), &total_out);
printf("pdmqryQueryAppletEvent(): 0x%x\n", rc);
if (R_SUCCEEDED(rc)) {
printf("total_out: %d\n", total_out);
for (i=0; i<total_out; i++) {
time_t tmptime = events[i].timestamp_user;
printf("%d: ", i);
printf("program_id = 0x%08lX, entry_index = 0x%x, timestamp_user = %lu, timestamp_network = %lu, event_type = %u, timestamp_user = %s\n", events[i].program_id, events[i].entry_index, events[i].timestamp_user, events[i].timestamp_network, events[i].event_type, ctime(&tmptime));
}
}
// Get PdmPlayStatistics for the specified ApplicationId.
PdmPlayStatistics playstats[1]={0};
rc = pdmqryQueryPlayStatisticsByApplicationId(application_ids[0], false, &playstats[0]);
printf("pdmqryQueryPlayStatisticsByApplicationId(): 0x%x\n", rc);
if (R_SUCCEEDED(rc)) printf("program_id = 0x%016lX, playtime = %lu (%lu seconds), total_launches = %u\n", playstats[0].program_id, playstats[0].playtime, playstats[0].playtime / 1000000000UL, playstats[0].total_launches);
// Get PdmPlayStatistics for the specified ApplicationId and user.
rc = pdmqryQueryPlayStatisticsByApplicationIdAndUserAccountId(application_ids[0], preselected_uid, false, &playstats[0]);
printf("pdmqryQueryPlayStatisticsByApplicationIdAndUserAccountId(): 0x%x\n", rc);
if (R_SUCCEEDED(rc)) printf("program_id = 0x%016lX, playtime = %lu (%lu seconds), total_launches = %u\n", playstats[0].program_id, playstats[0].playtime, playstats[0].playtime / 1000000000UL, playstats[0].total_launches);
// Get a listing of PdmLastPlayTime for the specified applications.
PdmLastPlayTime playtimes[1]={0};
rc = pdmqryQueryLastPlayTime(false, playtimes, application_ids, 1, &total_out);
printf("pdmqryQueryLastPlayTime(): 0x%x, %d\n", rc, total_out);
if (R_SUCCEEDED(rc)) {
for (i=0; i<total_out; i++)
printf("%d: timestamp_user = %lu\n", i, pdmPlayTimestampToPosix(playtimes[i].timestamp_user));
}
// Get the available range for reading events, see pdm.h.
s32 total_entries=0, start_entryindex=0, end_entryindex=0;
rc = pdmqryGetAvailablePlayEventRange(&total_entries, &start_entryindex, &end_entryindex);
printf("pdmqryGetAvailablePlayEventRange(): 0x%x, 0x%x, 0x%x, 0x%x\n", rc, total_entries, start_entryindex, end_entryindex);
// Get account events.
PdmAccountEvent accevents[5]={};
rc = pdmqryQueryAccountEvent(0, accevents, 5, &total_out);
printf("pdmqryQueryAccountEvent(): 0x%x\n", rc);
if (R_SUCCEEDED(rc)) {
printf("total_out: %d\n", total_out);
for (i=0; i<total_out; i++) {
time_t tmptime = accevents[i].timestamp_user;
printf("%d: ", i);
printf("uid = 0x%lx 0x%lx, program_id = 0x%016lX, entry_index = 0x%x, timestamp_user = %lu, timestamp_network = %lu, event_type = %u, timestamp_user = %s\n", accevents[i].uid.uid[0], accevents[i].uid.uid[1], accevents[i].program_id, accevents[i].entry_index, accevents[i].timestamp_user, accevents[i].timestamp_network, accevents[i].type, ctime(&tmptime));
}
}
// For more cmds, see pdm.h.
}
// Update the console, sending a new frame to the display
consoleUpdate(NULL);
}
pdmqryExit();
// Deinitialize and clean up resources used by the console (important!)
consoleExit(NULL);
return 0;
}

View File

@ -0,0 +1,6 @@
# libapplets
These examples show how to launch/use LibraryApplets.
These must be run under an Application with nx-hbloader. Also, note that the last frame displayed by an app prior to launching swkbd will override the frame displayed while NROs are loaded.

View File

@ -0,0 +1,222 @@
#---------------------------------------------------------------------------------
.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 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
# 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
#
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
# If not set, it attempts to use one of the following (in this order):
# - <Project name>.json
# - config.json
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
# NACP building is skipped as well.
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := source
DATA := data
INCLUDES := include
#ROMFS := romfs
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -g -Wall -O2 -ffunction-sections \
$(ARCH) $(DEFINES)
CFLAGS += $(INCLUDE) -D__SWITCH__
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
LIBS := -lnx
#---------------------------------------------------------------------------------
# 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 := $(CURDIR)/$(TARGET)
export TOPDIR := $(CURDIR)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD)
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_BIN := $(addsuffix .o,$(BINFILES))
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
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)
ifeq ($(strip $(CONFIG_JSON)),)
jsons := $(wildcard *.json)
ifneq (,$(findstring $(TARGET).json,$(jsons)))
export APP_JSON := $(TOPDIR)/$(TARGET).json
else
ifneq (,$(findstring config.json,$(jsons)))
export APP_JSON := $(TOPDIR)/config.json
endif
endif
else
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
endif
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
.PHONY: $(BUILD) clean all
#---------------------------------------------------------------------------------
all: $(BUILD)
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
ifeq ($(strip $(APP_JSON)),)
@rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
else
@rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
endif
#---------------------------------------------------------------------------------
else
.PHONY: all
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
ifeq ($(strip $(APP_JSON)),)
all : $(OUTPUT).nro
ifeq ($(strip $(NO_NACP)),)
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp
else
$(OUTPUT).nro : $(OUTPUT).elf
endif
else
all : $(OUTPUT).nsp
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
$(OUTPUT).nso : $(OUTPUT).elf
endif
$(OUTPUT).elf : $(OFILES)
$(OFILES_SRC) : $(HFILES_BIN)
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o %_bin.h : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

View File

@ -0,0 +1,68 @@
// Include the most common headers from the C standard library
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <time.h>
// Include the main libnx system header, for Switch development
#include <switch.h>
// See also libnx error.h.
// This example shows how to use the error LibraryApplet, see also libnx error.h.
// Main program entrypoint
int main(int argc, char* argv[])
{
Result rc=0;
// This example uses a text console, as a simple way to output text to the screen.
// If you want to write a software-rendered graphics application,
// take a look at the graphics/simplegfx example, which uses the libnx Framebuffer API instead.
// If on the other hand you want to write an OpenGL based application,
// take a look at the graphics/opengl set of examples, which uses EGL instead.
consoleInit(NULL);
// Configure our supported input layout: a single player with standard controller styles
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
// Initialize the default gamepad (which reads handheld mode inputs as well as the first connected controller)
PadState pad;
padInitializeDefault(&pad);
printf("error example\n");
consoleUpdate(NULL);
printf("Press A to launch error applet.\n");
printf("Press + to exit.\n");
// Main loop
while (appletMainLoop())
{
// Scan the gamepad. This should be done once for each frame
padUpdate(&pad);
// padGetButtonsDown returns the set of buttons that have been
// newly pressed in this frame compared to the previous one
u64 kDown = padGetButtonsDown(&pad);
if (kDown & HidNpadButton_Plus)
break; // break in order to return to hbmenu
if (kDown & HidNpadButton_A) {
printf("Running errorResultRecordShow()...\n");
consoleUpdate(NULL);
rc = errorResultRecordShow(MAKERESULT(16, 250), time(NULL));
printf("errorResultRecordShow(): 0x%x\n", rc);
}
// Update the console, sending a new frame to the display
consoleUpdate(NULL);
}
// Deinitialize and clean up resources used by the console (important!)
consoleExit(NULL);
return 0;
}

View File

@ -0,0 +1,222 @@
#---------------------------------------------------------------------------------
.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 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
# 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
#
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
# If not set, it attempts to use one of the following (in this order):
# - <Project name>.json
# - config.json
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
# NACP building is skipped as well.
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := source
DATA := data
INCLUDES := include
#ROMFS := romfs
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -g -Wall -O2 -ffunction-sections \
$(ARCH) $(DEFINES)
CFLAGS += $(INCLUDE) -D__SWITCH__
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
LIBS := -lnx
#---------------------------------------------------------------------------------
# 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 := $(CURDIR)/$(TARGET)
export TOPDIR := $(CURDIR)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD)
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_BIN := $(addsuffix .o,$(BINFILES))
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
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)
ifeq ($(strip $(CONFIG_JSON)),)
jsons := $(wildcard *.json)
ifneq (,$(findstring $(TARGET).json,$(jsons)))
export APP_JSON := $(TOPDIR)/$(TARGET).json
else
ifneq (,$(findstring config.json,$(jsons)))
export APP_JSON := $(TOPDIR)/config.json
endif
endif
else
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
endif
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
.PHONY: $(BUILD) clean all
#---------------------------------------------------------------------------------
all: $(BUILD)
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
ifeq ($(strip $(APP_JSON)),)
@rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
else
@rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
endif
#---------------------------------------------------------------------------------
else
.PHONY: all
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
ifeq ($(strip $(APP_JSON)),)
all : $(OUTPUT).nro
ifeq ($(strip $(NO_NACP)),)
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp
else
$(OUTPUT).nro : $(OUTPUT).elf
endif
else
all : $(OUTPUT).nsp
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
$(OUTPUT).nso : $(OUTPUT).elf
endif
$(OUTPUT).elf : $(OFILES)
$(OFILES_SRC) : $(HFILES_BIN)
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o %_bin.h : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

View File

@ -0,0 +1,81 @@
// Include the most common headers from the C standard library
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <time.h>
// Include the main libnx system header, for Switch development
#include <switch.h>
// This example shows how to use the Parental Controls service / auth LibraryApplet, see also libnx pctl.h/pctlauth.h.
// Main program entrypoint
int main(int argc, char* argv[])
{
Result rc=0;
bool pctlflag=0;
// This example uses a text console, as a simple way to output text to the screen.
// If you want to write a software-rendered graphics application,
// take a look at the graphics/simplegfx example, which uses the libnx Framebuffer API instead.
// If on the other hand you want to write an OpenGL based application,
// take a look at the graphics/opengl set of examples, which uses EGL instead.
consoleInit(NULL);
// Configure our supported input layout: a single player with standard controller styles
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
// Initialize the default gamepad (which reads handheld mode inputs as well as the first connected controller)
PadState pad;
padInitializeDefault(&pad);
printf("pctlauth example\n");
consoleUpdate(NULL);
rc = pctlInitialize();
printf("pctlInitialize(): 0x%x\n", rc);
if (R_SUCCEEDED(rc)) {
rc = pctlIsRestrictionEnabled(&pctlflag);
pctlExit();
printf("pctlIsRestrictionEnabled(): 0x%x", rc);
if (R_SUCCEEDED(rc)) printf(", flag=%d", pctlflag);
printf("\n");
}
if (pctlflag) {
printf("Press A to launch pctlauth applet.\n");
}
else {
printf("Parental Controls are not enabled, hence applet launching is disabled.\n");
}
printf("Press + to exit.\n");
// Main loop
while (appletMainLoop())
{
// Scan the gamepad. This should be done once for each frame
padUpdate(&pad);
// padGetButtonsDown returns the set of buttons that have been
// newly pressed in this frame compared to the previous one
u64 kDown = padGetButtonsDown(&pad);
if (kDown & HidNpadButton_Plus)
break; // break in order to return to hbmenu
if (pctlflag && (kDown & HidNpadButton_A)) {
printf("Running pctlauthShow()...\n");
consoleUpdate(NULL);
rc = pctlauthShow(true); // Launch the applet for validating the PIN.
printf("pctlauthShow(): 0x%x\n", rc);
}
// Update the console, sending a new frame to the display
consoleUpdate(NULL);
}
// Deinitialize and clean up resources used by the console (important!)
consoleExit(NULL);
return 0;
}

View File

@ -0,0 +1,222 @@
#---------------------------------------------------------------------------------
.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 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
# 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
#
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
# If not set, it attempts to use one of the following (in this order):
# - <Project name>.json
# - config.json
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
# NACP building is skipped as well.
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := source
DATA := data
INCLUDES := include
#ROMFS := romfs
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -g -Wall -O2 -ffunction-sections \
$(ARCH) $(DEFINES)
CFLAGS += $(INCLUDE) -D__SWITCH__
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
LIBS := -lnx -lm
#---------------------------------------------------------------------------------
# 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 := $(CURDIR)/$(TARGET)
export TOPDIR := $(CURDIR)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD)
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_BIN := $(addsuffix .o,$(BINFILES))
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
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)
ifeq ($(strip $(CONFIG_JSON)),)
jsons := $(wildcard *.json)
ifneq (,$(findstring $(TARGET).json,$(jsons)))
export APP_JSON := $(TOPDIR)/$(TARGET).json
else
ifneq (,$(findstring config.json,$(jsons)))
export APP_JSON := $(TOPDIR)/config.json
endif
endif
else
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
endif
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
.PHONY: $(BUILD) clean all
#---------------------------------------------------------------------------------
all: $(BUILD)
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
ifeq ($(strip $(APP_JSON)),)
@rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
else
@rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
endif
#---------------------------------------------------------------------------------
else
.PHONY: all
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
ifeq ($(strip $(APP_JSON)),)
all : $(OUTPUT).nro
ifeq ($(strip $(NO_NACP)),)
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp
else
$(OUTPUT).nro : $(OUTPUT).elf
endif
else
all : $(OUTPUT).nsp
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
$(OUTPUT).nso : $(OUTPUT).elf
endif
$(OUTPUT).elf : $(OFILES)
$(OFILES_SRC) : $(HFILES_BIN)
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o %_bin.h : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

View File

@ -0,0 +1,183 @@
// Include the most common headers from the C standard library
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
// Include the main libnx system header, for Switch development
#include <switch.h>
// See also libnx swkbd.h.
// This example shows how to use the software keyboard (swkbd) LibraryApplet.
// TextCheck callback, this can be removed when not using TextCheck.
SwkbdTextCheckResult validate_text(char* tmp_string, size_t tmp_string_size) {
if (strcmp(tmp_string, "bad")==0) {
strncpy(tmp_string, "Bad string.", tmp_string_size);
return SwkbdTextCheckResult_Bad;
}
return SwkbdTextCheckResult_OK;
}
// The rest of these callbacks are for swkbd-inline.
void finishinit_cb(void) {
printf("reply: FinishedInitialize\n");
}
void decidedcancel_cb(void) {
printf("reply: DecidedCancel\n");
}
// String changed callback.
void strchange_cb(const char* str, SwkbdChangedStringArg* arg) {
printf("reply: ChangedString. str = %s, arg->stringLen = 0x%x, arg->dicStartCursorPos = 0x%x, arg->dicEndCursorPos = 0x%x, arg->arg->cursorPos = 0x%x\n", str, arg->stringLen, arg->dicStartCursorPos, arg->dicEndCursorPos, arg->cursorPos);
}
// Moved cursor callback.
void movedcursor_cb(const char* str, SwkbdMovedCursorArg* arg) {
printf("reply: MovedCursor. str = %s, arg->stringLen = 0x%x, arg->cursorPos = 0x%x\n", str, arg->stringLen, arg->cursorPos);
}
// Text submitted callback, this is used to get the output text from submit.
void decidedenter_cb(const char* str, SwkbdDecidedEnterArg* arg) {
printf("reply: DecidedEnter. str = %s, arg->stringLen = 0x%x\n", str, arg->stringLen);
}
// Main program entrypoint
int main(int argc, char* argv[])
{
Result rc=0;
// This example uses a text console, as a simple way to output text to the screen.
// If you want to write a software-rendered graphics application,
// take a look at the graphics/simplegfx example, which uses the libnx gfx API instead.
// If on the other hand you want to write an OpenGL based application,
// take a look at the graphics/opengl set of examples, which uses EGL instead.
consoleInit(NULL);
// Configure our supported input layout: a single player with standard controller styles
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
// Initialize the default gamepad (which reads handheld mode inputs as well as the first connected controller)
PadState pad;
padInitializeDefault(&pad);
printf("swkbd example\n");
consoleUpdate(NULL);
SwkbdConfig kbd;
char tmpoutstr[16] = {0};
rc = swkbdCreate(&kbd, 0);
printf("swkbdCreate(): 0x%x\n", rc);
if (R_SUCCEEDED(rc)) {
// Select a Preset to use, if any.
swkbdConfigMakePresetDefault(&kbd);
//swkbdConfigMakePresetPassword(&kbd);
//swkbdConfigMakePresetUserName(&kbd);
//swkbdConfigMakePresetDownloadCode(&kbd);
// Optional, set any text if you want (see swkbd.h).
//swkbdConfigSetOkButtonText(&kbd, "Submit");
//swkbdConfigSetLeftOptionalSymbolKey(&kbd, "a");
//swkbdConfigSetRightOptionalSymbolKey(&kbd, "b");
//swkbdConfigSetHeaderText(&kbd, "Header");
//swkbdConfigSetSubText(&kbd, "Sub");
//swkbdConfigSetGuideText(&kbd, "Guide");
swkbdConfigSetTextCheckCallback(&kbd, validate_text);//Optional, can be removed if not using TextCheck.
// Set the initial string if you want.
//swkbdConfigSetInitialText(&kbd, "Initial");
// You can also use swkbdConfigSet*() funcs if you want.
printf("Running swkbdShow...\n");
rc = swkbdShow(&kbd, tmpoutstr, sizeof(tmpoutstr));
printf("swkbdShow(): 0x%x\n", rc);
if (R_SUCCEEDED(rc)) {
printf("out str: %s\n", tmpoutstr);
}
swkbdClose(&kbd);
}
// The rest of this example shows how to use swkbd-inline, which can be ignored if you're not using swkbd-inline. See libnx swkbd.h.
SwkbdInline kbdinline;
rc = swkbdInlineCreate(&kbdinline);
printf("swkbdInlineCreate(): 0x%x\n", rc);
swkbdInlineSetFinishedInitializeCallback(&kbdinline, finishinit_cb);
// Launch the applet.
// If you want to display the image manually, you can also use swkbdInlineLaunch and swkbdInlineGetImageMemoryRequirement/swkbdInlineGetImage.
if (R_SUCCEEDED(rc)) {
rc = swkbdInlineLaunchForLibraryApplet(&kbdinline, SwkbdInlineMode_AppletDisplay, 0);
printf("swkbdInlineLaunch(): 0x%x\n", rc);
}
// Set the callbacks.
swkbdInlineSetChangedStringCallback(&kbdinline, strchange_cb);
swkbdInlineSetMovedCursorCallback(&kbdinline, movedcursor_cb);
swkbdInlineSetDecidedEnterCallback(&kbdinline, decidedenter_cb);
swkbdInlineSetDecidedCancelCallback(&kbdinline, decidedcancel_cb);
// Optionally set swkbd-inline state, this can also be done after the applet appears. swkbdInlineUpdate() must be called for changes to take affect.
//swkbdInlineSetInputText(&kbdinline, "test");
//swkbdInlineUpdate(&kbdinline, NULL);
// Make the applet appear, can be used whenever.
SwkbdAppearArg appearArg;
swkbdInlineMakeAppearArg(&appearArg, SwkbdType_Normal);
// You can optionally set appearArg text / fields here.
swkbdInlineAppearArgSetOkButtonText(&appearArg, "Submit");
appearArg.dicFlag = 1;
appearArg.returnButtonFlag = 1;
swkbdInlineAppear(&kbdinline, &appearArg);
if (R_SUCCEEDED(rc)) {
rc = swkbdInlineUpdate(&kbdinline, NULL);
printf("swkbdInlineUpdate(): 0x%x\n", rc);
}
printf("Press + to exit.\n");
// Main loop
while (appletMainLoop())
{
// Scan the gamepad. This should be done once for each frame
padUpdate(&pad);
// padGetButtonsDown returns the set of buttons that have been
// newly pressed in this frame compared to the previous one
u64 kDown = padGetButtonsDown(&pad);
if (kDown & HidNpadButton_Plus)
break; // break in order to return to hbmenu
// Your code goes here
if (R_SUCCEEDED(rc)) {
if (kDown & HidNpadButton_ZR) swkbdInlineDisappear(&kbdinline); //Optional, you can have swkbd (dis)appear whenever.
if (kDown & HidNpadButton_Y) swkbdInlineAppear(&kbdinline, &appearArg); // If you use swkbdInlineAppear again after text was submitted, you may want to use swkbdInlineSetInputText since the current-text will be the same as when it was submitted otherwise.
rc = swkbdInlineUpdate(&kbdinline, NULL); // Handles updating SwkbdInline state, this should be called periodically.
if (R_FAILED(rc)) printf("swkbdInlineUpdate(): 0x%x\n", rc);
}
// Update the console, sending a new frame to the display
consoleUpdate(NULL);
}
// Normally the applet will keep running until swkbdInlineClose is used.
rc = swkbdInlineClose(&kbdinline);
printf("swkbdInlineClose(): 0x%x\n", rc);
// Deinitialize and clean up resources used by the console (important!)
consoleExit(NULL);
return 0;
}

View File

@ -41,14 +41,14 @@ EXEFS_SRC := exefs_src
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -g -Wall -O2 -ffunction-sections \
$(ARCH) $(DEFINES)
CFLAGS += $(INCLUDE) -D__SWITCH__
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)

View File

@ -0,0 +1,91 @@
// Include the most common headers from the C standard library
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
// Include the main libnx system header, for Switch development
#include <switch.h>
// See also libnx web.h.
// This example shows how to use the web LibraryApplets.
// Main program entrypoint
int main(int argc, char* argv[])
{
Result rc=0;
// This example uses a text console, as a simple way to output text to the screen.
// If you want to write a software-rendered graphics application,
// take a look at the graphics/simplegfx example, which uses the libnx Framebuffer API instead.
// If on the other hand you want to write an OpenGL based application,
// take a look at the graphics/opengl set of examples, which uses EGL instead.
consoleInit(NULL);
// Configure our supported input layout: a single player with standard controller styles
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
// Initialize the default gamepad (which reads handheld mode inputs as well as the first connected controller)
PadState pad;
padInitializeDefault(&pad);
printf("web example\n");
consoleUpdate(NULL);
printf("Press A to launch Web applet.\n");
printf("Press + to exit.\n");
// Main loop
while (appletMainLoop())
{
// Scan the gamepad. This should be done once for each frame
padUpdate(&pad);
// padGetButtonsDown returns the set of buttons that have been
// newly pressed in this frame compared to the previous one
u64 kDown = padGetButtonsDown(&pad);
if (kDown & HidNpadButton_Plus)
break; // break in order to return to hbmenu
if (kDown & HidNpadButton_A) {
WebCommonConfig config;
WebCommonReply reply;
WebExitReason exitReason=0;
// Create the config. There's a number of web*Create() funcs, see libnx web.h.
// webPageCreate/webNewsCreate requires running under a host Application which has HtmlDocument content. When the host program is an Application when using webPageCreate/webNewsCreate, and webConfigSetWhitelist is not used, the whitelist will be loaded from the content. Atmosphère hbl_html can be used to handle this.
rc = webPageCreate(&config, "https://google.com/");
printf("webPageCreate(): 0x%x\n", rc);
if (R_SUCCEEDED(rc)) {
// At this point you can use any webConfigSet* funcs you want.
rc = webConfigSetWhitelist(&config, "^http*"); // Set the whitelist, adjust as needed. If you're only using a single domain, you could remove this and use webNewsCreate for the above (see web.h for webNewsCreate).
printf("webConfigSetWhitelist(): 0x%x\n", rc);
if (R_SUCCEEDED(rc)) { // Launch the applet and wait for it to exit.
printf("Running webConfigShow...\n");
rc = webConfigShow(&config, &reply); // If you don't use reply you can pass NULL for it.
printf("webConfigShow(): 0x%x\n", rc);
}
if (R_SUCCEEDED(rc)) { // Normally you can ignore exitReason.
rc = webReplyGetExitReason(&reply, &exitReason);
printf("webReplyGetExitReason(): 0x%x", rc);
if (R_SUCCEEDED(rc)) printf(", 0x%x", exitReason);
printf("\n");
}
}
}
// Update the console, sending a new frame to the display
consoleUpdate(NULL);
}
// Deinitialize and clean up resources used by the console (important!)
consoleExit(NULL);
return 0;
}

View File

@ -0,0 +1,222 @@
#---------------------------------------------------------------------------------
.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 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
# 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
#
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
# If not set, it attempts to use one of the following (in this order):
# - <Project name>.json
# - config.json
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
# NACP building is skipped as well.
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := source
DATA := data
INCLUDES := include
#ROMFS := romfs
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -g -Wall -O2 -ffunction-sections \
$(ARCH) $(DEFINES)
CFLAGS += $(INCLUDE) -D__SWITCH__
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
LIBS := -lnx
#---------------------------------------------------------------------------------
# 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 := $(CURDIR)/$(TARGET)
export TOPDIR := $(CURDIR)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD)
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_BIN := $(addsuffix .o,$(BINFILES))
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
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)
ifeq ($(strip $(CONFIG_JSON)),)
jsons := $(wildcard *.json)
ifneq (,$(findstring $(TARGET).json,$(jsons)))
export APP_JSON := $(TOPDIR)/$(TARGET).json
else
ifneq (,$(findstring config.json,$(jsons)))
export APP_JSON := $(TOPDIR)/config.json
endif
endif
else
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
endif
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
.PHONY: $(BUILD) clean all
#---------------------------------------------------------------------------------
all: $(BUILD)
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
ifeq ($(strip $(APP_JSON)),)
@rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
else
@rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
endif
#---------------------------------------------------------------------------------
else
.PHONY: all
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
ifeq ($(strip $(APP_JSON)),)
all : $(OUTPUT).nro
ifeq ($(strip $(NO_NACP)),)
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp
else
$(OUTPUT).nro : $(OUTPUT).elf
endif
else
all : $(OUTPUT).nsp
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
$(OUTPUT).nso : $(OUTPUT).elf
endif
$(OUTPUT).elf : $(OFILES)
$(OFILES_SRC) : $(HFILES_BIN)
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o %_bin.h : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

View File

@ -0,0 +1,78 @@
// Include the most common headers from the C standard library
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Include the main libnx system header, for Switch development
#include <switch.h>
// This example shows how to use the light sensor, see also libnx applet.h.
// Main program entrypoint
int main(int argc, char* argv[])
{
// This example uses a text console, as a simple way to output text to the screen.
// If you want to write a software-rendered graphics application,
// take a look at the graphics/simplegfx example, which uses the libnx Framebuffer API instead.
// If on the other hand you want to write an OpenGL based application,
// take a look at the graphics/opengl set of examples, which uses EGL instead.
consoleInit(NULL);
// Configure our supported input layout: a single player with standard controller styles
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
// Initialize the default gamepad (which reads handheld mode inputs as well as the first connected controller)
PadState pad;
padInitializeDefault(&pad);
printf("light-sensor example\n");
printf("Press A to check light-sensor.\n");
printf("Press + to exit.\n");
// Main loop
while (appletMainLoop())
{
// Scan the gamepad. This should be done once for each frame
padUpdate(&pad);
// padGetButtonsDown returns the set of buttons that have been
// newly pressed in this frame compared to the previous one
u64 kDown = padGetButtonsDown(&pad);
if (kDown & HidNpadButton_Plus)
break; // break in order to return to hbmenu
if (kDown & HidNpadButton_A) {
Result rc = 0;
float fLux=0;
bool availableflag=0;
bool bOverLimit=0;
rc = appletIsIlluminanceAvailable(&availableflag);
printf("appletIsIlluminanceAvailable(): 0x%x", rc);
if (R_SUCCEEDED(rc)) printf(", %d", availableflag);
printf("\n");
if (R_SUCCEEDED(rc) && availableflag) {
// appletGetCurrentIlluminance(Ex) are the same except Ex has the bOverLimit param.
rc = appletGetCurrentIlluminance(&fLux);
printf("appletGetCurrentIlluminance(): 0x%x", rc);
if (R_SUCCEEDED(rc)) printf(", %f", fLux);
printf("\n");
rc = appletGetCurrentIlluminanceEx(&bOverLimit, &fLux);
printf("appletGetCurrentIlluminanceEx(): 0x%x", rc);
if (R_SUCCEEDED(rc)) printf(", %d, %f", bOverLimit, fLux);
printf("\n");
}
}
// Update the console, sending a new frame to the display
consoleUpdate(NULL);
}
// Deinitialize and clean up resources used by the console (important!)
consoleExit(NULL);
return 0;
}

222
applet/lockexit/Makefile Normal file
View File

@ -0,0 +1,222 @@
#---------------------------------------------------------------------------------
.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 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
# 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
#
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
# If not set, it attempts to use one of the following (in this order):
# - <Project name>.json
# - config.json
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
# NACP building is skipped as well.
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := source
DATA := data
INCLUDES := include
#ROMFS := romfs
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -g -Wall -O2 -ffunction-sections \
$(ARCH) $(DEFINES)
CFLAGS += $(INCLUDE) -D__SWITCH__
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
LIBS := -lnx
#---------------------------------------------------------------------------------
# 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 := $(CURDIR)/$(TARGET)
export TOPDIR := $(CURDIR)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD)
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_BIN := $(addsuffix .o,$(BINFILES))
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
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)
ifeq ($(strip $(CONFIG_JSON)),)
jsons := $(wildcard *.json)
ifneq (,$(findstring $(TARGET).json,$(jsons)))
export APP_JSON := $(TOPDIR)/$(TARGET).json
else
ifneq (,$(findstring config.json,$(jsons)))
export APP_JSON := $(TOPDIR)/config.json
endif
endif
else
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
endif
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
.PHONY: $(BUILD) clean all
#---------------------------------------------------------------------------------
all: $(BUILD)
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
ifeq ($(strip $(APP_JSON)),)
@rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
else
@rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
endif
#---------------------------------------------------------------------------------
else
.PHONY: all
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
ifeq ($(strip $(APP_JSON)),)
all : $(OUTPUT).nro
ifeq ($(strip $(NO_NACP)),)
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp
else
$(OUTPUT).nro : $(OUTPUT).elf
endif
else
all : $(OUTPUT).nsp
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
$(OUTPUT).nso : $(OUTPUT).elf
endif
$(OUTPUT).elf : $(OFILES)
$(OFILES_SRC) : $(HFILES_BIN)
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o %_bin.h : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

View File

@ -0,0 +1,61 @@
// Include the most common headers from the C standard library
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Include the main libnx system header, for Switch development
#include <switch.h>
// See also libnx applet.h.
// Main program entrypoint
int main(int argc, char* argv[])
{
appletLockExit();//Lock exiting so that the process won't be force-terminated by applet-exit (HOME button / application-close). This allows the app to run cleanup when exiting before getting terminated. For example, this allows closing files / FS operations before termination, etc.
//However, the process will be force-terminated if appletUnlockExit was not called within 15 seconds after exit was requested.
//You can also use appletLockExit/appletUnlockExit outside of main() around operations that need it, if you want.
// This example uses a text console, as a simple way to output text to the screen.
// If you want to write a software-rendered graphics application,
// take a look at the graphics/simplegfx example, which uses the libnx Framebuffer API instead.
// If on the other hand you want to write an OpenGL based application,
// take a look at the graphics/opengl set of examples, which uses EGL instead.
consoleInit(NULL);
// Configure our supported input layout: a single player with standard controller styles
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
// Initialize the default gamepad (which reads handheld mode inputs as well as the first connected controller)
PadState pad;
padInitializeDefault(&pad);
printf("applet exit-locking example.\n");
// Main loop
while (appletMainLoop())//This loop will automatically exit when applet requests exit.
{
// Scan the gamepad. This should be done once for each frame
padUpdate(&pad);
// padGetButtonsDown returns the set of buttons that have been
// newly pressed in this frame compared to the previous one
u64 kDown = padGetButtonsDown(&pad);
if (kDown & HidNpadButton_Plus)
break; // break in order to return to hbmenu
// Your code goes here
// Update the console, sending a new frame to the display
consoleUpdate(NULL);
}
// Deinitialize and clean up resources used by the console (important!)
consoleExit(NULL);
svcSleepThread(5000000000ULL);//Sleep for 5 seconds. This shows the additional delay when exiting, actual apps do not need this.
//You must run cleanup inbetween the loop exiting (or at some point after appletLockExit) and appletUnlockExit.
appletUnlockExit();//Must be called at some point before main() returns when appletLockExit was used. The process will be terminated when calling this if exit was requested.
return 0;
}

222
applet/recording/Makefile Normal file
View File

@ -0,0 +1,222 @@
#---------------------------------------------------------------------------------
.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 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
# 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
#
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
# If not set, it attempts to use one of the following (in this order):
# - <Project name>.json
# - config.json
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
# NACP building is skipped as well.
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := source
DATA := data
INCLUDES := include
#ROMFS := romfs
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -g -Wall -O2 -ffunction-sections \
$(ARCH) $(DEFINES)
CFLAGS += $(INCLUDE) -D__SWITCH__
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
LIBS := -lnx
#---------------------------------------------------------------------------------
# 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 := $(CURDIR)/$(TARGET)
export TOPDIR := $(CURDIR)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD)
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_BIN := $(addsuffix .o,$(BINFILES))
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
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)
ifeq ($(strip $(CONFIG_JSON)),)
jsons := $(wildcard *.json)
ifneq (,$(findstring $(TARGET).json,$(jsons)))
export APP_JSON := $(TOPDIR)/$(TARGET).json
else
ifneq (,$(findstring config.json,$(jsons)))
export APP_JSON := $(TOPDIR)/config.json
endif
endif
else
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
endif
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
.PHONY: $(BUILD) clean all
#---------------------------------------------------------------------------------
all: $(BUILD)
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
ifeq ($(strip $(APP_JSON)),)
@rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
else
@rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
endif
#---------------------------------------------------------------------------------
else
.PHONY: all
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
ifeq ($(strip $(APP_JSON)),)
all : $(OUTPUT).nro
ifeq ($(strip $(NO_NACP)),)
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp
else
$(OUTPUT).nro : $(OUTPUT).elf
endif
else
all : $(OUTPUT).nsp
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
$(OUTPUT).nso : $(OUTPUT).elf
endif
$(OUTPUT).elf : $(OFILES)
$(OFILES_SRC) : $(HFILES_BIN)
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o %_bin.h : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

View File

@ -0,0 +1,82 @@
// Include the most common headers from the C standard library
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Include the main libnx system header, for Switch development
#include <switch.h>
//See also libnx applet.h. See applet.h for the requirements for using this.
// Define the desired framebuffer resolution (here we set it to 720p).
#define FB_WIDTH 1280
#define FB_HEIGHT 720
// Remove above and uncomment below for 1080p
//#define FB_WIDTH 1920
//#define FB_HEIGHT 1080
int main(int argc, char **argv)
{
// Retrieve the default window
NWindow* win = nwindowGetDefault();
// Create a linear double-buffered framebuffer
Framebuffer fb;
framebufferCreate(&fb, win, FB_WIDTH, FB_HEIGHT, PIXEL_FORMAT_RGBA_8888, 2);
framebufferMakeLinear(&fb);
appletInitializeGamePlayRecording();//Normally this is only recording func you need to call.
// Configure our supported input layout: a single player with standard controller styles
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
// Initialize the default gamepad (which reads handheld mode inputs as well as the first connected controller)
PadState pad;
padInitializeDefault(&pad);
u32 cnt = 0;
while(appletMainLoop())
{
// Scan the gamepad. This should be done once for each frame
padUpdate(&pad);
// padGetButtonsDown returns the set of buttons that have been newly pressed in this frame compared to the previous one
u64 kDown = padGetButtonsDown(&pad);
if (kDown & HidNpadButton_Plus) break; // break in order to return to hbmenu
if (kDown & HidNpadButton_Y) {
appletSetGamePlayRecordingState(0);//Disable recording.
}
else if (kDown & HidNpadButton_X) {
appletSetGamePlayRecordingState(1);//Enable recording.
}
// Retrieve the framebuffer
u32 stride;
u32* framebuf = (u32*) framebufferBegin(&fb, &stride);
if (cnt != 60)
cnt ++;
else
cnt = 0;
// Each pixel is 4-bytes due to RGBA8888.
for (u32 y = 0; y < FB_HEIGHT; y ++)
{
for (u32 x = 0; x < FB_WIDTH; x ++)
{
u32 pos = y * stride / sizeof(u32) + x;
framebuf[pos] = 0x01010101 * cnt * 4;//Set framebuf to different shades of grey.
}
}
// We're done rendering, so we end the frame here.
framebufferEnd(&fb);
}
framebufferClose(&fb);
return 0;
}

View File

@ -0,0 +1,222 @@
#---------------------------------------------------------------------------------
.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 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
# 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
#
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
# If not set, it attempts to use one of the following (in this order):
# - <Project name>.json
# - config.json
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
# NACP building is skipped as well.
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := source
DATA := data
INCLUDES := include
#ROMFS := romfs
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -g -Wall -O2 -ffunction-sections \
$(ARCH) $(DEFINES)
CFLAGS += $(INCLUDE) -D__SWITCH__
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
LIBS := -lnx
#---------------------------------------------------------------------------------
# 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 := $(CURDIR)/$(TARGET)
export TOPDIR := $(CURDIR)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD)
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_BIN := $(addsuffix .o,$(BINFILES))
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
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)
ifeq ($(strip $(CONFIG_JSON)),)
jsons := $(wildcard *.json)
ifneq (,$(findstring $(TARGET).json,$(jsons)))
export APP_JSON := $(TOPDIR)/$(TARGET).json
else
ifneq (,$(findstring config.json,$(jsons)))
export APP_JSON := $(TOPDIR)/config.json
endif
endif
else
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
endif
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
.PHONY: $(BUILD) clean all
#---------------------------------------------------------------------------------
all: $(BUILD)
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
ifeq ($(strip $(APP_JSON)),)
@rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
else
@rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
endif
#---------------------------------------------------------------------------------
else
.PHONY: all
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
ifeq ($(strip $(APP_JSON)),)
all : $(OUTPUT).nro
ifeq ($(strip $(NO_NACP)),)
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp
else
$(OUTPUT).nro : $(OUTPUT).elf
endif
else
all : $(OUTPUT).nsp
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
$(OUTPUT).nso : $(OUTPUT).elf
endif
$(OUTPUT).elf : $(OFILES)
$(OFILES_SRC) : $(HFILES_BIN)
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o %_bin.h : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

View File

@ -0,0 +1,83 @@
// Include the most common headers from the C standard library
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Include the main libnx system header, for Switch development
#include <switch.h>
// This example shows how to use applet ApplicationCopyright (used as a screenshot overlay), see also libnx applet.h.
// Main program entrypoint
int main(int argc, char* argv[])
{
// This example uses a text console, as a simple way to output text to the screen.
// If you want to write a software-rendered graphics application,
// take a look at the graphics/simplegfx example, which uses the libnx Framebuffer API instead.
// If on the other hand you want to write an OpenGL based application,
// take a look at the graphics/opengl set of examples, which uses EGL instead.
consoleInit(NULL);
// Configure our supported input layout: a single player with standard controller styles
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
// Initialize the default gamepad (which reads handheld mode inputs as well as the first connected controller)
PadState pad;
padInitializeDefault(&pad);
printf("screenshot-overlay example\n");
// Setup the RGBA8 image.
Result rc=0;
size_t size = 128*128*4;
u8 *tmpbuf = malloc(size);
if (tmpbuf)
memset(tmpbuf, 0xff, size); // Set the image to all white.
else {
rc = 1;
printf("Failed to allocate memory.\n");
}
// Initialize.
if (R_SUCCEEDED(rc)) {
rc = appletInitializeApplicationCopyrightFrameBuffer();
printf("appletInitializeApplicationCopyrightFrameBuffer(): 0x%x\n", rc);
}
// Set the image.
if (R_SUCCEEDED(rc)) {
rc = appletSetApplicationCopyrightImage(tmpbuf, size, 0, 0, 128, 128, 1);
printf("appletSetApplicationCopyrightImage(): 0x%x\n", rc);
}
// Set the overlay visibility in the screenshot. This is set to true by default, so normally this is only needed for disabling/re-enabling visibility.
if (R_SUCCEEDED(rc)) {
rc = appletSetApplicationCopyrightVisibility(true);
printf("appletSetApplicationCopyrightVisibility(): 0x%x\n", rc);
}
free(tmpbuf);
printf("Press + to exit.\n");
// Main loop
while (appletMainLoop())
{
// Scan the gamepad. This should be done once for each frame
padUpdate(&pad);
// padGetButtonsDown returns the set of buttons that have been
// newly pressed in this frame compared to the previous one
u64 kDown = padGetButtonsDown(&pad);
if (kDown & HidNpadButton_Plus)
break; // break in order to return to hbmenu
// Update the console, sending a new frame to the display
consoleUpdate(NULL);
}
// Deinitialize and clean up resources used by the console (important!)
consoleExit(NULL);
return 0;
}

222
applet/vrmode/Makefile Normal file
View File

@ -0,0 +1,222 @@
#---------------------------------------------------------------------------------
.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 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
# 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
#
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
# If not set, it attempts to use one of the following (in this order):
# - <Project name>.json
# - config.json
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
# NACP building is skipped as well.
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := source
DATA := data
INCLUDES := include
#ROMFS := romfs
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -g -Wall -O2 -ffunction-sections \
$(ARCH) $(DEFINES)
CFLAGS += $(INCLUDE) -D__SWITCH__
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
LIBS := -lnx
#---------------------------------------------------------------------------------
# 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 := $(CURDIR)/$(TARGET)
export TOPDIR := $(CURDIR)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD)
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_BIN := $(addsuffix .o,$(BINFILES))
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
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)
ifeq ($(strip $(CONFIG_JSON)),)
jsons := $(wildcard *.json)
ifneq (,$(findstring $(TARGET).json,$(jsons)))
export APP_JSON := $(TOPDIR)/$(TARGET).json
else
ifneq (,$(findstring config.json,$(jsons)))
export APP_JSON := $(TOPDIR)/config.json
endif
endif
else
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
endif
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
.PHONY: $(BUILD) clean all
#---------------------------------------------------------------------------------
all: $(BUILD)
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
ifeq ($(strip $(APP_JSON)),)
@rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
else
@rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
endif
#---------------------------------------------------------------------------------
else
.PHONY: all
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
ifeq ($(strip $(APP_JSON)),)
all : $(OUTPUT).nro
ifeq ($(strip $(NO_NACP)),)
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp
else
$(OUTPUT).nro : $(OUTPUT).elf
endif
else
all : $(OUTPUT).nsp
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
$(OUTPUT).nso : $(OUTPUT).elf
endif
$(OUTPUT).elf : $(OFILES)
$(OFILES_SRC) : $(HFILES_BIN)
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o %_bin.h : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

106
applet/vrmode/source/main.c Normal file
View File

@ -0,0 +1,106 @@
// Include the most common headers from the C standard library
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Include the main libnx system header, for Switch development
#include <switch.h>
// This example shows how to use VrMode, see also libnx applet.h and pctl.h.
// TODO: This needs replaced with GL rendering, to handle the special VrMode rendering.
// When running on pre-7.0.0, hbl has to run under the labo-vr application for the host application.
// Main program entrypoint
int main(int argc, char* argv[])
{
Result rc = 0;
bool pctlinit=0;
// This example uses a text console, as a simple way to output text to the screen.
// If you want to write a software-rendered graphics application,
// take a look at the graphics/simplegfx example, which uses the libnx Framebuffer API instead.
// If on the other hand you want to write an OpenGL based application,
// take a look at the graphics/opengl set of examples, which uses EGL instead.
consoleInit(NULL);
// Configure our supported input layout: a single player with standard controller styles
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
// Initialize the default gamepad (which reads handheld mode inputs as well as the first connected controller)
PadState pad;
padInitializeDefault(&pad);
printf("VrMode example\n");
// Using pctl is optional.
rc = pctlInitialize();
if (R_FAILED(rc)) printf("pctlInitialize(): 0x%x\n", rc);
if (R_SUCCEEDED(rc)) pctlinit = 1;
if (pctlinit) {
printf("Press A to enable VrMode, B to disable, and X to check VrMode.\n");
}
else {
printf("VrMode functionality disabled since pctlInitialize failed.\n");
}
printf("Press + to exit.\n");
// Main loop
while (appletMainLoop())
{
// Scan the gamepad. This should be done once for each frame
padUpdate(&pad);
// padGetButtonsDown returns the set of buttons that have been
// newly pressed in this frame compared to the previous one
u64 kDown = padGetButtonsDown(&pad);
if (kDown & HidNpadButton_Plus)
break; // break in order to return to hbmenu
if (pctlinit) {
if (kDown & HidNpadButton_A) {
// Not sure what this is.
/*rc = pctlConfirmStereoVisionPermission();
if (R_FAILED(rc)) printf("pctlConfirmStereoVisionPermission(): 0x%x\n", rc);*/
bool tmpflag=0;
rc = pctlIsStereoVisionPermitted(&tmpflag);
if (R_FAILED(rc)) printf("pctlIsStereoVisionPermitted(): 0x%x\n", rc);
if (!tmpflag) printf("Parental Controls doesn't allow using VrMode.\n");
if (R_SUCCEEDED(rc) && tmpflag) {
rc = appletSetVrModeEnabled(true);
printf("appletSetVrModeEnabled(true): 0x%x\n", rc);
}
}
else if (kDown & HidNpadButton_B) {
rc = appletSetVrModeEnabled(false);
printf("appletSetVrModeEnabled(false): 0x%x\n", rc);
// See above comment.
/*rc = pctlResetConfirmedStereoVisionPermission();
printf("pctlResetConfirmedStereoVisionPermission(): 0x%x\n", rc);*/
}
else if (kDown & HidNpadButton_X) {
bool flag=0;
rc = appletIsVrModeEnabled(&flag);
printf("appletIsVrModeEnabled(): 0x%x", rc);
if (R_SUCCEEDED(rc)) printf(", %d\n", flag);
printf("\n");
}
}
// Update the console, sending a new frame to the display
consoleUpdate(NULL);
}
if (pctlinit) pctlExit();
// Deinitialize and clean up resources used by the console (important!)
consoleExit(NULL);
return 0;
}

View File

@ -15,7 +15,6 @@ include $(DEVKITPRO)/libnx/switch_rules
# 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.
@ -29,26 +28,33 @@ include $(DEVKITPRO)/libnx/switch_rules
# - <Project name>.jpg
# - icon.jpg
# - <libnx folder>/default_icon.jpg
#
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
# If not set, it attempts to use one of the following (in this order):
# - <Project name>.json
# - config.json
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
# NACP building is skipped as well.
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := source
DATA := data
INCLUDES := include
EXEFS_SRC := exefs_src
#ROMFS := romfs
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -g -Wall -O2 -ffunction-sections -save-temps \
CFLAGS := -g -Wall -O2 -ffunction-sections \
$(ARCH) $(DEFINES)
CFLAGS += $(INCLUDE) -D__SWITCH__
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
@ -107,7 +113,18 @@ export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC)
ifeq ($(strip $(CONFIG_JSON)),)
jsons := $(wildcard *.json)
ifneq (,$(findstring $(TARGET).json,$(jsons)))
export APP_JSON := $(TOPDIR)/$(TARGET).json
else
ifneq (,$(findstring config.json,$(jsons)))
export APP_JSON := $(TOPDIR)/config.json
endif
endif
else
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
endif
ifeq ($(strip $(ICON)),)
icons := $(wildcard *.jpg)
@ -150,7 +167,11 @@ $(BUILD):
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) $(TARGET).pfs0 $(TARGET).nso $(TARGET).nro $(TARGET).nacp $(TARGET).elf
ifeq ($(strip $(APP_JSON)),)
@rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
else
@rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
endif
#---------------------------------------------------------------------------------
@ -162,11 +183,9 @@ DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
all : $(OUTPUT).pfs0 $(OUTPUT).nro
ifeq ($(strip $(APP_JSON)),)
$(OUTPUT).pfs0 : $(OUTPUT).nso
$(OUTPUT).nso : $(OUTPUT).elf
all : $(OUTPUT).nro
ifeq ($(strip $(NO_NACP)),)
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp
@ -174,6 +193,16 @@ else
$(OUTPUT).nro : $(OUTPUT).elf
endif
else
all : $(OUTPUT).nsp
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
$(OUTPUT).nso : $(OUTPUT).elf
endif
$(OUTPUT).elf : $(OFILES)
$(OFILES_SRC) : $(HFILES_BIN)

View File

@ -11,9 +11,15 @@
int main(void)
{
gfxInitDefault();
consoleInit(NULL);
// Configure our supported input layout: a single player with standard controller styles
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
// Initialize the default gamepad (which reads handheld mode inputs as well as the first connected controller)
PadState pad;
padInitializeDefault(&pad);
printf("Simple audren demonstration program\n");
static const AudioRendererConfig arConfig =
@ -58,7 +64,7 @@ int main(void)
audrvMemPoolAttach(&drv, mpid);
static const u8 sink_channels[] = { 0, 1 };
int sink = audrvDeviceSinkAdd(&drv, AUDREN_DEFAULT_DEVICE_NAME, 2, sink_channels);
/*int sink =*/ audrvDeviceSinkAdd(&drv, AUDREN_DEFAULT_DEVICE_NAME, 2, sink_channels);
res = audrvUpdate(&drv);
printf("audrvUpdate: %" PRIx32 "\n", res);
@ -78,17 +84,16 @@ int main(void)
// Main loop
while (appletMainLoop())
{
gfxSwapBuffers();
hidScanInput();
padUpdate(&pad);
u64 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
u64 kDown = padGetButtonsDown(&pad);
if (kDown & KEY_PLUS)
if (kDown & HidNpadButton_Plus)
break;
if (initedDriver)
{
if (kDown & KEY_A)
if (kDown & HidNpadButton_A)
{
audrvVoiceStop(&drv, 0);
audrvVoiceAddWaveBuf(&drv, 0, &wavebuf);
@ -102,7 +107,7 @@ int main(void)
printf("sample count = %" PRIu32 "\n", audrvVoiceGetPlayedSampleCount(&drv, 0));
}
gfxFlushBuffers();
consoleUpdate(NULL);
}
if (initedDriver)
@ -110,6 +115,6 @@ int main(void)
if (initedAudren)
audrenExit();
gfxExit();
consoleExit(NULL);
return 0;
}

View File

@ -15,7 +15,6 @@ include $(DEVKITPRO)/libnx/switch_rules
# 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.
@ -29,26 +28,33 @@ include $(DEVKITPRO)/libnx/switch_rules
# - <Project name>.jpg
# - icon.jpg
# - <libnx folder>/default_icon.jpg
#
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
# If not set, it attempts to use one of the following (in this order):
# - <Project name>.json
# - config.json
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
# NACP building is skipped as well.
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := source
DATA := data
INCLUDES := include
EXEFS_SRC := exefs_src
#ROMFS := romfs
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -g -Wall -O2 -ffunction-sections \
$(ARCH) $(DEFINES)
CFLAGS += $(INCLUDE) -D__SWITCH__
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
@ -59,7 +65,7 @@ LIBS := -lnx -lm
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS := $(PORTLIBS) $(LIBNX)
LIBDIRS := $(PORTLIBS) $(LIBNX)
#---------------------------------------------------------------------------------
@ -107,7 +113,18 @@ export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC)
ifeq ($(strip $(CONFIG_JSON)),)
jsons := $(wildcard *.json)
ifneq (,$(findstring $(TARGET).json,$(jsons)))
export APP_JSON := $(TOPDIR)/$(TARGET).json
else
ifneq (,$(findstring config.json,$(jsons)))
export APP_JSON := $(TOPDIR)/config.json
endif
endif
else
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
endif
ifeq ($(strip $(ICON)),)
icons := $(wildcard *.jpg)
@ -150,23 +167,25 @@ $(BUILD):
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) $(TARGET).pfs0 $(TARGET).nso $(TARGET).nro $(TARGET).nacp $(TARGET).elf
ifeq ($(strip $(APP_JSON)),)
@rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
else
@rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
endif
#---------------------------------------------------------------------------------
else
.PHONY: all
.PHONY: all
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
all : $(OUTPUT).pfs0 $(OUTPUT).nro
ifeq ($(strip $(APP_JSON)),)
$(OUTPUT).pfs0 : $(OUTPUT).nso
$(OUTPUT).nso : $(OUTPUT).elf
all : $(OUTPUT).nro
ifeq ($(strip $(NO_NACP)),)
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp
@ -174,6 +193,16 @@ else
$(OUTPUT).nro : $(OUTPUT).elf
endif
else
all : $(OUTPUT).nsp
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
$(OUTPUT).nso : $(OUTPUT).elf
endif
$(OUTPUT).elf : $(OFILES)
$(OFILES_SRC) : $(HFILES_BIN)

View File

@ -18,23 +18,28 @@
int main(int argc, char **argv)
{
Result rc = 0;
gfxInitDefault();
// Initialize console. Using NULL as the second argument tells the console library to use the internal console structure as current one.
consoleInit(NULL);
// Configure our supported input layout: a single player with standard controller styles
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
// Initialize the default gamepad (which reads handheld mode inputs as well as the first connected controller)
PadState pad;
padInitializeDefault(&pad);
AudioInBuffer audin_buf;
AudioOutBuffer audout_buf;
AudioInBuffer *released_in_buffer;
AudioOutBuffer *released_out_buffer;
u32 released_in_count;
u32 released_out_count;
// Make sure the sample buffer size is aligned to 0x1000 bytes.
u32 data_size = (SAMPLECOUNT * CHANNELCOUNT * BYTESPERSAMPLE);
u32 buffer_size = (data_size + 0xfff) & ~0xfff;
// Allocate the buffers.
u8* in_buf_data = memalign(0x1000, buffer_size);
u8* out_buf_data = memalign(0x1000, buffer_size);
@ -45,99 +50,98 @@ int main(int argc, char **argv)
rc = MAKERESULT(Module_Libnx, LibnxError_OutOfMemory);
printf("Failed to allocate sample data buffers\n");
}
if (R_SUCCEEDED(rc))
{
memset(in_buf_data, 0, buffer_size);
memset(out_buf_data, 0, buffer_size);
}
if (R_SUCCEEDED(rc))
{
// Initialize the default audio input device.
rc = audinInitialize();
printf("audinInitialize() returned 0x%x\n", rc);
}
if (R_SUCCEEDED(rc))
{
// Initialize the default audio output device.
rc = audoutInitialize();
printf("audoutInitialize() returned 0x%x\n", rc);
}
if (R_SUCCEEDED(rc))
{
// Start audio capture.
rc = audinStartAudioIn();
printf("audinStartAudioIn() returned 0x%x\n", rc);
}
if (R_SUCCEEDED(rc))
{
// Start audio playback.
rc = audoutStartAudioOut();
printf("audoutStartAudioOut() returned 0x%x\n", rc);
}
// Prepare the input buffer.
audin_buf.next = NULL;
audin_buf.buffer = in_buf_data;
audin_buf.buffer_size = buffer_size;
audin_buf.data_size = data_size;
audin_buf.data_offset = 0;
// Prepare the output buffer.
audout_buf.next = NULL;
audout_buf.buffer = out_buf_data;
audout_buf.buffer_size = buffer_size;
audout_buf.data_size = data_size;
audout_buf.data_offset = 0;
// Prepare pointers and counters for released buffers.
released_in_buffer = NULL;
released_out_buffer = NULL;
released_in_count = 0;
released_out_count = 0;
// Append the initial input buffer.
rc = audinAppendAudioInBuffer(&audin_buf);
printf("audinAppendAudioInBuffer() returned 0x%x\n", rc);
// Append the initial output buffer.
rc = audoutAppendAudioOutBuffer(&audout_buf);
printf("audoutAppendAudioOutBuffer() returned 0x%x\n", rc);
while (appletMainLoop())
{
//Scan all the inputs. This should be done once for each frame
hidScanInput();
// Scan the gamepad. This should be done once for each frame
padUpdate(&pad);
//hidKeysDown returns information about which buttons have been just pressed (and they weren't in the previous frame)
u64 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
// padGetButtonsDown returns the set of buttons that have been newly pressed in this frame compared to the previous one
u64 kDown = padGetButtonsDown(&pad);
if (kDown & HidNpadButton_Plus) break; // break in order to return to hbmenu
if (kDown & KEY_PLUS) break; // break in order to return to hbmenu
// Wait for audio capture and playback to finish.
audinWaitCaptureFinish(&released_in_buffer, &released_in_count, U64_MAX);
audoutWaitPlayFinish(&released_out_buffer, &released_out_count, U64_MAX);
audinWaitCaptureFinish(&released_in_buffer, &released_in_count, UINT64_MAX);
audoutWaitPlayFinish(&released_out_buffer, &released_out_count, UINT64_MAX);
// Copy the captured audio data into the playback buffer.
if ((released_in_buffer != NULL) && (released_out_buffer != NULL))
memcpy(released_out_buffer->buffer, released_in_buffer->buffer, released_in_buffer->data_size);
// Append the released buffers again.
audinAppendAudioInBuffer(released_in_buffer);
audoutAppendAudioOutBuffer(released_out_buffer);
gfxFlushBuffers();
gfxSwapBuffers();
consoleUpdate(NULL);
}
// Stop audio capture.
rc = audinStopAudioIn();
printf("audinStopAudioIn() returned 0x%x\n", rc);
// Stop audio playback.
rc = audoutStopAudioOut();
printf("audoutStopAudioOut() returned 0x%x\n", rc);
@ -145,7 +149,7 @@ int main(int argc, char **argv)
// Terminate the default audio devices.
audinExit();
audoutExit();
gfxExit();
consoleExit(NULL);
return 0;
}

View File

@ -0,0 +1,228 @@
#---------------------------------------------------------------------------------
.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 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
# 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
#
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
# If not set, it attempts to use one of the following (in this order):
# - <Project name>.json
# - config.json
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
# NACP building is skipped as well.
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := source
DATA := data
INCLUDES := include
#ROMFS := romfs
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -g -Wall -O2 -ffunction-sections \
$(ARCH) $(DEFINES)
CFLAGS += $(INCLUDE) -D__SWITCH__ `aarch64-none-elf-pkg-config --cflags opusfile`
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
LIBS := -lnx `aarch64-none-elf-pkg-config --libs opusfile`
#---------------------------------------------------------------------------------
# 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 := $(CURDIR)/$(TARGET)
export TOPDIR := $(CURDIR)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD)
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_BIN := $(addsuffix .o,$(BINFILES))
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
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)
ifeq ($(strip $(CONFIG_JSON)),)
jsons := $(wildcard *.json)
ifneq (,$(findstring $(TARGET).json,$(jsons)))
export APP_JSON := $(TOPDIR)/$(TARGET).json
else
ifneq (,$(findstring config.json,$(jsons)))
export APP_JSON := $(TOPDIR)/config.json
endif
endif
else
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
endif
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
.PHONY: $(BUILD) clean all
#---------------------------------------------------------------------------------
all: $(BUILD)
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
ifeq ($(strip $(APP_JSON)),)
@rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
else
@rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
endif
#---------------------------------------------------------------------------------
else
.PHONY: all
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
ifeq ($(strip $(APP_JSON)),)
all : $(OUTPUT).nro
ifeq ($(strip $(NO_NACP)),)
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp
else
$(OUTPUT).nro : $(OUTPUT).elf
endif
else
all : $(OUTPUT).nsp
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
$(OUTPUT).nso : $(OUTPUT).elf
endif
$(OUTPUT).elf : $(OFILES)
$(OFILES_SRC) : $(HFILES_BIN)
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o %_bin.h : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
#---------------------------------------------------------------------------------
%.opus.o %_opus.h : %.opus
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

Binary file not shown.

View File

@ -0,0 +1,262 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <inttypes.h>
#include <switch.h>
#include <opus/opusfile.h>
#include "sample_opus.h"
// Sample comes from this website:
// https://www.soundjay.com/magic-sound-effect.html
//Example for playing audio decoded with hwopus using audren. This decodes Opus in hardware. For encoding this is not available with hwopus, hence that has to be done in software.
//Requires package switch-opusfile.
//This uses libopusfile, see also the docs for that.
//Note that actual apps should handle audio on a dedicated thread.
static size_t opuspkt_tmpbuf_size = sizeof(HwopusHeader) + 4096*48;
static u8* opuspkt_tmpbuf;
int hw_decode(void *_ctx, OpusMSDecoder *_decoder, void *_pcm, const ogg_packet *_op, int _nsamples, int _nchannels, int _format, int _li) {
HwopusDecoder *decoder = (HwopusDecoder*)_ctx;
HwopusHeader *hdr = NULL;
size_t pktsize, pktsize_extra;
Result rc = 0;
s32 DecodedDataSize = 0;
s32 DecodedSampleCount = 0;
if (_format != OP_DEC_FORMAT_SHORT) return OPUS_BAD_ARG;
pktsize = _op->bytes;//Opus packet size.
pktsize_extra = pktsize+8;//Packet size with HwopusHeader.
if (pktsize_extra > opuspkt_tmpbuf_size) return OPUS_INTERNAL_ERROR;
hdr = (HwopusHeader*)opuspkt_tmpbuf;
memset(opuspkt_tmpbuf, 0, pktsize_extra);
hdr->size = __builtin_bswap32(pktsize);
memcpy(&opuspkt_tmpbuf[sizeof(HwopusHeader)], _op->packet, pktsize);
rc = hwopusDecodeInterleaved(decoder, &DecodedDataSize, &DecodedSampleCount, opuspkt_tmpbuf, pktsize_extra, _pcm, _nsamples * _nchannels * sizeof(opus_int16));
if (R_FAILED(rc)) return OPUS_INTERNAL_ERROR;
if (DecodedDataSize != pktsize_extra || DecodedSampleCount != _nsamples) return OPUS_INVALID_PACKET;
return 0;
}
int main(void)
{
consoleInit(NULL);
// Configure our supported input layout: a single player with standard controller styles
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
// Initialize the default gamepad (which reads handheld mode inputs as well as the first connected controller)
PadState pad;
padInitializeDefault(&pad);
printf("Simple hwopus-decoder example with audren\n");
static const AudioRendererConfig arConfig =
{
.output_rate = AudioRendererOutputRate_48kHz,
.num_voices = 24,
.num_effects = 0,
.num_sinks = 1,
.num_mix_objs = 1,
.num_mix_buffers = 2,
};
size_t num_channels = 1;
size_t samplerate = 48000;
size_t max_samples = samplerate/20;//each wavebuf can hold upto 1sec/20 = 50ms of audio data
size_t max_samples_datasize = max_samples*num_channels*sizeof(opus_int16);
size_t mempool_size = (max_samples_datasize*4 + 0xFFF) &~ 0xFFF;//*4 for 4 wavebufs.
void* mempool_ptr = memalign(0x1000, mempool_size);
void* tmpdata_ptr = malloc(max_samples_datasize);
opuspkt_tmpbuf = (u8*)malloc(opuspkt_tmpbuf_size);
opus_int16* curbuf = NULL;
AudioDriverWaveBuf wavebuf[4] = {0};
int i, wavei;
HwopusDecoder hwdecoder = {0};
AudioDriver drv;
Result res=0;
bool initedDriver = false;
bool initedAudren = false;
bool audio_playing = false;
int opret=0;
int total_samples_size=0;
OggOpusFile *of = NULL;
if (mempool_ptr) memset(mempool_ptr, 0, mempool_size);
if (tmpdata_ptr) memset(tmpdata_ptr, 0, max_samples_datasize);
if (opuspkt_tmpbuf) memset(opuspkt_tmpbuf, 0, opuspkt_tmpbuf_size);
if (mempool_ptr==NULL || tmpdata_ptr==NULL || opuspkt_tmpbuf==NULL) {
res = 1;
printf("Failed to allocate memory.\n");
}
if (R_SUCCEEDED(res)) {
res = hwopusDecoderInitialize(&hwdecoder, samplerate, num_channels);//This assumes that the opus file is <samplerate> with <num_channels>.
if (R_FAILED(res))
printf("hwopusDecoderInitialize: %08" PRIx32 "\n", res);
else
{
res = audrenInitialize(&arConfig);
initedAudren = R_SUCCEEDED(res);
if (!initedAudren)
printf("audrenInitialize: %08" PRIx32 "\n", res);
else
{
printf("audren initted!\n");
res = audrvCreate(&drv, &arConfig, 2);
initedDriver = R_SUCCEEDED(res);
if (R_FAILED(res))
printf("audrvCreate: %08" PRIx32 "\n", res);
else
{
int mpid = audrvMemPoolAdd(&drv, mempool_ptr, mempool_size);
audrvMemPoolAttach(&drv, mpid);
static const u8 sink_channels[] = { 0, 1 };
/*int sink =*/ audrvDeviceSinkAdd(&drv, AUDREN_DEFAULT_DEVICE_NAME, 2, sink_channels);
res = audrvUpdate(&drv);
printf("audrvUpdate: %" PRIx32 "\n", res);
res = audrenStartAudioRenderer();
printf("audrenStartAudioRenderer: %" PRIx32 "\n", res);
audrvVoiceInit(&drv, 0, num_channels, PcmFormat_Int16, samplerate);
audrvVoiceSetDestinationMix(&drv, 0, AUDREN_FINAL_MIX_ID);
if (num_channels == 1) {//mono
audrvVoiceSetMixFactor(&drv, 0, 1.0f, 0, 0);
audrvVoiceSetMixFactor(&drv, 0, 1.0f, 0, 1);
}
else {//stereo
audrvVoiceSetMixFactor(&drv, 0, 1.0f, 0, 0);
audrvVoiceSetMixFactor(&drv, 0, 0.0f, 0, 1);
audrvVoiceSetMixFactor(&drv, 0, 0.0f, 1, 0);
audrvVoiceSetMixFactor(&drv, 0, 1.0f, 1, 1);
}
audrvVoiceStart(&drv, 0);
for(i=0; i<4; i++) {
wavebuf[i].data_raw = mempool_ptr;
wavebuf[i].size = max_samples_datasize*4;//*4 for 4 wavebufs.
wavebuf[i].start_sample_offset = i * max_samples;
wavebuf[i].end_sample_offset = wavebuf[i].start_sample_offset + max_samples;
}
}
}
}
}
if (initedDriver)
printf("done. Press A to play a sound.\n");
else
printf("Init failed.\n");
// Main loop
while (appletMainLoop())
{
padUpdate(&pad);
u64 kDown = padGetButtonsDown(&pad);
if (kDown & HidNpadButton_Plus)
break;
if (initedDriver)
{
if (kDown & HidNpadButton_A)
{
//Close the opus-file if needed and (re)open it, since libopusfile doesn't support seek-to-beginning.
if (of)
op_free(of);
of = op_open_memory (sample_opus, sample_opus_size, NULL);
if (of == NULL) {
printf("Failed to open OggOpusFile.\n");
}
else {
op_set_decode_callback(of, hw_decode, &hwdecoder);
audrvVoiceStop(&drv, 0);
audio_playing = true;
}
}
if (audio_playing) {
wavei = -1;
for(i=0; i<4; i++) {
if (wavebuf[i].state == AudioDriverWaveBufState_Free || wavebuf[i].state == AudioDriverWaveBufState_Done) {
wavei = i;
break;
}
}
if (wavei >= 0) {
curbuf = (opus_int16*)(mempool_ptr + wavebuf[wavei].start_sample_offset * sizeof(opus_int16));
opret = op_read(of, tmpdata_ptr, max_samples * num_channels, NULL);//The buffer used here has to be seperate from mempool_ptr.
if (opret < 0)
printf("op_read() failed: %d\n", opret);
else if (opret == 0) {//End of file reached (see also libopusfile docs).
audio_playing = false;
if (of)
op_free(of);
of = NULL;
}
else {
if (opret > max_samples) opret = max_samples;//Should never happen.
total_samples_size = opret*sizeof(opus_int16);//Total samples data-size per channel.
memcpy(curbuf, tmpdata_ptr, total_samples_size);
armDCacheFlush(curbuf, total_samples_size);
wavebuf[wavei].end_sample_offset = wavebuf[wavei].start_sample_offset + total_samples_size/sizeof(opus_int16);
audrvVoiceAddWaveBuf(&drv, 0, &wavebuf[wavei]);
}
}
else {
if (!audrvVoiceIsPlaying(&drv, 0))
audrvVoiceStart(&drv, 0);
}
}
res = audrvUpdate(&drv);
if (R_FAILED(res))
printf("audrvUpdate: %" PRIx32 "\n", res);
if (audrvVoiceIsPlaying(&drv, 0))
printf("sample count = %" PRIu32 "\n", audrvVoiceGetPlayedSampleCount(&drv, 0));
}
consoleUpdate(NULL);
}
hwopusDecoderExit(&hwdecoder);
if (of)
op_free(of);
if (initedDriver)
audrvClose(&drv);
if (initedAudren)
audrenExit();
free(mempool_ptr);
free(tmpdata_ptr);
free(opuspkt_tmpbuf);
consoleExit(NULL);
return 0;
}

View File

@ -15,7 +15,6 @@ include $(DEVKITPRO)/libnx/switch_rules
# 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.
@ -29,26 +28,33 @@ include $(DEVKITPRO)/libnx/switch_rules
# - <Project name>.jpg
# - icon.jpg
# - <libnx folder>/default_icon.jpg
#
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
# If not set, it attempts to use one of the following (in this order):
# - <Project name>.json
# - config.json
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
# NACP building is skipped as well.
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := source
DATA := data
INCLUDES := include
EXEFS_SRC := exefs_src
#ROMFS := romfs
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -g -Wall -O2 -ffunction-sections \
$(ARCH) $(DEFINES)
CFLAGS += $(INCLUDE) -D__SWITCH__
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
@ -59,7 +65,7 @@ LIBS := -lnx -lm
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS := $(PORTLIBS) $(LIBNX)
LIBDIRS := $(PORTLIBS) $(LIBNX)
#---------------------------------------------------------------------------------
@ -107,7 +113,18 @@ export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC)
ifeq ($(strip $(CONFIG_JSON)),)
jsons := $(wildcard *.json)
ifneq (,$(findstring $(TARGET).json,$(jsons)))
export APP_JSON := $(TOPDIR)/$(TARGET).json
else
ifneq (,$(findstring config.json,$(jsons)))
export APP_JSON := $(TOPDIR)/config.json
endif
endif
else
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
endif
ifeq ($(strip $(ICON)),)
icons := $(wildcard *.jpg)
@ -150,23 +167,25 @@ $(BUILD):
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) $(TARGET).pfs0 $(TARGET).nso $(TARGET).nro $(TARGET).nacp $(TARGET).elf
ifeq ($(strip $(APP_JSON)),)
@rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
else
@rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
endif
#---------------------------------------------------------------------------------
else
.PHONY: all
.PHONY: all
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
all : $(OUTPUT).pfs0 $(OUTPUT).nro
ifeq ($(strip $(APP_JSON)),)
$(OUTPUT).pfs0 : $(OUTPUT).nso
$(OUTPUT).nso : $(OUTPUT).elf
all : $(OUTPUT).nro
ifeq ($(strip $(NO_NACP)),)
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp
@ -174,6 +193,16 @@ else
$(OUTPUT).nro : $(OUTPUT).elf
endif
else
all : $(OUTPUT).nsp
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
$(OUTPUT).nso : $(OUTPUT).elf
endif
$(OUTPUT).elf : $(OFILES)
$(OFILES_SRC) : $(HFILES_BIN)

View File

@ -27,143 +27,148 @@ void fill_audio_buffer(void* audio_buffer, size_t offset, size_t size, int frequ
int main(int argc, char **argv)
{
Result rc = 0;
int notefreq[] = {
220,
440, 880, 1760, 3520, 7040,
14080,
7040, 3520, 1760, 880, 440
};
gfxInitDefault();
// Initialize console. Using NULL as the second argument tells the console library to use the internal console structure as current one.
consoleInit(NULL);
// Configure our supported input layout: a single player with standard controller styles
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
// Initialize the default gamepad (which reads handheld mode inputs as well as the first connected controller)
PadState pad;
padInitializeDefault(&pad);
AudioOutBuffer audout_buf;
AudioOutBuffer *audout_released_buf;
// Make sure the sample buffer size is aligned to 0x1000 bytes.
u32 data_size = (SAMPLECOUNT * CHANNELCOUNT * BYTESPERSAMPLE);
u32 buffer_size = (data_size + 0xfff) & ~0xfff;
// Allocate the buffer.
u8* out_buf_data = memalign(0x1000, buffer_size);
// Ensure buffers were properly allocated.
if (out_buf_data == NULL)
{
rc = MAKERESULT(Module_Libnx, LibnxError_OutOfMemory);
printf("Failed to allocate sample data buffers\n");
}
if (R_SUCCEEDED(rc))
memset(out_buf_data, 0, buffer_size);
if (R_SUCCEEDED(rc))
{
// Initialize the default audio output device.
rc = audoutInitialize();
printf("audoutInitialize() returned 0x%x\n", rc);
}
if (R_SUCCEEDED(rc))
{
printf("Sample rate: 0x%x\n", audoutGetSampleRate());
printf("Channel count: 0x%x\n", audoutGetChannelCount());
printf("PCM format: 0x%x\n", audoutGetPcmFormat());
printf("Device state: 0x%x\n", audoutGetDeviceState());
// Start audio playback.
rc = audoutStartAudioOut();
printf("audoutStartAudioOut() returned 0x%x\n", rc);
}
bool play_tone = false;
printf("Press A, B, Y, X, Left, Up, Right, Down, L, R, ZL or ZR to play a different tone.\n");
while (appletMainLoop())
{
//Scan all the inputs. This should be done once for each frame
hidScanInput();
// Scan the gamepad. This should be done once for each frame
padUpdate(&pad);
//hidKeysDown returns information about which buttons have been just pressed (and they weren't in the previous frame)
u64 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
// padGetButtonsDown returns the set of buttons that have been newly pressed in this frame compared to the previous one
u64 kDown = padGetButtonsDown(&pad);
if (kDown & KEY_PLUS) break; // break in order to return to hbmenu
if (kDown & KEY_A)
if (kDown & HidNpadButton_Plus) break; // break in order to return to hbmenu
if (kDown & HidNpadButton_A)
{
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[0]);
play_tone = true;
}
if (kDown & KEY_B)
if (kDown & HidNpadButton_B)
{
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[1]);
play_tone = true;
}
if (kDown & KEY_Y)
if (kDown & HidNpadButton_Y)
{
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[2]);
play_tone = true;
}
if (kDown & KEY_X)
if (kDown & HidNpadButton_X)
{
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[3]);
play_tone = true;
}
if (kDown & KEY_DLEFT)
if (kDown & HidNpadButton_Left)
{
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[4]);
play_tone = true;
}
if (kDown & KEY_DUP)
if (kDown & HidNpadButton_Up)
{
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[5]);
play_tone = true;
}
if (kDown & KEY_DRIGHT)
if (kDown & HidNpadButton_Right)
{
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[6]);
play_tone = true;
}
if (kDown & KEY_DDOWN)
if (kDown & HidNpadButton_Down)
{
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[7]);
play_tone = true;
}
if (kDown & KEY_L)
if (kDown & HidNpadButton_L)
{
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[8]);
play_tone = true;
}
if (kDown & KEY_R)
if (kDown & HidNpadButton_R)
{
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[9]);
play_tone = true;
}
if (kDown & KEY_ZL)
if (kDown & HidNpadButton_ZL)
{
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[10]);
play_tone = true;
}
if (kDown & KEY_ZR)
if (kDown & HidNpadButton_ZR)
{
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[11]);
play_tone = true;
}
if (R_SUCCEEDED(rc) && play_tone)
{
// Prepare the audio data source buffer.
@ -172,30 +177,29 @@ int main(int argc, char **argv)
audout_buf.buffer_size = buffer_size;
audout_buf.data_size = data_size;
audout_buf.data_offset = 0;
// Prepare pointer for the released buffer.
audout_released_buf = NULL;
// Play the buffer.
rc = audoutPlayBuffer(&audout_buf, &audout_released_buf);
if (R_FAILED(rc))
printf("audoutPlayBuffer() returned 0x%x\n", rc);
play_tone = false;
}
gfxFlushBuffers();
gfxSwapBuffers();
consoleUpdate(NULL);
}
// Stop audio playback.
rc = audoutStopAudioOut();
printf("audoutStopAudioOut() returned 0x%x\n", rc);
// Terminate the default audio output device.
audoutExit();
gfxExit();
consoleExit(NULL);
return 0;
}

213
audio/sdl2-audio/Makefile Normal file
View File

@ -0,0 +1,213 @@
#---------------------------------------------------------------------------------
.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 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
# 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
#
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
# If not set, it attempts to use one of the following (in this order):
# - <Project name>.json
# - config.json
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
# NACP building is skipped as well.
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := source
DATA := data
INCLUDES := include
ROMFS := romfs
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -g -Wall -O2 -ffunction-sections \
$(ARCH) $(DEFINES)
CFLAGS += -D__SWITCH__ $(INCLUDE) `$(PREFIX)pkg-config --cflags SDL2_mixer`
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
LIBS := `$(PREFIX)pkg-config --libs SDL2_mixer`
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS := $(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 := $(CURDIR)/$(TARGET)
export TOPDIR := $(CURDIR)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD)
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 since SDL2 is dependent on libstdc++
#---------------------------------------------------------------------------------
export LD := $(CXX)
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
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)
ifeq ($(strip $(CONFIG_JSON)),)
jsons := $(wildcard *.json)
ifneq (,$(findstring $(TARGET).json,$(jsons)))
export APP_JSON := $(TOPDIR)/$(TARGET).json
else
ifneq (,$(findstring config.json,$(jsons)))
export APP_JSON := $(TOPDIR)/config.json
endif
endif
else
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
endif
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
.PHONY: $(BUILD) clean all
#---------------------------------------------------------------------------------
all: $(BUILD)
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
ifeq ($(strip $(APP_JSON)),)
@rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
else
@rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
endif
#---------------------------------------------------------------------------------
else
.PHONY: all
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
ifeq ($(strip $(APP_JSON)),)
all : $(OUTPUT).nro
ifeq ($(strip $(NO_NACP)),)
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp
else
$(OUTPUT).nro : $(OUTPUT).elf
endif
else
all : $(OUTPUT).nsp
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
$(OUTPUT).nso : $(OUTPUT).elf
endif
$(OUTPUT).elf : $(OFILES)
$(OFILES_SRC) : $(HFILES_BIN)
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o %_bin.h : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

Binary file not shown.

View File

@ -0,0 +1,78 @@
// Include the most common headers from the C standard library
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Include the main libnx system header, for Switch development
#include <switch.h>
// Include sdl2 headers
#include <SDL.h>
#include <SDL_mixer.h>
// Main program entrypoint
int main(int argc, char *argv[])
{
// This example uses sdl2 library to play a mp3 file
consoleInit(NULL);
// Configure our supported input layout: a single player with standard controller styles
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
// Initialize the default gamepad (which reads handheld mode inputs as well as the first connected controller)
PadState pad;
padInitializeDefault(&pad);
Result rc = romfsInit();
if (R_FAILED(rc))
printf("romfsInit: %08X\n", rc);
else
printf("Press A button to play the sound!\n");
// Start SDL with audio support
SDL_Init(SDL_INIT_AUDIO);
// Load support for the MP3 format
Mix_Init(MIX_INIT_MP3);
// open 44.1KHz, signed 16bit, system byte order,
// stereo audio, using 4096 byte chunks
Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, MIX_DEFAULT_CHANNELS, 4096);
// Load sound file to use
// Sound from https://freesound.org/people/jens.enk/sounds/434610/
Mix_Music *audio = Mix_LoadMUS("romfs:/test.mp3");
// Main loop
while (appletMainLoop())
{
// Scan the gamepad. This should be done once for each frame
padUpdate(&pad);
// padGetButtonsDown returns the set of buttons that have been
// newly pressed in this frame compared to the previous one
u64 kDown = padGetButtonsDown(&pad);
if (kDown & HidNpadButton_Plus)
break; // break in order to return to hbmenu
if (kDown & HidNpadButton_A)
Mix_PlayMusic(audio, 1); //Play the audio file
// Update the console, sending a new frame to the display
consoleUpdate(NULL);
}
// Free the loaded sound
Mix_FreeMusic(audio);
// Shuts down SDL subsystems
SDL_Quit();
// Deinitialize and clean up resources used by the console (important!)
romfsExit();
consoleExit(NULL);
return 0;
}

222
exception-handler/Makefile Normal file
View File

@ -0,0 +1,222 @@
#---------------------------------------------------------------------------------
.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 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
# 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
#
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
# If not set, it attempts to use one of the following (in this order):
# - <Project name>.json
# - config.json
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
# NACP building is skipped as well.
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := source
DATA := data
INCLUDES := include
#ROMFS := romfs
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -g -Wall -O2 -ffunction-sections \
$(ARCH) $(DEFINES)
CFLAGS += $(INCLUDE) -D__SWITCH__
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
LIBS := -lnx
#---------------------------------------------------------------------------------
# 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 := $(CURDIR)/$(TARGET)
export TOPDIR := $(CURDIR)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD)
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_BIN := $(addsuffix .o,$(BINFILES))
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
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)
ifeq ($(strip $(CONFIG_JSON)),)
jsons := $(wildcard *.json)
ifneq (,$(findstring $(TARGET).json,$(jsons)))
export APP_JSON := $(TOPDIR)/$(TARGET).json
else
ifneq (,$(findstring config.json,$(jsons)))
export APP_JSON := $(TOPDIR)/config.json
endif
endif
else
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
endif
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
.PHONY: $(BUILD) clean all
#---------------------------------------------------------------------------------
all: $(BUILD)
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
ifeq ($(strip $(APP_JSON)),)
@rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
else
@rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
endif
#---------------------------------------------------------------------------------
else
.PHONY: all
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
ifeq ($(strip $(APP_JSON)),)
all : $(OUTPUT).nro
ifeq ($(strip $(NO_NACP)),)
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp
else
$(OUTPUT).nro : $(OUTPUT).elf
endif
else
all : $(OUTPUT).nsp
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
$(OUTPUT).nso : $(OUTPUT).elf
endif
$(OUTPUT).elf : $(OFILES)
$(OFILES_SRC) : $(HFILES_BIN)
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o %_bin.h : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

View File

@ -0,0 +1,85 @@
// Include the most common headers from the C standard library
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Include the main libnx system header, for Switch development
#include <switch.h>
//This example shows how to use userland exception handling. See also libnx init.c and thread_context.h.
alignas(16) u8 __nx_exception_stack[0x1000];
u64 __nx_exception_stack_size = sizeof(__nx_exception_stack);
void __libnx_exception_handler(ThreadExceptionDump *ctx)
{
int i;
FILE *f = fopen("exception_dump", "w");
if(f==NULL)return;
fprintf(f, "error_desc: 0x%x\n", ctx->error_desc);//You can also parse this with ThreadExceptionDesc.
//This assumes AArch64, however you can also use threadExceptionIsAArch64().
for(i=0; i<29; i++)fprintf(f, "[X%d]: 0x%lx\n", i, ctx->cpu_gprs[i].x);
fprintf(f, "fp: 0x%lx\n", ctx->fp.x);
fprintf(f, "lr: 0x%lx\n", ctx->lr.x);
fprintf(f, "sp: 0x%lx\n", ctx->sp.x);
fprintf(f, "pc: 0x%lx\n", ctx->pc.x);
//You could print fpu_gprs if you want.
fprintf(f, "pstate: 0x%x\n", ctx->pstate);
fprintf(f, "afsr0: 0x%x\n", ctx->afsr0);
fprintf(f, "afsr1: 0x%x\n", ctx->afsr1);
fprintf(f, "esr: 0x%x\n", ctx->esr);
fprintf(f, "far: 0x%lx\n", ctx->far.x);
fclose(f);
}
// Main program entrypoint
int main(int argc, char* argv[])
{
// This example uses a text console, as a simple way to output text to the screen.
// If you want to write a software-rendered graphics application,
// take a look at the graphics/simplegfx example, which uses the libnx Framebuffer API instead.
// If on the other hand you want to write an OpenGL based application,
// take a look at the graphics/opengl set of examples, which uses EGL instead.
consoleInit(NULL);
// Configure our supported input layout: a single player with standard controller styles
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
// Initialize the default gamepad (which reads handheld mode inputs as well as the first connected controller)
PadState pad;
padInitializeDefault(&pad);
printf("Triggering crash...\n");
consoleUpdate(NULL);
//Trigger a crash.
*((u64*)8) = 16;
// Main loop
while (appletMainLoop())
{
// Scan the gamepad. This should be done once for each frame
padUpdate(&pad);
// padGetButtonsDown returns the set of buttons that have been
// newly pressed in this frame compared to the previous one
u64 kDown = padGetButtonsDown(&pad);
if (kDown & HidNpadButton_Plus)
break; // break in order to return to hbmenu
// Your code goes here
// Update the console, sending a new frame to the display
consoleUpdate(NULL);
}
// Deinitialize and clean up resources used by the console (important!)
consoleExit(NULL);
return 0;
}

View File

@ -15,7 +15,6 @@ include $(DEVKITPRO)/libnx/switch_rules
# 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.
@ -29,26 +28,33 @@ include $(DEVKITPRO)/libnx/switch_rules
# - <Project name>.jpg
# - icon.jpg
# - <libnx folder>/default_icon.jpg
#
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
# If not set, it attempts to use one of the following (in this order):
# - <Project name>.json
# - config.json
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
# NACP building is skipped as well.
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := source
DATA := data
INCLUDES := include
EXEFS_SRC := exefs_src
ROMFS := romfs
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -g -Wall -O2 -ffunction-sections \
$(ARCH) $(DEFINES)
CFLAGS += $(INCLUDE) -D__SWITCH__
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
@ -107,7 +113,18 @@ export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC)
ifeq ($(strip $(CONFIG_JSON)),)
jsons := $(wildcard *.json)
ifneq (,$(findstring $(TARGET).json,$(jsons)))
export APP_JSON := $(TOPDIR)/$(TARGET).json
else
ifneq (,$(findstring config.json,$(jsons)))
export APP_JSON := $(TOPDIR)/config.json
endif
endif
else
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
endif
ifeq ($(strip $(ICON)),)
icons := $(wildcard *.jpg)
@ -150,7 +167,11 @@ $(BUILD):
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) $(TARGET).pfs0 $(TARGET).nso $(TARGET).nro $(TARGET).nacp $(TARGET).elf
ifeq ($(strip $(APP_JSON)),)
@rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
else
@rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
endif
#---------------------------------------------------------------------------------
@ -162,11 +183,9 @@ DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
all : $(OUTPUT).pfs0 $(OUTPUT).nro
ifeq ($(strip $(APP_JSON)),)
$(OUTPUT).pfs0 : $(OUTPUT).nso
$(OUTPUT).nso : $(OUTPUT).elf
all : $(OUTPUT).nro
ifeq ($(strip $(NO_NACP)),)
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp
@ -174,6 +193,16 @@ else
$(OUTPUT).nro : $(OUTPUT).elf
endif
else
all : $(OUTPUT).nsp
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
$(OUTPUT).nso : $(OUTPUT).elf
endif
$(OUTPUT).elf : $(OFILES)
$(OFILES_SRC) : $(HFILES_BIN)

View File

@ -1,5 +1,2 @@
# romfs
This example shows how to use RomFS embedded in the application.
Currently this is not yet ready for use (automatic RomFS-building missing).

View File

@ -30,9 +30,15 @@ void printfile(const char* path)
int main(int argc, char **argv)
{
gfxInitDefault();
consoleInit(NULL);
// Configure our supported input layout: a single player with standard controller styles
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
// Initialize the default gamepad (which reads handheld mode inputs as well as the first connected controller)
PadState pad;
padInitializeDefault(&pad);
Result rc = romfsInit();
if (R_FAILED(rc))
printf("romfsInit: %08X\n", rc);
@ -47,20 +53,18 @@ int main(int argc, char **argv)
// Main loop
while(appletMainLoop())
{
//Scan all the inputs. This should be done once for each frame
hidScanInput();
// Scan the gamepad. This should be done once for each frame
padUpdate(&pad);
//hidKeysDown returns information about which buttons have been just pressed (and they weren't in the previous frame)
u64 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
// padGetButtonsDown returns the set of buttons that have been newly pressed in this frame compared to the previous one
u64 kDown = padGetButtonsDown(&pad);
if (kDown & KEY_PLUS) break; // break in order to return to hbmenu
if (kDown & HidNpadButton_Plus) break; // break in order to return to hbmenu
gfxFlushBuffers();
gfxSwapBuffers();
consoleUpdate(NULL);
}
romfsExit();
gfxExit();
consoleExit(NULL);
return 0;
}

View File

@ -15,7 +15,6 @@ include $(DEVKITPRO)/libnx/switch_rules
# 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.
@ -29,26 +28,33 @@ include $(DEVKITPRO)/libnx/switch_rules
# - <Project name>.jpg
# - icon.jpg
# - <libnx folder>/default_icon.jpg
#
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
# If not set, it attempts to use one of the following (in this order):
# - <Project name>.json
# - config.json
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
# NACP building is skipped as well.
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := source
DATA := data
INCLUDES := include
EXEFS_SRC := exefs_src
#ROMFS := romfs
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -g -Wall -O2 -ffunction-sections \
$(ARCH) $(DEFINES)
CFLAGS += $(INCLUDE) -D__SWITCH__
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
@ -107,7 +113,18 @@ export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC)
ifeq ($(strip $(CONFIG_JSON)),)
jsons := $(wildcard *.json)
ifneq (,$(findstring $(TARGET).json,$(jsons)))
export APP_JSON := $(TOPDIR)/$(TARGET).json
else
ifneq (,$(findstring config.json,$(jsons)))
export APP_JSON := $(TOPDIR)/config.json
endif
endif
else
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
endif
ifeq ($(strip $(ICON)),)
icons := $(wildcard *.jpg)
@ -150,7 +167,11 @@ $(BUILD):
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) $(TARGET).pfs0 $(TARGET).nso $(TARGET).nro $(TARGET).nacp $(TARGET).elf
ifeq ($(strip $(APP_JSON)),)
@rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
else
@rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
endif
#---------------------------------------------------------------------------------
@ -162,11 +183,9 @@ DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
all : $(OUTPUT).pfs0 $(OUTPUT).nro
ifeq ($(strip $(APP_JSON)),)
$(OUTPUT).pfs0 : $(OUTPUT).nso
$(OUTPUT).nso : $(OUTPUT).elf
all : $(OUTPUT).nro
ifeq ($(strip $(NO_NACP)),)
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp
@ -174,6 +193,16 @@ else
$(OUTPUT).nro : $(OUTPUT).elf
endif
else
all : $(OUTPUT).nsp
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
$(OUTPUT).nso : $(OUTPUT).elf
endif
$(OUTPUT).elf : $(OFILES)
$(OFILES_SRC) : $(HFILES_BIN)

View File

@ -6,34 +6,35 @@
//This example shows how to access savedata for (official) applications/games.
Result get_save(u64 *titleID, u128 *userID)
{
Result get_save(u64 *application_id, AccountUid *uid) {
Result rc=0;
FsSaveDataIterator iterator;
size_t total_entries=0;
FsSaveDataInfoReader reader;
s64 total_entries=0;
FsSaveDataInfo info;
bool found=0;
rc = fsOpenSaveDataIterator(&iterator, FsSaveDataSpaceId_NandUser);//See libnx fs.h.
rc = fsOpenSaveDataInfoReader(&reader, FsSaveDataSpaceId_User);//See libnx fs.h.
if (R_FAILED(rc)) {
printf("fsOpenSaveDataIterator() failed: 0x%x\n", rc);
printf("fsOpenSaveDataInfoReader() failed: 0x%x\n", rc);
return rc;
}
//Find the first savedata with FsSaveDataType_SaveData.
while(1) {
rc = fsSaveDataIteratorRead(&iterator, &info, 1, &total_entries);//See libnx fs.h.
rc = fsSaveDataInfoReaderRead(&reader, &info, 1, &total_entries);//See libnx fs.h.
if (R_FAILED(rc) || total_entries==0) break;
if (info.SaveDataType == FsSaveDataType_SaveData) {//Filter by FsSaveDataType_SaveData, however note that NandUser can have non-FsSaveDataType_SaveData.
*titleID = info.titleID;
*userID = info.userID;
return 0;
if (info.save_data_type == FsSaveDataType_Account) {//Filter by FsSaveDataType_Account, however note that FsSaveDataSpaceId_User can have non-FsSaveDataType_Account.
*application_id = info.application_id;
*uid = info.uid;
found = 1;
break;
}
}
fsSaveDataIteratorClose(&iterator);
fsSaveDataInfoReaderClose(&reader);
if (R_SUCCEEDED(rc)) return MAKERESULT(Module_Libnx, LibnxError_NotFound);
if (R_SUCCEEDED(rc) && !found) return MAKERESULT(Module_Libnx, LibnxError_NotFound);
return rc;
}
@ -41,61 +42,52 @@ Result get_save(u64 *titleID, u128 *userID)
int main(int argc, char **argv)
{
Result rc=0;
int ret=0;
DIR* dir;
struct dirent* ent;
FsFileSystem tmpfs;
u128 userID=0;
bool account_selected=0;
u64 titleID=0x01007ef00011e000;//titleID of the save to mount, in this case BOTW.
AccountUid uid={0};
u64 application_id=0x01007ef00011e000;//ApplicationId of the save to mount, in this case BOTW.
gfxInitDefault();
consoleInit(NULL);
//Get the userID for save mounting. To mount common savedata, use FS_SAVEDATA_USERID_COMMONSAVE.
// Configure our supported input layout: a single player with standard controller styles
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
//Try to find savedata to use with get_save() first, otherwise fallback to the above hard-coded TID + the userID from accountGetActiveUser(). Note that you can use either method.
// Initialize the default gamepad (which reads handheld mode inputs as well as the first connected controller)
PadState pad;
padInitializeDefault(&pad);
//Get the userID for save mounting. To mount common savedata, use an all-zero userID.
//Try to find savedata to use with get_save() first, otherwise fallback to the above hard-coded TID + the userID from accountGetPreselectedUser(). Note that you can use either method.
//See the account example for getting account info for an userID.
//See also the app_controldata example for getting info for a titleID.
if (R_FAILED(get_save(&titleID, &userID))) {
rc = accountInitialize();
//See also the app_controldata example for getting info for an application_id.
if (R_FAILED(get_save(&application_id, &uid))) {
rc = accountInitialize(AccountServiceType_Application);
if (R_FAILED(rc)) {
printf("accountInitialize() failed: 0x%x\n", rc);
}
if (R_SUCCEEDED(rc)) {
rc = accountGetActiveUser(&userID, &account_selected);
rc = accountGetPreselectedUser(&uid);
accountExit();
if (R_FAILED(rc)) {
printf("accountGetActiveUser() failed: 0x%x\n", rc);
}
else if(!account_selected) {
printf("No user is currently selected.\n");
rc = -1;
printf("accountGetPreselectedUser() failed: 0x%x\n", rc);
}
}
}
if (R_SUCCEEDED(rc)) {
printf("Using titleID=0x%016lx userID: 0x%lx 0x%lx\n", titleID, (u64)(userID>>64), (u64)userID);
}
if (R_SUCCEEDED(rc)) {
rc = fsMount_SaveData(&tmpfs, titleID, userID);//See also libnx fs.h.
if (R_FAILED(rc)) {
printf("fsMount_SaveData() failed: 0x%x\n", rc);
}
printf("Using application_id=0x%016lx uid: 0x%lx 0x%lx\n", application_id, uid.uid[1], uid.uid[0]);
}
//You can use any device-name. If you want multiple saves mounted at the same time, you must use different device-names for each one.
if (R_SUCCEEDED(rc)) {
ret = fsdevMountDevice("save", tmpfs);
if (ret==-1) {
printf("fsdevMountDevice() failed.\n");
rc = ret;
rc = fsdevMountSaveData("save", application_id, uid);//See also libnx fs.h/fs_dev.h
if (R_FAILED(rc)) {
printf("fsdevMountSaveData() failed: 0x%x\n", rc);
}
}
@ -128,19 +120,17 @@ int main(int argc, char **argv)
// Main loop
while(appletMainLoop())
{
//Scan all the inputs. This should be done once for each frame
hidScanInput();
// Scan the gamepad. This should be done once for each frame
padUpdate(&pad);
//hidKeysDown returns information about which buttons have been just pressed (and they weren't in the previous frame)
u64 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
// padGetButtonsDown returns the set of buttons that have been newly pressed in this frame compared to the previous one
u64 kDown = padGetButtonsDown(&pad);
if (kDown & KEY_PLUS) break; // break in order to return to hbmenu
if (kDown & HidNpadButton_Plus) break; // break in order to return to hbmenu
gfxFlushBuffers();
gfxSwapBuffers();
consoleUpdate(NULL);
}
gfxExit();
consoleExit(NULL);
return 0;
}

View File

@ -15,7 +15,6 @@ include $(DEVKITPRO)/libnx/switch_rules
# 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.
@ -29,26 +28,33 @@ include $(DEVKITPRO)/libnx/switch_rules
# - <Project name>.jpg
# - icon.jpg
# - <libnx folder>/default_icon.jpg
#
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
# If not set, it attempts to use one of the following (in this order):
# - <Project name>.json
# - config.json
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
# NACP building is skipped as well.
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := source
DATA := data
INCLUDES := include
EXEFS_SRC := exefs_src
#ROMFS := romfs
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -g -Wall -O2 -ffunction-sections \
$(ARCH) $(DEFINES)
CFLAGS += $(INCLUDE) -D__SWITCH__
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
@ -107,7 +113,18 @@ export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC)
ifeq ($(strip $(CONFIG_JSON)),)
jsons := $(wildcard *.json)
ifneq (,$(findstring $(TARGET).json,$(jsons)))
export APP_JSON := $(TOPDIR)/$(TARGET).json
else
ifneq (,$(findstring config.json,$(jsons)))
export APP_JSON := $(TOPDIR)/config.json
endif
endif
else
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
endif
ifeq ($(strip $(ICON)),)
icons := $(wildcard *.jpg)
@ -150,7 +167,11 @@ $(BUILD):
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) $(TARGET).pfs0 $(TARGET).nso $(TARGET).nro $(TARGET).nacp $(TARGET).elf
ifeq ($(strip $(APP_JSON)),)
@rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
else
@rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
endif
#---------------------------------------------------------------------------------
@ -162,11 +183,9 @@ DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
all : $(OUTPUT).pfs0 $(OUTPUT).nro
ifeq ($(strip $(APP_JSON)),)
$(OUTPUT).pfs0 : $(OUTPUT).nso
$(OUTPUT).nso : $(OUTPUT).elf
all : $(OUTPUT).nro
ifeq ($(strip $(NO_NACP)),)
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp
@ -174,6 +193,16 @@ else
$(OUTPUT).nro : $(OUTPUT).elf
endif
else
all : $(OUTPUT).nsp
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
$(OUTPUT).nso : $(OUTPUT).elf
endif
$(OUTPUT).elf : $(OFILES)
$(OFILES_SRC) : $(HFILES_BIN)

View File

@ -9,9 +9,15 @@
int main(int argc, char **argv)
{
gfxInitDefault();
consoleInit(NULL);
// Configure our supported input layout: a single player with standard controller styles
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
// Initialize the default gamepad (which reads handheld mode inputs as well as the first connected controller)
PadState pad;
padInitializeDefault(&pad);
DIR* dir;
struct dirent* ent;
@ -34,19 +40,17 @@ int main(int argc, char **argv)
// Main loop
while(appletMainLoop())
{
//Scan all the inputs. This should be done once for each frame
hidScanInput();
// Scan the gamepad. This should be done once for each frame
padUpdate(&pad);
//hidKeysDown returns information about which buttons have been just pressed (and they weren't in the previous frame)
u64 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
// padGetButtonsDown returns the set of buttons that have been newly pressed in this frame compared to the previous one
u64 kDown = padGetButtonsDown(&pad);
if (kDown & KEY_PLUS) break; // break in order to return to hbmenu
if (kDown & HidNpadButton_Plus) break; // break in order to return to hbmenu
gfxFlushBuffers();
gfxSwapBuffers();
consoleUpdate(NULL);
}
gfxExit();
consoleExit(NULL);
return 0;
}

View File

@ -0,0 +1,271 @@
#---------------------------------------------------------------------------------
.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 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
# 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
#
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
# If not set, it attempts to use one of the following (in this order):
# - <Project name>.json
# - config.json
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
# NACP building is skipped as well.
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := source
DATA := data
INCLUDES := include
ROMFS := romfs
# Output folders for autogenerated files in romfs
OUT_SHADERS := shaders
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -g -Wall -O2 -ffunction-sections \
$(ARCH) $(DEFINES)
CFLAGS += $(INCLUDE) -D__SWITCH__
CXXFLAGS := $(CFLAGS) -std=gnu++17 -fno-exceptions -fno-rtti
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
LIBS := -ldeko3dd -lnx
#---------------------------------------------------------------------------------
# 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 := $(CURDIR)/$(TARGET)
export TOPDIR := $(CURDIR)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD)
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
GLSLFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.glsl)))
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_BIN := $(addsuffix .o,$(BINFILES))
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
ifneq ($(strip $(ROMFS)),)
ROMFS_TARGETS :=
ROMFS_FOLDERS :=
ifneq ($(strip $(OUT_SHADERS)),)
ROMFS_SHADERS := $(ROMFS)/$(OUT_SHADERS)
ROMFS_TARGETS += $(patsubst %.glsl, $(ROMFS_SHADERS)/%.dksh, $(GLSLFILES))
ROMFS_FOLDERS += $(ROMFS_SHADERS)
endif
export ROMFS_DEPS := $(foreach file,$(ROMFS_TARGETS),$(CURDIR)/$(file))
endif
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)
ifeq ($(strip $(CONFIG_JSON)),)
jsons := $(wildcard *.json)
ifneq (,$(findstring $(TARGET).json,$(jsons)))
export APP_JSON := $(TOPDIR)/$(TARGET).json
else
ifneq (,$(findstring config.json,$(jsons)))
export APP_JSON := $(TOPDIR)/config.json
endif
endif
else
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
endif
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
.PHONY: all clean
#---------------------------------------------------------------------------------
all: $(ROMFS_TARGETS) | $(BUILD)
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
$(BUILD):
@mkdir -p $@
ifneq ($(strip $(ROMFS_TARGETS)),)
$(ROMFS_TARGETS): | $(ROMFS_FOLDERS)
$(ROMFS_FOLDERS):
@mkdir -p $@
$(ROMFS_SHADERS)/%_vsh.dksh: %_vsh.glsl
@echo {vert} $(notdir $<)
@uam -s vert -o $@ $<
$(ROMFS_SHADERS)/%_tcsh.dksh: %_tcsh.glsl
@echo {tess_ctrl} $(notdir $<)
@uam -s tess_ctrl -o $@ $<
$(ROMFS_SHADERS)/%_tesh.dksh: %_tesh.glsl
@echo {tess_eval} $(notdir $<)
@uam -s tess_eval -o $@ $<
$(ROMFS_SHADERS)/%_gsh.dksh: %_gsh.glsl
@echo {geom} $(notdir $<)
@uam -s geom -o $@ $<
$(ROMFS_SHADERS)/%_fsh.dksh: %_fsh.glsl
@echo {frag} $(notdir $<)
@uam -s frag -o $@ $<
$(ROMFS_SHADERS)/%.dksh: %.glsl
@echo {comp} $(notdir $<)
@uam -s comp -o $@ $<
endif
#---------------------------------------------------------------------------------
clean:
@echo clean ...
ifeq ($(strip $(APP_JSON)),)
@rm -fr $(BUILD) $(ROMFS_FOLDERS) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
else
@rm -fr $(BUILD) $(ROMFS_FOLDERS) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
endif
#---------------------------------------------------------------------------------
else
.PHONY: all
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
ifeq ($(strip $(APP_JSON)),)
all : $(OUTPUT).nro
ifeq ($(strip $(NO_NACP)),)
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp $(ROMFS_DEPS)
else
$(OUTPUT).nro : $(OUTPUT).elf $(ROMFS_DEPS)
endif
else
all : $(OUTPUT).nsp
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
$(OUTPUT).nso : $(OUTPUT).elf
endif
$(OUTPUT).elf : $(OFILES)
$(OFILES_SRC) : $(HFILES_BIN)
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o %_bin.h : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

View File

@ -0,0 +1,9 @@
#version 460
layout (location = 0) in vec4 inColor;
layout (location = 0) out vec4 outColor;
void main()
{
outColor = inColor;
}

View File

@ -0,0 +1,233 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <switch.h>
#include <deko3d.h>
// Define the desired number of framebuffers
#define FB_NUM 2
// Define the desired framebuffer resolution (here we set it to 720p).
#define FB_WIDTH 1280
#define FB_HEIGHT 720
// Remove above and uncomment below for 1080p
//#define FB_WIDTH 1920
//#define FB_HEIGHT 1080
// Define the size of the memory block that will hold code
#define CODEMEMSIZE (64*1024)
// Define the size of the memory block that will hold command lists
#define CMDMEMSIZE (16*1024)
static DkDevice g_device;
static DkMemBlock g_framebufferMemBlock;
static DkImage g_framebuffers[FB_NUM];
static DkSwapchain g_swapchain;
static DkMemBlock g_codeMemBlock;
static uint32_t g_codeMemOffset;
static DkShader g_vertexShader;
static DkShader g_fragmentShader;
static DkMemBlock g_cmdbufMemBlock;
static DkCmdBuf g_cmdbuf;
static DkCmdList g_cmdsBindFramebuffer[FB_NUM];
static DkCmdList g_cmdsRender;
static DkQueue g_renderQueue;
// Simple function for loading a shader from the filesystem
static void loadShader(DkShader* pShader, const char* path)
{
// Open the file, and retrieve its size
FILE* f = fopen(path, "rb");
fseek(f, 0, SEEK_END);
uint32_t size = ftell(f);
rewind(f);
// Look for a spot in the code memory block for loading this shader. Note that
// we are just using a simple incremental offset; this isn't a general purpose
// allocation algorithm.
uint32_t codeOffset = g_codeMemOffset;
g_codeMemOffset += (size + DK_SHADER_CODE_ALIGNMENT - 1) &~ (DK_SHADER_CODE_ALIGNMENT - 1);
// Read the file into memory, and close the file
fread((uint8_t*)dkMemBlockGetCpuAddr(g_codeMemBlock) + codeOffset, size, 1, f);
fclose(f);
// Initialize the user provided shader object with the code we've just loaded
DkShaderMaker shaderMaker;
dkShaderMakerDefaults(&shaderMaker, g_codeMemBlock, codeOffset);
dkShaderInitialize(pShader, &shaderMaker);
}
// This function creates all the necessary graphical resources.
static void graphicsInitialize(void)
{
// Create the device, which is the root object
DkDeviceMaker deviceMaker;
dkDeviceMakerDefaults(&deviceMaker);
g_device = dkDeviceCreate(&deviceMaker);
// Calculate layout for the framebuffers
DkImageLayoutMaker imageLayoutMaker;
dkImageLayoutMakerDefaults(&imageLayoutMaker, g_device);
imageLayoutMaker.flags = DkImageFlags_UsageRender | DkImageFlags_UsagePresent | DkImageFlags_HwCompression;
imageLayoutMaker.format = DkImageFormat_RGBA8_Unorm;
imageLayoutMaker.dimensions[0] = FB_WIDTH;
imageLayoutMaker.dimensions[1] = FB_HEIGHT;
// Calculate layout for the framebuffers
DkImageLayout framebufferLayout;
dkImageLayoutInitialize(&framebufferLayout, &imageLayoutMaker);
// Retrieve necessary size and alignment for the framebuffers
uint32_t framebufferSize = dkImageLayoutGetSize(&framebufferLayout);
uint32_t framebufferAlign = dkImageLayoutGetAlignment(&framebufferLayout);
framebufferSize = (framebufferSize + framebufferAlign - 1) &~ (framebufferAlign - 1);
// Create a memory block that will host the framebuffers
DkMemBlockMaker memBlockMaker;
dkMemBlockMakerDefaults(&memBlockMaker, g_device, FB_NUM*framebufferSize);
memBlockMaker.flags = DkMemBlockFlags_GpuCached | DkMemBlockFlags_Image;
g_framebufferMemBlock = dkMemBlockCreate(&memBlockMaker);
// Initialize the framebuffers with the layout and backing memory we've just created
DkImage const* swapchainImages[FB_NUM];
for (unsigned i = 0; i < FB_NUM; i ++)
{
swapchainImages[i] = &g_framebuffers[i];
dkImageInitialize(&g_framebuffers[i], &framebufferLayout, g_framebufferMemBlock, i*framebufferSize);
}
// Create a swapchain out of the framebuffers we've just initialized
DkSwapchainMaker swapchainMaker;
dkSwapchainMakerDefaults(&swapchainMaker, g_device, nwindowGetDefault(), swapchainImages, FB_NUM);
g_swapchain = dkSwapchainCreate(&swapchainMaker);
// Create a memory block onto which we will load shader code
dkMemBlockMakerDefaults(&memBlockMaker, g_device, CODEMEMSIZE);
memBlockMaker.flags = DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached | DkMemBlockFlags_Code;
g_codeMemBlock = dkMemBlockCreate(&memBlockMaker);
g_codeMemOffset = 0;
// Load our shaders (both vertex and fragment)
loadShader(&g_vertexShader, "romfs:/shaders/triangle_vsh.dksh");
loadShader(&g_fragmentShader, "romfs:/shaders/color_fsh.dksh");
// Create a memory block which will be used for recording command lists using a command buffer
dkMemBlockMakerDefaults(&memBlockMaker, g_device, CMDMEMSIZE);
memBlockMaker.flags = DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached;
g_cmdbufMemBlock = dkMemBlockCreate(&memBlockMaker);
// Create a command buffer object
DkCmdBufMaker cmdbufMaker;
dkCmdBufMakerDefaults(&cmdbufMaker, g_device);
g_cmdbuf = dkCmdBufCreate(&cmdbufMaker);
// Feed our memory to the command buffer so that we can start recording commands
dkCmdBufAddMemory(g_cmdbuf, g_cmdbufMemBlock, 0, CMDMEMSIZE);
// Generate a command list for each framebuffer, which will bind each of them as a render target
for (unsigned i = 0; i < FB_NUM; i ++)
{
DkImageView imageView;
dkImageViewDefaults(&imageView, &g_framebuffers[i]);
dkCmdBufBindRenderTarget(g_cmdbuf, &imageView, NULL);
g_cmdsBindFramebuffer[i] = dkCmdBufFinishList(g_cmdbuf);
}
// Declare structs that will be used for binding state
DkViewport viewport = { 0.0f, 0.0f, (float)FB_WIDTH, (float)FB_HEIGHT, 0.0f, 1.0f };
DkScissor scissor = { 0, 0, FB_WIDTH, FB_HEIGHT };
DkShader const* shaders[] = { &g_vertexShader, &g_fragmentShader };
DkRasterizerState rasterizerState;
DkColorState colorState;
DkColorWriteState colorWriteState;
// Initialize state structs with the deko3d defaults
dkRasterizerStateDefaults(&rasterizerState);
dkColorStateDefaults(&colorState);
dkColorWriteStateDefaults(&colorWriteState);
// Generate the main rendering command list
dkCmdBufSetViewports(g_cmdbuf, 0, &viewport, 1);
dkCmdBufSetScissors(g_cmdbuf, 0, &scissor, 1);
dkCmdBufClearColorFloat(g_cmdbuf, 0, DkColorMask_RGBA, 0.125f, 0.294f, 0.478f, 1.0f);
dkCmdBufBindShaders(g_cmdbuf, DkStageFlag_GraphicsMask, shaders, sizeof(shaders)/sizeof(shaders[0]));
dkCmdBufBindRasterizerState(g_cmdbuf, &rasterizerState);
dkCmdBufBindColorState(g_cmdbuf, &colorState);
dkCmdBufBindColorWriteState(g_cmdbuf, &colorWriteState);
dkCmdBufDraw(g_cmdbuf, DkPrimitive_Triangles, 3, 1, 0, 0);
g_cmdsRender = dkCmdBufFinishList(g_cmdbuf);
// Create a queue, to which we will submit our command lists
DkQueueMaker queueMaker;
dkQueueMakerDefaults(&queueMaker, g_device);
queueMaker.flags = DkQueueFlags_Graphics;
g_renderQueue = dkQueueCreate(&queueMaker);
}
// This function is to be called at each frame, and it is in charge of rendering.
static void graphicsUpdate(void)
{
// Acquire a framebuffer from the swapchain (and wait for it to be available)
int slot = dkQueueAcquireImage(g_renderQueue, g_swapchain);
// Run the command list that binds said framebuffer as a render target
dkQueueSubmitCommands(g_renderQueue, g_cmdsBindFramebuffer[slot]);
// Run the main rendering command list
dkQueueSubmitCommands(g_renderQueue, g_cmdsRender);
// Now that we are done rendering, present it to the screen
dkQueuePresentImage(g_renderQueue, g_swapchain, slot);
}
// This function destroys the graphical resources created by graphicsInitialize.
static void graphicsExit(void)
{
// Make sure the rendering queue is idle before destroying anything
dkQueueWaitIdle(g_renderQueue);
// Destroy all the resources we've created
dkQueueDestroy(g_renderQueue);
dkCmdBufDestroy(g_cmdbuf);
dkMemBlockDestroy(g_cmdbufMemBlock);
dkMemBlockDestroy(g_codeMemBlock);
dkSwapchainDestroy(g_swapchain);
dkMemBlockDestroy(g_framebufferMemBlock);
dkDeviceDestroy(g_device);
}
// Main entrypoint
int main(int argc, char* argv[])
{
romfsInit();
graphicsInitialize();
// Configure our supported input layout: a single player with standard controller styles
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
// Initialize the default gamepad (which reads handheld mode inputs as well as the first connected controller)
PadState pad;
padInitializeDefault(&pad);
while (appletMainLoop())
{
padUpdate(&pad);
u64 kDown = padGetButtonsDown(&pad);
if (kDown & HidNpadButton_Plus)
break; // break in order to return to hbmenu
graphicsUpdate();
}
graphicsExit();
romfsExit();
return 0;
}

View File

@ -0,0 +1,23 @@
#version 460
// Hardcoded array of vertex positions for our triangle
const vec4 positions[3] = vec4[](
vec4( 0.0, +1.0, 0.0, 1.0),
vec4(-1.0, -1.0, 0.0, 1.0),
vec4(+1.0, -1.0, 0.0, 1.0)
);
// Hardcoded array of vertex colors for our triangle
const vec4 colors[3] = vec4[](
vec4(1.0, 0.0, 0.0, 1.0),
vec4(0.0, 1.0, 0.0, 1.0),
vec4(0.0, 0.0, 1.0, 1.0)
);
layout (location = 0) out vec4 outColor;
void main()
{
gl_Position = positions[gl_VertexID];
outColor = colors[gl_VertexID];
}

View File

@ -0,0 +1,271 @@
#---------------------------------------------------------------------------------
.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 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
# 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
#
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
# If not set, it attempts to use one of the following (in this order):
# - <Project name>.json
# - config.json
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
# NACP building is skipped as well.
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := source
DATA := data
INCLUDES := include
ROMFS := romfs
# Output folders for autogenerated files in romfs
OUT_SHADERS := shaders
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -g -Wall -O2 -ffunction-sections \
$(ARCH) $(DEFINES)
CFLAGS += $(INCLUDE) -D__SWITCH__
CXXFLAGS := $(CFLAGS) -std=gnu++17 -fno-exceptions -fno-rtti
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
LIBS := -ldeko3d -lnx -lm
#---------------------------------------------------------------------------------
# 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 := $(CURDIR)/$(TARGET)
export TOPDIR := $(CURDIR)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD)
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
GLSLFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.glsl)))
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_BIN := $(addsuffix .o,$(BINFILES))
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
ifneq ($(strip $(ROMFS)),)
ROMFS_TARGETS :=
ROMFS_FOLDERS :=
ifneq ($(strip $(OUT_SHADERS)),)
ROMFS_SHADERS := $(ROMFS)/$(OUT_SHADERS)
ROMFS_TARGETS += $(patsubst %.glsl, $(ROMFS_SHADERS)/%.dksh, $(GLSLFILES))
ROMFS_FOLDERS += $(ROMFS_SHADERS)
endif
export ROMFS_DEPS := $(foreach file,$(ROMFS_TARGETS),$(CURDIR)/$(file))
endif
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)
ifeq ($(strip $(CONFIG_JSON)),)
jsons := $(wildcard *.json)
ifneq (,$(findstring $(TARGET).json,$(jsons)))
export APP_JSON := $(TOPDIR)/$(TARGET).json
else
ifneq (,$(findstring config.json,$(jsons)))
export APP_JSON := $(TOPDIR)/config.json
endif
endif
else
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
endif
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
.PHONY: all clean
#---------------------------------------------------------------------------------
all: $(ROMFS_TARGETS) | $(BUILD)
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
$(BUILD):
@mkdir -p $@
ifneq ($(strip $(ROMFS_TARGETS)),)
$(ROMFS_TARGETS): | $(ROMFS_FOLDERS)
$(ROMFS_FOLDERS):
@mkdir -p $@
$(ROMFS_SHADERS)/%_vsh.dksh: %_vsh.glsl
@echo {vert} $(notdir $<)
@uam -s vert -o $@ $<
$(ROMFS_SHADERS)/%_tcsh.dksh: %_tcsh.glsl
@echo {tess_ctrl} $(notdir $<)
@uam -s tess_ctrl -o $@ $<
$(ROMFS_SHADERS)/%_tesh.dksh: %_tesh.glsl
@echo {tess_eval} $(notdir $<)
@uam -s tess_eval -o $@ $<
$(ROMFS_SHADERS)/%_gsh.dksh: %_gsh.glsl
@echo {geom} $(notdir $<)
@uam -s geom -o $@ $<
$(ROMFS_SHADERS)/%_fsh.dksh: %_fsh.glsl
@echo {frag} $(notdir $<)
@uam -s frag -o $@ $<
$(ROMFS_SHADERS)/%.dksh: %.glsl
@echo {comp} $(notdir $<)
@uam -s comp -o $@ $<
endif
#---------------------------------------------------------------------------------
clean:
@echo clean ...
ifeq ($(strip $(APP_JSON)),)
@rm -fr $(BUILD) $(ROMFS_FOLDERS) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
else
@rm -fr $(BUILD) $(ROMFS_FOLDERS) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
endif
#---------------------------------------------------------------------------------
else
.PHONY: all
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
ifeq ($(strip $(APP_JSON)),)
all : $(OUTPUT).nro
ifeq ($(strip $(NO_NACP)),)
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp $(ROMFS_DEPS)
else
$(OUTPUT).nro : $(OUTPUT).elf $(ROMFS_DEPS)
endif
else
all : $(OUTPUT).nsp
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
$(OUTPUT).nso : $(OUTPUT).elf
endif
$(OUTPUT).elf : $(OFILES)
$(OFILES_SRC) : $(HFILES_BIN)
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o %_bin.h : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

View File

@ -0,0 +1,15 @@
#version 460
layout (location = 0) noperspective in vec3 inTexCoord;
layout (location = 1) flat in vec4 inFrontPal;
layout (location = 2) flat in vec4 inBackPal;
layout (location = 0) out vec4 outColor;
layout (binding = 0) uniform sampler2DArray tileset;
void main()
{
float value = texture(tileset, inTexCoord).r;
outColor = mix(inBackPal, inFrontPal, value);
}

View File

@ -0,0 +1,35 @@
#version 460
layout (location = 0) in float inTileId;
layout (location = 1) in uvec2 inColorId;
layout (location = 0) out vec3 outTexCoord;
layout (location = 1) out vec4 outFrontPal;
layout (location = 2) out vec4 outBackPal;
layout (std140, binding = 0) uniform Config
{
vec4 dimensions;
vec4 vertices[3];
vec4 palettes[24];
} u;
void main()
{
float id = float(gl_InstanceID);
float tileRow = floor(id / u.dimensions.z);
float tileCol = id - tileRow * u.dimensions.z;
vec2 basePos;
basePos.x = 2.0 * (tileCol + 0.5) / u.dimensions.z - 1.0;
basePos.y = 2.0 * (1.0 - (tileRow + 0.5) / u.dimensions.w) - 1.0;
vec2 vtxData = u.vertices[gl_VertexID].xy;
vec2 scale = vec2(1.0) / u.dimensions.zw;
gl_Position.xy = vtxData * scale + basePos;
gl_Position.zw = vec2(0.5, 1.0);
outTexCoord = vec3(u.vertices[gl_VertexID].zw, inTileId);
outFrontPal = u.palettes[inColorId.x];
outBackPal = u.palettes[inColorId.y];
}

View File

@ -0,0 +1,486 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/iosupport.h>
#include <switch.h>
#include <deko3d.h>
// Define the desired number of framebuffers
#define FB_NUM 2
// Define the size of the memory block that will hold code
#define CODEMEMSIZE (64*1024)
// Define the size of the memory block that will hold command lists
#define CMDMEMSIZE (64*1024)
#define NUM_IMAGE_SLOTS 1
#define NUM_SAMPLER_SLOTS 1
typedef struct {
float pos[2];
float tex[2];
} VertexDef;
typedef struct {
float red;
float green;
float blue;
float alpha;
} PaletteColor;
typedef struct {
float dimensions[4];
VertexDef vertices[3];
PaletteColor palettes[24];
} ConsoleConfig;
static const VertexDef g_vertexData[3] = {
{ { 0.0f, +1.0f }, { 0.5f, 0.0f, } },
{ { -1.0f, -1.0f }, { 0.0f, 1.0f, } },
{ { +1.0f, -1.0f }, { 1.0f, 1.0f, } },
};
static const PaletteColor g_paletteData[24] = {
{ 0.0f, 0.0f, 0.0f, 0.0f }, // black
{ 0.5f, 0.0f, 0.0f, 1.0f }, // red
{ 0.0f, 0.5f, 0.0f, 1.0f }, // green
{ 0.5f, 0.5f, 0.0f, 1.0f }, // yellow
{ 0.0f, 0.0f, 0.5f, 1.0f }, // blue
{ 0.5f, 0.0f, 0.5f, 1.0f }, // magenta
{ 0.0f, 0.5f, 0.5f, 1.0f }, // cyan
{ 0.75f, 0.75f, 0.75f, 1.0f }, // white
{ 0.5f, 0.5f, 0.5f, 1.0f }, // bright black
{ 1.0f, 0.0f, 0.0f, 1.0f }, // bright red
{ 0.0f, 1.0f, 0.0f, 1.0f }, // bright green
{ 1.0f, 1.0f, 0.0f, 1.0f }, // bright yellow
{ 0.0f, 0.0f, 1.0f, 1.0f }, // bright blue
{ 1.0f, 0.0f, 1.0f, 1.0f }, // bright magenta
{ 0.0f, 1.0f, 1.0f, 1.0f }, // bright cyan
{ 1.0f, 1.0f, 1.0f, 1.0f }, // bright white
{ 0.0f, 0.0f, 0.0f, 0.0f }, // faint black
{ 0.25f, 0.0f, 0.0f, 1.0f }, // faint red
{ 0.0f, 0.25f, 0.0f, 1.0f }, // faint green
{ 0.25f, 0.25f, 0.0f, 1.0f }, // faint yellow
{ 0.0f, 0.0f, 0.25f, 1.0f }, // faint blue
{ 0.25f, 0.0f, 0.25f, 1.0f }, // faint magenta
{ 0.0f, 0.25f, 0.25f, 1.0f }, // faint cyan
{ 0.375f, 0.375f, 0.375f, 1.0f }, // faint white
};
typedef struct {
uint16_t tileId;
uint8_t frontPal;
uint8_t backPal;
} ConsoleChar;
static const DkVtxAttribState g_attribState[] = {
{ .bufferId=0, .isFixed=0, .offset=offsetof(ConsoleChar,tileId), .size=DkVtxAttribSize_1x16, .type=DkVtxAttribType_Uscaled, .isBgra=0 },
{ .bufferId=0, .isFixed=0, .offset=offsetof(ConsoleChar,frontPal), .size=DkVtxAttribSize_2x8, .type=DkVtxAttribType_Uint, .isBgra=0 },
};
static const DkVtxBufferState g_vtxbufState[] = {
{ .stride=sizeof(ConsoleChar), .divisor=1 },
};
struct GpuRenderer {
ConsoleRenderer base;
bool initialized;
DkDevice device;
DkQueue queue;
DkMemBlock imageMemBlock;
DkMemBlock codeMemBlock;
DkMemBlock dataMemBlock;
DkSwapchain swapchain;
DkImage framebuffers[FB_NUM];
DkImage tileset;
ConsoleChar* charBuf;
uint32_t codeMemOffset;
DkShader vertexShader;
DkShader fragmentShader;
DkCmdBuf cmdbuf;
DkCmdList cmdsBindFramebuffer[FB_NUM];
DkCmdList cmdsRender;
DkFence lastRenderFence;
};
static struct GpuRenderer* GpuRenderer(PrintConsole* con)
{
return (struct GpuRenderer*)con->renderer;
}
static void GpuRenderer_destroy(struct GpuRenderer* r)
{
// Make sure the queue is idle before destroying anything
dkQueueWaitIdle(r->queue);
// Destroy all the resources we've created
dkQueueDestroy(r->queue);
dkCmdBufDestroy(r->cmdbuf);
dkSwapchainDestroy(r->swapchain);
dkMemBlockDestroy(r->dataMemBlock);
dkMemBlockDestroy(r->codeMemBlock);
dkMemBlockDestroy(r->imageMemBlock);
dkDeviceDestroy(r->device);
// Clear out all state
memset(&r->initialized, 0, sizeof(*r) - offsetof(struct GpuRenderer, initialized));
}
// Simple function for loading a shader from the filesystem
static void GpuRenderer_loadShader(struct GpuRenderer* r, DkShader* pShader, const char* path)
{
// Open the file, and retrieve its size
FILE* f = fopen(path, "rb");
fseek(f, 0, SEEK_END);
uint32_t size = ftell(f);
rewind(f);
// Look for a spot in the code memory block for loading this shader. Note that
// we are just using a simple incremental offset; this isn't a general purpose
// allocation algorithm.
uint32_t codeOffset = r->codeMemOffset;
r->codeMemOffset += (size + DK_SHADER_CODE_ALIGNMENT - 1) &~ (DK_SHADER_CODE_ALIGNMENT - 1);
// Read the file into memory, and close the file
fread((uint8_t*)dkMemBlockGetCpuAddr(r->codeMemBlock) + codeOffset, size, 1, f);
fclose(f);
// Initialize the user provided shader object with the code we've just loaded
DkShaderMaker shaderMaker;
dkShaderMakerDefaults(&shaderMaker, r->codeMemBlock, codeOffset);
dkShaderInitialize(pShader, &shaderMaker);
}
static bool GpuRenderer_init(PrintConsole* con)
{
struct GpuRenderer* r = GpuRenderer(con);
if (r->initialized) {
// We're already initialized
return true;
}
// Create the deko3d device, which is the root object
DkDeviceMaker deviceMaker;
dkDeviceMakerDefaults(&deviceMaker);
r->device = dkDeviceCreate(&deviceMaker);
// Create the queue
DkQueueMaker queueMaker;
dkQueueMakerDefaults(&queueMaker, r->device);
queueMaker.flags = DkQueueFlags_Graphics;
r->queue = dkQueueCreate(&queueMaker);
// Calculate required width/height for the framebuffers
u32 width = con->font.tileWidth * con->consoleWidth;
u32 height = con->font.tileHeight * con->consoleHeight;
u32 totalConSize = con->consoleWidth * con->consoleHeight;
// Calculate layout for the framebuffers
DkImageLayoutMaker imageLayoutMaker;
dkImageLayoutMakerDefaults(&imageLayoutMaker, r->device);
imageLayoutMaker.flags = DkImageFlags_UsageRender | DkImageFlags_UsagePresent | DkImageFlags_HwCompression;
imageLayoutMaker.format = DkImageFormat_RGBA8_Unorm;
imageLayoutMaker.dimensions[0] = width;
imageLayoutMaker.dimensions[1] = height;
// Calculate layout for the framebuffers
DkImageLayout framebufferLayout;
dkImageLayoutInitialize(&framebufferLayout, &imageLayoutMaker);
// Calculate layout for the tileset
dkImageLayoutMakerDefaults(&imageLayoutMaker, r->device);
imageLayoutMaker.type = DkImageType_2DArray;
imageLayoutMaker.format = DkImageFormat_R32_Float;
imageLayoutMaker.dimensions[0] = con->font.tileWidth;
imageLayoutMaker.dimensions[1] = con->font.tileHeight;
imageLayoutMaker.dimensions[2] = con->font.numChars;
// Calculate layout for the tileset
DkImageLayout tilesetLayout;
dkImageLayoutInitialize(&tilesetLayout, &imageLayoutMaker);
// Retrieve necessary size and alignment for the framebuffers
uint32_t framebufferSize = dkImageLayoutGetSize(&framebufferLayout);
uint32_t framebufferAlign = dkImageLayoutGetAlignment(&framebufferLayout);
framebufferSize = (framebufferSize + framebufferAlign - 1) &~ (framebufferAlign - 1);
// Retrieve necessary size and alignment for the tileset
uint32_t tilesetSize = dkImageLayoutGetSize(&tilesetLayout);
uint32_t tilesetAlign = dkImageLayoutGetAlignment(&tilesetLayout);
tilesetSize = (tilesetSize + tilesetAlign - 1) &~ (tilesetAlign - 1);
// Create a memory block that will host the framebuffers and the tileset
DkMemBlockMaker memBlockMaker;
dkMemBlockMakerDefaults(&memBlockMaker, r->device, FB_NUM*framebufferSize + tilesetSize);
memBlockMaker.flags = DkMemBlockFlags_GpuCached | DkMemBlockFlags_Image;
r->imageMemBlock = dkMemBlockCreate(&memBlockMaker);
// Initialize the framebuffers with the layout and backing memory we've just created
DkImage const* swapchainImages[FB_NUM];
for (unsigned i = 0; i < FB_NUM; i ++) {
swapchainImages[i] = &r->framebuffers[i];
dkImageInitialize(&r->framebuffers[i], &framebufferLayout, r->imageMemBlock, i*framebufferSize);
}
// Create a swapchain out of the framebuffers we've just initialized
DkSwapchainMaker swapchainMaker;
dkSwapchainMakerDefaults(&swapchainMaker, r->device, nwindowGetDefault(), swapchainImages, FB_NUM);
r->swapchain = dkSwapchainCreate(&swapchainMaker);
// Initialize the tileset
dkImageInitialize(&r->tileset, &tilesetLayout, r->imageMemBlock, FB_NUM*framebufferSize);
// Create a memory block onto which we will load shader code
dkMemBlockMakerDefaults(&memBlockMaker, r->device, CODEMEMSIZE);
memBlockMaker.flags = DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached | DkMemBlockFlags_Code;
r->codeMemBlock = dkMemBlockCreate(&memBlockMaker);
r->codeMemOffset = 0;
// Load our shaders (both vertex and fragment)
GpuRenderer_loadShader(r, &r->vertexShader, "romfs:/shaders/console_vsh.dksh");
GpuRenderer_loadShader(r, &r->fragmentShader, "romfs:/shaders/console_fsh.dksh");
// Generate the descriptors
struct {
DkImageDescriptor images[NUM_IMAGE_SLOTS];
DkSamplerDescriptor samplers[NUM_SAMPLER_SLOTS];
} descriptors;
// Generate a image descriptor for the tileset
DkImageView tilesetView;
dkImageViewDefaults(&tilesetView, &r->tileset);
dkImageDescriptorInitialize(&descriptors.images[0], &tilesetView, false, false);
// Generate a sampler descriptor for the tileset
DkSampler sampler;
dkSamplerDefaults(&sampler);
sampler.wrapMode[0] = DkWrapMode_ClampToEdge;
sampler.wrapMode[1] = DkWrapMode_ClampToEdge;
sampler.minFilter = DkFilter_Nearest;
sampler.magFilter = DkFilter_Nearest;
dkSamplerDescriptorInitialize(&descriptors.samplers[0], &sampler);
uint32_t descriptorsOffset = CMDMEMSIZE;
uint32_t configOffset = (descriptorsOffset + sizeof(descriptors) + DK_UNIFORM_BUF_ALIGNMENT - 1) &~ (DK_UNIFORM_BUF_ALIGNMENT - 1);
uint32_t configSize = (sizeof(ConsoleConfig) + DK_UNIFORM_BUF_ALIGNMENT - 1) &~ (DK_UNIFORM_BUF_ALIGNMENT - 1);
uint32_t charBufOffset = configOffset + configSize;
uint32_t charBufSize = totalConSize * sizeof(ConsoleChar);
// Create a memory block which will be used for recording command lists using a command buffer
dkMemBlockMakerDefaults(&memBlockMaker, r->device,
(charBufOffset + charBufSize + DK_MEMBLOCK_ALIGNMENT - 1) &~ (DK_MEMBLOCK_ALIGNMENT - 1)
);
memBlockMaker.flags = DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached;
r->dataMemBlock = dkMemBlockCreate(&memBlockMaker);
// Create a command buffer object
DkCmdBufMaker cmdbufMaker;
dkCmdBufMakerDefaults(&cmdbufMaker, r->device);
r->cmdbuf = dkCmdBufCreate(&cmdbufMaker);
// Feed our memory to the command buffer so that we can start recording commands
dkCmdBufAddMemory(r->cmdbuf, r->dataMemBlock, 0, CMDMEMSIZE);
// Create a temporary buffer that will hold the tileset
dkMemBlockMakerDefaults(&memBlockMaker, r->device,
(sizeof(float)*con->font.tileWidth*con->font.tileHeight*con->font.numChars + DK_MEMBLOCK_ALIGNMENT - 1) &~ (DK_MEMBLOCK_ALIGNMENT - 1)
);
memBlockMaker.flags = DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached;
DkMemBlock scratchMemBlock = dkMemBlockCreate(&memBlockMaker);
float* scratchMem = (float*)dkMemBlockGetCpuAddr(scratchMemBlock);
// Unpack 1bpp tileset into a texture image the GPU can read
unsigned packedTileWidth = (con->font.tileWidth+7)/8;
for (unsigned tile = 0; tile < con->font.numChars; tile ++) {
const uint8_t* data = (const uint8_t*)con->font.gfx + con->font.tileHeight*packedTileWidth*tile;
for (unsigned y = 0; y < con->font.tileHeight; y ++) {
const uint8_t* row = &data[packedTileWidth*(y+1)];
uint8_t c = 0;
for (unsigned x = 0; x < con->font.tileWidth; x ++) {
if (!(x & 7))
c = *--row;
*scratchMem++ = (c & 0x80) ? 1.0f : 0.0f;
c <<= 1;
}
}
}
// Set up configuration
DkGpuAddr configAddr = dkMemBlockGetGpuAddr(r->dataMemBlock) + configOffset;
ConsoleConfig consoleConfig = {};
consoleConfig.dimensions[0] = width;
consoleConfig.dimensions[1] = height;
consoleConfig.dimensions[2] = con->consoleWidth;
consoleConfig.dimensions[3] = con->consoleHeight;
memcpy(consoleConfig.vertices, g_vertexData, sizeof(g_vertexData));
memcpy(consoleConfig.palettes, g_paletteData, sizeof(g_paletteData));
// Generate a temporary command list for uploading stuff and run it
DkGpuAddr descriptorSet = dkMemBlockGetGpuAddr(r->dataMemBlock) + descriptorsOffset;
DkCopyBuf copySrc = { dkMemBlockGetGpuAddr(scratchMemBlock), 0, 0 };
DkImageRect copyDst = { 0, 0, 0, con->font.tileWidth, con->font.tileHeight, con->font.numChars };
dkCmdBufPushData(r->cmdbuf, descriptorSet, &descriptors, sizeof(descriptors));
dkCmdBufPushConstants(r->cmdbuf, configAddr, configSize, 0, sizeof(consoleConfig), &consoleConfig);
dkCmdBufBindImageDescriptorSet(r->cmdbuf, descriptorSet, NUM_IMAGE_SLOTS);
dkCmdBufBindSamplerDescriptorSet(r->cmdbuf, descriptorSet + NUM_IMAGE_SLOTS*sizeof(DkImageDescriptor), NUM_SAMPLER_SLOTS);
dkCmdBufCopyBufferToImage(r->cmdbuf, &copySrc, &tilesetView, &copyDst, 0);
dkQueueSubmitCommands(r->queue, dkCmdBufFinishList(r->cmdbuf));
dkQueueFlush(r->queue);
dkQueueWaitIdle(r->queue);
dkCmdBufClear(r->cmdbuf);
// Destroy the scratch memory block since we don't need it anymore
dkMemBlockDestroy(scratchMemBlock);
// Retrieve the address of the character buffer
DkGpuAddr charBufAddr = dkMemBlockGetGpuAddr(r->dataMemBlock) + charBufOffset;
r->charBuf = (ConsoleChar*)((uint8_t*)dkMemBlockGetCpuAddr(r->dataMemBlock) + charBufOffset);
memset(r->charBuf, 0, charBufSize);
// Generate a command list for each framebuffer, which will bind each of them as a render target
for (unsigned i = 0; i < FB_NUM; i ++) {
DkImageView imageView;
dkImageViewDefaults(&imageView, &r->framebuffers[i]);
dkCmdBufBindRenderTarget(r->cmdbuf, &imageView, NULL);
r->cmdsBindFramebuffer[i] = dkCmdBufFinishList(r->cmdbuf);
}
// Declare structs that will be used for binding state
DkViewport viewport = { 0.0f, 0.0f, (float)width, (float)height, 0.0f, 1.0f };
DkScissor scissor = { 0, 0, width, height };
DkShader const* shaders[] = { &r->vertexShader, &r->fragmentShader };
DkRasterizerState rasterizerState;
DkColorState colorState;
DkColorWriteState colorWriteState;
// Initialize state structs with the deko3d defaults
dkRasterizerStateDefaults(&rasterizerState);
dkColorStateDefaults(&colorState);
dkColorWriteStateDefaults(&colorWriteState);
rasterizerState.fillRectangleEnable = true;
colorState.alphaCompareOp = DkCompareOp_Greater;
// Generate the main rendering command list
dkCmdBufSetViewports(r->cmdbuf, 0, &viewport, 1);
dkCmdBufSetScissors(r->cmdbuf, 0, &scissor, 1);
//dkCmdBufClearColorFloat(r->cmdbuf, 0, DkColorMask_RGBA, 0.125f, 0.294f, 0.478f, 0.0f);
dkCmdBufClearColorFloat(r->cmdbuf, 0, DkColorMask_RGBA, 0.0f, 0.0f, 0.0f, 0.0f);
dkCmdBufBindShaders(r->cmdbuf, DkStageFlag_GraphicsMask, shaders, sizeof(shaders)/sizeof(shaders[0]));
dkCmdBufBindRasterizerState(r->cmdbuf, &rasterizerState);
dkCmdBufBindColorState(r->cmdbuf, &colorState);
dkCmdBufBindColorWriteState(r->cmdbuf, &colorWriteState);
dkCmdBufBindUniformBuffer(r->cmdbuf, DkStage_Vertex, 0, configAddr, configSize);
dkCmdBufBindTexture(r->cmdbuf, DkStage_Fragment, 0, dkMakeTextureHandle(0, 0));
dkCmdBufBindVtxAttribState(r->cmdbuf, g_attribState, sizeof(g_attribState)/sizeof(g_attribState[0]));
dkCmdBufBindVtxBufferState(r->cmdbuf, g_vtxbufState, sizeof(g_vtxbufState)/sizeof(g_vtxbufState[0]));
dkCmdBufBindVtxBuffer(r->cmdbuf, 0, charBufAddr, charBufSize);
dkCmdBufSetAlphaRef(r->cmdbuf, 0.0f);
dkCmdBufDraw(r->cmdbuf, DkPrimitive_Triangles, 3, totalConSize, 0, 0);
r->cmdsRender = dkCmdBufFinishList(r->cmdbuf);
r->initialized = true;
return true;
}
static void GpuRenderer_deinit(PrintConsole* con)
{
struct GpuRenderer* r = GpuRenderer(con);
if (r->initialized) {
GpuRenderer_destroy(r);
}
}
static void GpuRenderer_drawChar(PrintConsole* con, int x, int y, int c)
{
struct GpuRenderer* r = GpuRenderer(con);
int writingColor = con->fg;
int screenColor = con->bg;
if (con->flags & CONSOLE_COLOR_BOLD) {
writingColor += 8;
} else if (con->flags & CONSOLE_COLOR_FAINT) {
writingColor += 16;
}
if (con->flags & CONSOLE_COLOR_REVERSE) {
int tmp = writingColor;
writingColor = screenColor;
screenColor = tmp;
}
// Wait for the fence
dkFenceWait(&r->lastRenderFence, UINT64_MAX);
ConsoleChar* pos = &r->charBuf[y*con->consoleWidth+x];
pos->tileId = c;
pos->frontPal = writingColor;
pos->backPal = screenColor;
}
static void GpuRenderer_scrollWindow(PrintConsole* con)
{
struct GpuRenderer* r = GpuRenderer(con);
// Wait for the fence
dkFenceWait(&r->lastRenderFence, UINT64_MAX);
// Perform the scrolling
for (int y = 0; y < con->windowHeight-1; y ++) {
memcpy(
&r->charBuf[(con->windowY+y+0)*con->consoleWidth + con->windowX],
&r->charBuf[(con->windowY+y+1)*con->consoleWidth + con->windowX],
sizeof(ConsoleChar)*con->windowWidth);
}
}
static void GpuRenderer_flushAndSwap(PrintConsole* con)
{
struct GpuRenderer* r = GpuRenderer(con);
// Acquire a framebuffer from the swapchain (and wait for it to be available)
int slot = dkQueueAcquireImage(r->queue, r->swapchain);
// Run the command list that binds said framebuffer as a render target
dkQueueSubmitCommands(r->queue, r->cmdsBindFramebuffer[slot]);
// Run the main rendering command list
dkQueueSubmitCommands(r->queue, r->cmdsRender);
// Signal the fence
dkQueueSignalFence(r->queue, &r->lastRenderFence, false);
// Now that we are done rendering, present it to the screen
dkQueuePresentImage(r->queue, r->swapchain, slot);
}
static struct GpuRenderer s_gpuRenderer =
{
{
GpuRenderer_init,
GpuRenderer_deinit,
GpuRenderer_drawChar,
GpuRenderer_scrollWindow,
GpuRenderer_flushAndSwap,
}
};
ConsoleRenderer* getDefaultConsoleRenderer(void)
{
return &s_gpuRenderer.base;
}

View File

@ -0,0 +1,86 @@
#include <string.h>
#include <stdio.h>
#include <switch.h>
int main(int argc, char **argv)
{
consoleInit(NULL);
// Configure our supported input layout: a single player with standard controller styles
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
// Initialize the default gamepad (which reads handheld mode inputs as well as the first connected controller)
PadState pad;
padInitializeDefault(&pad);
// clear screen and home cursor
printf( CONSOLE_ESC(2J) );
// Set print co-ordinates
// /x1b[row;columnH
printf(CONSOLE_ESC(42;37m));
printf(CONSOLE_ESC( 7;4H) "%s", " _ _ ____ _ ");
printf(CONSOLE_ESC( 8;4H) "%s", " | | | | |___ \\ | |");
printf(CONSOLE_ESC( 9;4H) "%s", " __| | ___| | _____ __) | __| |");
printf(CONSOLE_ESC(10;4H) "%s", " / _` |/ _ \\ |/ / _ \\|__ < / _` |");
printf(CONSOLE_ESC(11;4H) "%s", "| (_| | __/ < (_) |__) | (_| |");
printf(CONSOLE_ESC(12;4H) "%s", " \\__,_|\\___|_|\\_\\___/____/ \\__,_|");
printf(CONSOLE_ESC(0m));
// move cursor up
// /x1b[linesA
printf(CONSOLE_ESC(10A)"Line 0");
// move cursor left
// /x1b[columnsD
printf(CONSOLE_ESC(28D)"Column 0");
// move cursor down
// /x1b[linesB
printf(CONSOLE_ESC(19B)"Line 19");
// move cursor right
// /x1b[columnsC
printf(CONSOLE_ESC(5C)"Column 20");
printf("\n");
// Color codes and attributes
for(int i=0; i<8; i++)
{
printf( CONSOLE_ESC(%1$dm) /* Set color */
"Default "
CONSOLE_ESC(1m) "Bold "
CONSOLE_ESC(7m) "Reversed "
CONSOLE_ESC(0m) /* revert attributes*/
CONSOLE_ESC(%1$dm)
CONSOLE_ESC(2m) "Light "
CONSOLE_ESC(7m) "Reversed "
"\n"
CONSOLE_ESC(0m) /* revert attributes*/
, i + 30);
}
// Main loop
while(appletMainLoop())
{
// Scan the gamepad. This should be done once for each frame
padUpdate(&pad);
// Your code goes here
// padGetButtonsDown returns the set of buttons that have been newly pressed in this frame compared to the previous one
u64 kDown = padGetButtonsDown(&pad);
if (kDown & HidNpadButton_Plus) break; // break in order to return to hbmenu
consoleUpdate(NULL);
}
consoleExit(NULL);
return 0;
}

View File

@ -0,0 +1,13 @@
#include <switch.h>
void userAppInit(void)
{
Result res = romfsInit();
if (R_FAILED(res))
diagAbortWithResult(res);
}
void userAppExit(void)
{
romfsExit();
}

View File

@ -0,0 +1,271 @@
#---------------------------------------------------------------------------------
.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 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
# 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
#
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
# If not set, it attempts to use one of the following (in this order):
# - <Project name>.json
# - config.json
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
# NACP building is skipped as well.
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := source source/SampleFramework
DATA := data
INCLUDES := include
ROMFS := romfs
# Output folders for autogenerated files in romfs
OUT_SHADERS := shaders
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -g -Wall -O2 -ffunction-sections \
$(ARCH) $(DEFINES)
CFLAGS += $(INCLUDE) -D__SWITCH__
CXXFLAGS := $(CFLAGS) -std=gnu++17 -fno-exceptions -fno-rtti
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
LIBS := -ldeko3dd -lnx
#---------------------------------------------------------------------------------
# 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 := $(CURDIR)/$(TARGET)
export TOPDIR := $(CURDIR)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD)
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
GLSLFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.glsl)))
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_BIN := $(addsuffix .o,$(BINFILES))
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
ifneq ($(strip $(ROMFS)),)
ROMFS_TARGETS :=
ROMFS_FOLDERS :=
ifneq ($(strip $(OUT_SHADERS)),)
ROMFS_SHADERS := $(ROMFS)/$(OUT_SHADERS)
ROMFS_TARGETS += $(patsubst %.glsl, $(ROMFS_SHADERS)/%.dksh, $(GLSLFILES))
ROMFS_FOLDERS += $(ROMFS_SHADERS)
endif
export ROMFS_DEPS := $(foreach file,$(ROMFS_TARGETS),$(CURDIR)/$(file))
endif
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)
ifeq ($(strip $(CONFIG_JSON)),)
jsons := $(wildcard *.json)
ifneq (,$(findstring $(TARGET).json,$(jsons)))
export APP_JSON := $(TOPDIR)/$(TARGET).json
else
ifneq (,$(findstring config.json,$(jsons)))
export APP_JSON := $(TOPDIR)/config.json
endif
endif
else
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
endif
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
.PHONY: all clean
#---------------------------------------------------------------------------------
all: $(ROMFS_TARGETS) | $(BUILD)
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
$(BUILD):
@mkdir -p $@
ifneq ($(strip $(ROMFS_TARGETS)),)
$(ROMFS_TARGETS): | $(ROMFS_FOLDERS)
$(ROMFS_FOLDERS):
@mkdir -p $@
$(ROMFS_SHADERS)/%_vsh.dksh: %_vsh.glsl
@echo {vert} $(notdir $<)
@uam -s vert -o $@ $<
$(ROMFS_SHADERS)/%_tcsh.dksh: %_tcsh.glsl
@echo {tess_ctrl} $(notdir $<)
@uam -s tess_ctrl -o $@ $<
$(ROMFS_SHADERS)/%_tesh.dksh: %_tesh.glsl
@echo {tess_eval} $(notdir $<)
@uam -s tess_eval -o $@ $<
$(ROMFS_SHADERS)/%_gsh.dksh: %_gsh.glsl
@echo {geom} $(notdir $<)
@uam -s geom -o $@ $<
$(ROMFS_SHADERS)/%_fsh.dksh: %_fsh.glsl
@echo {frag} $(notdir $<)
@uam -s frag -o $@ $<
$(ROMFS_SHADERS)/%.dksh: %.glsl
@echo {comp} $(notdir $<)
@uam -s comp -o $@ $<
endif
#---------------------------------------------------------------------------------
clean:
@echo clean ...
ifeq ($(strip $(APP_JSON)),)
@rm -fr $(BUILD) $(ROMFS_FOLDERS) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
else
@rm -fr $(BUILD) $(ROMFS_FOLDERS) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
endif
#---------------------------------------------------------------------------------
else
.PHONY: all
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
ifeq ($(strip $(APP_JSON)),)
all : $(OUTPUT).nro
ifeq ($(strip $(NO_NACP)),)
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp $(ROMFS_DEPS)
else
$(OUTPUT).nro : $(OUTPUT).elf $(ROMFS_DEPS)
endif
else
all : $(OUTPUT).nsp
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
$(OUTPUT).nso : $(OUTPUT).elf
endif
$(OUTPUT).elf : $(OFILES)
$(OFILES_SRC) : $(HFILES_BIN)
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o %_bin.h : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

View File

@ -0,0 +1,182 @@
/*
** deko3d Example 01: Simple Setup
** This example shows how to setup deko3d for rendering scenes with the GPU.
** New concepts in this example:
** - Creating devices and queues
** - Basic memory management
** - Setting up framebuffers and swapchains
** - Recording a static command list with rendering commands
** - Acquiring and presenting images with the queue and swapchain
*/
// Sample Framework headers
#include "SampleFramework/CApplication.h"
#include "SampleFramework/CMemPool.h"
// C++ standard library headers
#include <array>
#include <optional>
class CExample01 final : public CApplication
{
static constexpr unsigned NumFramebuffers = 2;
static constexpr uint32_t FramebufferWidth = 1280;
static constexpr uint32_t FramebufferHeight = 720;
static constexpr unsigned StaticCmdSize = 0x1000;
PadState pad;
dk::UniqueDevice device;
dk::UniqueQueue queue;
std::optional<CMemPool> pool_images;
std::optional<CMemPool> pool_data;
dk::UniqueCmdBuf cmdbuf;
CMemPool::Handle framebuffers_mem[NumFramebuffers];
dk::Image framebuffers[NumFramebuffers];
DkCmdList framebuffer_cmdlists[NumFramebuffers];
dk::UniqueSwapchain swapchain;
DkCmdList render_cmdlist;
public:
CExample01()
{
// Create the deko3d device
device = dk::DeviceMaker{}.create();
// Create the main queue
queue = dk::QueueMaker{device}.setFlags(DkQueueFlags_Graphics).create();
// Create the memory pools
pool_images.emplace(device, DkMemBlockFlags_GpuCached | DkMemBlockFlags_Image, 16*1024*1024);
pool_data.emplace(device, DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached, 1*1024*1024);
// Create the static command buffer and feed it freshly allocated memory
cmdbuf = dk::CmdBufMaker{device}.create();
CMemPool::Handle cmdmem = pool_data->allocate(StaticCmdSize);
cmdbuf.addMemory(cmdmem.getMemBlock(), cmdmem.getOffset(), cmdmem.getSize());
// Create the framebuffer resources
createFramebufferResources();
// Initialize gamepad
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
padInitializeDefault(&pad);
}
~CExample01()
{
// Destroy the framebuffer resources
destroyFramebufferResources();
}
void createFramebufferResources()
{
// Create layout for the framebuffers
dk::ImageLayout layout_framebuffer;
dk::ImageLayoutMaker{device}
.setFlags(DkImageFlags_UsageRender | DkImageFlags_UsagePresent | DkImageFlags_HwCompression)
.setFormat(DkImageFormat_RGBA8_Unorm)
.setDimensions(FramebufferWidth, FramebufferHeight)
.initialize(layout_framebuffer);
// Create the framebuffers
std::array<DkImage const*, NumFramebuffers> fb_array;
uint64_t fb_size = layout_framebuffer.getSize();
uint32_t fb_align = layout_framebuffer.getAlignment();
for (unsigned i = 0; i < NumFramebuffers; i ++)
{
// Allocate a framebuffer
framebuffers_mem[i] = pool_images->allocate(fb_size, fb_align);
framebuffers[i].initialize(layout_framebuffer, framebuffers_mem[i].getMemBlock(), framebuffers_mem[i].getOffset());
// Generate a command list that binds it
dk::ImageView colorTarget{ framebuffers[i] };
cmdbuf.bindRenderTargets(&colorTarget);
framebuffer_cmdlists[i] = cmdbuf.finishList();
// Fill in the array for use later by the swapchain creation code
fb_array[i] = &framebuffers[i];
}
// Create the swapchain using the framebuffers
swapchain = dk::SwapchainMaker{device, nwindowGetDefault(), fb_array}.create();
// Generate the main rendering cmdlist
recordStaticCommands();
}
void destroyFramebufferResources()
{
// Return early if we have nothing to destroy
if (!swapchain) return;
// Make sure the queue is idle before destroying anything
queue.waitIdle();
// Clear the static cmdbuf, destroying the static cmdlists in the process
cmdbuf.clear();
// Destroy the swapchain
swapchain.destroy();
// Destroy the framebuffers
for (unsigned i = 0; i < NumFramebuffers; i ++)
framebuffers_mem[i].destroy();
}
void recordStaticCommands()
{
// Calculate several measurements for the scene
unsigned HalfWidth = FramebufferWidth/2, HalfHeight = FramebufferHeight/2;
unsigned BoxSize = 400;
unsigned BoxX = HalfWidth - BoxSize/2, BoxY = HalfHeight - BoxSize/2;
unsigned TileWidth = BoxSize/5, TileHeight = BoxSize/4;
// Draw a scene using only scissors and clear colors
cmdbuf.setScissors(0, { { 0, 0, FramebufferWidth, FramebufferHeight } });
cmdbuf.clearColor(0, DkColorMask_RGBA, 0.0f, 0.25f, 0.0f, 1.0f);
cmdbuf.setScissors(0, { { BoxX, BoxY, BoxSize, BoxSize } });
cmdbuf.clearColor(0, DkColorMask_RGBA, 229/255.0f, 1.0f, 232/255.0f, 1.0f);
cmdbuf.setScissors(0, { { BoxX + 2*TileWidth, BoxY + 1*TileHeight, 1*TileWidth, 1*TileHeight } });
cmdbuf.clearColor(0, DkColorMask_RGBA, 0.0f, 0.5f, 0.0f, 1.0f);
cmdbuf.setScissors(0, { { BoxX + 1*TileWidth, BoxY + 2*TileHeight, 3*TileWidth, 1*TileHeight } });
cmdbuf.clearColor(0, DkColorMask_RGBA, 0.0f, 0.5f, 0.0f, 1.0f);
render_cmdlist = cmdbuf.finishList();
}
void render()
{
// Acquire a framebuffer from the swapchain (and wait for it to be available)
int slot = queue.acquireImage(swapchain);
// Run the command list that attaches said framebuffer to the queue
queue.submitCommands(framebuffer_cmdlists[slot]);
// Run the main rendering command list
queue.submitCommands(render_cmdlist);
// Now that we are done rendering, present it to the screen
queue.presentImage(swapchain, slot);
}
bool onFrame(u64 ns) override
{
padUpdate(&pad);
u64 kDown = padGetButtonsDown(&pad);
if (kDown & HidNpadButton_Plus)
return false;
render();
return true;
}
};
void Example01(void)
{
CExample01 app;
app.run();
}

View File

@ -0,0 +1,237 @@
/*
** deko3d Example 02: Triangle
** This example shows how to draw a basic multi-colored triangle.
** New concepts in this example:
** - Loading and using shaders
** - Setting up basic 3D engine state
** - Setting up vertex attributes and vertex buffers
** - Drawing primitives
*/
// Sample Framework headers
#include "SampleFramework/CApplication.h"
#include "SampleFramework/CMemPool.h"
#include "SampleFramework/CShader.h"
// C++ standard library headers
#include <array>
#include <optional>
namespace
{
struct Vertex
{
float position[3];
float color[3];
};
constexpr std::array VertexAttribState =
{
DkVtxAttribState{ 0, 0, offsetof(Vertex, position), DkVtxAttribSize_3x32, DkVtxAttribType_Float, 0 },
DkVtxAttribState{ 0, 0, offsetof(Vertex, color), DkVtxAttribSize_3x32, DkVtxAttribType_Float, 0 },
};
constexpr std::array VertexBufferState =
{
DkVtxBufferState{ sizeof(Vertex), 0 },
};
constexpr std::array TriangleVertexData =
{
Vertex{ { 0.0f, +1.0f, 0.0f }, { 1.0f, 0.0f, 0.0f } },
Vertex{ { -1.0f, -1.0f, 0.0f }, { 0.0f, 1.0f, 0.0f } },
Vertex{ { +1.0f, -1.0f, 0.0f }, { 0.0f, 0.0f, 1.0f } },
};
}
class CExample02 final : public CApplication
{
static constexpr unsigned NumFramebuffers = 2;
static constexpr uint32_t FramebufferWidth = 1280;
static constexpr uint32_t FramebufferHeight = 720;
static constexpr unsigned StaticCmdSize = 0x10000;
PadState pad;
dk::UniqueDevice device;
dk::UniqueQueue queue;
std::optional<CMemPool> pool_images;
std::optional<CMemPool> pool_code;
std::optional<CMemPool> pool_data;
dk::UniqueCmdBuf cmdbuf;
CShader vertexShader;
CShader fragmentShader;
CMemPool::Handle vertexBuffer;
CMemPool::Handle framebuffers_mem[NumFramebuffers];
dk::Image framebuffers[NumFramebuffers];
DkCmdList framebuffer_cmdlists[NumFramebuffers];
dk::UniqueSwapchain swapchain;
DkCmdList render_cmdlist;
public:
CExample02()
{
// Create the deko3d device
device = dk::DeviceMaker{}.create();
// Create the main queue
queue = dk::QueueMaker{device}.setFlags(DkQueueFlags_Graphics).create();
// Create the memory pools
pool_images.emplace(device, DkMemBlockFlags_GpuCached | DkMemBlockFlags_Image, 16*1024*1024);
pool_code.emplace(device, DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached | DkMemBlockFlags_Code, 128*1024);
pool_data.emplace(device, DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached, 1*1024*1024);
// Create the static command buffer and feed it freshly allocated memory
cmdbuf = dk::CmdBufMaker{device}.create();
CMemPool::Handle cmdmem = pool_data->allocate(StaticCmdSize);
cmdbuf.addMemory(cmdmem.getMemBlock(), cmdmem.getOffset(), cmdmem.getSize());
// Load the shaders
vertexShader.load(*pool_code, "romfs:/shaders/basic_vsh.dksh");
fragmentShader.load(*pool_code, "romfs:/shaders/color_fsh.dksh");
// Load the vertex buffer
vertexBuffer = pool_data->allocate(sizeof(TriangleVertexData), alignof(Vertex));
memcpy(vertexBuffer.getCpuAddr(), TriangleVertexData.data(), vertexBuffer.getSize());
// Create the framebuffer resources
createFramebufferResources();
// Initialize gamepad
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
padInitializeDefault(&pad);
}
~CExample02()
{
// Destroy the framebuffer resources
destroyFramebufferResources();
// Destroy the vertex buffer (not strictly needed in this case)
vertexBuffer.destroy();
}
void createFramebufferResources()
{
// Create layout for the framebuffers
dk::ImageLayout layout_framebuffer;
dk::ImageLayoutMaker{device}
.setFlags(DkImageFlags_UsageRender | DkImageFlags_UsagePresent | DkImageFlags_HwCompression)
.setFormat(DkImageFormat_RGBA8_Unorm)
.setDimensions(FramebufferWidth, FramebufferHeight)
.initialize(layout_framebuffer);
// Create the framebuffers
std::array<DkImage const*, NumFramebuffers> fb_array;
uint64_t fb_size = layout_framebuffer.getSize();
uint32_t fb_align = layout_framebuffer.getAlignment();
for (unsigned i = 0; i < NumFramebuffers; i ++)
{
// Allocate a framebuffer
framebuffers_mem[i] = pool_images->allocate(fb_size, fb_align);
framebuffers[i].initialize(layout_framebuffer, framebuffers_mem[i].getMemBlock(), framebuffers_mem[i].getOffset());
// Generate a command list that binds it
dk::ImageView colorTarget{ framebuffers[i] };
cmdbuf.bindRenderTargets(&colorTarget);
framebuffer_cmdlists[i] = cmdbuf.finishList();
// Fill in the array for use later by the swapchain creation code
fb_array[i] = &framebuffers[i];
}
// Create the swapchain using the framebuffers
swapchain = dk::SwapchainMaker{device, nwindowGetDefault(), fb_array}.create();
// Generate the main rendering cmdlist
recordStaticCommands();
}
void destroyFramebufferResources()
{
// Return early if we have nothing to destroy
if (!swapchain) return;
// Make sure the queue is idle before destroying anything
queue.waitIdle();
// Clear the static cmdbuf, destroying the static cmdlists in the process
cmdbuf.clear();
// Destroy the swapchain
swapchain.destroy();
// Destroy the framebuffers
for (unsigned i = 0; i < NumFramebuffers; i ++)
framebuffers_mem[i].destroy();
}
void recordStaticCommands()
{
// Initialize state structs with deko3d defaults
dk::RasterizerState rasterizerState;
dk::ColorState colorState;
dk::ColorWriteState colorWriteState;
// Configure viewport and scissor
cmdbuf.setViewports(0, { { 0.0f, 0.0f, FramebufferWidth, FramebufferHeight, 0.0f, 1.0f } });
cmdbuf.setScissors(0, { { 0, 0, FramebufferWidth, FramebufferHeight } });
// Clear the color buffer
cmdbuf.clearColor(0, DkColorMask_RGBA, 0.0f, 0.0f, 0.0f, 0.0f);
// Bind state required for drawing the triangle
cmdbuf.bindShaders(DkStageFlag_GraphicsMask, { vertexShader, fragmentShader });
cmdbuf.bindRasterizerState(rasterizerState);
cmdbuf.bindColorState(colorState);
cmdbuf.bindColorWriteState(colorWriteState);
cmdbuf.bindVtxBuffer(0, vertexBuffer.getGpuAddr(), vertexBuffer.getSize());
cmdbuf.bindVtxAttribState(VertexAttribState);
cmdbuf.bindVtxBufferState(VertexBufferState);
// Draw the triangle
cmdbuf.draw(DkPrimitive_Triangles, TriangleVertexData.size(), 1, 0, 0);
// Finish off this command list
render_cmdlist = cmdbuf.finishList();
}
void render()
{
// Acquire a framebuffer from the swapchain (and wait for it to be available)
int slot = queue.acquireImage(swapchain);
// Run the command list that attaches said framebuffer to the queue
queue.submitCommands(framebuffer_cmdlists[slot]);
// Run the main rendering command list
queue.submitCommands(render_cmdlist);
// Now that we are done rendering, present it to the screen
queue.presentImage(swapchain, slot);
}
bool onFrame(u64 ns) override
{
padUpdate(&pad);
u64 kDown = padGetButtonsDown(&pad);
if (kDown & HidNpadButton_Plus)
return false;
render();
return true;
}
};
void Example02(void)
{
CExample02 app;
app.run();
}

View File

@ -0,0 +1,378 @@
/*
** deko3d Example 03: Cube
** This example shows how to draw a basic rotating cube.
** New concepts in this example:
** - Setting up and using a depth buffer
** - Setting up uniform buffers
** - Basic 3D maths, including projection matrices
** - Updating uniforms with a dynamic command buffer
** - Adjusting resolution dynamically by recreating resources (720p handheld/1080p docked)
** - Depth buffer discard after a barrier
*/
// Sample Framework headers
#include "SampleFramework/CApplication.h"
#include "SampleFramework/CMemPool.h"
#include "SampleFramework/CShader.h"
#include "SampleFramework/CCmdMemRing.h"
// C++ standard library headers
#include <array>
#include <optional>
// GLM headers
#define GLM_FORCE_DEFAULT_ALIGNED_GENTYPES // Enforces GLSL std140/std430 alignment rules for glm types
#define GLM_FORCE_INTRINSICS // Enables usage of SIMD CPU instructions (requiring the above as well)
#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/mat4x4.hpp>
#include <glm/gtc/matrix_transform.hpp>
namespace
{
struct Vertex
{
float position[3];
float color[3];
};
constexpr std::array VertexAttribState =
{
DkVtxAttribState{ 0, 0, offsetof(Vertex, position), DkVtxAttribSize_3x32, DkVtxAttribType_Float, 0 },
DkVtxAttribState{ 0, 0, offsetof(Vertex, color), DkVtxAttribSize_3x32, DkVtxAttribType_Float, 0 },
};
constexpr std::array VertexBufferState =
{
DkVtxBufferState{ sizeof(Vertex), 0 },
};
constexpr std::array CubeVertexData =
{
// +X face
Vertex{ { +1.0f, +1.0f, +1.0f }, { 1.0f, 0.0f, 0.0f } },
Vertex{ { +1.0f, -1.0f, +1.0f }, { 0.0f, 1.0f, 0.0f } },
Vertex{ { +1.0f, -1.0f, -1.0f }, { 0.0f, 0.0f, 1.0f } },
Vertex{ { +1.0f, +1.0f, -1.0f }, { 1.0f, 1.0f, 0.0f } },
// -X face
Vertex{ { -1.0f, +1.0f, -1.0f }, { 1.0f, 0.0f, 0.0f } },
Vertex{ { -1.0f, -1.0f, -1.0f }, { 0.0f, 1.0f, 0.0f } },
Vertex{ { -1.0f, -1.0f, +1.0f }, { 0.0f, 0.0f, 1.0f } },
Vertex{ { -1.0f, +1.0f, +1.0f }, { 1.0f, 1.0f, 0.0f } },
// +Y face
Vertex{ { -1.0f, +1.0f, -1.0f }, { 1.0f, 0.0f, 0.0f } },
Vertex{ { -1.0f, +1.0f, +1.0f }, { 0.0f, 1.0f, 0.0f } },
Vertex{ { +1.0f, +1.0f, +1.0f }, { 0.0f, 0.0f, 1.0f } },
Vertex{ { +1.0f, +1.0f, -1.0f }, { 1.0f, 1.0f, 0.0f } },
// -Y face
Vertex{ { -1.0f, -1.0f, +1.0f }, { 1.0f, 0.0f, 0.0f } },
Vertex{ { -1.0f, -1.0f, -1.0f }, { 0.0f, 1.0f, 0.0f } },
Vertex{ { +1.0f, -1.0f, -1.0f }, { 0.0f, 0.0f, 1.0f } },
Vertex{ { +1.0f, -1.0f, +1.0f }, { 1.0f, 1.0f, 0.0f } },
// +Z face
Vertex{ { -1.0f, +1.0f, +1.0f }, { 1.0f, 0.0f, 0.0f } },
Vertex{ { -1.0f, -1.0f, +1.0f }, { 0.0f, 1.0f, 0.0f } },
Vertex{ { +1.0f, -1.0f, +1.0f }, { 0.0f, 0.0f, 1.0f } },
Vertex{ { +1.0f, +1.0f, +1.0f }, { 1.0f, 1.0f, 0.0f } },
// -Z face
Vertex{ { +1.0f, +1.0f, -1.0f }, { 1.0f, 0.0f, 0.0f } },
Vertex{ { +1.0f, -1.0f, -1.0f }, { 0.0f, 1.0f, 0.0f } },
Vertex{ { -1.0f, -1.0f, -1.0f }, { 0.0f, 0.0f, 1.0f } },
Vertex{ { -1.0f, +1.0f, -1.0f }, { 1.0f, 1.0f, 0.0f } },
};
struct Transformation
{
glm::mat4 mdlvMtx;
glm::mat4 projMtx;
};
inline float fractf(float x)
{
return x - floorf(x);
}
}
class CExample03 final : public CApplication
{
static constexpr unsigned NumFramebuffers = 2;
static constexpr unsigned StaticCmdSize = 0x10000;
static constexpr unsigned DynamicCmdSize = 0x10000;
PadState pad;
dk::UniqueDevice device;
dk::UniqueQueue queue;
std::optional<CMemPool> pool_images;
std::optional<CMemPool> pool_code;
std::optional<CMemPool> pool_data;
dk::UniqueCmdBuf cmdbuf;
dk::UniqueCmdBuf dyncmd;
CCmdMemRing<NumFramebuffers> dynmem;
CShader vertexShader;
CShader fragmentShader;
Transformation transformState;
CMemPool::Handle transformUniformBuffer;
CMemPool::Handle vertexBuffer;
uint32_t framebufferWidth;
uint32_t framebufferHeight;
CMemPool::Handle depthBuffer_mem;
CMemPool::Handle framebuffers_mem[NumFramebuffers];
dk::Image depthBuffer;
dk::Image framebuffers[NumFramebuffers];
DkCmdList framebuffer_cmdlists[NumFramebuffers];
dk::UniqueSwapchain swapchain;
DkCmdList render_cmdlist;
public:
CExample03()
{
// Create the deko3d device
device = dk::DeviceMaker{}.create();
// Create the main queue
queue = dk::QueueMaker{device}.setFlags(DkQueueFlags_Graphics).create();
// Create the memory pools
pool_images.emplace(device, DkMemBlockFlags_GpuCached | DkMemBlockFlags_Image, 16*1024*1024);
pool_code.emplace(device, DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached | DkMemBlockFlags_Code, 128*1024);
pool_data.emplace(device, DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached, 1*1024*1024);
// Create the static command buffer and feed it freshly allocated memory
cmdbuf = dk::CmdBufMaker{device}.create();
CMemPool::Handle cmdmem = pool_data->allocate(StaticCmdSize);
cmdbuf.addMemory(cmdmem.getMemBlock(), cmdmem.getOffset(), cmdmem.getSize());
// Create the dynamic command buffer and allocate memory for it
dyncmd = dk::CmdBufMaker{device}.create();
dynmem.allocate(*pool_data, DynamicCmdSize);
// Load the shaders
vertexShader.load(*pool_code, "romfs:/shaders/transform_vsh.dksh");
fragmentShader.load(*pool_code, "romfs:/shaders/color_fsh.dksh");
// Create the transformation uniform buffer
transformUniformBuffer = pool_data->allocate(sizeof(transformState), DK_UNIFORM_BUF_ALIGNMENT);
// Load the vertex buffer
vertexBuffer = pool_data->allocate(sizeof(CubeVertexData), alignof(Vertex));
memcpy(vertexBuffer.getCpuAddr(), CubeVertexData.data(), vertexBuffer.getSize());
// Initialize gamepad
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
padInitializeDefault(&pad);
}
~CExample03()
{
// Destroy the framebuffer resources
destroyFramebufferResources();
// Destroy the vertex buffer (not strictly needed in this case)
vertexBuffer.destroy();
// Destroy the uniform buffer (not strictly needed in this case)
transformUniformBuffer.destroy();
}
void createFramebufferResources()
{
// Create layout for the depth buffer
dk::ImageLayout layout_depthbuffer;
dk::ImageLayoutMaker{device}
.setFlags(DkImageFlags_UsageRender | DkImageFlags_HwCompression)
.setFormat(DkImageFormat_Z24S8)
.setDimensions(framebufferWidth, framebufferHeight)
.initialize(layout_depthbuffer);
// Create the depth buffer
depthBuffer_mem = pool_images->allocate(layout_depthbuffer.getSize(), layout_depthbuffer.getAlignment());
depthBuffer.initialize(layout_depthbuffer, depthBuffer_mem.getMemBlock(), depthBuffer_mem.getOffset());
// Create layout for the framebuffers
dk::ImageLayout layout_framebuffer;
dk::ImageLayoutMaker{device}
.setFlags(DkImageFlags_UsageRender | DkImageFlags_UsagePresent | DkImageFlags_HwCompression)
.setFormat(DkImageFormat_RGBA8_Unorm)
.setDimensions(framebufferWidth, framebufferHeight)
.initialize(layout_framebuffer);
// Create the framebuffers
std::array<DkImage const*, NumFramebuffers> fb_array;
uint64_t fb_size = layout_framebuffer.getSize();
uint32_t fb_align = layout_framebuffer.getAlignment();
for (unsigned i = 0; i < NumFramebuffers; i ++)
{
// Allocate a framebuffer
framebuffers_mem[i] = pool_images->allocate(fb_size, fb_align);
framebuffers[i].initialize(layout_framebuffer, framebuffers_mem[i].getMemBlock(), framebuffers_mem[i].getOffset());
// Generate a command list that binds it
dk::ImageView colorTarget{ framebuffers[i] }, depthTarget{ depthBuffer };
cmdbuf.bindRenderTargets(&colorTarget, &depthTarget);
framebuffer_cmdlists[i] = cmdbuf.finishList();
// Fill in the array for use later by the swapchain creation code
fb_array[i] = &framebuffers[i];
}
// Create the swapchain using the framebuffers
swapchain = dk::SwapchainMaker{device, nwindowGetDefault(), fb_array}.create();
// Generate the main rendering cmdlist
recordStaticCommands();
// Initialize the projection matrix
transformState.projMtx = glm::perspectiveRH_ZO(
glm::radians(40.0f),
float(framebufferWidth)/float(framebufferHeight),
0.01f, 1000.0f);
}
void destroyFramebufferResources()
{
// Return early if we have nothing to destroy
if (!swapchain) return;
// Make sure the queue is idle before destroying anything
queue.waitIdle();
// Clear the static cmdbuf, destroying the static cmdlists in the process
cmdbuf.clear();
// Destroy the swapchain
swapchain.destroy();
// Destroy the framebuffers
for (unsigned i = 0; i < NumFramebuffers; i ++)
framebuffers_mem[i].destroy();
// Destroy the depth buffer
depthBuffer_mem.destroy();
}
void recordStaticCommands()
{
// Initialize state structs with deko3d defaults
dk::RasterizerState rasterizerState;
dk::ColorState colorState;
dk::ColorWriteState colorWriteState;
dk::DepthStencilState depthStencilState;
// Configure viewport and scissor
cmdbuf.setViewports(0, { { 0.0f, 0.0f, (float)framebufferWidth, (float)framebufferHeight, 0.0f, 1.0f } });
cmdbuf.setScissors(0, { { 0, 0, framebufferWidth, framebufferHeight } });
// Clear the color and depth buffers
cmdbuf.clearColor(0, DkColorMask_RGBA, 0.0f, 0.0f, 0.0f, 0.0f);
cmdbuf.clearDepthStencil(true, 1.0f, 0xFF, 0);
// Bind state required for drawing the cube
cmdbuf.bindShaders(DkStageFlag_GraphicsMask, { vertexShader, fragmentShader });
cmdbuf.bindUniformBuffer(DkStage_Vertex, 0, transformUniformBuffer.getGpuAddr(), transformUniformBuffer.getSize());
cmdbuf.bindRasterizerState(rasterizerState);
cmdbuf.bindColorState(colorState);
cmdbuf.bindColorWriteState(colorWriteState);
cmdbuf.bindDepthStencilState(depthStencilState);
cmdbuf.bindVtxBuffer(0, vertexBuffer.getGpuAddr(), vertexBuffer.getSize());
cmdbuf.bindVtxAttribState(VertexAttribState);
cmdbuf.bindVtxBufferState(VertexBufferState);
// Draw the cube
cmdbuf.draw(DkPrimitive_Quads, CubeVertexData.size(), 1, 0, 0);
// Fragment barrier, to make sure we finish previous work before discarding the depth buffer
cmdbuf.barrier(DkBarrier_Fragments, 0);
// Discard the depth buffer since we don't need it anymore
cmdbuf.discardDepthStencil();
// Finish off this command list
render_cmdlist = cmdbuf.finishList();
}
void render()
{
// Begin generating the dynamic command list, for commands that need to be sent only this frame specifically
dynmem.begin(dyncmd);
// Update the uniform buffer with the new transformation state (this data gets inlined in the command list)
dyncmd.pushConstants(
transformUniformBuffer.getGpuAddr(), transformUniformBuffer.getSize(),
0, sizeof(transformState), &transformState);
// Finish off the dynamic command list, and submit it to the queue
queue.submitCommands(dynmem.end(dyncmd));
// Acquire a framebuffer from the swapchain (and wait for it to be available)
int slot = queue.acquireImage(swapchain);
// Run the command list that attaches said framebuffer to the queue
queue.submitCommands(framebuffer_cmdlists[slot]);
// Run the main rendering command list
queue.submitCommands(render_cmdlist);
// Now that we are done rendering, present it to the screen
queue.presentImage(swapchain, slot);
}
void onOperationMode(AppletOperationMode mode) override
{
// Destroy the framebuffer resources
destroyFramebufferResources();
// Choose framebuffer size
chooseFramebufferSize(framebufferWidth, framebufferHeight, mode);
// Recreate the framebuffers and its associated resources
createFramebufferResources();
}
bool onFrame(u64 ns) override
{
padUpdate(&pad);
u64 kDown = padGetButtonsDown(&pad);
if (kDown & HidNpadButton_Plus)
return false;
float time = ns / 1000000000.0; // double precision division; followed by implicit cast to single precision
float tau = glm::two_pi<float>();
float period1 = fractf(time/8.0f);
float period2 = fractf(time/4.0f);
// Generate the model-view matrix for this frame
// Keep in mind that GLM transformation functions multiply to the right, so essentially we have:
// mdlvMtx = Translate * RotateX * RotateY * Scale
// This means that the Scale operation is applied first, then RotateY, and so on.
transformState.mdlvMtx = glm::mat4{1.0f};
transformState.mdlvMtx = glm::translate(transformState.mdlvMtx, glm::vec3{0.0f, 0.0f, -3.0f});
transformState.mdlvMtx = glm::rotate(transformState.mdlvMtx, sinf(period2 * tau) * tau / 8.0f, glm::vec3{1.0f, 0.0f, 0.0f});
transformState.mdlvMtx = glm::rotate(transformState.mdlvMtx, -period1 * tau, glm::vec3{0.0f, 1.0f, 0.0f});
transformState.mdlvMtx = glm::scale(transformState.mdlvMtx, glm::vec3{0.5f});
render();
return true;
}
};
void Example03(void)
{
CExample03 app;
app.run();
}

View File

@ -0,0 +1,420 @@
/*
** deko3d Example 04: Textured Cube
** This example shows how to render a textured cube.
** New concepts in this example:
** - Loading a texture image from the filesystem
** - Creating and using image descriptors
** - Creating and using samplers and sampler descriptors
** - Calculating combined image+sampler handles for use by shaders
** - Initializing persistent state in a queue
**
** The texture used in this example was borrowed from https://pixabay.com/photos/cat-animal-pet-cats-close-up-300572/
*/
// Sample Framework headers
#include "SampleFramework/CApplication.h"
#include "SampleFramework/CMemPool.h"
#include "SampleFramework/CShader.h"
#include "SampleFramework/CCmdMemRing.h"
#include "SampleFramework/CDescriptorSet.h"
#include "SampleFramework/CExternalImage.h"
// C++ standard library headers
#include <array>
#include <optional>
// GLM headers
#define GLM_FORCE_DEFAULT_ALIGNED_GENTYPES // Enforces GLSL std140/std430 alignment rules for glm types
#define GLM_FORCE_INTRINSICS // Enables usage of SIMD CPU instructions (requiring the above as well)
#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/mat4x4.hpp>
#include <glm/gtc/matrix_transform.hpp>
namespace
{
struct Vertex
{
float position[3];
float texcoord[2];
};
constexpr std::array VertexAttribState =
{
DkVtxAttribState{ 0, 0, offsetof(Vertex, position), DkVtxAttribSize_3x32, DkVtxAttribType_Float, 0 },
DkVtxAttribState{ 0, 0, offsetof(Vertex, texcoord), DkVtxAttribSize_2x32, DkVtxAttribType_Float, 0 },
};
constexpr std::array VertexBufferState =
{
DkVtxBufferState{ sizeof(Vertex), 0 },
};
constexpr std::array CubeVertexData =
{
// +X face
Vertex{ { +1.0f, +1.0f, +1.0f }, { 0.0f, 0.0f } },
Vertex{ { +1.0f, -1.0f, +1.0f }, { 0.0f, 1.0f } },
Vertex{ { +1.0f, -1.0f, -1.0f }, { 1.0f, 1.0f } },
Vertex{ { +1.0f, +1.0f, -1.0f }, { 1.0f, 0.0f } },
// -X face
Vertex{ { -1.0f, +1.0f, -1.0f }, { 0.0f, 0.0f } },
Vertex{ { -1.0f, -1.0f, -1.0f }, { 0.0f, 1.0f } },
Vertex{ { -1.0f, -1.0f, +1.0f }, { 1.0f, 1.0f } },
Vertex{ { -1.0f, +1.0f, +1.0f }, { 1.0f, 0.0f } },
// +Y face
Vertex{ { -1.0f, +1.0f, -1.0f }, { 0.0f, 0.0f } },
Vertex{ { -1.0f, +1.0f, +1.0f }, { 0.0f, 1.0f } },
Vertex{ { +1.0f, +1.0f, +1.0f }, { 1.0f, 1.0f } },
Vertex{ { +1.0f, +1.0f, -1.0f }, { 1.0f, 0.0f } },
// -Y face
Vertex{ { -1.0f, -1.0f, +1.0f }, { 0.0f, 0.0f } },
Vertex{ { -1.0f, -1.0f, -1.0f }, { 0.0f, 1.0f } },
Vertex{ { +1.0f, -1.0f, -1.0f }, { 1.0f, 1.0f } },
Vertex{ { +1.0f, -1.0f, +1.0f }, { 1.0f, 0.0f } },
// +Z face
Vertex{ { -1.0f, +1.0f, +1.0f }, { 0.0f, 0.0f } },
Vertex{ { -1.0f, -1.0f, +1.0f }, { 0.0f, 1.0f } },
Vertex{ { +1.0f, -1.0f, +1.0f }, { 1.0f, 1.0f } },
Vertex{ { +1.0f, +1.0f, +1.0f }, { 1.0f, 0.0f } },
// -Z face
Vertex{ { +1.0f, +1.0f, -1.0f }, { 0.0f, 0.0f } },
Vertex{ { +1.0f, -1.0f, -1.0f }, { 0.0f, 1.0f } },
Vertex{ { -1.0f, -1.0f, -1.0f }, { 1.0f, 1.0f } },
Vertex{ { -1.0f, +1.0f, -1.0f }, { 1.0f, 0.0f } },
};
struct Transformation
{
glm::mat4 mdlvMtx;
glm::mat4 projMtx;
};
inline float fractf(float x)
{
return x - floorf(x);
}
}
class CExample04 final : public CApplication
{
static constexpr unsigned NumFramebuffers = 2;
static constexpr unsigned StaticCmdSize = 0x10000;
static constexpr unsigned DynamicCmdSize = 0x10000;
static constexpr unsigned MaxImages = 1;
static constexpr unsigned MaxSamplers = 1;
PadState pad;
dk::UniqueDevice device;
dk::UniqueQueue queue;
std::optional<CMemPool> pool_images;
std::optional<CMemPool> pool_code;
std::optional<CMemPool> pool_data;
dk::UniqueCmdBuf cmdbuf;
dk::UniqueCmdBuf dyncmd;
CCmdMemRing<NumFramebuffers> dynmem;
CDescriptorSet<MaxImages> imageDescriptorSet;
CDescriptorSet<MaxSamplers> samplerDescriptorSet;
CShader vertexShader;
CShader fragmentShader;
Transformation transformState;
CMemPool::Handle transformUniformBuffer;
CMemPool::Handle vertexBuffer;
CExternalImage texImage;
uint32_t framebufferWidth;
uint32_t framebufferHeight;
CMemPool::Handle depthBuffer_mem;
CMemPool::Handle framebuffers_mem[NumFramebuffers];
dk::Image depthBuffer;
dk::Image framebuffers[NumFramebuffers];
DkCmdList framebuffer_cmdlists[NumFramebuffers];
dk::UniqueSwapchain swapchain;
DkCmdList render_cmdlist;
public:
CExample04()
{
// Create the deko3d device
device = dk::DeviceMaker{}.create();
// Create the main queue
queue = dk::QueueMaker{device}.setFlags(DkQueueFlags_Graphics).create();
// Create the memory pools
pool_images.emplace(device, DkMemBlockFlags_GpuCached | DkMemBlockFlags_Image, 16*1024*1024);
pool_code.emplace(device, DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached | DkMemBlockFlags_Code, 128*1024);
pool_data.emplace(device, DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached, 1*1024*1024);
// Create the static command buffer and feed it freshly allocated memory
cmdbuf = dk::CmdBufMaker{device}.create();
CMemPool::Handle cmdmem = pool_data->allocate(StaticCmdSize);
cmdbuf.addMemory(cmdmem.getMemBlock(), cmdmem.getOffset(), cmdmem.getSize());
// Create the dynamic command buffer and allocate memory for it
dyncmd = dk::CmdBufMaker{device}.create();
dynmem.allocate(*pool_data, DynamicCmdSize);
// Create the image and sampler descriptor sets
imageDescriptorSet.allocate(*pool_data);
samplerDescriptorSet.allocate(*pool_data);
// Load the shaders
vertexShader.load(*pool_code, "romfs:/shaders/transform_vsh.dksh");
fragmentShader.load(*pool_code, "romfs:/shaders/texture_fsh.dksh");
// Create the transformation uniform buffer
transformUniformBuffer = pool_data->allocate(sizeof(transformState), DK_UNIFORM_BUF_ALIGNMENT);
// Load the vertex buffer
vertexBuffer = pool_data->allocate(sizeof(CubeVertexData), alignof(Vertex));
memcpy(vertexBuffer.getCpuAddr(), CubeVertexData.data(), vertexBuffer.getSize());
// Load the image
texImage.load(*pool_images, *pool_data, device, queue, "romfs:/cat-256x256.bc1", 256, 256, DkImageFormat_RGB_BC1);
// Configure persistent state in the queue
{
// Upload the image descriptor
imageDescriptorSet.update(cmdbuf, 0, texImage.getDescriptor());
// Configure a sampler
dk::Sampler sampler;
sampler.setFilter(DkFilter_Linear, DkFilter_Linear);
sampler.setWrapMode(DkWrapMode_ClampToEdge, DkWrapMode_ClampToEdge, DkWrapMode_ClampToEdge);
// Upload the sampler descriptor
dk::SamplerDescriptor samplerDescriptor;
samplerDescriptor.initialize(sampler);
samplerDescriptorSet.update(cmdbuf, 0, samplerDescriptor);
// Bind the image and sampler descriptor sets
imageDescriptorSet.bindForImages(cmdbuf);
samplerDescriptorSet.bindForSamplers(cmdbuf);
// Submit the configuration commands to the queue
queue.submitCommands(cmdbuf.finishList());
queue.waitIdle();
cmdbuf.clear();
}
// Initialize gamepad
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
padInitializeDefault(&pad);
}
~CExample04()
{
// Destroy the framebuffer resources
destroyFramebufferResources();
// Destroy the vertex buffer (not strictly needed in this case)
vertexBuffer.destroy();
// Destroy the uniform buffer (not strictly needed in this case)
transformUniformBuffer.destroy();
}
void createFramebufferResources()
{
// Create layout for the depth buffer
dk::ImageLayout layout_depthbuffer;
dk::ImageLayoutMaker{device}
.setFlags(DkImageFlags_UsageRender | DkImageFlags_HwCompression)
.setFormat(DkImageFormat_Z24S8)
.setDimensions(framebufferWidth, framebufferHeight)
.initialize(layout_depthbuffer);
// Create the depth buffer
depthBuffer_mem = pool_images->allocate(layout_depthbuffer.getSize(), layout_depthbuffer.getAlignment());
depthBuffer.initialize(layout_depthbuffer, depthBuffer_mem.getMemBlock(), depthBuffer_mem.getOffset());
// Create layout for the framebuffers
dk::ImageLayout layout_framebuffer;
dk::ImageLayoutMaker{device}
.setFlags(DkImageFlags_UsageRender | DkImageFlags_UsagePresent | DkImageFlags_HwCompression)
.setFormat(DkImageFormat_RGBA8_Unorm)
.setDimensions(framebufferWidth, framebufferHeight)
.initialize(layout_framebuffer);
// Create the framebuffers
std::array<DkImage const*, NumFramebuffers> fb_array;
uint64_t fb_size = layout_framebuffer.getSize();
uint32_t fb_align = layout_framebuffer.getAlignment();
for (unsigned i = 0; i < NumFramebuffers; i ++)
{
// Allocate a framebuffer
framebuffers_mem[i] = pool_images->allocate(fb_size, fb_align);
framebuffers[i].initialize(layout_framebuffer, framebuffers_mem[i].getMemBlock(), framebuffers_mem[i].getOffset());
// Generate a command list that binds it
dk::ImageView colorTarget{ framebuffers[i] }, depthTarget { depthBuffer };
cmdbuf.bindRenderTargets(&colorTarget, &depthTarget);
framebuffer_cmdlists[i] = cmdbuf.finishList();
// Fill in the array for use later by the swapchain creation code
fb_array[i] = &framebuffers[i];
}
// Create the swapchain using the framebuffers
swapchain = dk::SwapchainMaker{device, nwindowGetDefault(), fb_array}.create();
// Generate the main rendering cmdlist
recordStaticCommands();
// Initialize the projection matrix
transformState.projMtx = glm::perspectiveRH_ZO(
glm::radians(40.0f),
float(framebufferWidth)/float(framebufferHeight),
0.01f, 1000.0f);
}
void destroyFramebufferResources()
{
// Return early if we have nothing to destroy
if (!swapchain) return;
// Make sure the queue is idle before destroying anything
queue.waitIdle();
// Clear the static cmdbuf, destroying the static cmdlists in the process
cmdbuf.clear();
// Destroy the swapchain
swapchain.destroy();
// Destroy the framebuffers
for (unsigned i = 0; i < NumFramebuffers; i ++)
framebuffers_mem[i].destroy();
// Destroy the depth buffer
depthBuffer_mem.destroy();
}
void recordStaticCommands()
{
// Initialize state structs with deko3d defaults
dk::RasterizerState rasterizerState;
dk::ColorState colorState;
dk::ColorWriteState colorWriteState;
dk::DepthStencilState depthStencilState;
// Configure viewport and scissor
cmdbuf.setViewports(0, { { 0.0f, 0.0f, (float)framebufferWidth, (float)framebufferHeight, 0.0f, 1.0f } });
cmdbuf.setScissors(0, { { 0, 0, framebufferWidth, framebufferHeight } });
// Clear the color and depth buffers
cmdbuf.clearColor(0, DkColorMask_RGBA, 0.0f, 0.0f, 0.0f, 0.0f);
cmdbuf.clearDepthStencil(true, 1.0f, 0xFF, 0);
// Bind state required for drawing the cube
cmdbuf.bindShaders(DkStageFlag_GraphicsMask, { vertexShader, fragmentShader });
cmdbuf.bindUniformBuffer(DkStage_Vertex, 0, transformUniformBuffer.getGpuAddr(), transformUniformBuffer.getSize());
cmdbuf.bindTextures(DkStage_Fragment, 0, dkMakeTextureHandle(0, 0));
cmdbuf.bindRasterizerState(rasterizerState);
cmdbuf.bindColorState(colorState);
cmdbuf.bindColorWriteState(colorWriteState);
cmdbuf.bindDepthStencilState(depthStencilState);
cmdbuf.bindVtxBuffer(0, vertexBuffer.getGpuAddr(), vertexBuffer.getSize());
cmdbuf.bindVtxAttribState(VertexAttribState);
cmdbuf.bindVtxBufferState(VertexBufferState);
// Draw the cube
cmdbuf.draw(DkPrimitive_Quads, CubeVertexData.size(), 1, 0, 0);
// Fragment barrier, to make sure we finish previous work before discarding the depth buffer
cmdbuf.barrier(DkBarrier_Fragments, 0);
// Discard the depth buffer since we don't need it anymore
cmdbuf.discardDepthStencil();
// Finish off this command list
render_cmdlist = cmdbuf.finishList();
}
void render()
{
// Begin generating the dynamic command list, for commands that need to be sent only this frame specifically
dynmem.begin(dyncmd);
// Update the uniform buffer with the new transformation state (this data gets inlined in the command list)
dyncmd.pushConstants(
transformUniformBuffer.getGpuAddr(), transformUniformBuffer.getSize(),
0, sizeof(transformState), &transformState);
// Finish off the dynamic command list (which also submits it to the queue)
queue.submitCommands(dynmem.end(dyncmd));
// Acquire a framebuffer from the swapchain (and wait for it to be available)
int slot = queue.acquireImage(swapchain);
// Run the command list that attaches said framebuffer to the queue
queue.submitCommands(framebuffer_cmdlists[slot]);
// Run the main rendering command list
queue.submitCommands(render_cmdlist);
// Now that we are done rendering, present it to the screen
queue.presentImage(swapchain, slot);
}
void onOperationMode(AppletOperationMode mode) override
{
// Destroy the framebuffer resources
destroyFramebufferResources();
// Choose framebuffer size
chooseFramebufferSize(framebufferWidth, framebufferHeight, mode);
// Recreate the framebuffers and its associated resources
createFramebufferResources();
}
bool onFrame(u64 ns) override
{
padUpdate(&pad);
u64 kDown = padGetButtonsDown(&pad);
if (kDown & HidNpadButton_Plus)
return false;
float time = ns / 1000000000.0; // double precision division; followed by implicit cast to single precision
float tau = glm::two_pi<float>();
float period1 = fractf(time/8.0f);
float period2 = fractf(time/4.0f);
// Generate the model-view matrix for this frame
// Keep in mind that GLM transformation functions multiply to the right, so essentially we have:
// mdlvMtx = Translate * RotateX * RotateY * Scale
// This means that the Scale operation is applied first, then RotateY, and so on.
transformState.mdlvMtx = glm::mat4{1.0f};
transformState.mdlvMtx = glm::translate(transformState.mdlvMtx, glm::vec3{0.0f, 0.0f, -3.0f});
transformState.mdlvMtx = glm::rotate(transformState.mdlvMtx, sinf(period2 * tau) * tau / 8.0f, glm::vec3{1.0f, 0.0f, 0.0f});
transformState.mdlvMtx = glm::rotate(transformState.mdlvMtx, -period1 * tau, glm::vec3{0.0f, 1.0f, 0.0f});
transformState.mdlvMtx = glm::scale(transformState.mdlvMtx, glm::vec3{0.5f});
render();
return true;
}
};
void Example04(void)
{
CExample04 app;
app.run();
}

View File

@ -0,0 +1,259 @@
/*
** deko3d Example 05: Simple Tessellation
** This example shows how to use tessellation.
** New concepts in this example:
** - Using tessellation control and evaluation shaders
** - Controlling tessellation parameters
** - Configuring and using line polygon mode
** - Configuring and using built-in edge smoothing
** - Configuring and using blending (needed for obeying alpha generated by edge smoothing)
*/
// Sample Framework headers
#include "SampleFramework/CApplication.h"
#include "SampleFramework/CMemPool.h"
#include "SampleFramework/CShader.h"
// C++ standard library headers
#include <array>
#include <optional>
namespace
{
struct Vertex
{
float position[3];
float color[3];
};
constexpr std::array VertexAttribState =
{
DkVtxAttribState{ 0, 0, offsetof(Vertex, position), DkVtxAttribSize_3x32, DkVtxAttribType_Float, 0 },
DkVtxAttribState{ 0, 0, offsetof(Vertex, color), DkVtxAttribSize_3x32, DkVtxAttribType_Float, 0 },
};
constexpr std::array VertexBufferState =
{
DkVtxBufferState{ sizeof(Vertex), 0 },
};
constexpr std::array TriangleVertexData =
{
Vertex{ { 0.0f, +1.0f, 0.0f }, { 1.0f, 0.0f, 0.0f } },
Vertex{ { -1.0f, -1.0f, 0.0f }, { 0.0f, 1.0f, 0.0f } },
Vertex{ { +1.0f, -1.0f, 0.0f }, { 0.0f, 0.0f, 1.0f } },
};
}
class CExample05 final : public CApplication
{
static constexpr unsigned NumFramebuffers = 2;
static constexpr uint32_t FramebufferWidth = 1280;
static constexpr uint32_t FramebufferHeight = 720;
static constexpr unsigned StaticCmdSize = 0x10000;
PadState pad;
dk::UniqueDevice device;
dk::UniqueQueue queue;
std::optional<CMemPool> pool_images;
std::optional<CMemPool> pool_code;
std::optional<CMemPool> pool_data;
dk::UniqueCmdBuf cmdbuf;
CShader vertexShader;
CShader tessCtrlShader;
CShader tessEvalShader;
CShader fragmentShader;
CMemPool::Handle vertexBuffer;
CMemPool::Handle framebuffers_mem[NumFramebuffers];
dk::Image framebuffers[NumFramebuffers];
DkCmdList framebuffer_cmdlists[NumFramebuffers];
dk::UniqueSwapchain swapchain;
DkCmdList render_cmdlist;
public:
CExample05()
{
// Create the deko3d device
device = dk::DeviceMaker{}.create();
// Create the main queue
queue = dk::QueueMaker{device}.setFlags(DkQueueFlags_Graphics).create();
// Create the memory pools
pool_images.emplace(device, DkMemBlockFlags_GpuCached | DkMemBlockFlags_Image, 16*1024*1024);
pool_code.emplace(device, DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached | DkMemBlockFlags_Code, 128*1024);
pool_data.emplace(device, DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached, 1*1024*1024);
// Create the static command buffer and feed it freshly allocated memory
cmdbuf = dk::CmdBufMaker{device}.create();
CMemPool::Handle cmdmem = pool_data->allocate(StaticCmdSize);
cmdbuf.addMemory(cmdmem.getMemBlock(), cmdmem.getOffset(), cmdmem.getSize());
// Load the shaders
vertexShader.load(*pool_code, "romfs:/shaders/basic_vsh.dksh");
tessCtrlShader.load(*pool_code, "romfs:/shaders/tess_simple_tcsh.dksh");
tessEvalShader.load(*pool_code, "romfs:/shaders/tess_simple_tesh.dksh");
fragmentShader.load(*pool_code, "romfs:/shaders/color_fsh.dksh");
// Load the vertex buffer
vertexBuffer = pool_data->allocate(sizeof(TriangleVertexData), alignof(Vertex));
memcpy(vertexBuffer.getCpuAddr(), TriangleVertexData.data(), vertexBuffer.getSize());
// Create the framebuffer resources
createFramebufferResources();
// Initialize gamepad
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
padInitializeDefault(&pad);
}
~CExample05()
{
// Destroy the framebuffer resources
destroyFramebufferResources();
// Destroy the vertex buffer (not strictly needed in this case)
vertexBuffer.destroy();
}
void createFramebufferResources()
{
// Create layout for the framebuffers
dk::ImageLayout layout_framebuffer;
dk::ImageLayoutMaker{device}
.setFlags(DkImageFlags_UsageRender | DkImageFlags_UsagePresent | DkImageFlags_HwCompression)
.setFormat(DkImageFormat_RGBA8_Unorm)
.setDimensions(FramebufferWidth, FramebufferHeight)
.initialize(layout_framebuffer);
// Create the framebuffers
std::array<DkImage const*, NumFramebuffers> fb_array;
uint64_t fb_size = layout_framebuffer.getSize();
uint32_t fb_align = layout_framebuffer.getAlignment();
for (unsigned i = 0; i < NumFramebuffers; i ++)
{
// Allocate a framebuffer
framebuffers_mem[i] = pool_images->allocate(fb_size, fb_align);
framebuffers[i].initialize(layout_framebuffer, framebuffers_mem[i].getMemBlock(), framebuffers_mem[i].getOffset());
// Generate a command list that binds it
dk::ImageView colorTarget{ framebuffers[i] };
cmdbuf.bindRenderTargets(&colorTarget);
framebuffer_cmdlists[i] = cmdbuf.finishList();
// Fill in the array for use later by the swapchain creation code
fb_array[i] = &framebuffers[i];
}
// Create the swapchain using the framebuffers
swapchain = dk::SwapchainMaker{device, nwindowGetDefault(), fb_array}.create();
// Generate the main rendering cmdlist
recordStaticCommands();
}
void destroyFramebufferResources()
{
// Return early if we have nothing to destroy
if (!swapchain) return;
// Make sure the queue is idle before destroying anything
queue.waitIdle();
// Clear the static cmdbuf, destroying the static cmdlists in the process
cmdbuf.clear();
// Destroy the swapchain
swapchain.destroy();
// Destroy the framebuffers
for (unsigned i = 0; i < NumFramebuffers; i ++)
framebuffers_mem[i].destroy();
}
void recordStaticCommands()
{
// Initialize state structs with deko3d defaults
dk::RasterizerState rasterizerState;
dk::ColorState colorState;
dk::ColorWriteState colorWriteState;
dk::BlendState blendState;
// Configure rasterizer state: draw polygons as lines, and enable polygon smoothing
rasterizerState.setPolygonMode(DkPolygonMode_Line);
rasterizerState.setPolygonSmoothEnable(true);
// Configure color state: enable blending (needed for polygon smoothing since it generates alpha values)
colorState.setBlendEnable(0, true);
// Configure viewport and scissor
cmdbuf.setViewports(0, { { 0.0f, 0.0f, FramebufferWidth, FramebufferHeight, 0.0f, 1.0f } });
cmdbuf.setScissors(0, { { 0, 0, FramebufferWidth, FramebufferHeight } });
// Clear the color buffer
cmdbuf.clearColor(0, DkColorMask_RGBA, 0.0f, 0.0f, 0.0f, 0.0f);
// Bind state required for drawing the triangle
cmdbuf.bindShaders(DkStageFlag_GraphicsMask, { vertexShader, tessCtrlShader, tessEvalShader, fragmentShader });
cmdbuf.bindRasterizerState(rasterizerState);
cmdbuf.bindColorState(colorState);
cmdbuf.bindColorWriteState(colorWriteState);
cmdbuf.bindBlendStates(0, blendState);
cmdbuf.bindVtxBuffer(0, vertexBuffer.getGpuAddr(), vertexBuffer.getSize());
cmdbuf.bindVtxAttribState(VertexAttribState);
cmdbuf.bindVtxBufferState(VertexBufferState);
cmdbuf.setLineWidth(4.0f);
cmdbuf.setPatchSize(3);
// Note that the tessellation control shader is optional. If no such shader is bound,
// the following commands can be used to control tessellation:
// (try it out! remove the "tessCtrlShader" from the bindShaders call and uncomment these)
//cmdbuf.setTessInnerLevels(5.0f);
//cmdbuf.setTessOuterLevels(7.0f, 3.0f, 5.0f);
// Draw the triangle
cmdbuf.draw(DkPrimitive_Patches, TriangleVertexData.size(), 1, 0, 0);
// Finish off this command list
render_cmdlist = cmdbuf.finishList();
}
void render()
{
// Acquire a framebuffer from the swapchain (and wait for it to be available)
int slot = queue.acquireImage(swapchain);
// Run the command list that attaches said framebuffer to the queue
queue.submitCommands(framebuffer_cmdlists[slot]);
// Run the main rendering command list
queue.submitCommands(render_cmdlist);
// Now that we are done rendering, present it to the screen
queue.presentImage(swapchain, slot);
}
bool onFrame(u64 ns) override
{
padUpdate(&pad);
u64 kDown = padGetButtonsDown(&pad);
if (kDown & HidNpadButton_Plus)
return false;
render();
return true;
}
};
void Example05(void)
{
CExample05 app;
app.run();
}

View File

@ -0,0 +1,414 @@
/*
** deko3d Example 06: Simple Multisampling
** This example shows how to use a multisampled render target, which is then resolved into the final framebuffer.
** New concepts in this example:
** - Creating multisampled render targets
** - Rendering to non-swapchain render targets
** - Configuring multisample state
** - Performing a resolve step
** - Discarding color/depth buffers that are not used for presentation
*/
// Sample Framework headers
#include "SampleFramework/CApplication.h"
#include "SampleFramework/CMemPool.h"
#include "SampleFramework/CShader.h"
#include "SampleFramework/CCmdMemRing.h"
// C++ standard library headers
#include <array>
#include <optional>
// GLM headers
#define GLM_FORCE_DEFAULT_ALIGNED_GENTYPES // Enforces GLSL std140/std430 alignment rules for glm types
#define GLM_FORCE_INTRINSICS // Enables usage of SIMD CPU instructions (requiring the above as well)
#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/mat4x4.hpp>
#include <glm/gtc/matrix_transform.hpp>
namespace
{
struct Vertex
{
float position[3];
float color[3];
};
constexpr std::array VertexAttribState =
{
DkVtxAttribState{ 0, 0, offsetof(Vertex, position), DkVtxAttribSize_3x32, DkVtxAttribType_Float, 0 },
DkVtxAttribState{ 0, 0, offsetof(Vertex, color), DkVtxAttribSize_3x32, DkVtxAttribType_Float, 0 },
};
constexpr std::array VertexBufferState =
{
DkVtxBufferState{ sizeof(Vertex), 0 },
};
constexpr std::array CubeVertexData =
{
// +X face
Vertex{ { +1.0f, +1.0f, +1.0f }, { 1.0f, 0.0f, 0.0f } },
Vertex{ { +1.0f, -1.0f, +1.0f }, { 0.0f, 1.0f, 0.0f } },
Vertex{ { +1.0f, -1.0f, -1.0f }, { 0.0f, 0.0f, 1.0f } },
Vertex{ { +1.0f, +1.0f, -1.0f }, { 1.0f, 1.0f, 0.0f } },
// -X face
Vertex{ { -1.0f, +1.0f, -1.0f }, { 1.0f, 0.0f, 0.0f } },
Vertex{ { -1.0f, -1.0f, -1.0f }, { 0.0f, 1.0f, 0.0f } },
Vertex{ { -1.0f, -1.0f, +1.0f }, { 0.0f, 0.0f, 1.0f } },
Vertex{ { -1.0f, +1.0f, +1.0f }, { 1.0f, 1.0f, 0.0f } },
// +Y face
Vertex{ { -1.0f, +1.0f, -1.0f }, { 1.0f, 0.0f, 0.0f } },
Vertex{ { -1.0f, +1.0f, +1.0f }, { 0.0f, 1.0f, 0.0f } },
Vertex{ { +1.0f, +1.0f, +1.0f }, { 0.0f, 0.0f, 1.0f } },
Vertex{ { +1.0f, +1.0f, -1.0f }, { 1.0f, 1.0f, 0.0f } },
// -Y face
Vertex{ { -1.0f, -1.0f, +1.0f }, { 1.0f, 0.0f, 0.0f } },
Vertex{ { -1.0f, -1.0f, -1.0f }, { 0.0f, 1.0f, 0.0f } },
Vertex{ { +1.0f, -1.0f, -1.0f }, { 0.0f, 0.0f, 1.0f } },
Vertex{ { +1.0f, -1.0f, +1.0f }, { 1.0f, 1.0f, 0.0f } },
// +Z face
Vertex{ { -1.0f, +1.0f, +1.0f }, { 1.0f, 0.0f, 0.0f } },
Vertex{ { -1.0f, -1.0f, +1.0f }, { 0.0f, 1.0f, 0.0f } },
Vertex{ { +1.0f, -1.0f, +1.0f }, { 0.0f, 0.0f, 1.0f } },
Vertex{ { +1.0f, +1.0f, +1.0f }, { 1.0f, 1.0f, 0.0f } },
// -Z face
Vertex{ { +1.0f, +1.0f, -1.0f }, { 1.0f, 0.0f, 0.0f } },
Vertex{ { +1.0f, -1.0f, -1.0f }, { 0.0f, 1.0f, 0.0f } },
Vertex{ { -1.0f, -1.0f, -1.0f }, { 0.0f, 0.0f, 1.0f } },
Vertex{ { -1.0f, +1.0f, -1.0f }, { 1.0f, 1.0f, 0.0f } },
};
struct Transformation
{
glm::mat4 mdlvMtx;
glm::mat4 projMtx;
};
inline float fractf(float x)
{
return x - floorf(x);
}
}
class CExample06 final : public CApplication
{
static constexpr unsigned NumFramebuffers = 2;
static constexpr unsigned StaticCmdSize = 0x10000;
static constexpr unsigned DynamicCmdSize = 0x10000;
static constexpr DkMsMode MultisampleMode = DkMsMode_4x;
PadState pad;
dk::UniqueDevice device;
dk::UniqueQueue queue;
std::optional<CMemPool> pool_images;
std::optional<CMemPool> pool_code;
std::optional<CMemPool> pool_data;
dk::UniqueCmdBuf cmdbuf;
dk::UniqueCmdBuf dyncmd;
CCmdMemRing<NumFramebuffers> dynmem;
CShader vertexShader;
CShader fragmentShader;
Transformation transformState;
CMemPool::Handle transformUniformBuffer;
CMemPool::Handle vertexBuffer;
uint32_t framebufferWidth;
uint32_t framebufferHeight;
CMemPool::Handle colorBuffer_mem;
CMemPool::Handle depthBuffer_mem;
CMemPool::Handle framebuffers_mem[NumFramebuffers];
dk::Image colorBuffer;
dk::Image depthBuffer;
dk::Image framebuffers[NumFramebuffers];
DkCmdList framebuffer_cmdlists[NumFramebuffers];
dk::UniqueSwapchain swapchain;
DkCmdList render_cmdlist, discard_cmdlist;
public:
CExample06()
{
// Create the deko3d device
device = dk::DeviceMaker{}.create();
// Create the main queue
queue = dk::QueueMaker{device}.setFlags(DkQueueFlags_Graphics).create();
// Create the memory pools
pool_images.emplace(device, DkMemBlockFlags_GpuCached | DkMemBlockFlags_Image, 64*1024*1024);
pool_code.emplace(device, DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached | DkMemBlockFlags_Code, 128*1024);
pool_data.emplace(device, DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached, 1*1024*1024);
// Create the static command buffer and feed it freshly allocated memory
cmdbuf = dk::CmdBufMaker{device}.create();
CMemPool::Handle cmdmem = pool_data->allocate(StaticCmdSize);
cmdbuf.addMemory(cmdmem.getMemBlock(), cmdmem.getOffset(), cmdmem.getSize());
// Create the dynamic command buffer and allocate memory for it
dyncmd = dk::CmdBufMaker{device}.create();
dynmem.allocate(*pool_data, DynamicCmdSize);
// Load the shaders
vertexShader.load(*pool_code, "romfs:/shaders/transform_vsh.dksh");
fragmentShader.load(*pool_code, "romfs:/shaders/color_fsh.dksh");
// Create the transformation uniform buffer
transformUniformBuffer = pool_data->allocate(sizeof(transformState), DK_UNIFORM_BUF_ALIGNMENT);
// Load the vertex buffer
vertexBuffer = pool_data->allocate(sizeof(CubeVertexData), alignof(Vertex));
memcpy(vertexBuffer.getCpuAddr(), CubeVertexData.data(), vertexBuffer.getSize());
// Initialize gamepad
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
padInitializeDefault(&pad);
}
~CExample06()
{
// Destroy the framebuffer resources
destroyFramebufferResources();
// Destroy the vertex buffer (not strictly needed in this case)
vertexBuffer.destroy();
// Destroy the uniform buffer (not strictly needed in this case)
transformUniformBuffer.destroy();
}
void createFramebufferResources()
{
// Create layout for the (multisampled) color buffer
dk::ImageLayout layout_colorbuffer;
dk::ImageLayoutMaker{device}
.setType(DkImageType_2DMS)
.setFlags(DkImageFlags_UsageRender | DkImageFlags_Usage2DEngine | DkImageFlags_HwCompression)
.setFormat(DkImageFormat_RGBA8_Unorm)
.setMsMode(MultisampleMode)
.setDimensions(framebufferWidth, framebufferHeight)
.initialize(layout_colorbuffer);
// Create layout for the (also multisampled) depth buffer
dk::ImageLayout layout_depthbuffer;
dk::ImageLayoutMaker{device}
.setType(DkImageType_2DMS)
.setFlags(DkImageFlags_UsageRender | DkImageFlags_HwCompression)
.setFormat(DkImageFormat_Z24S8)
.setMsMode(MultisampleMode)
.setDimensions(framebufferWidth, framebufferHeight)
.initialize(layout_depthbuffer);
// Create the color buffer
colorBuffer_mem = pool_images->allocate(layout_colorbuffer.getSize(), layout_colorbuffer.getAlignment());
colorBuffer.initialize(layout_colorbuffer, colorBuffer_mem.getMemBlock(), colorBuffer_mem.getOffset());
// Create the depth buffer
depthBuffer_mem = pool_images->allocate(layout_depthbuffer.getSize(), layout_depthbuffer.getAlignment());
depthBuffer.initialize(layout_depthbuffer, depthBuffer_mem.getMemBlock(), depthBuffer_mem.getOffset());
// Create layout for the framebuffers
dk::ImageLayout layout_framebuffer;
dk::ImageLayoutMaker{device}
.setFlags(DkImageFlags_Usage2DEngine | DkImageFlags_UsagePresent)
.setFormat(DkImageFormat_RGBA8_Unorm)
.setDimensions(framebufferWidth, framebufferHeight)
.initialize(layout_framebuffer);
// Create the framebuffers
std::array<DkImage const*, NumFramebuffers> fb_array;
uint64_t fb_size = layout_framebuffer.getSize();
uint32_t fb_align = layout_framebuffer.getAlignment();
for (unsigned i = 0; i < NumFramebuffers; i ++)
{
// Allocate a framebuffer
framebuffers_mem[i] = pool_images->allocate(fb_size, fb_align);
framebuffers[i].initialize(layout_framebuffer, framebuffers_mem[i].getMemBlock(), framebuffers_mem[i].getOffset());
// Generate a command list that resolves the color buffer into the framebuffer
dk::ImageView colorView { colorBuffer }, framebufferView { framebuffers[i] };
cmdbuf.resolveImage(colorView, framebufferView);
framebuffer_cmdlists[i] = cmdbuf.finishList();
// Fill in the array for use later by the swapchain creation code
fb_array[i] = &framebuffers[i];
}
// Create the swapchain using the framebuffers
swapchain = dk::SwapchainMaker{device, nwindowGetDefault(), fb_array}.create();
// Generate the main command lists
recordStaticCommands();
// Initialize the projection matrix
transformState.projMtx = glm::perspectiveRH_ZO(
glm::radians(40.0f),
float(framebufferWidth)/float(framebufferHeight),
0.01f, 1000.0f);
}
void destroyFramebufferResources()
{
// Return early if we have nothing to destroy
if (!swapchain) return;
// Make sure the queue is idle before destroying anything
queue.waitIdle();
// Clear the static cmdbuf, destroying the static cmdlists in the process
cmdbuf.clear();
// Destroy the swapchain
swapchain.destroy();
// Destroy the framebuffers
for (unsigned i = 0; i < NumFramebuffers; i ++)
framebuffers_mem[i].destroy();
// Destroy the depth buffer
depthBuffer_mem.destroy();
// Destroy the color buffer
colorBuffer_mem.destroy();
}
void recordStaticCommands()
{
// Initialize state structs with deko3d defaults
dk::RasterizerState rasterizerState;
dk::MultisampleState multisampleState;
dk::ColorState colorState;
dk::ColorWriteState colorWriteState;
dk::DepthStencilState depthStencilState;
// Configure multisample state
multisampleState.setMode(MultisampleMode);
multisampleState.setLocations();
// Bind color buffer and depth buffer
dk::ImageView colorTarget { colorBuffer }, depthTarget { depthBuffer };
cmdbuf.bindRenderTargets(&colorTarget, &depthTarget);
// Configure viewport and scissor
cmdbuf.setViewports(0, { { 0.0f, 0.0f, (float)framebufferWidth, (float)framebufferHeight, 0.0f, 1.0f } });
cmdbuf.setScissors(0, { { 0, 0, framebufferWidth, framebufferHeight } });
// Clear the color and depth buffers
cmdbuf.clearColor(0, DkColorMask_RGBA, 0.0f, 0.0f, 0.0f, 0.0f);
cmdbuf.clearDepthStencil(true, 1.0f, 0xFF, 0);
// Bind state required for drawing the cube
cmdbuf.bindShaders(DkStageFlag_GraphicsMask, { vertexShader, fragmentShader });
cmdbuf.bindUniformBuffer(DkStage_Vertex, 0, transformUniformBuffer.getGpuAddr(), transformUniformBuffer.getSize());
cmdbuf.bindRasterizerState(rasterizerState);
cmdbuf.bindMultisampleState(multisampleState);
cmdbuf.bindColorState(colorState);
cmdbuf.bindColorWriteState(colorWriteState);
cmdbuf.bindDepthStencilState(depthStencilState);
cmdbuf.bindVtxBuffer(0, vertexBuffer.getGpuAddr(), vertexBuffer.getSize());
cmdbuf.bindVtxAttribState(VertexAttribState);
cmdbuf.bindVtxBufferState(VertexBufferState);
// Draw the cube
cmdbuf.draw(DkPrimitive_Quads, CubeVertexData.size(), 1, 0, 0);
// Finish off this command list
render_cmdlist = cmdbuf.finishList();
// Discard the color and depth buffers since we don't need them anymore
cmdbuf.bindRenderTargets(&colorTarget, &depthTarget);
cmdbuf.discardColor(0);
cmdbuf.discardDepthStencil();
// Finish off this command list
discard_cmdlist = cmdbuf.finishList();
}
void render()
{
// Begin generating the dynamic command list, for commands that need to be sent only this frame specifically
dynmem.begin(dyncmd);
// Update the uniform buffer with the new transformation state (this data gets inlined in the command list)
dyncmd.pushConstants(
transformUniformBuffer.getGpuAddr(), transformUniformBuffer.getSize(),
0, sizeof(transformState), &transformState);
// Finish off the dynamic command list (which also submits it to the queue)
queue.submitCommands(dynmem.end(dyncmd));
// Run the main rendering command list
queue.submitCommands(render_cmdlist);
// Acquire a framebuffer from the swapchain
int slot = queue.acquireImage(swapchain);
// Submit the command list that resolves the color buffer to the framebuffer
queue.submitCommands(framebuffer_cmdlists[slot]);
// Submit the command list used for discarding the color and depth buffers
queue.submitCommands(discard_cmdlist);
// Now that we are done rendering, present it to the screen (this also flushes the queue)
queue.presentImage(swapchain, slot);
}
void onOperationMode(AppletOperationMode mode) override
{
// Destroy the framebuffer resources
destroyFramebufferResources();
// Choose framebuffer size
chooseFramebufferSize(framebufferWidth, framebufferHeight, mode);
// Recreate the framebuffers and its associated resources
createFramebufferResources();
}
bool onFrame(u64 ns) override
{
padUpdate(&pad);
u64 kDown = padGetButtonsDown(&pad);
if (kDown & HidNpadButton_Plus)
return false;
float time = ns / 1000000000.0; // double precision division; followed by implicit cast to single precision
float tau = glm::two_pi<float>();
float period1 = fractf(time/8.0f);
float period2 = fractf(time/4.0f);
// Generate the model-view matrix for this frame
// Keep in mind that GLM transformation functions multiply to the right, so essentially we have:
// mdlvMtx = Translate * RotateX * RotateY * Scale
// This means that the Scale operation is applied first, then RotateY, and so on.
transformState.mdlvMtx = glm::mat4{1.0f};
transformState.mdlvMtx = glm::translate(transformState.mdlvMtx, glm::vec3{0.0f, 0.0f, -3.0f});
transformState.mdlvMtx = glm::rotate(transformState.mdlvMtx, sinf(period2 * tau) * tau / 8.0f, glm::vec3{1.0f, 0.0f, 0.0f});
transformState.mdlvMtx = glm::rotate(transformState.mdlvMtx, -period1 * tau, glm::vec3{0.0f, 1.0f, 0.0f});
transformState.mdlvMtx = glm::scale(transformState.mdlvMtx, glm::vec3{0.5f});
render();
return true;
}
};
void Example06(void)
{
CExample06 app;
app.run();
}

View File

@ -0,0 +1,407 @@
/*
** deko3d Example 07: Mesh Loading and Lighting (sRGB)
** This example shows how to load a mesh, and render it using per-fragment lighting.
** New concepts in this example:
** - Loading geometry data (mesh) from the filesystem
** - Configuring and using index buffers
** - Using sRGB framebuffers
** - Using multiple uniform buffers on different stages
** - Basic Blinn-Phong lighting with Reinhard tone mapping
*/
// Sample Framework headers
#include "SampleFramework/CApplication.h"
#include "SampleFramework/CMemPool.h"
#include "SampleFramework/CShader.h"
#include "SampleFramework/CCmdMemRing.h"
#include "SampleFramework/FileLoader.h"
// C++ standard library headers
#include <array>
#include <optional>
// GLM headers
#define GLM_FORCE_DEFAULT_ALIGNED_GENTYPES // Enforces GLSL std140/std430 alignment rules for glm types
#define GLM_FORCE_INTRINSICS // Enables usage of SIMD CPU instructions (requiring the above as well)
#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/mat4x4.hpp>
#include <glm/gtc/matrix_transform.hpp>
namespace
{
struct Vertex
{
float position[3];
float normal[3];
};
constexpr std::array VertexAttribState =
{
DkVtxAttribState{ 0, 0, offsetof(Vertex, position), DkVtxAttribSize_3x32, DkVtxAttribType_Float, 0 },
DkVtxAttribState{ 0, 0, offsetof(Vertex, normal), DkVtxAttribSize_3x32, DkVtxAttribType_Float, 0 },
};
constexpr std::array VertexBufferState =
{
DkVtxBufferState{ sizeof(Vertex), 0 },
};
struct Transformation
{
glm::mat4 mdlvMtx;
glm::mat4 projMtx;
};
struct Lighting
{
glm::vec4 lightPos; // if w=0 this is lightDir
glm::vec3 ambient;
glm::vec3 diffuse;
glm::vec4 specular; // w is shininess
};
inline float fractf(float x)
{
return x - floorf(x);
}
}
class CExample07 final : public CApplication
{
static constexpr unsigned NumFramebuffers = 2;
static constexpr unsigned StaticCmdSize = 0x10000;
static constexpr unsigned DynamicCmdSize = 0x10000;
static constexpr DkMsMode MultisampleMode = DkMsMode_4x;
PadState pad;
dk::UniqueDevice device;
dk::UniqueQueue queue;
std::optional<CMemPool> pool_images;
std::optional<CMemPool> pool_code;
std::optional<CMemPool> pool_data;
dk::UniqueCmdBuf cmdbuf;
dk::UniqueCmdBuf dyncmd;
CCmdMemRing<NumFramebuffers> dynmem;
CShader vertexShader;
CShader fragmentShader;
Transformation transformState;
CMemPool::Handle transformUniformBuffer;
Lighting lightingState;
CMemPool::Handle lightingUniformBuffer;
CMemPool::Handle vertexBuffer;
CMemPool::Handle indexBuffer;
uint32_t framebufferWidth;
uint32_t framebufferHeight;
CMemPool::Handle colorBuffer_mem;
CMemPool::Handle depthBuffer_mem;
CMemPool::Handle framebuffers_mem[NumFramebuffers];
dk::Image colorBuffer;
dk::Image depthBuffer;
dk::Image framebuffers[NumFramebuffers];
DkCmdList framebuffer_cmdlists[NumFramebuffers];
dk::UniqueSwapchain swapchain;
DkCmdList render_cmdlist, discard_cmdlist;
public:
CExample07()
{
// Create the deko3d device
device = dk::DeviceMaker{}.create();
// Create the main queue
queue = dk::QueueMaker{device}.setFlags(DkQueueFlags_Graphics).create();
// Create the memory pools
pool_images.emplace(device, DkMemBlockFlags_GpuCached | DkMemBlockFlags_Image, 64*1024*1024);
pool_code.emplace(device, DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached | DkMemBlockFlags_Code, 128*1024);
pool_data.emplace(device, DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached, 1*1024*1024);
// Create the static command buffer and feed it freshly allocated memory
cmdbuf = dk::CmdBufMaker{device}.create();
CMemPool::Handle cmdmem = pool_data->allocate(StaticCmdSize);
cmdbuf.addMemory(cmdmem.getMemBlock(), cmdmem.getOffset(), cmdmem.getSize());
// Create the dynamic command buffer and allocate memory for it
dyncmd = dk::CmdBufMaker{device}.create();
dynmem.allocate(*pool_data, DynamicCmdSize);
// Load the shaders
vertexShader.load(*pool_code, "romfs:/shaders/transform_normal_vsh.dksh");
fragmentShader.load(*pool_code, "romfs:/shaders/basic_lighting_fsh.dksh");
// Create the transformation uniform buffer
transformUniformBuffer = pool_data->allocate(sizeof(transformState), DK_UNIFORM_BUF_ALIGNMENT);
// Create the lighting uniform buffer
lightingUniformBuffer = pool_data->allocate(sizeof(lightingState), DK_UNIFORM_BUF_ALIGNMENT);
// Initialize the lighting state
lightingState.lightPos = glm::vec4{0.0f, 4.0f, 1.0f, 1.0f};
lightingState.ambient = glm::vec3{0.046227f,0.028832f,0.003302f};
lightingState.diffuse = glm::vec3{0.564963f,0.367818f,0.051293f};
lightingState.specular = glm::vec4{24.0f*glm::vec3{0.394737f,0.308916f,0.134004f}, 64.0f};
// Load the teapot mesh
vertexBuffer = LoadFile(*pool_data, "romfs:/teapot-vtx.bin", alignof(Vertex));
indexBuffer = LoadFile(*pool_data, "romfs:/teapot-idx.bin", alignof(u16));
// Initialize gamepad
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
padInitializeDefault(&pad);
}
~CExample07()
{
// Destroy the framebuffer resources
destroyFramebufferResources();
// Destroy the index buffer (not strictly needed in this case)
indexBuffer.destroy();
// Destroy the vertex buffer (not strictly needed in this case)
vertexBuffer.destroy();
// Destroy the uniform buffer (not strictly needed in this case)
transformUniformBuffer.destroy();
}
void createFramebufferResources()
{
// Create layout for the (multisampled) color buffer
dk::ImageLayout layout_colorbuffer;
dk::ImageLayoutMaker{device}
.setType(DkImageType_2DMS)
.setFlags(DkImageFlags_UsageRender | DkImageFlags_Usage2DEngine | DkImageFlags_HwCompression)
.setFormat(DkImageFormat_RGBA8_Unorm_sRGB)
.setMsMode(MultisampleMode)
.setDimensions(framebufferWidth, framebufferHeight)
.initialize(layout_colorbuffer);
// Create layout for the (also multisampled) depth buffer
dk::ImageLayout layout_depthbuffer;
dk::ImageLayoutMaker{device}
.setType(DkImageType_2DMS)
.setFlags(DkImageFlags_UsageRender | DkImageFlags_HwCompression)
.setFormat(DkImageFormat_Z24S8)
.setMsMode(MultisampleMode)
.setDimensions(framebufferWidth, framebufferHeight)
.initialize(layout_depthbuffer);
// Create the color buffer
colorBuffer_mem = pool_images->allocate(layout_colorbuffer.getSize(), layout_colorbuffer.getAlignment());
colorBuffer.initialize(layout_colorbuffer, colorBuffer_mem.getMemBlock(), colorBuffer_mem.getOffset());
// Create the depth buffer
depthBuffer_mem = pool_images->allocate(layout_depthbuffer.getSize(), layout_depthbuffer.getAlignment());
depthBuffer.initialize(layout_depthbuffer, depthBuffer_mem.getMemBlock(), depthBuffer_mem.getOffset());
// Create layout for the framebuffers
dk::ImageLayout layout_framebuffer;
dk::ImageLayoutMaker{device}
.setFlags(DkImageFlags_Usage2DEngine | DkImageFlags_UsagePresent)
.setFormat(DkImageFormat_RGBA8_Unorm_sRGB)
.setDimensions(framebufferWidth, framebufferHeight)
.initialize(layout_framebuffer);
// Create the framebuffers
std::array<DkImage const*, NumFramebuffers> fb_array;
uint64_t fb_size = layout_framebuffer.getSize();
uint32_t fb_align = layout_framebuffer.getAlignment();
for (unsigned i = 0; i < NumFramebuffers; i ++)
{
// Allocate a framebuffer
framebuffers_mem[i] = pool_images->allocate(fb_size, fb_align);
framebuffers[i].initialize(layout_framebuffer, framebuffers_mem[i].getMemBlock(), framebuffers_mem[i].getOffset());
// Generate a command list that resolves the color buffer into the framebuffer
dk::ImageView colorView { colorBuffer }, framebufferView { framebuffers[i] };
cmdbuf.resolveImage(colorView, framebufferView);
framebuffer_cmdlists[i] = cmdbuf.finishList();
// Fill in the array for use later by the swapchain creation code
fb_array[i] = &framebuffers[i];
}
// Create the swapchain using the framebuffers
swapchain = dk::SwapchainMaker{device, nwindowGetDefault(), fb_array}.create();
// Generate the main command lists
recordStaticCommands();
// Initialize the projection matrix
transformState.projMtx = glm::perspectiveRH_ZO(
glm::radians(40.0f),
float(framebufferWidth)/float(framebufferHeight),
0.01f, 1000.0f);
}
void destroyFramebufferResources()
{
// Return early if we have nothing to destroy
if (!swapchain) return;
// Make sure the queue is idle before destroying anything
queue.waitIdle();
// Clear the static cmdbuf, destroying the static cmdlists in the process
cmdbuf.clear();
// Destroy the swapchain
swapchain.destroy();
// Destroy the framebuffers
for (unsigned i = 0; i < NumFramebuffers; i ++)
framebuffers_mem[i].destroy();
// Destroy the depth buffer
depthBuffer_mem.destroy();
// Destroy the color buffer
colorBuffer_mem.destroy();
}
void recordStaticCommands()
{
// Initialize state structs with deko3d defaults
dk::RasterizerState rasterizerState;
dk::MultisampleState multisampleState;
dk::ColorState colorState;
dk::ColorWriteState colorWriteState;
dk::DepthStencilState depthStencilState;
// Configure multisample state
multisampleState.setMode(MultisampleMode);
multisampleState.setLocations();
// Bind color buffer and depth buffer
dk::ImageView colorTarget { colorBuffer }, depthTarget { depthBuffer };
cmdbuf.bindRenderTargets(&colorTarget, &depthTarget);
// Configure viewport and scissor
cmdbuf.setViewports(0, { { 0.0f, 0.0f, (float)framebufferWidth, (float)framebufferHeight, 0.0f, 1.0f } });
cmdbuf.setScissors(0, { { 0, 0, framebufferWidth, framebufferHeight } });
// Clear the color and depth buffers
cmdbuf.clearColor(0, DkColorMask_RGBA, 0.0f, 0.0f, 0.0f, 0.0f);
cmdbuf.clearDepthStencil(true, 1.0f, 0xFF, 0);
// Bind state required for drawing the mesh
cmdbuf.bindShaders(DkStageFlag_GraphicsMask, { vertexShader, fragmentShader });
cmdbuf.bindUniformBuffer(DkStage_Vertex, 0, transformUniformBuffer.getGpuAddr(), transformUniformBuffer.getSize());
cmdbuf.bindUniformBuffer(DkStage_Fragment, 0, lightingUniformBuffer.getGpuAddr(), lightingUniformBuffer.getSize());
cmdbuf.bindRasterizerState(rasterizerState);
cmdbuf.bindMultisampleState(multisampleState);
cmdbuf.bindColorState(colorState);
cmdbuf.bindColorWriteState(colorWriteState);
cmdbuf.bindDepthStencilState(depthStencilState);
cmdbuf.bindVtxBuffer(0, vertexBuffer.getGpuAddr(), vertexBuffer.getSize());
cmdbuf.bindVtxAttribState(VertexAttribState);
cmdbuf.bindVtxBufferState(VertexBufferState);
cmdbuf.bindIdxBuffer(DkIdxFormat_Uint16, indexBuffer.getGpuAddr());
// Draw the mesh
cmdbuf.drawIndexed(DkPrimitive_Triangles, indexBuffer.getSize() / sizeof(u16), 1, 0, 0, 0);
// Finish off this command list
render_cmdlist = cmdbuf.finishList();
// Discard the color and depth buffers since we don't need them anymore
cmdbuf.bindRenderTargets(&colorTarget, &depthTarget);
cmdbuf.discardColor(0);
cmdbuf.discardDepthStencil();
// Finish off this command list
discard_cmdlist = cmdbuf.finishList();
}
void render()
{
// Begin generating the dynamic command list, for commands that need to be sent only this frame specifically
dynmem.begin(dyncmd);
// Update the transformation uniform buffer with the new state (this data gets inlined in the command list)
dyncmd.pushConstants(
transformUniformBuffer.getGpuAddr(), transformUniformBuffer.getSize(),
0, sizeof(transformState), &transformState);
// Update the lighting uniform buffer with the new state
dyncmd.pushConstants(
lightingUniformBuffer.getGpuAddr(), lightingUniformBuffer.getSize(),
0, sizeof(lightingState), &lightingState);
// Finish off the dynamic command list (which also submits it to the queue)
queue.submitCommands(dynmem.end(dyncmd));
// Run the main rendering command list
queue.submitCommands(render_cmdlist);
// Acquire a framebuffer from the swapchain
int slot = queue.acquireImage(swapchain);
// Submit the command list that resolves the color buffer to the framebuffer
queue.submitCommands(framebuffer_cmdlists[slot]);
// Submit the command list used for discarding the color and depth buffers
queue.submitCommands(discard_cmdlist);
// Now that we are done rendering, present it to the screen (this also flushes the queue)
queue.presentImage(swapchain, slot);
}
void onOperationMode(AppletOperationMode mode) override
{
// Destroy the framebuffer resources
destroyFramebufferResources();
// Choose framebuffer size
chooseFramebufferSize(framebufferWidth, framebufferHeight, mode);
// Recreate the framebuffers and its associated resources
createFramebufferResources();
}
bool onFrame(u64 ns) override
{
padUpdate(&pad);
u64 kDown = padGetButtonsDown(&pad);
if (kDown & HidNpadButton_Plus)
return false;
float time = ns / 1000000000.0; // double precision division; followed by implicit cast to single precision
float tau = glm::two_pi<float>();
float period1 = fractf(time/8.0f);
float period2 = fractf(time/4.0f);
// Generate the model-view matrix for this frame
// Keep in mind that GLM transformation functions multiply to the right, so essentially we have:
// mdlvMtx = Translate1 * RotateX * RotateY * Translate2
// This means that the Translate2 operation is applied first, then RotateY, and so on.
transformState.mdlvMtx = glm::mat4{1.0f};
transformState.mdlvMtx = glm::translate(transformState.mdlvMtx, glm::vec3{0.0f, 0.0f, -3.0f});
transformState.mdlvMtx = glm::rotate(transformState.mdlvMtx, sinf(period2 * tau) * tau / 8.0f, glm::vec3{1.0f, 0.0f, 0.0f});
transformState.mdlvMtx = glm::rotate(transformState.mdlvMtx, -period1 * tau, glm::vec3{0.0f, 1.0f, 0.0f});
transformState.mdlvMtx = glm::translate(transformState.mdlvMtx, glm::vec3{0.0f, -0.5f, 0.0f});
render();
return true;
}
};
void Example07(void)
{
CExample07 app;
app.run();
}

View File

@ -0,0 +1,488 @@
/*
** deko3d Example 08: Deferred Shading (Multipass Rendering with Tiled Cache)
** This example shows how to perform deferred shading, a multipass rendering technique that goes well with the tiled cache.
** New concepts in this example:
** - Rendering to multiple render targets (MRT) at once
** - Floating point render targets
** - Enabling and configuring the tiled cache
** - Using the tiled barrier for relaxing ordering to the tiles generated by the binner (as opposed to a full fragment barrier)
** - Custom composition step reading the output of previous rendering passes as textures
*/
// Sample Framework headers
#include "SampleFramework/CApplication.h"
#include "SampleFramework/CMemPool.h"
#include "SampleFramework/CShader.h"
#include "SampleFramework/CCmdMemRing.h"
#include "SampleFramework/CDescriptorSet.h"
#include "SampleFramework/FileLoader.h"
// C++ standard library headers
#include <array>
#include <optional>
// GLM headers
#define GLM_FORCE_DEFAULT_ALIGNED_GENTYPES // Enforces GLSL std140/std430 alignment rules for glm types
#define GLM_FORCE_INTRINSICS // Enables usage of SIMD CPU instructions (requiring the above as well)
#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/mat4x4.hpp>
#include <glm/gtc/matrix_transform.hpp>
namespace
{
struct Vertex
{
float position[3];
float normal[3];
};
constexpr std::array VertexAttribState =
{
DkVtxAttribState{ 0, 0, offsetof(Vertex, position), DkVtxAttribSize_3x32, DkVtxAttribType_Float, 0 },
DkVtxAttribState{ 0, 0, offsetof(Vertex, normal), DkVtxAttribSize_3x32, DkVtxAttribType_Float, 0 },
};
constexpr std::array VertexBufferState =
{
DkVtxBufferState{ sizeof(Vertex), 0 },
};
struct Transformation
{
glm::mat4 mdlvMtx;
glm::mat4 projMtx;
};
struct Lighting
{
glm::vec4 lightPos; // if w=0 this is lightDir
glm::vec3 ambient;
glm::vec3 diffuse;
glm::vec4 specular; // w is shininess
};
inline float fractf(float x)
{
return x - floorf(x);
}
}
class CExample08 final : public CApplication
{
static constexpr unsigned NumFramebuffers = 2;
static constexpr unsigned StaticCmdSize = 0x10000;
static constexpr unsigned DynamicCmdSize = 0x10000;
static constexpr unsigned MaxImages = 3;
static constexpr unsigned MaxSamplers = 1;
PadState pad;
dk::UniqueDevice device;
dk::UniqueQueue queue;
std::optional<CMemPool> pool_images;
std::optional<CMemPool> pool_code;
std::optional<CMemPool> pool_data;
dk::UniqueCmdBuf cmdbuf;
dk::UniqueCmdBuf dyncmd;
CCmdMemRing<NumFramebuffers> dynmem;
CDescriptorSet<MaxImages> imageDescriptorSet;
CDescriptorSet<MaxSamplers> samplerDescriptorSet;
CShader vertexShader;
CShader fragmentShader;
CShader compositionVertexShader;
CShader compositionFragmentShader;
Transformation transformState;
CMemPool::Handle transformUniformBuffer;
Lighting lightingState;
CMemPool::Handle lightingUniformBuffer;
CMemPool::Handle vertexBuffer;
CMemPool::Handle indexBuffer;
uint32_t framebufferWidth;
uint32_t framebufferHeight;
CMemPool::Handle albedoBuffer_mem;
CMemPool::Handle normalBuffer_mem;
CMemPool::Handle viewDirBuffer_mem;
CMemPool::Handle depthBuffer_mem;
CMemPool::Handle framebuffers_mem[NumFramebuffers];
dk::Image albedoBuffer;
dk::Image normalBuffer;
dk::Image viewDirBuffer;
dk::Image depthBuffer;
dk::Image framebuffers[NumFramebuffers];
DkCmdList framebuffer_cmdlists[NumFramebuffers];
dk::UniqueSwapchain swapchain;
DkCmdList render_cmdlist, composition_cmdlist;
public:
CExample08()
{
// Create the deko3d device
device = dk::DeviceMaker{}.create();
// Create the main queue
queue = dk::QueueMaker{device}.setFlags(DkQueueFlags_Graphics).create();
// Create the memory pools
pool_images.emplace(device, DkMemBlockFlags_GpuCached | DkMemBlockFlags_Image, 64*1024*1024);
pool_code.emplace(device, DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached | DkMemBlockFlags_Code, 1*1024*1024);
pool_data.emplace(device, DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached, 1*1024*1024);
// Create the static command buffer and feed it freshly allocated memory
cmdbuf = dk::CmdBufMaker{device}.create();
CMemPool::Handle cmdmem = pool_data->allocate(StaticCmdSize);
cmdbuf.addMemory(cmdmem.getMemBlock(), cmdmem.getOffset(), cmdmem.getSize());
// Create the dynamic command buffer and allocate memory for it
dyncmd = dk::CmdBufMaker{device}.create();
dynmem.allocate(*pool_data, DynamicCmdSize);
// Create the image and sampler descriptor sets
imageDescriptorSet.allocate(*pool_data);
samplerDescriptorSet.allocate(*pool_data);
// Load the shaders
vertexShader.load(*pool_code, "romfs:/shaders/transform_normal_vsh.dksh");
fragmentShader.load(*pool_code, "romfs:/shaders/basic_deferred_fsh.dksh");
compositionVertexShader.load(*pool_code, "romfs:/shaders/composition_vsh.dksh");
compositionFragmentShader.load(*pool_code, "romfs:/shaders/composition_fsh.dksh");
// Create the transformation uniform buffer
transformUniformBuffer = pool_data->allocate(sizeof(transformState), DK_UNIFORM_BUF_ALIGNMENT);
// Create the lighting uniform buffer
lightingUniformBuffer = pool_data->allocate(sizeof(lightingState), DK_UNIFORM_BUF_ALIGNMENT);
// Initialize the lighting state
lightingState.lightPos = glm::vec4{0.0f, 4.0f, 1.0f, 1.0f};
lightingState.ambient = glm::vec3{0.046227f,0.028832f,0.003302f};
lightingState.diffuse = glm::vec3{0.564963f,0.367818f,0.051293f};
lightingState.specular = glm::vec4{24.0f*glm::vec3{0.394737f,0.308916f,0.134004f}, 64.0f};
// Load the teapot mesh
vertexBuffer = LoadFile(*pool_data, "romfs:/teapot-vtx.bin", alignof(Vertex));
indexBuffer = LoadFile(*pool_data, "romfs:/teapot-idx.bin", alignof(u16));
// Configure persistent state in the queue
{
// Bind the image and sampler descriptor sets
imageDescriptorSet.bindForImages(cmdbuf);
samplerDescriptorSet.bindForSamplers(cmdbuf);
// Enable the tiled cache
cmdbuf.setTileSize(64, 64); // example size, please experiment with this
cmdbuf.tiledCacheOp(DkTiledCacheOp_Enable);
// Submit the configuration commands to the queue
queue.submitCommands(cmdbuf.finishList());
queue.waitIdle();
cmdbuf.clear();
}
// Initialize gamepad
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
padInitializeDefault(&pad);
}
void createFramebufferResources()
{
// Calculate layout for the different buffers part of the g-buffer
dk::ImageLayout layout_gbuffer;
dk::ImageLayoutMaker{device}
.setFlags(DkImageFlags_UsageRender | DkImageFlags_HwCompression)
.setFormat(DkImageFormat_RGBA16_Float)
.setDimensions(framebufferWidth, framebufferHeight)
.initialize(layout_gbuffer);
// Calculate layout for the depth buffer
dk::ImageLayout layout_depthbuffer;
dk::ImageLayoutMaker{device}
.setFlags(DkImageFlags_UsageRender | DkImageFlags_HwCompression)
.setFormat(DkImageFormat_Z24S8)
.setDimensions(framebufferWidth, framebufferHeight)
.initialize(layout_depthbuffer);
// Calculate layout for the framebuffers
dk::ImageLayout layout_framebuffer;
dk::ImageLayoutMaker{device}
.setFlags(DkImageFlags_UsageRender | DkImageFlags_UsagePresent)
.setFormat(DkImageFormat_RGBA8_Unorm_sRGB)
.setDimensions(framebufferWidth, framebufferHeight)
.initialize(layout_framebuffer);
// Create the albedo buffer
albedoBuffer_mem = pool_images->allocate(layout_gbuffer.getSize(), layout_gbuffer.getAlignment());
albedoBuffer.initialize(layout_gbuffer, albedoBuffer_mem.getMemBlock(), albedoBuffer_mem.getOffset());
// Create the normal buffer
normalBuffer_mem = pool_images->allocate(layout_gbuffer.getSize(), layout_gbuffer.getAlignment());
normalBuffer.initialize(layout_gbuffer, normalBuffer_mem.getMemBlock(), normalBuffer_mem.getOffset());
// Create the view direction buffer
viewDirBuffer_mem = pool_images->allocate(layout_gbuffer.getSize(), layout_gbuffer.getAlignment());
viewDirBuffer.initialize(layout_gbuffer, viewDirBuffer_mem.getMemBlock(), viewDirBuffer_mem.getOffset());
// Create the depth buffer
depthBuffer_mem = pool_images->allocate(layout_depthbuffer.getSize(), layout_depthbuffer.getAlignment());
depthBuffer.initialize(layout_depthbuffer, depthBuffer_mem.getMemBlock(), depthBuffer_mem.getOffset());
// Create the framebuffers
uint64_t fb_size = layout_framebuffer.getSize();
uint32_t fb_align = layout_framebuffer.getAlignment();
DkImage const* fb_array[NumFramebuffers];
for (unsigned i = 0; i < NumFramebuffers; i ++)
{
// Allocate a framebuffer
framebuffers_mem[i] = pool_images->allocate(fb_size, fb_align);
framebuffers[i].initialize(layout_framebuffer, framebuffers_mem[i].getMemBlock(), framebuffers_mem[i].getOffset());
// Generate a command list that binds the framebuffer
dk::ImageView framebufferView { framebuffers[i] };
cmdbuf.bindRenderTargets(&framebufferView);
framebuffer_cmdlists[i] = cmdbuf.finishList();
// Fill in the array for use later by the swapchain creation code
fb_array[i] = &framebuffers[i];
}
// Create the swapchain using the framebuffers
swapchain = dk::SwapchainMaker{device, nwindowGetDefault(), fb_array, NumFramebuffers}.create();
// Generate the static command lists
recordStaticCommands();
// Initialize the projection matrix
transformState.projMtx = glm::perspectiveRH_ZO(
glm::radians(40.0f),
float(framebufferWidth)/float(framebufferHeight),
0.01f, 1000.0f);
}
void destroyFramebufferResources()
{
// Return early if we have nothing to destroy
if (!swapchain) return;
// Make sure the queue is idle before destroying anything
queue.waitIdle();
// Clear the static cmdbuf, destroying the static cmdlists in the process
cmdbuf.clear();
// Destroy the swapchain
swapchain.destroy();
// Destroy the framebuffers
for (unsigned i = 0; i < NumFramebuffers; i ++)
framebuffers_mem[i].destroy();
// Destroy the rendertargets
depthBuffer_mem.destroy();
viewDirBuffer_mem.destroy();
normalBuffer_mem.destroy();
albedoBuffer_mem.destroy();
}
~CExample08()
{
// Destory the framebuffer resources
destroyFramebufferResources();
// Destroy the index buffer (not strictly needed in this case)
indexBuffer.destroy();
// Destroy the vertex buffer (not strictly needed in this case)
vertexBuffer.destroy();
// Destroy the uniform buffers (not strictly needed in this case)
lightingUniformBuffer.destroy();
transformUniformBuffer.destroy();
}
void recordStaticCommands()
{
// Initialize state structs with deko3d defaults
dk::RasterizerState rasterizerState;
dk::ColorState colorState;
dk::ColorWriteState colorWriteState;
dk::DepthStencilState depthStencilState;
// Bind g-buffer and depth buffer
dk::ImageView albedoTarget { albedoBuffer }, normalTarget { normalBuffer }, viewDirTarget { viewDirBuffer }, depthTarget { depthBuffer };
cmdbuf.bindRenderTargets({ &albedoTarget, &normalTarget, &viewDirTarget }, &depthTarget);
// Configure viewport and scissor
const DkViewport viewport = { 0.0f, 0.0f, float(framebufferWidth), float(framebufferHeight), 0.0f, 1.0f };
const DkScissor scissor = { 0, 0, framebufferWidth, framebufferHeight };
cmdbuf.setViewports(0, { viewport, viewport, viewport });
cmdbuf.setScissors(0, { scissor, scissor, scissor });
// Clear the g-buffer and the depth buffer
cmdbuf.clearColor(0, DkColorMask_RGBA, 0.0f, 0.0f, 0.0f, 0.0f);
cmdbuf.clearColor(1, DkColorMask_RGBA, 0.0f, 0.0f, 0.0f, 0.0f);
cmdbuf.clearColor(2, DkColorMask_RGBA, 0.0f, 0.0f, 0.0f, 0.0f);
cmdbuf.clearDepthStencil(true, 1.0f, 0xFF, 0);
// Bind state required for drawing the mesh
cmdbuf.bindShaders(DkStageFlag_GraphicsMask, { vertexShader, fragmentShader });
cmdbuf.bindUniformBuffer(DkStage_Vertex, 0, transformUniformBuffer.getGpuAddr(), transformUniformBuffer.getSize());
cmdbuf.bindRasterizerState(rasterizerState);
cmdbuf.bindColorState(colorState);
cmdbuf.bindColorWriteState(colorWriteState);
cmdbuf.bindDepthStencilState(depthStencilState);
cmdbuf.bindVtxBuffer(0, vertexBuffer.getGpuAddr(), vertexBuffer.getSize());
cmdbuf.bindVtxAttribState(VertexAttribState);
cmdbuf.bindVtxBufferState(VertexBufferState);
cmdbuf.bindIdxBuffer(DkIdxFormat_Uint16, indexBuffer.getGpuAddr());
// Draw the mesh
cmdbuf.drawIndexed(DkPrimitive_Triangles, indexBuffer.getSize() / sizeof(u16), 1, 0, 0, 0);
// Tiled barrier (similar to using Vulkan's vkCmdNextSubpass) + image cache
// flush so that the next rendering step can access the output from this step
cmdbuf.barrier(DkBarrier_Tiles, DkInvalidateFlags_Image);
// Discard the depth buffer since we don't need it anymore
cmdbuf.discardDepthStencil();
// End of the main rendering command list
render_cmdlist = cmdbuf.finishList();
// Upload image descriptors
std::array<dk::ImageDescriptor, MaxImages> descriptors;
descriptors[0].initialize(albedoTarget);
descriptors[1].initialize(normalTarget);
descriptors[2].initialize(viewDirTarget);
imageDescriptorSet.update(cmdbuf, 0, descriptors);
// Upload sampler descriptor
dk::Sampler sampler;
dk::SamplerDescriptor samplerDescriptor;
samplerDescriptor.initialize(sampler);
samplerDescriptorSet.update(cmdbuf, 0, samplerDescriptor);
// Flush the descriptor cache
cmdbuf.barrier(DkBarrier_None, DkInvalidateFlags_Descriptors);
// Bind state required for doing the composition
cmdbuf.setViewports(0, viewport);
cmdbuf.setScissors(0, scissor);
cmdbuf.bindShaders(DkStageFlag_GraphicsMask, { compositionVertexShader, compositionFragmentShader });
cmdbuf.bindUniformBuffer(DkStage_Fragment, 0, lightingUniformBuffer.getGpuAddr(), lightingUniformBuffer.getSize());
cmdbuf.bindTextures(DkStage_Fragment, 0, {
dkMakeTextureHandle(0, 0),
dkMakeTextureHandle(1, 0),
dkMakeTextureHandle(2, 0),
});
cmdbuf.bindRasterizerState(dk::RasterizerState{});
cmdbuf.bindColorState(dk::ColorState{});
cmdbuf.bindColorWriteState(dk::ColorWriteState{});
cmdbuf.bindVtxAttribState({});
// Draw the full screen quad
cmdbuf.draw(DkPrimitive_Quads, 4, 1, 0, 0);
// Tiled barrier
cmdbuf.barrier(DkBarrier_Tiles, 0);
// Discard the g-buffer since we don't need it anymore
cmdbuf.bindRenderTargets({ &albedoTarget, &normalTarget, &viewDirTarget });
cmdbuf.discardColor(0);
cmdbuf.discardColor(1);
cmdbuf.discardColor(2);
// End of the composition cmdlist
composition_cmdlist = cmdbuf.finishList();
}
void render()
{
// Begin generating the dynamic command list, for commands that need to be sent only this frame specifically
dynmem.begin(dyncmd);
// Update the transformation uniform buffer with the new state (this data gets inlined in the command list)
dyncmd.pushConstants(
transformUniformBuffer.getGpuAddr(), transformUniformBuffer.getSize(),
0, sizeof(transformState), &transformState);
// Update the lighting uniform buffer with the new state
dyncmd.pushConstants(
lightingUniformBuffer.getGpuAddr(), lightingUniformBuffer.getSize(),
0, sizeof(lightingState), &lightingState);
// Finish off the dynamic command list (which also submits it to the queue)
queue.submitCommands(dynmem.end(dyncmd));
// Run the main rendering command list
queue.submitCommands(render_cmdlist);
// Acquire a framebuffer from the swapchain
int slot = queue.acquireImage(swapchain);
// Submit the command list that binds the correct framebuffer
queue.submitCommands(framebuffer_cmdlists[slot]);
// Submit the command list used for performing the composition
queue.submitCommands(composition_cmdlist);
// Now that we are done rendering, present it to the screen (this also flushes the queue)
queue.presentImage(swapchain, slot);
}
void onOperationMode(AppletOperationMode mode) override
{
// Destroy the framebuffer resources
destroyFramebufferResources();
// Choose framebuffer size
chooseFramebufferSize(framebufferWidth, framebufferHeight, mode);
// Recreate the framebuffers and its associated resources
createFramebufferResources();
}
bool onFrame(u64 ns) override
{
padUpdate(&pad);
u64 kDown = padGetButtonsDown(&pad);
if (kDown & HidNpadButton_Plus)
return false;
float time = ns / 1000000000.0; // double precision division; followed by implicit cast to single precision
float tau = glm::two_pi<float>();
float period1 = fractf(time/8.0f);
float period2 = fractf(time/4.0f);
// Generate the model-view matrix for this frame
// Keep in mind that GLM transformation functions multiply to the right, so essentially we have:
// mdlvMtx = Translate * RotateX * RotateY * Translate
// This means that the Scale operation is applied first, then RotateY, and so on.
transformState.mdlvMtx = glm::mat4{1.0f};
transformState.mdlvMtx = glm::translate(transformState.mdlvMtx, glm::vec3{sinf(period1*tau), 0.0f, -3.0f});
transformState.mdlvMtx = glm::rotate(transformState.mdlvMtx, sinf(period2 * tau) * tau / 8.0f, glm::vec3{1.0f, 0.0f, 0.0f});
transformState.mdlvMtx = glm::rotate(transformState.mdlvMtx, -period1 * tau, glm::vec3{0.0f, 1.0f, 0.0f});
transformState.mdlvMtx = glm::translate(transformState.mdlvMtx, glm::vec3{0.0f, -0.5f, 0.0f});
render();
return true;
}
};
void Example08(void)
{
CExample08 app;
app.run();
}

View File

@ -0,0 +1,324 @@
/*
** deko3d Example 09: Simple Compute Shader (Geometry Generation)
** This example shows how to use a compute shader to dynamically generate geometry.
** New concepts in this example:
** - Enabling compute support in a queue
** - Configuring and using compute shaders
** - Setting up shader storage buffers (SSBOs)
** - Dispatching compute jobs
** - Using a primitive barrier to ensure ordering of items
** - Drawing geometry generated dynamically by the GPU itself
*/
// Sample Framework headers
#include "SampleFramework/CApplication.h"
#include "SampleFramework/CMemPool.h"
#include "SampleFramework/CShader.h"
#include "SampleFramework/CCmdMemRing.h"
// C++ standard library headers
#include <array>
#include <optional>
// GLM headers
#define GLM_FORCE_DEFAULT_ALIGNED_GENTYPES // Enforces GLSL std140/std430 alignment rules for glm types
#define GLM_FORCE_INTRINSICS // Enables usage of SIMD CPU instructions (requiring the above as well)
#include <glm/vec4.hpp>
#include <glm/gtc/matrix_transform.hpp>
namespace
{
struct Vertex
{
float position[4];
float color[4];
};
constexpr std::array VertexAttribState =
{
DkVtxAttribState{ 0, 0, offsetof(Vertex, position), DkVtxAttribSize_4x32, DkVtxAttribType_Float, 0 },
DkVtxAttribState{ 0, 0, offsetof(Vertex, color), DkVtxAttribSize_4x32, DkVtxAttribType_Float, 0 },
};
constexpr std::array VertexBufferState =
{
DkVtxBufferState{ sizeof(Vertex), 0 },
};
struct GeneratorParams
{
glm::vec4 colorA;
glm::vec4 colorB;
float offset;
float scale;
float padding[2];
};
inline float fractf(float x)
{
return x - floorf(x);
}
}
class CExample09 final : public CApplication
{
static constexpr unsigned NumFramebuffers = 2;
static constexpr uint32_t FramebufferWidth = 1280;
static constexpr uint32_t FramebufferHeight = 720;
static constexpr unsigned StaticCmdSize = 0x10000;
static constexpr unsigned DynamicCmdSize = 0x10000;
static constexpr unsigned NumVertices = 256;
PadState pad;
dk::UniqueDevice device;
dk::UniqueQueue queue;
std::optional<CMemPool> pool_images;
std::optional<CMemPool> pool_code;
std::optional<CMemPool> pool_data;
dk::UniqueCmdBuf cmdbuf;
dk::UniqueCmdBuf dyncmd;
CCmdMemRing<NumFramebuffers> dynmem;
GeneratorParams params;
CMemPool::Handle paramsUniformBuffer;
CShader computeShader;
CShader vertexShader;
CShader fragmentShader;
CMemPool::Handle vertexBuffer;
CMemPool::Handle framebuffers_mem[NumFramebuffers];
dk::Image framebuffers[NumFramebuffers];
DkCmdList framebuffer_cmdlists[NumFramebuffers];
dk::UniqueSwapchain swapchain;
DkCmdList compute_cmdlist, render_cmdlist;
public:
CExample09()
{
// Create the deko3d device
device = dk::DeviceMaker{}.create();
// Create the main queue
queue = dk::QueueMaker{device}.setFlags(DkQueueFlags_Graphics | DkQueueFlags_Compute).create();
// Create the memory pools
pool_images.emplace(device, DkMemBlockFlags_GpuCached | DkMemBlockFlags_Image, 16*1024*1024);
pool_code.emplace(device, DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached | DkMemBlockFlags_Code, 128*1024);
pool_data.emplace(device, DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached, 1*1024*1024);
// Create the static command buffer and feed it freshly allocated memory
cmdbuf = dk::CmdBufMaker{device}.create();
CMemPool::Handle cmdmem = pool_data->allocate(StaticCmdSize);
cmdbuf.addMemory(cmdmem.getMemBlock(), cmdmem.getOffset(), cmdmem.getSize());
// Create the dynamic command buffer and allocate memory for it
dyncmd = dk::CmdBufMaker{device}.create();
dynmem.allocate(*pool_data, DynamicCmdSize);
// Load the shaders
computeShader.load(*pool_code, "romfs:/shaders/sinewave.dksh");
vertexShader.load(*pool_code, "romfs:/shaders/basic_vsh.dksh");
fragmentShader.load(*pool_code, "romfs:/shaders/color_fsh.dksh");
// Create the uniform buffer
paramsUniformBuffer = pool_data->allocate(sizeof(params), DK_UNIFORM_BUF_ALIGNMENT);
// Initialize the params
params.colorA = glm::vec4 { 1.0f, 0.0f, 1.0f, 1.0f };
params.colorB = glm::vec4 { 0.0f, 1.0f, 0.0f, 1.0f };
params.offset = 0.0f;
params.scale = 1.0f;
// Allocate memory for the vertex buffer
vertexBuffer = pool_data->allocate(sizeof(Vertex)*NumVertices, alignof(Vertex));
// Create the framebuffer resources
createFramebufferResources();
// Initialize gamepad
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
padInitializeDefault(&pad);
}
~CExample09()
{
// Destroy the framebuffer resources
destroyFramebufferResources();
// Destroy the vertex buffer (not strictly needed in this case)
vertexBuffer.destroy();
// Destroy the uniform buffer (not strictly needed in this case)
paramsUniformBuffer.destroy();
}
void createFramebufferResources()
{
// Create layout for the framebuffers
dk::ImageLayout layout_framebuffer;
dk::ImageLayoutMaker{device}
.setFlags(DkImageFlags_UsageRender | DkImageFlags_UsagePresent | DkImageFlags_HwCompression)
.setFormat(DkImageFormat_RGBA8_Unorm)
.setDimensions(FramebufferWidth, FramebufferHeight)
.initialize(layout_framebuffer);
// Create the framebuffers
std::array<DkImage const*, NumFramebuffers> fb_array;
uint64_t fb_size = layout_framebuffer.getSize();
uint32_t fb_align = layout_framebuffer.getAlignment();
for (unsigned i = 0; i < NumFramebuffers; i ++)
{
// Allocate a framebuffer
framebuffers_mem[i] = pool_images->allocate(fb_size, fb_align);
framebuffers[i].initialize(layout_framebuffer, framebuffers_mem[i].getMemBlock(), framebuffers_mem[i].getOffset());
// Generate a command list that binds it
dk::ImageView colorTarget{ framebuffers[i] };
cmdbuf.bindRenderTargets(&colorTarget);
framebuffer_cmdlists[i] = cmdbuf.finishList();
// Fill in the array for use later by the swapchain creation code
fb_array[i] = &framebuffers[i];
}
// Create the swapchain using the framebuffers
swapchain = dk::SwapchainMaker{device, nwindowGetDefault(), fb_array}.create();
// Generate the main rendering cmdlist
recordStaticCommands();
}
void destroyFramebufferResources()
{
// Return early if we have nothing to destroy
if (!swapchain) return;
// Make sure the queue is idle before destroying anything
queue.waitIdle();
// Clear the static cmdbuf, destroying the static cmdlists in the process
cmdbuf.clear();
// Destroy the swapchain
swapchain.destroy();
// Destroy the framebuffers
for (unsigned i = 0; i < NumFramebuffers; i ++)
framebuffers_mem[i].destroy();
}
void recordStaticCommands()
{
// Bind state required for running the compute job
cmdbuf.bindShaders(DkStageFlag_Compute, { computeShader });
cmdbuf.bindUniformBuffer(DkStage_Compute, 0, paramsUniformBuffer.getGpuAddr(), paramsUniformBuffer.getSize());
cmdbuf.bindStorageBuffer(DkStage_Compute, 0, vertexBuffer.getGpuAddr(), vertexBuffer.getSize());
// Run the compute job
cmdbuf.dispatchCompute(NumVertices/32, 1, 1);
// Place a barrier
cmdbuf.barrier(DkBarrier_Primitives, 0);
// Finish off this command list
compute_cmdlist = cmdbuf.finishList();
// Initialize state structs with deko3d defaults
dk::RasterizerState rasterizerState;
dk::ColorState colorState;
dk::ColorWriteState colorWriteState;
dk::BlendState blendState;
// Configure rasterizer state: enable polygon smoothing
rasterizerState.setPolygonSmoothEnable(true);
// Configure color state: enable blending (needed for polygon smoothing since it generates alpha values)
colorState.setBlendEnable(0, true);
// Configure viewport and scissor
cmdbuf.setViewports(0, { { 0.0f, 0.0f, FramebufferWidth, FramebufferHeight, 0.0f, 1.0f } });
cmdbuf.setScissors(0, { { 0, 0, FramebufferWidth, FramebufferHeight } });
// Clear the color buffer
cmdbuf.clearColor(0, DkColorMask_RGBA, 0.0f, 0.0f, 0.0f, 0.0f);
// Bind state required for drawing the triangle
cmdbuf.bindShaders(DkStageFlag_GraphicsMask, { vertexShader, fragmentShader });
cmdbuf.bindRasterizerState(rasterizerState);
cmdbuf.bindColorState(colorState);
cmdbuf.bindColorWriteState(colorWriteState);
cmdbuf.bindBlendStates(0, blendState);
cmdbuf.bindVtxBuffer(0, vertexBuffer.getGpuAddr(), vertexBuffer.getSize());
cmdbuf.bindVtxAttribState(VertexAttribState);
cmdbuf.bindVtxBufferState(VertexBufferState);
cmdbuf.setLineWidth(16.0f);
// Draw the line
cmdbuf.draw(DkPrimitive_LineStrip, NumVertices, 1, 0, 0);
// Finish off this command list
render_cmdlist = cmdbuf.finishList();
}
void render()
{
// Begin generating the dynamic command list, for commands that need to be sent only this frame specifically
dynmem.begin(dyncmd);
// Update the uniform buffer with the new state (this data gets inlined in the command list)
dyncmd.pushConstants(
paramsUniformBuffer.getGpuAddr(), paramsUniformBuffer.getSize(),
0, sizeof(params), &params);
// Finish off the dynamic command list (which also submits it to the queue)
queue.submitCommands(dynmem.end(dyncmd));
// Run the compute command list
queue.submitCommands(compute_cmdlist);
// Acquire a framebuffer from the swapchain (and wait for it to be available)
int slot = queue.acquireImage(swapchain);
// Run the command list that attaches said framebuffer to the queue
queue.submitCommands(framebuffer_cmdlists[slot]);
// Run the main rendering command list
queue.submitCommands(render_cmdlist);
// Now that we are done rendering, present it to the screen
queue.presentImage(swapchain, slot);
}
bool onFrame(u64 ns) override
{
padUpdate(&pad);
u64 kDown = padGetButtonsDown(&pad);
if (kDown & HidNpadButton_Plus)
return false;
float time = ns / 1000000000.0; // double precision division; followed by implicit cast to single precision
float tau = glm::two_pi<float>();
params.offset = fractf(time/4.0f);
float xx = fractf(time * 135.0f / 60.0f / 2.0f);
params.scale = cosf(xx*tau);
params.colorA.g = powf(fabsf(params.scale), 4.0f);
params.colorB.g = 1.0f - params.colorA.g;
render();
return true;
}
};
void Example09(void)
{
CExample09 app;
app.run();
}

View File

@ -0,0 +1,69 @@
/*
** Sample Framework for deko3d Applications
** CApplication.cpp: Wrapper class containing common application boilerplate
*/
#include "CApplication.h"
CApplication::CApplication()
{
appletLockExit();
appletSetFocusHandlingMode(AppletFocusHandlingMode_NoSuspend);
}
CApplication::~CApplication()
{
appletSetFocusHandlingMode(AppletFocusHandlingMode_SuspendHomeSleep);
appletUnlockExit();
}
void CApplication::run()
{
u64 tick_ref = armGetSystemTick();
u64 tick_saved = tick_ref;
bool focused = appletGetFocusState() == AppletFocusState_InFocus;
onOperationMode(appletGetOperationMode());
for (;;)
{
u32 msg = 0;
Result rc = appletGetMessage(&msg);
if (R_SUCCEEDED(rc))
{
bool should_close = !appletProcessMessage(msg);
if (should_close)
return;
switch (msg)
{
case AppletMessage_FocusStateChanged:
{
bool old_focused = focused;
AppletFocusState state = appletGetFocusState();
focused = state == AppletFocusState_InFocus;
onFocusState(state);
if (focused == old_focused)
break;
if (focused)
{
appletSetFocusHandlingMode(AppletFocusHandlingMode_NoSuspend);
tick_ref += armGetSystemTick() - tick_saved;
}
else
{
tick_saved = armGetSystemTick();
appletSetFocusHandlingMode(AppletFocusHandlingMode_SuspendHomeSleepNotify);
}
break;
}
case AppletMessage_OperationModeChanged:
onOperationMode(appletGetOperationMode());
break;
}
}
if (focused && !onFrame(armTicksToNs(armGetSystemTick() - tick_ref)))
break;
}
}

View File

@ -0,0 +1,38 @@
/*
** Sample Framework for deko3d Applications
** CApplication.h: Wrapper class containing common application boilerplate
*/
#pragma once
#include "common.h"
class CApplication
{
protected:
virtual void onFocusState(AppletFocusState) { }
virtual void onOperationMode(AppletOperationMode) { }
virtual bool onFrame(u64) { return true; }
public:
CApplication();
~CApplication();
void run();
static constexpr void chooseFramebufferSize(uint32_t& width, uint32_t& height, AppletOperationMode mode);
};
constexpr void CApplication::chooseFramebufferSize(uint32_t& width, uint32_t& height, AppletOperationMode mode)
{
switch (mode)
{
default:
case AppletOperationMode_Handheld:
width = 1280;
height = 720;
break;
case AppletOperationMode_Console:
width = 1920;
height = 1080;
break;
}
}

View File

@ -0,0 +1,62 @@
/*
** Sample Framework for deko3d Applications
** CCmdMemRing.h: Memory provider class for dynamic command buffers
*/
#pragma once
#include "common.h"
#include "CMemPool.h"
template <unsigned NumSlices>
class CCmdMemRing
{
static_assert(NumSlices > 0, "Need a non-zero number of slices...");
CMemPool::Handle m_mem;
unsigned m_curSlice;
dk::Fence m_fences[NumSlices];
public:
CCmdMemRing() : m_mem{}, m_curSlice{}, m_fences{} { }
CCmdMemRing(const CCmdMemRing&) = delete;
CCmdMemRing& operator=(const CCmdMemRing&) = delete;
~CCmdMemRing()
{
m_mem.destroy();
}
bool allocate(CMemPool& pool, uint32_t sliceSize)
{
sliceSize = (sliceSize + DK_CMDMEM_ALIGNMENT - 1) &~ (DK_CMDMEM_ALIGNMENT - 1);
m_mem = pool.allocate(NumSlices*sliceSize);
return m_mem;
}
void begin(dk::CmdBuf cmdbuf)
{
// Clear/reset the command buffer, which also destroys all command list handles
// (but remember: it does *not* in fact destroy the command data)
cmdbuf.clear();
// Wait for the current slice of memory to be available, and feed it to the command buffer
uint32_t sliceSize = m_mem.getSize() / NumSlices;
m_fences[m_curSlice].wait();
// Feed the memory to the command buffer
cmdbuf.addMemory(m_mem.getMemBlock(), m_mem.getOffset() + m_curSlice * sliceSize, sliceSize);
}
DkCmdList end(dk::CmdBuf cmdbuf)
{
// Signal the fence corresponding to the current slice; so that in the future when we want
// to use it again, we can wait for the completion of the commands we've just submitted
// (and as such we don't overwrite in-flight command data with new one)
cmdbuf.signalFence(m_fences[m_curSlice]);
// Advance the current slice counter; wrapping around when we reach the end
m_curSlice = (m_curSlice + 1) % NumSlices;
// Finish off the command list, returning it to the caller
return cmdbuf.finishList();
}
};

View File

@ -0,0 +1,76 @@
/*
** Sample Framework for deko3d Applications
** CDescriptorSet.h: Image/Sampler descriptor set class
*/
#pragma once
#include "common.h"
#include "CMemPool.h"
template <unsigned NumDescriptors>
class CDescriptorSet
{
static_assert(NumDescriptors > 0, "Need a non-zero number of descriptors...");
static_assert(sizeof(DkImageDescriptor) == sizeof(DkSamplerDescriptor), "shouldn't happen");
static_assert(DK_IMAGE_DESCRIPTOR_ALIGNMENT == DK_SAMPLER_DESCRIPTOR_ALIGNMENT, "shouldn't happen");
static constexpr size_t DescriptorSize = sizeof(DkImageDescriptor);
static constexpr size_t DescriptorAlign = DK_IMAGE_DESCRIPTOR_ALIGNMENT;
CMemPool::Handle m_mem;
public:
CDescriptorSet() : m_mem{} { }
CDescriptorSet(const CDescriptorSet&) = delete;
CDescriptorSet& operator=(const CDescriptorSet&) = delete;
~CDescriptorSet()
{
m_mem.destroy();
}
bool allocate(CMemPool& pool)
{
m_mem = pool.allocate(NumDescriptors*DescriptorSize, DescriptorAlign);
return m_mem;
}
void bindForImages(dk::CmdBuf cmdbuf)
{
cmdbuf.bindImageDescriptorSet(m_mem.getGpuAddr(), NumDescriptors);
}
void bindForSamplers(dk::CmdBuf cmdbuf)
{
cmdbuf.bindSamplerDescriptorSet(m_mem.getGpuAddr(), NumDescriptors);
}
template <typename T>
void update(dk::CmdBuf cmdbuf, uint32_t id, T const& descriptor)
{
static_assert(sizeof(T) == DescriptorSize);
cmdbuf.pushData(m_mem.getGpuAddr() + id*DescriptorSize, &descriptor, DescriptorSize);
}
template <typename T, size_t N>
void update(dk::CmdBuf cmdbuf, uint32_t id, std::array<T, N> const& descriptors)
{
static_assert(sizeof(T) == DescriptorSize);
cmdbuf.pushData(m_mem.getGpuAddr() + id*DescriptorSize, descriptors.data(), descriptors.size()*DescriptorSize);
}
#ifdef DK_HPP_SUPPORT_VECTOR
template <typename T, typename Allocator = std::allocator<T>>
void update(dk::CmdBuf cmdbuf, uint32_t id, std::vector<T,Allocator> const& descriptors)
{
static_assert(sizeof(T) == DescriptorSize);
cmdbuf.pushData(m_mem.getGpuAddr() + id*DescriptorSize, descriptors.data(), descriptors.size()*DescriptorSize);
}
#endif
template <typename T>
void update(dk::CmdBuf cmdbuf, uint32_t id, std::initializer_list<T const> const& descriptors)
{
static_assert(sizeof(T) == DescriptorSize);
cmdbuf.pushData(m_mem.getGpuAddr() + id*DescriptorSize, descriptors.data(), descriptors.size()*DescriptorSize);
}
};

View File

@ -0,0 +1,37 @@
/*
** Sample Framework for deko3d Applications
** CExternalImage.cpp: Utility class for loading images from the filesystem
*/
#include "CExternalImage.h"
#include "FileLoader.h"
bool CExternalImage::load(CMemPool& imagePool, CMemPool& scratchPool, dk::Device device, dk::Queue transferQueue, const char* path, uint32_t width, uint32_t height, DkImageFormat format, uint32_t flags)
{
CMemPool::Handle tempimgmem = LoadFile(scratchPool, path, DK_IMAGE_LINEAR_STRIDE_ALIGNMENT);
if (!tempimgmem)
return false;
dk::UniqueCmdBuf tempcmdbuf = dk::CmdBufMaker{device}.create();
CMemPool::Handle tempcmdmem = scratchPool.allocate(DK_MEMBLOCK_ALIGNMENT);
tempcmdbuf.addMemory(tempcmdmem.getMemBlock(), tempcmdmem.getOffset(), tempcmdmem.getSize());
dk::ImageLayout layout;
dk::ImageLayoutMaker{device}
.setFlags(flags)
.setFormat(format)
.setDimensions(width, height)
.initialize(layout);
m_mem = imagePool.allocate(layout.getSize(), layout.getAlignment());
m_image.initialize(layout, m_mem.getMemBlock(), m_mem.getOffset());
m_descriptor.initialize(m_image);
dk::ImageView imageView{m_image};
tempcmdbuf.copyBufferToImage({ tempimgmem.getGpuAddr() }, imageView, { 0, 0, 0, width, height, 1 });
transferQueue.submitCommands(tempcmdbuf.finishList());
transferQueue.waitIdle();
tempcmdmem.destroy();
tempimgmem.destroy();
return true;
}

View File

@ -0,0 +1,42 @@
/*
** Sample Framework for deko3d Applications
** CExternalImage.h: Utility class for loading images from the filesystem
*/
#pragma once
#include "common.h"
#include "CMemPool.h"
class CExternalImage
{
dk::Image m_image;
dk::ImageDescriptor m_descriptor;
CMemPool::Handle m_mem;
public:
CExternalImage() : m_image{}, m_descriptor{}, m_mem{} { }
CExternalImage(const CExternalImage&) = delete;
CExternalImage& operator=(const CExternalImage&) = delete;
~CExternalImage()
{
m_mem.destroy();
}
constexpr operator bool() const
{
return m_mem;
}
constexpr dk::Image& get()
{
return m_image;
}
constexpr dk::ImageDescriptor const& getDescriptor() const
{
return m_descriptor;
}
bool load(CMemPool& imagePool, CMemPool& scratchPool, dk::Device device, dk::Queue transferQueue, const char* path, uint32_t width, uint32_t height, DkImageFormat format, uint32_t flags = 0);
};

View File

@ -0,0 +1,119 @@
/*
** Sample Framework for deko3d Applications
** CIntrusiveList.h: Intrusive doubly-linked list helper class
*/
#pragma once
#include "common.h"
template <typename T>
struct CIntrusiveListNode
{
T *m_next, *m_prev;
constexpr CIntrusiveListNode() : m_next{}, m_prev{} { }
constexpr operator bool() const { return m_next || m_prev; }
};
template <typename T, CIntrusiveListNode<T> T::* node_ptr>
class CIntrusiveList
{
T *m_first, *m_last;
public:
constexpr CIntrusiveList() : m_first{}, m_last{} { }
constexpr T* first() const { return m_first; }
constexpr T* last() const { return m_last; }
constexpr bool empty() const { return !m_first; }
constexpr void clear() { m_first = m_last = nullptr; }
constexpr bool isLinked(T* obj) const { return obj->*node_ptr || m_first == obj; }
constexpr T* prev(T* obj) const { return (obj->*node_ptr).m_prev; }
constexpr T* next(T* obj) const { return (obj->*node_ptr).m_next; }
void add(T* obj)
{
return addBefore(nullptr, obj);
}
void addBefore(T* pos, T* obj)
{
auto& node = obj->*node_ptr;
node.m_next = pos;
node.m_prev = pos ? (pos->*node_ptr).m_prev : m_last;
if (pos)
(pos->*node_ptr).m_prev = obj;
else
m_last = obj;
if (node.m_prev)
(node.m_prev->*node_ptr).m_next = obj;
else
m_first = obj;
}
void addAfter(T* pos, T* obj)
{
auto& node = obj->*node_ptr;
node.m_next = pos ? (pos->*node_ptr).m_next : m_first;
node.m_prev = pos;
if (pos)
(pos->*node_ptr).m_next = obj;
else
m_first = obj;
if (node.m_next)
(node.m_next->*node_ptr).m_prev = obj;
else
m_last = obj;
}
T* pop()
{
T* ret = m_first;
if (ret)
{
m_first = (ret->*node_ptr).m_next;
if (m_first)
(m_first->*node_ptr).m_prev = nullptr;
else
m_last = nullptr;
}
return ret;
}
void remove(T* obj)
{
auto& node = obj->*node_ptr;
if (node.m_prev)
{
(node.m_prev->*node_ptr).m_next = node.m_next;
if (node.m_next)
(node.m_next->*node_ptr).m_prev = node.m_prev;
else
m_last = node.m_prev;
} else
{
m_first = node.m_next;
if (m_first)
(m_first->*node_ptr).m_prev = nullptr;
else
m_last = nullptr;
}
node.m_next = node.m_prev = 0;
}
template <typename L>
void iterate(L lambda) const
{
T* next = nullptr;
for (T* cur = m_first; cur; cur = next)
{
next = (cur->*node_ptr).m_next;
lambda(cur);
}
}
};

View File

@ -0,0 +1,214 @@
/*
** Sample Framework for deko3d Applications
** CIntrusiveTree.cpp: Intrusive red-black tree helper class
*/
#include "CIntrusiveTree.h"
// This red-black tree implementation is mostly based on mtheall's work,
// which can be found here:
// https://github.com/smealum/ctrulib/tree/master/libctru/source/util/rbtree
void CIntrusiveTreeBase::rotate(N* node, N::Leaf leaf)
{
N *tmp = node->child(leaf);
N *parent = node->getParent();
node->child(leaf) = tmp->child(!leaf);
if (tmp->child(!leaf))
tmp->child(!leaf)->setParent(node);
tmp->child(!leaf) = node;
tmp->setParent(parent);
if (parent)
{
if (node == parent->child(!leaf))
parent->child(!leaf) = tmp;
else
parent->child(leaf) = tmp;
}
else
m_root = tmp;
node->setParent(tmp);
}
void CIntrusiveTreeBase::recolor(N* parent, N* node)
{
N *sibling;
while ((!node || node->isBlack()) && node != m_root)
{
N::Leaf leaf = node == parent->left() ? N::Right : N::Left;
sibling = parent->child(leaf);
if (sibling->isRed())
{
sibling->setBlack();
parent->setRed();
rotate(parent, leaf);
sibling = parent->child(leaf);
}
N::Color clr[2];
clr[N::Left] = sibling->left() ? sibling->left()->getColor() : N::Black;
clr[N::Right] = sibling->right() ? sibling->right()->getColor() : N::Black;
if (clr[N::Left] == N::Black && clr[N::Right] == N::Black)
{
sibling->setRed();
node = parent;
parent = node->getParent();
}
else
{
if (clr[leaf] == N::Black)
{
sibling->child(!leaf)->setBlack();
sibling->setRed();
rotate(sibling, !leaf);
sibling = parent->child(leaf);
}
sibling->setColor(parent->getColor());
parent->setBlack();
sibling->child(leaf)->setBlack();
rotate(parent, leaf);
node = m_root;
}
}
if (node)
node->setBlack();
}
auto CIntrusiveTreeBase::walk(N* node, N::Leaf leaf) const -> N*
{
if (node->child(leaf))
{
node = node->child(leaf);
while (node->child(!leaf))
node = node->child(!leaf);
}
else
{
N *parent = node->getParent();
while (parent && node == parent->child(leaf))
{
node = parent;
parent = node->getParent();
}
node = parent;
}
return node;
}
void CIntrusiveTreeBase::insert(N* node, N* parent)
{
node->left() = node->right() = nullptr;
node->setParent(parent);
node->setRed();
while ((parent = node->getParent()) && parent->isRed())
{
N *grandparent = parent->getParent();
N::Leaf leaf = parent == grandparent->left() ? N::Right : N::Left;
N *uncle = grandparent->child(leaf);
if (uncle && uncle->isRed())
{
uncle->setBlack();
parent->setBlack();
grandparent->setRed();
node = grandparent;
}
else
{
if (parent->child(leaf) == node)
{
rotate(parent, leaf);
N* tmp = parent;
parent = node;
node = tmp;
}
parent->setBlack();
grandparent->setRed();
rotate(grandparent, !leaf);
}
}
m_root->setBlack();
}
void CIntrusiveTreeBase::remove(N* node)
{
N::Color color;
N *child, *parent;
if (node->left() && node->right())
{
N *old = node;
node = node->right();
while (node->left())
node = node->left();
parent = old->getParent();
if (parent)
{
if (parent->left() == old)
parent->left() = node;
else
parent->right() = node;
}
else
m_root = node;
child = node->right();
parent = node->getParent();
color = node->getColor();
if (parent == old)
parent = node;
else
{
if (child)
child->setParent(parent);
parent->left() = child;
node->right() = old->right();
old->right()->setParent(node);
}
node->setParent(old->getParent());
node->setColor(old->getColor());
node->left() = old->left();
old->left()->setParent(node);
}
else
{
child = node->left() ? node->left() : node->right();
parent = node->getParent();
color = node->getColor();
if (child)
child->setParent(parent);
if (parent)
{
if (parent->left() == node)
parent->left() = child;
else
parent->right() = child;
}
else
m_root = child;
}
if (color == N::Black)
recolor(parent, child);
}

View File

@ -0,0 +1,230 @@
/*
** Sample Framework for deko3d Applications
** CIntrusiveTree.h: Intrusive red-black tree helper class
*/
#pragma once
#include "common.h"
#include <functional>
struct CIntrusiveTreeNode
{
enum Color
{
Red,
Black,
};
enum Leaf
{
Left,
Right,
};
private:
uintptr_t m_parent_color;
CIntrusiveTreeNode* m_children[2];
public:
constexpr CIntrusiveTreeNode() : m_parent_color{}, m_children{} { }
constexpr CIntrusiveTreeNode* getParent() const
{
return reinterpret_cast<CIntrusiveTreeNode*>(m_parent_color &~ 1);
}
void setParent(CIntrusiveTreeNode* parent)
{
m_parent_color = (m_parent_color & 1) | reinterpret_cast<uintptr_t>(parent);
}
constexpr Color getColor() const
{
return static_cast<Color>(m_parent_color & 1);
}
void setColor(Color color)
{
m_parent_color = (m_parent_color &~ 1) | static_cast<uintptr_t>(color);
}
constexpr CIntrusiveTreeNode*& child(Leaf leaf)
{
return m_children[leaf];
}
constexpr CIntrusiveTreeNode* const& child(Leaf leaf) const
{
return m_children[leaf];
}
//--------------------------------------
constexpr bool isRed() const { return getColor() == Red; }
constexpr bool isBlack() const { return getColor() == Black; }
void setRed() { setColor(Red); }
void setBlack() { setColor(Black); }
constexpr CIntrusiveTreeNode*& left() { return child(Left); }
constexpr CIntrusiveTreeNode*& right() { return child(Right); }
constexpr CIntrusiveTreeNode* const& left() const { return child(Left); }
constexpr CIntrusiveTreeNode* const& right() const { return child(Right); }
};
NX_CONSTEXPR CIntrusiveTreeNode::Leaf operator!(CIntrusiveTreeNode::Leaf val) noexcept
{
return static_cast<CIntrusiveTreeNode::Leaf>(!static_cast<unsigned>(val));
}
class CIntrusiveTreeBase
{
using N = CIntrusiveTreeNode;
void rotate(N* node, N::Leaf leaf);
void recolor(N* parent, N* node);
protected:
N* m_root;
constexpr CIntrusiveTreeBase() : m_root{} { }
N* walk(N* node, N::Leaf leaf) const;
void insert(N* node, N* parent);
void remove(N* node);
N* minmax(N::Leaf leaf) const
{
N* p = m_root;
if (!p)
return nullptr;
while (p->child(leaf))
p = p->child(leaf);
return p;
}
template <typename H>
N*& navigate(N*& node, N*& parent, N::Leaf leafOnEqual, H helm) const
{
node = nullptr;
parent = nullptr;
N** point = const_cast<N**>(&m_root);
while (*point)
{
int direction = helm(*point);
parent = *point;
if (direction < 0)
point = &(*point)->left();
else if (direction > 0)
point = &(*point)->right();
else
{
node = *point;
point = &(*point)->child(leafOnEqual);
}
}
return *point;
}
};
template <typename ClassT, typename MemberT>
constexpr ClassT* parent_obj(MemberT* member, MemberT ClassT::* ptr)
{
union whatever
{
MemberT ClassT::* ptr;
intptr_t offset;
};
// This is technically UB, but basically every compiler worth using admits it as an extension
return (ClassT*)((intptr_t)member - whatever{ptr}.offset);
}
template <
typename T,
CIntrusiveTreeNode T::* node_ptr,
typename Comparator = std::less<>
>
class CIntrusiveTree final : protected CIntrusiveTreeBase
{
using N = CIntrusiveTreeNode;
static constexpr T* toType(N* m)
{
return m ? parent_obj(m, node_ptr) : nullptr;
}
static constexpr N* toNode(T* m)
{
return m ? &(m->*node_ptr) : nullptr;
}
template <typename A, typename B>
static int compare(A const& a, B const& b)
{
Comparator comp;
if (comp(a, b))
return -1;
if (comp(b, a))
return 1;
return 0;
}
public:
constexpr CIntrusiveTree() : CIntrusiveTreeBase{} { }
T* first() const { return toType(minmax(N::Left)); }
T* last() const { return toType(minmax(N::Right)); }
bool empty() const { return m_root == nullptr; }
void clear() { m_root = nullptr; }
T* prev(T* node) const { return toType(walk(toNode(node), N::Left)); }
T* next(T* node) const { return toType(walk(toNode(node), N::Right)); }
enum SearchMode
{
Exact = 0,
LowerBound = 1,
UpperBound = 2,
};
template <typename Lambda>
T* search(SearchMode mode, Lambda lambda) const
{
N *node, *parent;
N*& point = navigate(node, parent,
mode != UpperBound ? N::Left : N::Right,
[&lambda](N* curnode) { return lambda(toType(curnode)); });
if (mode != Exact) {
if (mode == UpperBound && node)
node = walk(node, N::Right);
else if (!node && parent)
node = &parent->left() == &point ? parent : walk(parent, N::Right);
}
return toType(node);
}
template <typename K>
T* find(K const& key, SearchMode mode = Exact) const
{
return search(mode, [&key](T* obj) { return compare(key, *obj); });
}
T* insert(T* obj, bool allow_dupes = false)
{
N *node, *parent;
N*& point = navigate(node, parent, N::Right,
[obj](N* curnode) { return compare(*obj, *toType(curnode)); });
if (node && !allow_dupes)
return toType(node);
point = toNode(obj);
CIntrusiveTreeBase::insert(point, parent);
return obj;
}
void remove(T* obj)
{
CIntrusiveTreeBase::remove(toNode(obj));
}
};

View File

@ -0,0 +1,175 @@
/*
** Sample Framework for deko3d Applications
** CMemPool.cpp: Pooled dynamic memory allocation manager class
*/
#include "CMemPool.h"
inline auto CMemPool::_newSlice() -> Slice*
{
Slice* ret = m_sliceHeap.pop();
if (!ret) ret = (Slice*)::malloc(sizeof(Slice));
return ret;
}
inline void CMemPool::_deleteSlice(Slice* s)
{
if (!s) return;
m_sliceHeap.add(s);
}
CMemPool::~CMemPool()
{
m_memMap.iterate([](Slice* s) { ::free(s); });
m_sliceHeap.iterate([](Slice* s) { ::free(s); });
m_blocks.iterate([](Block* blk) {
blk->m_obj.destroy();
::free(blk);
});
}
auto CMemPool::allocate(uint32_t size, uint32_t alignment) -> Handle
{
if (!size) return nullptr;
if (alignment & (alignment - 1)) return nullptr;
size = (size + alignment - 1) &~ (alignment - 1);
#ifdef DEBUG_CMEMPOOL
printf("Allocating size=%u alignment=0x%x\n", size, alignment);
{
Slice* temp = /*m_freeList*/m_memMap.first();
while (temp)
{
printf("-- blk %p | 0x%08x-0x%08x | %s used\n", temp->m_block, temp->m_start, temp->m_end, temp->m_pool ? " " : "not");
temp = /*m_freeList*/m_memMap.next(temp);
}
}
#endif
uint32_t start_offset = 0;
uint32_t end_offset = 0;
Slice* slice = m_freeList.find(size, decltype(m_freeList)::LowerBound);
while (slice)
{
#ifdef DEBUG_CMEMPOOL
printf(" * Checking slice 0x%x - 0x%x\n", slice->m_start, slice->m_end);
#endif
start_offset = (slice->m_start + alignment - 1) &~ (alignment - 1);
end_offset = start_offset + size;
if (end_offset <= slice->m_end)
break;
slice = m_freeList.next(slice);
}
if (!slice)
{
Block* blk = (Block*)::malloc(sizeof(Block));
if (!blk)
return nullptr;
uint32_t unusableSize = (m_flags & DkMemBlockFlags_Code) ? DK_SHADER_CODE_UNUSABLE_SIZE : 0;
uint32_t blkSize = m_blockSize - unusableSize;
blkSize = size > blkSize ? size : blkSize;
blkSize = (blkSize + unusableSize + DK_MEMBLOCK_ALIGNMENT - 1) &~ (DK_MEMBLOCK_ALIGNMENT - 1);
#ifdef DEBUG_CMEMPOOL
printf(" ! Allocating block of size 0x%x\n", blkSize);
#endif
blk->m_obj = dk::MemBlockMaker{m_dev, blkSize}.setFlags(m_flags).create();
if (!blk->m_obj)
{
::free(blk);
return nullptr;
}
slice = _newSlice();
if (!slice)
{
blk->m_obj.destroy();
::free(blk);
return nullptr;
}
slice->m_pool = nullptr;
slice->m_block = blk;
slice->m_start = 0;
slice->m_end = blkSize - unusableSize;
m_memMap.add(slice);
blk->m_cpuAddr = blk->m_obj.getCpuAddr();
blk->m_gpuAddr = blk->m_obj.getGpuAddr();
m_blocks.add(blk);
start_offset = 0;
end_offset = size;
}
else
{
#ifdef DEBUG_CMEMPOOL
printf(" * found it\n");
#endif
m_freeList.remove(slice);
}
if (start_offset != slice->m_start)
{
Slice* t = _newSlice();
if (!t) goto _bad;
t->m_pool = nullptr;
t->m_block = slice->m_block;
t->m_start = slice->m_start;
t->m_end = start_offset;
#ifdef DEBUG_CMEMPOOL
printf("-> subdivide left: %08x-%08x\n", t->m_start, t->m_end);
#endif
m_memMap.addBefore(slice, t);
m_freeList.insert(t, true);
slice->m_start = start_offset;
}
if (end_offset != slice->m_end)
{
Slice* t = _newSlice();
if (!t) goto _bad;
t->m_pool = nullptr;
t->m_block = slice->m_block;
t->m_start = end_offset;
t->m_end = slice->m_end;
#ifdef DEBUG_CMEMPOOL
printf("-> subdivide right: %08x-%08x\n", t->m_start, t->m_end);
#endif
m_memMap.addAfter(slice, t);
m_freeList.insert(t, true);
slice->m_end = end_offset;
}
slice->m_pool = this;
return slice;
_bad:
m_freeList.insert(slice, true);
return nullptr;
}
void CMemPool::_destroy(Slice* slice)
{
slice->m_pool = nullptr;
Slice* left = m_memMap.prev(slice);
Slice* right = m_memMap.next(slice);
if (left && left->canCoalesce(*slice))
{
slice->m_start = left->m_start;
m_freeList.remove(left);
m_memMap.remove(left);
_deleteSlice(left);
}
if (right && slice->canCoalesce(*right))
{
slice->m_end = right->m_end;
m_freeList.remove(right);
m_memMap.remove(right);
_deleteSlice(right);
}
m_freeList.insert(slice, true);
}

View File

@ -0,0 +1,133 @@
/*
** Sample Framework for deko3d Applications
** CMemPool.h: Pooled dynamic memory allocation manager class
*/
#pragma once
#include "common.h"
#include "CIntrusiveList.h"
#include "CIntrusiveTree.h"
class CMemPool
{
dk::Device m_dev;
uint32_t m_flags;
uint32_t m_blockSize;
struct Block
{
CIntrusiveListNode<Block> m_node;
dk::MemBlock m_obj;
void* m_cpuAddr;
DkGpuAddr m_gpuAddr;
Block(const Block&) = delete;
Block& operator=(const Block&) = delete;
constexpr void* cpuOffset(uint32_t offset) const
{
return m_cpuAddr ? ((u8*)m_cpuAddr + offset) : nullptr;
}
constexpr DkGpuAddr gpuOffset(uint32_t offset) const
{
return m_gpuAddr != DK_GPU_ADDR_INVALID ? (m_gpuAddr + offset) : DK_GPU_ADDR_INVALID;
}
};
CIntrusiveList<Block, &Block::m_node> m_blocks;
struct Slice
{
CIntrusiveListNode<Slice> m_node;
CIntrusiveTreeNode m_treenode;
CMemPool* m_pool;
Block* m_block;
uint32_t m_start;
uint32_t m_end;
Slice(const Slice&) = delete;
Slice& operator=(const Slice&) = delete;
constexpr uint32_t getSize() const { return m_end - m_start; }
constexpr bool canCoalesce(Slice const& rhs) const { return m_pool == rhs.m_pool && m_block == rhs.m_block && m_end == rhs.m_start; }
constexpr bool operator<(Slice const& rhs) const { return getSize() < rhs.getSize(); }
constexpr bool operator<(uint32_t rhs) const { return getSize() < rhs; }
};
friend constexpr bool operator<(uint32_t lhs, Slice const& rhs);
CIntrusiveList<Slice, &Slice::m_node> m_memMap, m_sliceHeap;
CIntrusiveTree<Slice, &Slice::m_treenode> m_freeList;
Slice* _newSlice();
void _deleteSlice(Slice*);
void _destroy(Slice* slice);
public:
static constexpr uint32_t DefaultBlockSize = 0x800000;
class Handle
{
Slice* m_slice;
public:
constexpr Handle(Slice* slice = nullptr) : m_slice{slice} { }
constexpr operator bool() const { return m_slice != nullptr; }
constexpr operator Slice*() const { return m_slice; }
constexpr bool operator!() const { return !m_slice; }
constexpr bool operator==(Handle const& rhs) const { return m_slice == rhs.m_slice; }
constexpr bool operator!=(Handle const& rhs) const { return m_slice != rhs.m_slice; }
void destroy()
{
if (m_slice)
{
m_slice->m_pool->_destroy(m_slice);
m_slice = nullptr;
}
}
constexpr dk::MemBlock getMemBlock() const
{
return m_slice->m_block->m_obj;
}
constexpr uint32_t getOffset() const
{
return m_slice->m_start;
}
constexpr uint32_t getSize() const
{
return m_slice->getSize();
}
constexpr void* getCpuAddr() const
{
return m_slice->m_block->cpuOffset(m_slice->m_start);
}
constexpr DkGpuAddr getGpuAddr() const
{
return m_slice->m_block->gpuOffset(m_slice->m_start);
}
};
CMemPool(dk::Device dev, uint32_t flags = DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached, uint32_t blockSize = DefaultBlockSize) :
m_dev{dev}, m_flags{flags}, m_blockSize{blockSize}, m_blocks{}, m_memMap{}, m_sliceHeap{}, m_freeList{} { }
~CMemPool();
Handle allocate(uint32_t size, uint32_t alignment = DK_CMDMEM_ALIGNMENT);
CMemPool(const CMemPool&) = delete;
CMemPool& operator=(const CMemPool&) = delete;
};
constexpr bool operator<(uint32_t lhs, CMemPool::Slice const& rhs)
{
return lhs < rhs.getSize();
}

View File

@ -0,0 +1,62 @@
/*
** Sample Framework for deko3d Applications
** CShader.cpp: Utility class for loading shaders from the filesystem
*/
#include "CShader.h"
struct DkshHeader
{
uint32_t magic; // DKSH_MAGIC
uint32_t header_sz; // sizeof(DkshHeader)
uint32_t control_sz;
uint32_t code_sz;
uint32_t programs_off;
uint32_t num_programs;
};
bool CShader::load(CMemPool& pool, const char* path)
{
FILE* f;
DkshHeader hdr;
void* ctrlmem;
m_codemem.destroy();
f = fopen(path, "rb");
if (!f) return false;
if (!fread(&hdr, sizeof(hdr), 1, f))
goto _fail0;
ctrlmem = malloc(hdr.control_sz);
if (!ctrlmem)
goto _fail0;
rewind(f);
if (!fread(ctrlmem, hdr.control_sz, 1, f))
goto _fail1;
m_codemem = pool.allocate(hdr.code_sz, DK_SHADER_CODE_ALIGNMENT);
if (!m_codemem)
goto _fail1;
if (!fread(m_codemem.getCpuAddr(), hdr.code_sz, 1, f))
goto _fail2;
dk::ShaderMaker{m_codemem.getMemBlock(), m_codemem.getOffset()}
.setControl(ctrlmem)
.setProgramId(0)
.initialize(m_shader);
free(ctrlmem);
fclose(f);
return true;
_fail2:
m_codemem.destroy();
_fail1:
free(ctrlmem);
_fail0:
fclose(f);
return false;
}

View File

@ -0,0 +1,36 @@
/*
** Sample Framework for deko3d Applications
** CShader.h: Utility class for loading shaders from the filesystem
*/
#pragma once
#include "common.h"
#include "CMemPool.h"
class CShader
{
dk::Shader m_shader;
CMemPool::Handle m_codemem;
public:
CShader() : m_shader{}, m_codemem{} { }
CShader(const CShader&) = delete;
CShader& operator=(const CShader&) = delete;
~CShader()
{
m_codemem.destroy();
}
constexpr operator bool() const
{
return m_codemem;
}
constexpr operator dk::Shader const*() const
{
return &m_shader;
}
bool load(CMemPool& pool, const char* path);
};

View File

@ -0,0 +1,27 @@
/*
** Sample Framework for deko3d Applications
** FileLoader.cpp: Helpers for loading data from the filesystem directly into GPU memory
*/
#include "FileLoader.h"
CMemPool::Handle LoadFile(CMemPool& pool, const char* path, uint32_t alignment)
{
FILE *f = fopen(path, "rb");
if (!f) return nullptr;
fseek(f, 0, SEEK_END);
uint32_t fsize = ftell(f);
rewind(f);
CMemPool::Handle mem = pool.allocate(fsize, alignment);
if (!mem)
{
fclose(f);
return nullptr;
}
fread(mem.getCpuAddr(), fsize, 1, f);
fclose(f);
return mem;
}

View File

@ -0,0 +1,9 @@
/*
** Sample Framework for deko3d Applications
** FileLoader.h: Helpers for loading data from the filesystem directly into GPU memory
*/
#pragma once
#include "common.h"
#include "CMemPool.h"
CMemPool::Handle LoadFile(CMemPool& pool, const char* path, uint32_t alignment = DK_CMDMEM_ALIGNMENT);

View File

@ -0,0 +1,18 @@
Copyright (C) 2020 fincs
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you
must not claim that you wrote the original software. If you use
this software in a product, an acknowledgment in the product
documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.

View File

@ -0,0 +1,12 @@
/*
** Sample Framework for deko3d Applications
** common.h: Common includes
*/
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <switch.h>
#include <deko3d.hpp>

View File

@ -0,0 +1,35 @@
/*
** Sample Framework for deko3d Applications
** startup.cpp: Automatic initialization/deinitialization
*/
#include "common.h"
#include <unistd.h>
//#define DEBUG_NXLINK
#ifdef DEBUG_NXLINK
static int nxlink_sock = -1;
#endif
extern "C" void userAppInit(void)
{
Result res = romfsInit();
if (R_FAILED(res))
diagAbortWithResult(res);
#ifdef DEBUG_NXLINK
socketInitializeDefault();
nxlink_sock = nxlinkStdioForDebug();
#endif
}
extern "C" void userAppExit(void)
{
#ifdef DEBUG_NXLINK
if (nxlink_sock != -1)
close(nxlink_sock);
socketExit();
#endif
romfsExit();
}

View File

@ -0,0 +1,15 @@
#version 460
layout (location = 0) in vec3 inWorldPos;
layout (location = 1) in vec3 inNormal;
layout (location = 0) out vec4 outAlbedo;
layout (location = 1) out vec4 outNormal;
layout (location = 2) out vec4 outViewDir;
void main()
{
outAlbedo = vec4(1.0, 1.0, 1.0, 1.0);
outNormal = vec4(normalize(inNormal), 0.0);
outViewDir = vec4(-inWorldPos, 0.0);
}

View File

@ -0,0 +1,46 @@
#version 460
layout (location = 0) in vec3 inWorldPos;
layout (location = 1) in vec3 inNormal;
layout (location = 0) out vec4 outColor;
layout (std140, binding = 0) uniform Lighting
{
vec4 lightPos; // if w=0 this is lightDir
vec3 ambient;
vec3 diffuse;
vec4 specular; // w is shininess
} u;
void main()
{
// Renormalize the normal after interpolation
vec3 normal = normalize(inNormal);
// Calculate light direction (i.e. vector that points *towards* the light source)
vec3 lightDir;
if (u.lightPos.w != 0.0)
lightDir = normalize(u.lightPos.xyz - inWorldPos);
else
lightDir = -u.lightPos.xyz;
vec3 viewDir = normalize(-inWorldPos);
// Calculate diffuse factor
float diffuse = max(0.0, dot(normal,lightDir));
// Calculate specular factor (Blinn-Phong)
vec3 halfwayDir = normalize(lightDir + viewDir);
float specular = pow(max(0.0, dot(normal,halfwayDir)), u.specular.w);
// Calculate the color
vec3 color =
u.ambient +
u.diffuse*vec3(diffuse) +
u.specular.xyz*vec3(specular);
// Reinhard tone mapping
vec3 mappedColor = color / (vec3(1.0) + color);
// Output this color (no need to gamma adjust since the framebuffer is sRGB)
outColor = vec4(mappedColor, 1.0);
}

View File

@ -0,0 +1,12 @@
#version 460
layout (location = 0) in vec3 inPos;
layout (location = 1) in vec4 inAttrib;
layout (location = 0) out vec4 outAttrib;
void main()
{
gl_Position = vec4(inPos, 1.0);
outAttrib = inAttrib;
}

View File

@ -0,0 +1,9 @@
#version 460
layout (location = 0) in vec3 inColor;
layout (location = 0) out vec4 outColor;
void main()
{
outColor = vec4(inColor, 1.0);
}

View File

@ -0,0 +1,53 @@
#version 460
layout (location = 0) out vec4 outColor;
layout (binding = 0) uniform sampler2D texAlbedo;
layout (binding = 1) uniform sampler2D texNormal;
layout (binding = 2) uniform sampler2D texViewDir;
layout (std140, binding = 0) uniform Lighting
{
vec4 lightPos; // if w=0 this is lightDir
vec3 ambient;
vec3 diffuse;
vec4 specular; // w is shininess
} u;
void main()
{
// Uncomment the coordinate reversion below to observe the effects of tiled corruption
ivec2 coord = /*textureSize(texAlbedo, 0) - ivec2(1,1) -*/ ivec2(gl_FragCoord.xy);
// Retrieve values from the g-buffer
vec4 albedo = texelFetch(texAlbedo, coord, 0);
vec3 normal = texelFetch(texNormal, coord, 0).xyz;
vec3 viewDir = texelFetch(texViewDir, coord, 0).xyz;
// Calculate light direction (i.e. vector that points *towards* the light source)
vec3 lightDir;
if (u.lightPos.w != 0.0)
lightDir = normalize(u.lightPos.xyz + viewDir);
else
lightDir = -u.lightPos.xyz;
viewDir = normalize(viewDir);
// Calculate diffuse factor
float diffuse = max(0.0, dot(normal,lightDir));
// Calculate specular factor (Blinn-Phong)
vec3 halfwayDir = normalize(lightDir + viewDir);
float specular = pow(max(0.0, dot(normal,halfwayDir)), u.specular.w);
// Calculate the color
vec3 color =
u.ambient +
albedo.rgb*u.diffuse*vec3(diffuse) +
u.specular.xyz*vec3(specular);
// Reinhard tone mapping
vec3 mappedColor = albedo.a * color / (vec3(1.0) + color);
// Output this color (no need to gamma adjust since the framebuffer is sRGB)
outColor = vec4(mappedColor, albedo.a);
}

View File

@ -0,0 +1,24 @@
#version 460
/*
ID | gl_Position.xy
0 | -1.0 +1.0
1 | -1.0 -1.0
2 | +1.0 -1.0
3 | +1.0 +1.0
*/
void main()
{
if ((gl_VertexID & 2) == 0)
gl_Position.x = -1.0;
else
gl_Position.x = +1.0;
if (((gl_VertexID+1) & 2) == 0)
gl_Position.y = +1.0;
else
gl_Position.y = -1.0;
gl_Position.zw = vec2(0.5, 1.0);
}

View File

@ -0,0 +1,143 @@
/*
** deko3d Examples - Main Menu
*/
// Sample Framework headers
#include "SampleFramework/CApplication.h"
// C++ standard library headers
#include <array>
void Example01(void);
void Example02(void);
void Example03(void);
void Example04(void);
void Example05(void);
void Example06(void);
void Example07(void);
void Example08(void);
void Example09(void);
namespace
{
using ExampleFunc = void(*)(void);
struct Example
{
ExampleFunc mainfunc;
const char* name;
};
constexpr std::array Examples =
{
Example{ Example01, "01: Simple Setup" },
Example{ Example02, "02: Triangle" },
Example{ Example03, "03: Cube" },
Example{ Example04, "04: Textured Cube" },
Example{ Example05, "05: Simple Tessellation" },
Example{ Example06, "06: Simple Multisampling" },
Example{ Example07, "07: Mesh Loading and Lighting (sRGB)" },
Example{ Example08, "08: Deferred Shading (Multipass Rendering with Tiled Cache)" },
Example{ Example09, "09: Simple Compute Shader (Geometry Generation)" },
};
}
class CMainMenu final : public CApplication
{
static constexpr unsigned EntriesPerScreen = 39;
static constexpr unsigned EntryPageLength = 10;
PadState pad;
int screenPos;
int selectPos;
void renderMenu()
{
printf("\x1b[2J\n");
printf(" deko3d Examples\n");
printf(" Press PLUS(+) to exit; A to select an example to run\n");
printf("\n");
printf("--------------------------------------------------------------------------------");
printf("\n");
for (unsigned i = 0; i < (Examples.size() - screenPos) && i < EntriesPerScreen; i ++)
{
unsigned id = screenPos+i;
printf(" %c %s\n", id==unsigned(selectPos) ? '*' : ' ', Examples[id].name);
}
}
CMainMenu() : screenPos{}, selectPos{}
{
consoleInit(NULL);
renderMenu();
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
padInitializeDefault(&pad);
padUpdate(&pad);
}
~CMainMenu()
{
consoleExit(NULL);
}
bool onFrame(u64 ns) override
{
int oldPos = selectPos;
padUpdate(&pad);
u64 kDown = padGetButtonsDown(&pad);
if (kDown & HidNpadButton_Plus)
{
selectPos = -1;
return false;
}
if (kDown & HidNpadButton_A)
return false;
if (kDown & HidNpadButton_AnyUp)
selectPos -= 1;
if (kDown & HidNpadButton_AnyDown)
selectPos += 1;
if (kDown & HidNpadButton_AnyLeft)
selectPos -= EntryPageLength;
if (kDown & HidNpadButton_AnyRight)
selectPos += EntryPageLength;
if (selectPos < 0)
selectPos = 0;
if (unsigned(selectPos) >= Examples.size())
selectPos = Examples.size()-1;
if (selectPos != oldPos)
{
if (selectPos < screenPos)
screenPos = selectPos;
else if (selectPos >= screenPos + int(EntriesPerScreen))
screenPos = selectPos - EntriesPerScreen + 1;
renderMenu();
}
consoleUpdate(NULL);
return true;
}
public:
static ExampleFunc Display()
{
CMainMenu app;
app.run();
return app.selectPos >= 0 ? Examples[app.selectPos].mainfunc : nullptr;
}
};
int main(int argc, char* argv[])
{
for (;;)
{
ExampleFunc func = CMainMenu::Display();
if (!func) break;
func();
}
return 0;
}

View File

@ -0,0 +1,38 @@
#version 460
layout (local_size_x = 32) in;
struct Vertex
{
vec4 position;
vec4 color;
};
layout (std140, binding = 0) uniform Params
{
vec4 colorA;
vec4 colorB;
float offset;
float scale;
} u;
layout (std430, binding = 0) buffer Output
{
Vertex vertices[];
} o;
const float TAU = 6.2831853071795;
void calcVertex(out Vertex vtx, float x)
{
vtx.position = vec4(x * 2.0 - 1.0, u.scale * sin((u.offset + x)*TAU), 0.5, 1.0);
vtx.color = mix(u.colorA, u.colorB, x);
}
void main()
{
uint id = gl_GlobalInvocationID.x;
uint maxid = gl_WorkGroupSize.x * gl_NumWorkGroups.x - 1;
float x = float(id) / float(maxid);
calcVertex(o.vertices[id], x);
}

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