mirror of
https://github.com/switchbrew/switch-examples.git
synced 2025-06-21 21:32:40 +02:00
Compare commits
93 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
bf9df10259 | ||
|
5e36dc2902 | ||
|
399aa26c6e | ||
|
b41c4957e7 | ||
|
d1b2175d58 | ||
|
4f6e700642 | ||
|
455ad6732c | ||
|
fbbe90507d | ||
|
454610b266 | ||
|
713404797b | ||
|
73960d720c | ||
|
0947425fc5 | ||
|
b55cb17676 | ||
|
950e99a1ab | ||
|
f03b9864ce | ||
|
5980937bdf | ||
|
e423a0ab0e | ||
|
78b1ed515e | ||
|
ce7c9a99ea | ||
|
84be02950a | ||
|
a5f53afed0 | ||
|
b924d358ec | ||
|
62885bbbff | ||
|
ac702280bc | ||
|
40ec713452 | ||
|
07863f0585 | ||
|
a6ea828801 | ||
|
00a0bb3899 | ||
|
29484f5c06 | ||
|
5a42b4766b | ||
|
2b0d5062a5 | ||
|
1e59bd0699 | ||
|
4272e286cc | ||
|
8487e07653 | ||
|
11fa984774 | ||
|
f252e65ebd | ||
|
847bb129e9 | ||
|
fc98393d50 | ||
|
9db0cfd567 | ||
|
c21700136e | ||
|
179e9a847b | ||
|
a6de2da842 | ||
|
cb01bf3c37 | ||
|
ea27fe123d | ||
|
73b50688ab | ||
|
4437fb814f | ||
|
1392e6cad0 | ||
|
7cce42673e | ||
|
a0ce16d0f7 | ||
|
25cdf12ab0 | ||
|
52bdce1eb0 | ||
|
1f30cbcd17 | ||
|
edb1506560 | ||
|
1a4cb0662a | ||
|
0175f8020b | ||
|
d8e5cd4d2b | ||
|
794a26392b | ||
|
c68fc5e826 | ||
|
bbd8727804 | ||
|
36dff0db63 | ||
|
554d75d499 | ||
|
a34aba3b72 | ||
|
948ce48e58 | ||
|
2e7d0c198e | ||
|
2ee7aff9e4 | ||
|
f82d955dfe | ||
|
d279271037 | ||
|
9da8e4a9f4 | ||
|
f133405219 | ||
|
25d9880c3b | ||
|
b9df83b93d | ||
|
afb3d2132f | ||
|
147a5e2fc4 | ||
|
86a1af14fc | ||
|
d952469c56 | ||
|
89bfe5608d | ||
|
c3c4c4f07c | ||
|
c1524e86d6 | ||
|
9b44df4132 | ||
|
188b41ff9e | ||
|
68a9002b4a | ||
|
868da01cd5 | ||
|
a0b4ee1229 | ||
|
5363a04d9b | ||
|
da994a8a69 | ||
|
707d65171e | ||
|
3fc12d7765 | ||
|
b8cf089964 | ||
|
25a7b2adaa | ||
|
9d11f70f3c | ||
|
c24895c52f | ||
|
d1d712702a | ||
|
927256ce99 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -8,4 +8,5 @@ build
|
||||
*.nsp
|
||||
*.npdm
|
||||
*.bz2
|
||||
*.dksh
|
||||
*~
|
||||
|
14
Makefile
14
Makefile
@ -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 *
|
||||
|
@ -3,43 +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];
|
||||
|
||||
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);
|
||||
|
||||
@ -56,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.
|
||||
}
|
||||
@ -73,13 +85,13 @@ 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
|
||||
|
||||
consoleUpdate(NULL);
|
||||
}
|
||||
|
222
alarm-notifications/Makefile
Normal file
222
alarm-notifications/Makefile
Normal 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
|
||||
#---------------------------------------------------------------------------------------
|
188
alarm-notifications/source/main.c
Normal file
188
alarm-notifications/source/main.c
Normal 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;
|
||||
}
|
@ -4,21 +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];
|
||||
|
||||
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);
|
||||
@ -36,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);
|
||||
}
|
||||
@ -69,13 +76,13 @@ 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
|
||||
|
||||
consoleUpdate(NULL);
|
||||
}
|
||||
|
@ -20,20 +20,38 @@ int main(int argc, char* argv[])
|
||||
// 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];
|
||||
PdmApplicationEvent events[1];
|
||||
u64 titleIDs[1] = {0x010021B002EEA000}; // Change this to the titleID of the current-process / the titleID you want to use.
|
||||
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.
|
||||
rc = pdmqryInitialize();
|
||||
if (R_FAILED(rc)) printf("pdmqryInitialize(): 0x%x\n", rc);
|
||||
if (R_SUCCEEDED(rc)) initflag = true;
|
||||
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");
|
||||
@ -42,57 +60,100 @@ 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 in this frame compared to the previous one
|
||||
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)
|
||||
if (kDown & HidNpadButton_Plus)
|
||||
break; // break in order to return to hbmenu
|
||||
|
||||
if (kDown & KEY_A) {
|
||||
if (kDown & HidNpadButton_A) {
|
||||
// Use appletQueryApplicationPlayStatisticsByUid if you want playstats for a specific userID.
|
||||
|
||||
memset(stats, 0, sizeof(stats));
|
||||
total_out = 0;
|
||||
|
||||
rc = appletQueryApplicationPlayStatistics(stats, titleIDs, sizeof(titleIDs)/sizeof(u64), &total_out);
|
||||
printf("appletQueryApplicationPlayStatistics(): 0x%x\n", rc);
|
||||
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("titleID = 0x%08lX, totalPlayTime = %lu (%llu seconds), totalLaunches = %lu\n", stats[i].titleID, stats[i].totalPlayTime, stats[i].totalPlayTime / 1000000000ULL, stats[i].totalLaunches);
|
||||
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 & KEY_X)) {
|
||||
if (initflag && (kDown & HidNpadButton_X)) {
|
||||
memset(events, 0, sizeof(events));
|
||||
total_out = 0;
|
||||
|
||||
rc = pdmqryQueryApplicationEvent(0, events, sizeof(events)/sizeof(PdmApplicationEvent), &total_out);
|
||||
printf("pdmqryQueryApplicationEvent(): 0x%x\n", rc);
|
||||
// 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 = pdmPlayTimestampToPosix(events[i].timestampUser);
|
||||
time_t tmptime = events[i].timestamp_user;
|
||||
|
||||
printf("%d: ", i);
|
||||
printf("titleID = 0x%08lX, entryindex = 0x%x, timestampUser = %u, timestampNetwork = %u, eventType = %u, timestampUser = %s\n", events[i].titleID, events[i].entryindex, events[i].timestampUser, events[i].timestampNetwork, events[i].eventType, ctime(&tmptime));
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
// For more pdmqry cmds see pdm.h.
|
||||
// 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);
|
||||
}
|
||||
|
||||
if (initflag) pdmqryExit();
|
||||
pdmqryExit();
|
||||
|
||||
// Deinitialize and clean up resources used by the console (important!)
|
||||
consoleExit(NULL);
|
||||
|
@ -24,6 +24,13 @@ int main(int argc, char* argv[])
|
||||
// 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);
|
||||
@ -34,17 +41,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 in this frame compared to the previous one
|
||||
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)
|
||||
if (kDown & HidNpadButton_Plus)
|
||||
break; // break in order to return to hbmenu
|
||||
|
||||
if (kDown & KEY_A) {
|
||||
if (kDown & HidNpadButton_A) {
|
||||
printf("Running errorResultRecordShow()...\n");
|
||||
consoleUpdate(NULL);
|
||||
rc = errorResultRecordShow(MAKERESULT(16, 250), time(NULL));
|
||||
|
@ -23,6 +23,13 @@ int main(int argc, char* argv[])
|
||||
// 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);
|
||||
|
||||
@ -47,17 +54,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 in this frame compared to the previous one
|
||||
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)
|
||||
if (kDown & HidNpadButton_Plus)
|
||||
break; // break in order to return to hbmenu
|
||||
|
||||
if (pctlflag && (kDown & KEY_A)) {
|
||||
if (pctlflag && (kDown & HidNpadButton_A)) {
|
||||
printf("Running pctlauthShow()...\n");
|
||||
consoleUpdate(NULL);
|
||||
rc = pctlauthShow(true); // Launch the applet for validating the PIN.
|
||||
|
@ -59,7 +59,7 @@ CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
|
||||
ASFLAGS := -g $(ARCH)
|
||||
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
|
||||
|
||||
LIBS := -lnx
|
||||
LIBS := -lnx -lm
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level containing
|
||||
|
@ -57,6 +57,13 @@ int main(int argc, char* argv[])
|
||||
// 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);
|
||||
@ -107,6 +114,7 @@ int main(int argc, char* argv[])
|
||||
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);
|
||||
@ -141,21 +149,21 @@ 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 in this frame compared to the previous one
|
||||
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)
|
||||
if (kDown & HidNpadButton_Plus)
|
||||
break; // break in order to return to hbmenu
|
||||
|
||||
// Your code goes here
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
if (kDown & KEY_ZR) swkbdInlineDisappear(&kbdinline); //Optional, you can have swkbd (dis)appear whenever.
|
||||
if (kDown & KEY_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.
|
||||
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);
|
||||
|
@ -23,6 +23,13 @@ int main(int argc, char* argv[])
|
||||
// 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);
|
||||
@ -33,23 +40,23 @@ 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 in this frame compared to the previous one
|
||||
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)
|
||||
if (kDown & HidNpadButton_Plus)
|
||||
break; // break in order to return to hbmenu
|
||||
|
||||
if (kDown & KEY_A) {
|
||||
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 title which has HtmlDocument content, when the title is an Application. When the title 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.
|
||||
// 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);
|
||||
|
||||
|
@ -18,6 +18,13 @@ int main(int argc, char* argv[])
|
||||
// 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");
|
||||
@ -25,17 +32,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 in this frame compared to the previous one
|
||||
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)
|
||||
if (kDown & HidNpadButton_Plus)
|
||||
break; // break in order to return to hbmenu
|
||||
|
||||
if (kDown & KEY_A) {
|
||||
if (kDown & HidNpadButton_A) {
|
||||
Result rc = 0;
|
||||
float fLux=0;
|
||||
bool availableflag=0;
|
||||
|
@ -22,19 +22,26 @@ int main(int argc, char* argv[])
|
||||
// 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 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 in this frame compared to the previous one
|
||||
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)
|
||||
if (kDown & HidNpadButton_Plus)
|
||||
break; // break in order to return to hbmenu
|
||||
|
||||
// Your code goes here
|
||||
|
@ -28,22 +28,29 @@ int main(int argc, char **argv)
|
||||
|
||||
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 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
|
||||
|
||||
if (kDown & KEY_Y) {
|
||||
if (kDown & HidNpadButton_Y) {
|
||||
appletSetGamePlayRecordingState(0);//Disable recording.
|
||||
}
|
||||
else if (kDown & KEY_X) {
|
||||
else if (kDown & HidNpadButton_X) {
|
||||
appletSetGamePlayRecordingState(1);//Enable recording.
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,13 @@ int main(int argc, char* argv[])
|
||||
// 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.
|
||||
@ -56,14 +63,14 @@ 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 in this frame compared to the previous one
|
||||
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)
|
||||
if (kDown & HidNpadButton_Plus)
|
||||
break; // break in order to return to hbmenu
|
||||
|
||||
// Update the console, sending a new frame to the display
|
||||
|
@ -9,7 +9,7 @@
|
||||
// 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 title.
|
||||
// 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[])
|
||||
@ -24,6 +24,13 @@ int main(int argc, char* argv[])
|
||||
// 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.
|
||||
@ -43,18 +50,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 in this frame compared to the previous one
|
||||
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)
|
||||
if (kDown & HidNpadButton_Plus)
|
||||
break; // break in order to return to hbmenu
|
||||
|
||||
if (pctlinit) {
|
||||
if (kDown & KEY_A) {
|
||||
if (kDown & HidNpadButton_A) {
|
||||
// Not sure what this is.
|
||||
/*rc = pctlConfirmStereoVisionPermission();
|
||||
if (R_FAILED(rc)) printf("pctlConfirmStereoVisionPermission(): 0x%x\n", rc);*/
|
||||
@ -70,7 +77,7 @@ int main(int argc, char* argv[])
|
||||
printf("appletSetVrModeEnabled(true): 0x%x\n", rc);
|
||||
}
|
||||
}
|
||||
else if (kDown & KEY_B) {
|
||||
else if (kDown & HidNpadButton_B) {
|
||||
rc = appletSetVrModeEnabled(false);
|
||||
printf("appletSetVrModeEnabled(false): 0x%x\n", rc);
|
||||
|
||||
@ -78,10 +85,10 @@ int main(int argc, char* argv[])
|
||||
/*rc = pctlResetConfirmedStereoVisionPermission();
|
||||
printf("pctlResetConfirmedStereoVisionPermission(): 0x%x\n", rc);*/
|
||||
}
|
||||
else if (kDown & KEY_X) {
|
||||
else if (kDown & HidNpadButton_X) {
|
||||
bool flag=0;
|
||||
rc = appletIsVrModeEnabled(&flag);
|
||||
printf("appletIsVrModeEnabled(): 0x%x\n", rc);
|
||||
printf("appletIsVrModeEnabled(): 0x%x", rc);
|
||||
if (R_SUCCEEDED(rc)) printf(", %d\n", flag);
|
||||
printf("\n");
|
||||
}
|
||||
|
@ -13,6 +13,13 @@ 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 audren demonstration program\n");
|
||||
|
||||
static const AudioRendererConfig arConfig =
|
||||
@ -57,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);
|
||||
@ -77,16 +84,16 @@ int main(void)
|
||||
// Main loop
|
||||
while (appletMainLoop())
|
||||
{
|
||||
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);
|
||||
|
@ -22,6 +22,13 @@ int main(int argc, char **argv)
|
||||
// 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;
|
||||
@ -108,17 +115,17 @@ int main(int argc, char **argv)
|
||||
|
||||
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
|
||||
|
||||
// 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))
|
||||
@ -130,11 +137,11 @@ int main(int argc, char **argv)
|
||||
|
||||
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);
|
||||
@ -142,7 +149,7 @@ int main(int argc, char **argv)
|
||||
// Terminate the default audio devices.
|
||||
audinExit();
|
||||
audoutExit();
|
||||
|
||||
|
||||
consoleExit(NULL);
|
||||
return 0;
|
||||
}
|
||||
|
@ -53,6 +53,13 @@ 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 =
|
||||
@ -67,15 +74,15 @@ int main(void)
|
||||
|
||||
size_t num_channels = 1;
|
||||
size_t samplerate = 48000;
|
||||
size_t max_samples = samplerate;
|
||||
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*2 + 0xFFF) &~ 0xFFF;//*2 for 2 wavebufs.
|
||||
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[2] = {0};
|
||||
AudioDriverWaveBuf wavebuf[4] = {0};
|
||||
int i, wavei;
|
||||
|
||||
HwopusDecoder hwdecoder = {0};
|
||||
@ -122,7 +129,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);
|
||||
@ -144,9 +151,9 @@ int main(void)
|
||||
}
|
||||
audrvVoiceStart(&drv, 0);
|
||||
|
||||
for(i=0; i<2; i++) {
|
||||
for(i=0; i<4; i++) {
|
||||
wavebuf[i].data_raw = mempool_ptr;
|
||||
wavebuf[i].size = max_samples_datasize*2;//*2 for 2 wavebufs.
|
||||
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;
|
||||
}
|
||||
@ -163,16 +170,16 @@ int main(void)
|
||||
// Main loop
|
||||
while (appletMainLoop())
|
||||
{
|
||||
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)
|
||||
{
|
||||
//Close the opus-file if needed and (re)open it, since libopusfile doesn't support seek-to-beginning.
|
||||
if (of)
|
||||
@ -191,7 +198,7 @@ int main(void)
|
||||
|
||||
if (audio_playing) {
|
||||
wavei = -1;
|
||||
for(i=0; i<2; i++) {
|
||||
for(i=0; i<4; i++) {
|
||||
if (wavebuf[i].state == AudioDriverWaveBufState_Free || wavebuf[i].state == AudioDriverWaveBufState_Done) {
|
||||
wavei = i;
|
||||
break;
|
||||
@ -199,7 +206,7 @@ int main(void)
|
||||
}
|
||||
|
||||
if (wavei >= 0) {
|
||||
curbuf = (opus_int16*)(mempool_ptr + wavebuf[wavei].start_sample_offset);
|
||||
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)
|
||||
|
@ -38,6 +38,13 @@ int main(int argc, char **argv)
|
||||
// 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;
|
||||
|
||||
@ -82,81 +89,81 @@ int main(int argc, char **argv)
|
||||
|
||||
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
|
||||
|
||||
if (kDown & KEY_A)
|
||||
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;
|
||||
|
@ -52,20 +52,20 @@ ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
|
||||
CFLAGS := -g -Wall -O2 -ffunction-sections \
|
||||
$(ARCH) $(DEFINES)
|
||||
|
||||
CFLAGS += -D__SWITCH__ $(INCLUDE) `aarch64-none-elf-pkg-config --cflags SDL2_mixer`
|
||||
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 := `aarch64-none-elf-pkg-config --libs SDL2_mixer`
|
||||
LIBS := `$(PREFIX)pkg-config --libs SDL2_mixer`
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level containing
|
||||
# include and lib
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBDIRS := $(PORTLIBS) $(LIBNX)
|
||||
LIBDIRS := $(LIBNX)
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
|
@ -7,8 +7,8 @@
|
||||
#include <switch.h>
|
||||
|
||||
// Include sdl2 headers
|
||||
#include <SDL2/SDL.h>
|
||||
#include <SDL2/SDL_mixer.h>
|
||||
#include <SDL.h>
|
||||
#include <SDL_mixer.h>
|
||||
|
||||
// Main program entrypoint
|
||||
int main(int argc, char *argv[])
|
||||
@ -17,6 +17,13 @@ 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);
|
||||
|
||||
Result rc = romfsInit();
|
||||
if (R_FAILED(rc))
|
||||
printf("romfsInit: %08X\n", rc);
|
||||
@ -41,17 +48,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 in this frame compared to the previous one
|
||||
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)
|
||||
if (kDown & HidNpadButton_Plus)
|
||||
break; // break in order to return to hbmenu
|
||||
|
||||
if (kDown & KEY_A)
|
||||
if (kDown & HidNpadButton_A)
|
||||
Mix_PlayMusic(audio, 1); //Play the audio file
|
||||
|
||||
// Update the console, sending a new frame to the display
|
||||
|
@ -47,6 +47,13 @@ int main(int argc, char* argv[])
|
||||
// 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);
|
||||
|
||||
@ -56,14 +63,14 @@ 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 in this frame compared to the previous one
|
||||
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)
|
||||
if (kDown & HidNpadButton_Plus)
|
||||
break; // break in order to return to hbmenu
|
||||
|
||||
// Your code goes here
|
||||
|
@ -32,6 +32,13 @@ 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);
|
||||
|
||||
Result rc = romfsInit();
|
||||
if (R_FAILED(rc))
|
||||
printf("romfsInit: %08X\n", rc);
|
||||
@ -46,13 +53,13 @@ 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
|
||||
|
||||
consoleUpdate(NULL);
|
||||
}
|
||||
|
@ -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,60 +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.
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -127,13 +120,13 @@ 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
|
||||
|
||||
consoleUpdate(NULL);
|
||||
}
|
||||
|
@ -11,6 +11,13 @@ 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);
|
||||
|
||||
DIR* dir;
|
||||
struct dirent* ent;
|
||||
|
||||
@ -33,13 +40,13 @@ 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
|
||||
|
||||
consoleUpdate(NULL);
|
||||
}
|
||||
|
271
graphics/deko3d/deko_basic/Makefile
Normal file
271
graphics/deko3d/deko_basic/Makefile
Normal 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
|
||||
#---------------------------------------------------------------------------------------
|
9
graphics/deko3d/deko_basic/source/color_fsh.glsl
Normal file
9
graphics/deko3d/deko_basic/source/color_fsh.glsl
Normal file
@ -0,0 +1,9 @@
|
||||
#version 460
|
||||
|
||||
layout (location = 0) in vec4 inColor;
|
||||
layout (location = 0) out vec4 outColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
outColor = inColor;
|
||||
}
|
233
graphics/deko3d/deko_basic/source/main.c
Normal file
233
graphics/deko3d/deko_basic/source/main.c
Normal 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;
|
||||
}
|
23
graphics/deko3d/deko_basic/source/triangle_vsh.glsl
Normal file
23
graphics/deko3d/deko_basic/source/triangle_vsh.glsl
Normal 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];
|
||||
}
|
271
graphics/deko3d/deko_console/Makefile
Normal file
271
graphics/deko3d/deko_console/Makefile
Normal 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
|
||||
#---------------------------------------------------------------------------------------
|
15
graphics/deko3d/deko_console/source/console_fsh.glsl
Normal file
15
graphics/deko3d/deko_console/source/console_fsh.glsl
Normal 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);
|
||||
}
|
35
graphics/deko3d/deko_console/source/console_vsh.glsl
Normal file
35
graphics/deko3d/deko_console/source/console_vsh.glsl
Normal 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];
|
||||
}
|
486
graphics/deko3d/deko_console/source/gpu_console.c
Normal file
486
graphics/deko3d/deko_console/source/gpu_console.c
Normal 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, ©Src, &tilesetView, ©Dst, 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;
|
||||
}
|
86
graphics/deko3d/deko_console/source/main.c
Normal file
86
graphics/deko3d/deko_console/source/main.c
Normal 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;
|
||||
}
|
13
graphics/deko3d/deko_console/source/startup.c
Normal file
13
graphics/deko3d/deko_console/source/startup.c
Normal file
@ -0,0 +1,13 @@
|
||||
#include <switch.h>
|
||||
|
||||
void userAppInit(void)
|
||||
{
|
||||
Result res = romfsInit();
|
||||
if (R_FAILED(res))
|
||||
diagAbortWithResult(res);
|
||||
}
|
||||
|
||||
void userAppExit(void)
|
||||
{
|
||||
romfsExit();
|
||||
}
|
271
graphics/deko3d/deko_examples/Makefile
Normal file
271
graphics/deko3d/deko_examples/Makefile
Normal 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
|
||||
#---------------------------------------------------------------------------------------
|
BIN
graphics/deko3d/deko_examples/romfs/cat-256x256.bc1
Normal file
BIN
graphics/deko3d/deko_examples/romfs/cat-256x256.bc1
Normal file
Binary file not shown.
BIN
graphics/deko3d/deko_examples/romfs/teapot-idx.bin
Normal file
BIN
graphics/deko3d/deko_examples/romfs/teapot-idx.bin
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
BIN
graphics/deko3d/deko_examples/romfs/teapot-vtx.bin
Normal file
BIN
graphics/deko3d/deko_examples/romfs/teapot-vtx.bin
Normal file
Binary file not shown.
182
graphics/deko3d/deko_examples/source/Example01_SimpleSetup.cpp
Normal file
182
graphics/deko3d/deko_examples/source/Example01_SimpleSetup.cpp
Normal 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();
|
||||
}
|
237
graphics/deko3d/deko_examples/source/Example02_Triangle.cpp
Normal file
237
graphics/deko3d/deko_examples/source/Example02_Triangle.cpp
Normal 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();
|
||||
}
|
378
graphics/deko3d/deko_examples/source/Example03_Cube.cpp
Normal file
378
graphics/deko3d/deko_examples/source/Example03_Cube.cpp
Normal 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();
|
||||
}
|
420
graphics/deko3d/deko_examples/source/Example04_TexturedCube.cpp
Normal file
420
graphics/deko3d/deko_examples/source/Example04_TexturedCube.cpp
Normal 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();
|
||||
}
|
259
graphics/deko3d/deko_examples/source/Example05_Tessellation.cpp
Normal file
259
graphics/deko3d/deko_examples/source/Example05_Tessellation.cpp
Normal 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();
|
||||
}
|
414
graphics/deko3d/deko_examples/source/Example06_Multisampling.cpp
Normal file
414
graphics/deko3d/deko_examples/source/Example06_Multisampling.cpp
Normal 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();
|
||||
}
|
407
graphics/deko3d/deko_examples/source/Example07_MeshLighting.cpp
Normal file
407
graphics/deko3d/deko_examples/source/Example07_MeshLighting.cpp
Normal 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();
|
||||
}
|
@ -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();
|
||||
}
|
324
graphics/deko3d/deko_examples/source/Example09_SimpleCompute.cpp
Normal file
324
graphics/deko3d/deko_examples/source/Example09_SimpleCompute.cpp
Normal 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), ¶ms);
|
||||
|
||||
// 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();
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
};
|
@ -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);
|
||||
}
|
||||
};
|
@ -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;
|
||||
}
|
@ -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);
|
||||
};
|
@ -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);
|
||||
}
|
||||
}
|
||||
};
|
@ -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);
|
||||
}
|
@ -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));
|
||||
}
|
||||
};
|
@ -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);
|
||||
}
|
133
graphics/deko3d/deko_examples/source/SampleFramework/CMemPool.h
Normal file
133
graphics/deko3d/deko_examples/source/SampleFramework/CMemPool.h
Normal 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();
|
||||
}
|
@ -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;
|
||||
}
|
@ -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);
|
||||
};
|
@ -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;
|
||||
}
|
@ -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);
|
18
graphics/deko3d/deko_examples/source/SampleFramework/LICENSE
Normal file
18
graphics/deko3d/deko_examples/source/SampleFramework/LICENSE
Normal 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.
|
@ -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>
|
@ -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();
|
||||
}
|
15
graphics/deko3d/deko_examples/source/basic_deferred_fsh.glsl
Normal file
15
graphics/deko3d/deko_examples/source/basic_deferred_fsh.glsl
Normal 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);
|
||||
}
|
46
graphics/deko3d/deko_examples/source/basic_lighting_fsh.glsl
Normal file
46
graphics/deko3d/deko_examples/source/basic_lighting_fsh.glsl
Normal 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);
|
||||
}
|
12
graphics/deko3d/deko_examples/source/basic_vsh.glsl
Normal file
12
graphics/deko3d/deko_examples/source/basic_vsh.glsl
Normal 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;
|
||||
}
|
9
graphics/deko3d/deko_examples/source/color_fsh.glsl
Normal file
9
graphics/deko3d/deko_examples/source/color_fsh.glsl
Normal 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);
|
||||
}
|
53
graphics/deko3d/deko_examples/source/composition_fsh.glsl
Normal file
53
graphics/deko3d/deko_examples/source/composition_fsh.glsl
Normal 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);
|
||||
}
|
24
graphics/deko3d/deko_examples/source/composition_vsh.glsl
Normal file
24
graphics/deko3d/deko_examples/source/composition_vsh.glsl
Normal 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);
|
||||
}
|
143
graphics/deko3d/deko_examples/source/main.cpp
Normal file
143
graphics/deko3d/deko_examples/source/main.cpp
Normal 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;
|
||||
}
|
38
graphics/deko3d/deko_examples/source/sinewave.glsl
Normal file
38
graphics/deko3d/deko_examples/source/sinewave.glsl
Normal 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);
|
||||
}
|
21
graphics/deko3d/deko_examples/source/tess_simple_tcsh.glsl
Normal file
21
graphics/deko3d/deko_examples/source/tess_simple_tcsh.glsl
Normal file
@ -0,0 +1,21 @@
|
||||
#version 460
|
||||
|
||||
layout (vertices = 3) out;
|
||||
|
||||
layout (location = 0) in vec4 inAttrib[];
|
||||
|
||||
layout (location = 0) out vec4 outAttrib[];
|
||||
|
||||
void main()
|
||||
{
|
||||
if (gl_InvocationID == 0)
|
||||
{
|
||||
gl_TessLevelInner[0] = 5.0; // i.e. 2 concentric triangles with the center being a triangle
|
||||
gl_TessLevelOuter[0] = 2.0;
|
||||
gl_TessLevelOuter[1] = 3.0;
|
||||
gl_TessLevelOuter[2] = 5.0;
|
||||
}
|
||||
|
||||
gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;
|
||||
outAttrib[gl_InvocationID] = inAttrib[gl_InvocationID];
|
||||
}
|
21
graphics/deko3d/deko_examples/source/tess_simple_tesh.glsl
Normal file
21
graphics/deko3d/deko_examples/source/tess_simple_tesh.glsl
Normal file
@ -0,0 +1,21 @@
|
||||
#version 460
|
||||
|
||||
layout (triangles, equal_spacing, ccw) in;
|
||||
|
||||
layout (location = 0) in vec4 inAttrib[];
|
||||
|
||||
layout (location = 0) out vec4 outAttrib;
|
||||
|
||||
vec4 interpolate(in vec4 v0, in vec4 v1, in vec4 v2)
|
||||
{
|
||||
vec4 a0 = gl_TessCoord.x * v0;
|
||||
vec4 a1 = gl_TessCoord.y * v1;
|
||||
vec4 a2 = gl_TessCoord.z * v2;
|
||||
return a0 + a1 + a2;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = interpolate(gl_in[0].gl_Position, gl_in[1].gl_Position, gl_in[2].gl_Position);
|
||||
outAttrib = interpolate(inAttrib[0], inAttrib[1], inAttrib[2]);
|
||||
}
|
11
graphics/deko3d/deko_examples/source/texture_fsh.glsl
Normal file
11
graphics/deko3d/deko_examples/source/texture_fsh.glsl
Normal file
@ -0,0 +1,11 @@
|
||||
#version 460
|
||||
|
||||
layout (location = 0) in vec2 inTexCoord;
|
||||
layout (location = 0) out vec4 outColor;
|
||||
|
||||
layout (binding = 0) uniform sampler2D texture0;
|
||||
|
||||
void main()
|
||||
{
|
||||
outColor = texture(texture0, inTexCoord);
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
#version 460
|
||||
|
||||
layout (location = 0) in vec3 inPos;
|
||||
layout (location = 1) in vec3 inNormal;
|
||||
layout (location = 2) in vec4 inAttrib;
|
||||
|
||||
layout (location = 0) out vec3 outWorldPos;
|
||||
layout (location = 1) out vec3 outNormal;
|
||||
layout (location = 2) out vec4 outAttrib;
|
||||
|
||||
layout (std140, binding = 0) uniform Transformation
|
||||
{
|
||||
mat4 mdlvMtx;
|
||||
mat4 projMtx;
|
||||
} u;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 worldPos = u.mdlvMtx * vec4(inPos, 1.0);
|
||||
gl_Position = u.projMtx * worldPos;
|
||||
|
||||
outWorldPos = worldPos.xyz;
|
||||
|
||||
outNormal = normalize(mat3(u.mdlvMtx) * inNormal);
|
||||
|
||||
// Pass through the user-defined attribute
|
||||
outAttrib = inAttrib;
|
||||
}
|
20
graphics/deko3d/deko_examples/source/transform_vsh.glsl
Normal file
20
graphics/deko3d/deko_examples/source/transform_vsh.glsl
Normal file
@ -0,0 +1,20 @@
|
||||
#version 460
|
||||
|
||||
layout (location = 0) in vec3 inPos;
|
||||
layout (location = 1) in vec4 inAttrib;
|
||||
|
||||
layout (location = 0) out vec4 outAttrib;
|
||||
|
||||
layout (std140, binding = 0) uniform Transformation
|
||||
{
|
||||
mat4 mdlvMtx;
|
||||
mat4 projMtx;
|
||||
} u;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 pos = u.mdlvMtx * vec4(inPos, 1.0);
|
||||
gl_Position = u.projMtx * pos;
|
||||
|
||||
outAttrib = inAttrib;
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
#include <stdio.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <switch.h>
|
||||
@ -430,16 +430,16 @@ static void sceneUpdate(u32 kHeld)
|
||||
float deltaTime = curTime - s_updTime;
|
||||
s_updTime = curTime;
|
||||
|
||||
if (kHeld & KEY_LEFT)
|
||||
if (kHeld & HidNpadButton_AnyLeft)
|
||||
s_cameraAngle = fract(s_cameraAngle - deltaTime/4);
|
||||
else if (kHeld & KEY_RIGHT)
|
||||
else if (kHeld & HidNpadButton_AnyRight)
|
||||
s_cameraAngle = fract(s_cameraAngle + deltaTime/4);
|
||||
if (kHeld & (KEY_UP|KEY_DOWN))
|
||||
if (kHeld & (HidNpadButton_AnyUp|HidNpadButton_AnyDown))
|
||||
{
|
||||
glm::vec3 front = deltaTime * glm::rotate(glm::vec3{0.0f, 0.0f, -1.0f}, s_cameraAngle * TAU, glm::vec3{0.0f, -1.0f, 0.0f});
|
||||
if (kHeld & KEY_UP)
|
||||
if (kHeld & HidNpadButton_AnyUp)
|
||||
s_cameraPos += front;
|
||||
else if (kHeld & KEY_DOWN)
|
||||
else if (kHeld & HidNpadButton_AnyDown)
|
||||
s_cameraPos -= front;
|
||||
}
|
||||
|
||||
@ -464,7 +464,7 @@ static void configureResolution(NWindow* win, bool halved)
|
||||
width = 1280;
|
||||
height = 720;
|
||||
break;
|
||||
case AppletOperationMode_Docked:
|
||||
case AppletOperationMode_Console:
|
||||
width = 1920;
|
||||
height = 1080;
|
||||
break;
|
||||
@ -525,17 +525,24 @@ int main(int argc, char* argv[])
|
||||
// Initialize our scene
|
||||
sceneInit();
|
||||
|
||||
// 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);
|
||||
|
||||
// Main graphics loop
|
||||
while (appletMainLoop())
|
||||
{
|
||||
// Get and process input
|
||||
hidScanInput();
|
||||
u32 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
||||
u32 kHeld = hidKeysHeld(CONTROLLER_P1_AUTO);
|
||||
if (kDown & KEY_PLUS)
|
||||
padUpdate(&pad);
|
||||
u32 kDown = padGetButtonsDown(&pad);
|
||||
u32 kHeld = padGetButtons(&pad);
|
||||
if (kDown & HidNpadButton_Plus)
|
||||
break;
|
||||
|
||||
bool shouldHalveResolution = !!(kHeld & KEY_A);
|
||||
bool shouldHalveResolution = !!(kHeld & HidNpadButton_A);
|
||||
|
||||
// Configure the resolution used to render the scene, which
|
||||
// will be different in handheld mode/docked mode.
|
||||
|
@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
* Copyright (C) 1999-2001 Brian Paul All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
@ -718,9 +718,9 @@ gears_idle(void)
|
||||
static int frames = 0;
|
||||
static double tRot0 = -1.0, tRate0 = -1.0;
|
||||
double dt, t;
|
||||
static u64 origTicks = U64_MAX;
|
||||
static u64 origTicks = UINT64_MAX;
|
||||
|
||||
if (origTicks == U64_MAX)
|
||||
if (origTicks == UINT64_MAX)
|
||||
origTicks = armGetSystemTick();
|
||||
|
||||
u64 ticksElapsed = armGetSystemTick() - origTicks;
|
||||
@ -855,13 +855,20 @@ int main(int argc, char* argv[])
|
||||
gears_init();
|
||||
gears_reshape(1280, 720);
|
||||
|
||||
// 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);
|
||||
|
||||
// Main graphics loop
|
||||
while (appletMainLoop())
|
||||
{
|
||||
// Get and process input
|
||||
hidScanInput();
|
||||
u32 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
||||
if (kDown & KEY_PLUS)
|
||||
padUpdate(&pad);
|
||||
u32 kDown = padGetButtonsDown(&pad);
|
||||
if (kDown & HidNpadButton_Plus)
|
||||
break;
|
||||
|
||||
// Render stuff!
|
||||
|
@ -7,6 +7,13 @@ 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) );
|
||||
|
||||
@ -46,15 +53,15 @@ 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);
|
||||
|
||||
// Your code goes here
|
||||
|
||||
//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
|
||||
|
||||
consoleUpdate(NULL);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include <stdio.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <switch.h>
|
||||
@ -414,13 +414,20 @@ int main(int argc, char* argv[])
|
||||
// Initialize our scene
|
||||
sceneInit();
|
||||
|
||||
// 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);
|
||||
|
||||
// Main graphics loop
|
||||
while (appletMainLoop())
|
||||
{
|
||||
// Get and process input
|
||||
hidScanInput();
|
||||
u32 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
||||
if (kDown & KEY_PLUS)
|
||||
padUpdate(&pad);
|
||||
u32 kDown = padGetButtonsDown(&pad);
|
||||
if (kDown & HidNpadButton_Plus)
|
||||
break;
|
||||
|
||||
// Update our scene
|
||||
|
@ -325,13 +325,20 @@ int main(int argc, char* argv[])
|
||||
// Initialize our scene
|
||||
sceneInit();
|
||||
|
||||
// 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);
|
||||
|
||||
// Main graphics loop
|
||||
while (appletMainLoop())
|
||||
{
|
||||
// Get and process input
|
||||
hidScanInput();
|
||||
u32 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
||||
if (kDown & KEY_PLUS)
|
||||
padUpdate(&pad);
|
||||
u32 kDown = padGetButtonsDown(&pad);
|
||||
if (kDown & HidNpadButton_Plus)
|
||||
break;
|
||||
|
||||
// Render stuff!
|
||||
|
@ -514,13 +514,20 @@ int main(int argc, char* argv[])
|
||||
// Initialize our scene
|
||||
sceneInit();
|
||||
|
||||
// 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);
|
||||
|
||||
// Main graphics loop
|
||||
while (appletMainLoop())
|
||||
{
|
||||
// Get and process input
|
||||
hidScanInput();
|
||||
u32 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
||||
if (kDown & KEY_PLUS)
|
||||
padUpdate(&pad);
|
||||
u32 kDown = padGetButtonsDown(&pad);
|
||||
if (kDown & HidNpadButton_Plus)
|
||||
break;
|
||||
|
||||
// Update our scene
|
||||
|
@ -8,6 +8,13 @@ int main(int argc, char **argv)
|
||||
//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);
|
||||
|
||||
//Move the cursor to row 16 and column 20 and then prints "Hello World!"
|
||||
//To move the cursor you have to print "\x1b[r;cH", where r and c are respectively
|
||||
//the row and column where you want your cursor to move
|
||||
@ -15,13 +22,13 @@ int main(int argc, char **argv)
|
||||
|
||||
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
|
||||
|
||||
consoleUpdate(NULL);
|
||||
}
|
||||
|
@ -7,6 +7,13 @@ 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) );
|
||||
|
||||
@ -61,15 +68,15 @@ 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);
|
||||
|
||||
// Your code goes here
|
||||
|
||||
//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
|
||||
|
||||
consoleUpdate(NULL);
|
||||
}
|
||||
|
217
graphics/sdl2/sdl2-demo/Makefile
Normal file
217
graphics/sdl2/sdl2-demo/Makefile
Normal file
@ -0,0 +1,217 @@
|
||||
#---------------------------------------------------------------------------------
|
||||
.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
|
||||
|
||||
APP_TITLE := SDL2+mixer+image Demo
|
||||
APP_AUTHOR := carstene1ns
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# options for code generation
|
||||
#---------------------------------------------------------------------------------
|
||||
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
|
||||
|
||||
CFLAGS := `$(PREFIX)pkg-config --cflags sdl2 SDL2_mixer SDL2_image` -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 := `$(PREFIX)pkg-config --libs sdl2 SDL2_mixer SDL2_image SDL2_ttf` \
|
||||
-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++ and libEGL dependent projects
|
||||
#---------------------------------------------------------------------------------
|
||||
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
|
||||
#---------------------------------------------------------------------------------------
|
BIN
graphics/sdl2/sdl2-demo/romfs/data/LeroyLetteringLightBeta01.ttf
Normal file
BIN
graphics/sdl2/sdl2-demo/romfs/data/LeroyLetteringLightBeta01.ttf
Normal file
Binary file not shown.
BIN
graphics/sdl2/sdl2-demo/romfs/data/background.ogg
Normal file
BIN
graphics/sdl2/sdl2-demo/romfs/data/background.ogg
Normal file
Binary file not shown.
BIN
graphics/sdl2/sdl2-demo/romfs/data/pop1.wav
Normal file
BIN
graphics/sdl2/sdl2-demo/romfs/data/pop1.wav
Normal file
Binary file not shown.
BIN
graphics/sdl2/sdl2-demo/romfs/data/pop2.wav
Normal file
BIN
graphics/sdl2/sdl2-demo/romfs/data/pop2.wav
Normal file
Binary file not shown.
BIN
graphics/sdl2/sdl2-demo/romfs/data/pop3.wav
Normal file
BIN
graphics/sdl2/sdl2-demo/romfs/data/pop3.wav
Normal file
Binary file not shown.
BIN
graphics/sdl2/sdl2-demo/romfs/data/pop4.wav
Normal file
BIN
graphics/sdl2/sdl2-demo/romfs/data/pop4.wav
Normal file
Binary file not shown.
BIN
graphics/sdl2/sdl2-demo/romfs/data/sdl.png
Normal file
BIN
graphics/sdl2/sdl2-demo/romfs/data/sdl.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.2 KiB |
BIN
graphics/sdl2/sdl2-demo/romfs/data/switch.png
Normal file
BIN
graphics/sdl2/sdl2-demo/romfs/data/switch.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
259
graphics/sdl2/sdl2-demo/source/main.c
Normal file
259
graphics/sdl2/sdl2-demo/source/main.c
Normal file
@ -0,0 +1,259 @@
|
||||
/* Mini SDL Demo
|
||||
* featuring SDL2 + SDL2_mixer + SDL2_image + SDL2_ttf
|
||||
* on Nintendo Switch using libnx
|
||||
*
|
||||
* Copyright 2018 carsten1ns
|
||||
* 2020 WinterMute
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <SDL.h>
|
||||
#include <SDL_mixer.h>
|
||||
#include <SDL_image.h>
|
||||
#include <SDL_ttf.h>
|
||||
#include <switch.h>
|
||||
|
||||
// some switch buttons
|
||||
#define JOY_A 0
|
||||
#define JOY_B 1
|
||||
#define JOY_X 2
|
||||
#define JOY_Y 3
|
||||
#define JOY_PLUS 10
|
||||
#define JOY_MINUS 11
|
||||
#define JOY_LEFT 12
|
||||
#define JOY_UP 13
|
||||
#define JOY_RIGHT 14
|
||||
#define JOY_DOWN 15
|
||||
|
||||
#define SCREEN_W 1280
|
||||
#define SCREEN_H 720
|
||||
|
||||
SDL_Texture * render_text(SDL_Renderer *renderer, const char* text, TTF_Font *font, SDL_Color color, SDL_Rect *rect)
|
||||
{
|
||||
SDL_Surface *surface;
|
||||
SDL_Texture *texture;
|
||||
|
||||
surface = TTF_RenderText_Solid(font, text, color);
|
||||
texture = SDL_CreateTextureFromSurface(renderer, surface);
|
||||
rect->w = surface->w;
|
||||
rect->h = surface->h;
|
||||
|
||||
SDL_FreeSurface(surface);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
int rand_range(int min, int max){
|
||||
return min + rand() / (RAND_MAX / (max - min + 1) + 1);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
romfsInit();
|
||||
chdir("romfs:/");
|
||||
|
||||
int exit_requested = 0;
|
||||
int trail = 0;
|
||||
int wait = 25;
|
||||
|
||||
SDL_Texture *switchlogo_tex = NULL, *sdllogo_tex = NULL, *helloworld_tex = NULL;
|
||||
SDL_Rect pos = { 0, 0, 0, 0 }, sdl_pos = { 0, 0, 0, 0 };
|
||||
Mix_Music *music = NULL;
|
||||
Mix_Chunk *sound[4] = { NULL };
|
||||
SDL_Event event;
|
||||
|
||||
SDL_Color colors[] = {
|
||||
{ 128, 128, 128, 0 }, // gray
|
||||
{ 255, 255, 255, 0 }, // white
|
||||
{ 255, 0, 0, 0 }, // red
|
||||
{ 0, 255, 0, 0 }, // green
|
||||
{ 0, 0, 255, 0 }, // blue
|
||||
{ 255, 255, 0, 0 }, // brown
|
||||
{ 0, 255, 255, 0 }, // cyan
|
||||
{ 255, 0, 255, 0 }, // purple
|
||||
};
|
||||
int col = 0, snd = 0;
|
||||
|
||||
srand(time(NULL));
|
||||
int vel_x = rand_range(1, 5);
|
||||
int vel_y = rand_range(1, 5);
|
||||
|
||||
SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER);
|
||||
Mix_Init(MIX_INIT_OGG);
|
||||
IMG_Init(IMG_INIT_PNG);
|
||||
TTF_Init();
|
||||
|
||||
SDL_Window* window = SDL_CreateWindow("sdl2+mixer+image+ttf demo", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SCREEN_W, SCREEN_H, SDL_WINDOW_SHOWN);
|
||||
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
|
||||
|
||||
// load logos from file
|
||||
SDL_Surface *sdllogo = IMG_Load("data/sdl.png");
|
||||
if (sdllogo) {
|
||||
sdl_pos.w = sdllogo->w;
|
||||
sdl_pos.h = sdllogo->h;
|
||||
sdllogo_tex = SDL_CreateTextureFromSurface(renderer, sdllogo);
|
||||
SDL_FreeSurface(sdllogo);
|
||||
}
|
||||
|
||||
SDL_Surface *switchlogo = IMG_Load("data/switch.png");
|
||||
if (switchlogo) {
|
||||
pos.x = SCREEN_W / 2 - switchlogo->w / 2;
|
||||
pos.y = SCREEN_H / 2 - switchlogo->h / 2;
|
||||
pos.w = switchlogo->w;
|
||||
pos.h = switchlogo->h;
|
||||
switchlogo_tex = SDL_CreateTextureFromSurface(renderer, switchlogo);
|
||||
SDL_FreeSurface(switchlogo);
|
||||
}
|
||||
|
||||
col = rand_range(0, 7);
|
||||
|
||||
SDL_InitSubSystem(SDL_INIT_JOYSTICK);
|
||||
SDL_JoystickEventState(SDL_ENABLE);
|
||||
SDL_JoystickOpen(0);
|
||||
|
||||
// load font from romfs
|
||||
TTF_Font* font = TTF_OpenFont("data/LeroyLetteringLightBeta01.ttf", 36);
|
||||
|
||||
// render text as texture
|
||||
SDL_Rect helloworld_rect = { 0, SCREEN_H - 36, 0, 0 };
|
||||
helloworld_tex = render_text(renderer, "Hello, world!", font, colors[1], &helloworld_rect);
|
||||
|
||||
// no need to keep the font loaded
|
||||
TTF_CloseFont(font);
|
||||
|
||||
SDL_InitSubSystem(SDL_INIT_AUDIO);
|
||||
Mix_AllocateChannels(5);
|
||||
Mix_OpenAudio(48000, AUDIO_S16, 2, 4096);
|
||||
|
||||
// load music and sounds from files
|
||||
music = Mix_LoadMUS("data/background.ogg");
|
||||
sound[0] = Mix_LoadWAV("data/pop1.wav");
|
||||
sound[1] = Mix_LoadWAV("data/pop2.wav");
|
||||
sound[2] = Mix_LoadWAV("data/pop3.wav");
|
||||
sound[3] = Mix_LoadWAV("data/pop4.wav");
|
||||
if (music)
|
||||
Mix_PlayMusic(music, -1);
|
||||
|
||||
while (!exit_requested
|
||||
&& appletMainLoop()
|
||||
) {
|
||||
while (SDL_PollEvent(&event)) {
|
||||
if (event.type == SDL_QUIT)
|
||||
exit_requested = 1;
|
||||
|
||||
// use joystick
|
||||
if (event.type == SDL_JOYBUTTONDOWN) {
|
||||
if (event.jbutton.button == JOY_UP)
|
||||
if (wait > 0)
|
||||
wait--;
|
||||
if (event.jbutton.button == JOY_DOWN)
|
||||
if (wait < 100)
|
||||
wait++;
|
||||
|
||||
if (event.jbutton.button == JOY_PLUS)
|
||||
exit_requested = 1;
|
||||
|
||||
if (event.jbutton.button == JOY_B)
|
||||
trail =! trail;
|
||||
}
|
||||
}
|
||||
|
||||
// set position and bounce on the walls
|
||||
pos.y += vel_y;
|
||||
pos.x += vel_x;
|
||||
if (pos.x + pos.w > SCREEN_W) {
|
||||
pos.x = SCREEN_W - pos.w;
|
||||
vel_x = -rand_range(1, 5);
|
||||
col = rand_range(0, 4);
|
||||
snd = rand_range(0, 3);
|
||||
if (sound[snd])
|
||||
Mix_PlayChannel(-1, sound[snd], 0);
|
||||
}
|
||||
if (pos.x < 0) {
|
||||
pos.x = 0;
|
||||
vel_x = rand_range(1, 5);
|
||||
col = rand_range(0, 4);
|
||||
snd = rand_range(0, 3);
|
||||
if (sound[snd])
|
||||
Mix_PlayChannel(-1, sound[snd], 0);
|
||||
}
|
||||
if (pos.y + pos.h > SCREEN_H) {
|
||||
pos.y = SCREEN_H - pos.h;
|
||||
vel_y = -rand_range(1, 5);
|
||||
col = rand_range(0, 4);
|
||||
snd = rand_range(0, 3);
|
||||
if (sound[snd])
|
||||
Mix_PlayChannel(-1, sound[snd], 0);
|
||||
}
|
||||
if (pos.y < 0) {
|
||||
pos.y = 0;
|
||||
vel_y = rand_range(1, 5);
|
||||
col = rand_range(0, 4);
|
||||
snd = rand_range(0, 3);
|
||||
if (sound[snd])
|
||||
Mix_PlayChannel(-1, sound[snd], 0);
|
||||
}
|
||||
|
||||
if (!trail) {
|
||||
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0xFF);
|
||||
SDL_RenderClear(renderer);
|
||||
}
|
||||
|
||||
// put logos on screen
|
||||
if (sdllogo_tex)
|
||||
SDL_RenderCopy(renderer, sdllogo_tex, NULL, &sdl_pos);
|
||||
if (switchlogo_tex) {
|
||||
SDL_SetTextureColorMod(switchlogo_tex, colors[col].r, colors[col].g, colors[col].b);
|
||||
SDL_RenderCopy(renderer, switchlogo_tex, NULL, &pos);
|
||||
}
|
||||
|
||||
// put text on screen
|
||||
if (helloworld_tex)
|
||||
SDL_RenderCopy(renderer, helloworld_tex, NULL, &helloworld_rect);
|
||||
|
||||
SDL_RenderPresent(renderer);
|
||||
|
||||
SDL_Delay(wait);
|
||||
}
|
||||
|
||||
// clean up your textures when you are done with them
|
||||
if (sdllogo_tex)
|
||||
SDL_DestroyTexture(sdllogo_tex);
|
||||
|
||||
if (switchlogo_tex)
|
||||
SDL_DestroyTexture(switchlogo_tex);
|
||||
|
||||
if (helloworld_tex)
|
||||
SDL_DestroyTexture(helloworld_tex);
|
||||
|
||||
// stop sounds and free loaded data
|
||||
Mix_HaltChannel(-1);
|
||||
Mix_FreeMusic(music);
|
||||
for (snd = 0; snd < 4; snd++)
|
||||
if (sound[snd])
|
||||
Mix_FreeChunk(sound[snd]);
|
||||
|
||||
IMG_Quit();
|
||||
Mix_CloseAudio();
|
||||
TTF_Quit();
|
||||
Mix_Quit();
|
||||
SDL_Quit();
|
||||
romfsExit();
|
||||
return 0;
|
||||
}
|
@ -18,6 +18,8 @@
|
||||
|
||||
static u32 framebuf_width=0;
|
||||
|
||||
static PadState pad;
|
||||
|
||||
//Note that this doesn't handle any blending.
|
||||
void draw_glyph(FT_Bitmap* bitmap, u32* framebuf, u32 x, u32 y)
|
||||
{
|
||||
@ -102,8 +104,8 @@ static int error_screen(const char* fmt, ...)
|
||||
printf("Press PLUS to exit\n");
|
||||
while (appletMainLoop())
|
||||
{
|
||||
hidScanInput();
|
||||
if (hidKeysDown(CONTROLLER_P1_AUTO) & KEY_PLUS)
|
||||
padUpdate(&pad);
|
||||
if (padGetButtonsDown(&pad) & HidNpadButton_Plus)
|
||||
break;
|
||||
consoleUpdate(NULL);
|
||||
}
|
||||
@ -132,9 +134,9 @@ void userAppInit(void)
|
||||
{
|
||||
Result rc;
|
||||
|
||||
rc = plInitialize();
|
||||
rc = plInitialize(PlServiceType_User);
|
||||
if (R_FAILED(rc))
|
||||
fatalSimple(rc);
|
||||
diagAbortWithResult(rc);
|
||||
|
||||
LanguageCode = getSystemLanguage();
|
||||
}
|
||||
@ -149,6 +151,12 @@ int main(int argc, char **argv)
|
||||
Result rc=0;
|
||||
FT_Error ret=0;
|
||||
|
||||
// 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)
|
||||
padInitializeDefault(&pad);
|
||||
|
||||
//Use this when using multiple shared-fonts.
|
||||
/*
|
||||
PlFontData fonts[PlSharedFontType_Total];
|
||||
@ -198,13 +206,13 @@ int main(int argc, char **argv)
|
||||
|
||||
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)
|
||||
u32 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
||||
// padGetButtonsDown returns the set of buttons that have been newly pressed in this frame compared to the previous one
|
||||
u32 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
|
||||
|
||||
u32 stride;
|
||||
u32* framebuf = (u32*)framebufferBegin(&fb, &stride);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user