mirror of
https://github.com/switchbrew/switch-examples.git
synced 2025-07-04 10:32:15 +02:00
Compare commits
No commits in common. "master" and "v20191211" have entirely different histories.
1
.gitignore
vendored
1
.gitignore
vendored
@ -8,5 +8,4 @@ build
|
||||
*.nsp
|
||||
*.npdm
|
||||
*.bz2
|
||||
*.dksh
|
||||
*~
|
||||
|
14
Makefile
14
Makefile
@ -1,19 +1,13 @@
|
||||
MAKEFILES := $(subst ./,,$(shell find . -mindepth 2 -name Makefile))
|
||||
|
||||
TARGETS := $(dir $(MAKEFILES))
|
||||
MAKEFILES := $(shell find . -mindepth 2 -name Makefile)
|
||||
|
||||
DATESTRING := $(shell date +%Y)$(shell date +%m)$(shell date +%d)
|
||||
|
||||
all: $(TARGETS)
|
||||
|
||||
.PHONY: $(TARGETS)
|
||||
|
||||
$(TARGETS):
|
||||
@$(MAKE) -C $@
|
||||
all:
|
||||
@for i in $(MAKEFILES); do $(MAKE) -C `dirname $$i` || exit 1; done;
|
||||
|
||||
clean:
|
||||
@rm -f *.bz2
|
||||
@for i in $(TARGETS); do $(MAKE) -C $$i clean || exit 1; done;
|
||||
@for i in $(MAKEFILES); do $(MAKE) -C `dirname $$i` clean || exit 1; done;
|
||||
|
||||
dist: clean
|
||||
@tar -cvjf switch-examples-$(DATESTRING).tar.bz2 *
|
||||
|
@ -18,13 +18,6 @@ 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);
|
||||
|
||||
memset(&userdata, 0, sizeof(userdata));
|
||||
memset(&profilebase, 0, sizeof(profilebase));
|
||||
|
||||
@ -37,17 +30,7 @@ int main(int argc, char **argv)
|
||||
rc = accountGetPreselectedUser(&userID);
|
||||
|
||||
if (R_FAILED(rc)) {
|
||||
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);
|
||||
}
|
||||
printf("accountGetPreselectedUser() failed: 0x%x\n", rc);
|
||||
}
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
@ -85,13 +68,13 @@ int main(int argc, char **argv)
|
||||
// Main loop
|
||||
while(appletMainLoop())
|
||||
{
|
||||
// Scan the gamepad. This should be done once for each frame
|
||||
padUpdate(&pad);
|
||||
//Scan all the inputs. This should be done once for each frame
|
||||
hidScanInput();
|
||||
|
||||
// padGetButtonsDown returns the set of buttons that have been newly pressed in this frame compared to the previous one
|
||||
u64 kDown = padGetButtonsDown(&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);
|
||||
|
||||
if (kDown & HidNpadButton_Plus) break; // break in order to return to hbmenu
|
||||
if (kDown & KEY_PLUS) break; // break in order to return to hbmenu
|
||||
|
||||
consoleUpdate(NULL);
|
||||
}
|
||||
|
@ -37,13 +37,6 @@ 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("alarm-notifications example\n");
|
||||
|
||||
Result rc=0, rc2=0;
|
||||
@ -82,14 +75,14 @@ int main(int argc, char* argv[])
|
||||
// Main loop
|
||||
while (appletMainLoop())
|
||||
{
|
||||
// Scan the gamepad. This should be done once for each frame
|
||||
padUpdate(&pad);
|
||||
// Scan all the inputs. This should be done once for each frame
|
||||
hidScanInput();
|
||||
|
||||
// padGetButtonsDown returns the set of buttons that have been
|
||||
// newly pressed in this frame compared to the previous one
|
||||
u64 kDown = padGetButtonsDown(&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);
|
||||
|
||||
if (kDown & HidNpadButton_Plus)
|
||||
if (kDown & KEY_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.
|
||||
@ -103,7 +96,7 @@ int main(int argc, char* argv[])
|
||||
}
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
if (kDown & HidNpadButton_A) {
|
||||
if (kDown & KEY_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.
|
||||
|
||||
@ -128,7 +121,7 @@ int main(int argc, char* argv[])
|
||||
if (R_SUCCEEDED(rc)) printf("alarm_setting_id = 0x%x\n", alarm_setting_id);
|
||||
}
|
||||
}
|
||||
else if (kDown & HidNpadButton_B) {
|
||||
else if (kDown & KEY_B) {
|
||||
// List the Alarms.
|
||||
|
||||
total_out=0;
|
||||
@ -167,8 +160,8 @@ int main(int argc, char* argv[])
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ((kDown & HidNpadButton_X) && alarm_setting_id) {
|
||||
// Delete the AlarmSetting which was registered with the HidNpadButton_A block.
|
||||
else if ((kDown & KEY_X) && alarm_setting_id) {
|
||||
// Delete the AlarmSetting which was registered with the KEY_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);
|
||||
|
@ -19,13 +19,6 @@ 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);
|
||||
|
||||
buf = (NsApplicationControlData*)malloc(sizeof(NsApplicationControlData));
|
||||
if (buf==NULL) {
|
||||
rc = MAKERESULT(Module_Libnx, LibnxError_OutOfMemory);
|
||||
@ -76,13 +69,13 @@ int main(int argc, char **argv)
|
||||
// Main loop
|
||||
while(appletMainLoop())
|
||||
{
|
||||
// Scan the gamepad. This should be done once for each frame
|
||||
padUpdate(&pad);
|
||||
//Scan all the inputs. This should be done once for each frame
|
||||
hidScanInput();
|
||||
|
||||
// padGetButtonsDown returns the set of buttons that have been newly pressed in this frame compared to the previous one
|
||||
u64 kDown = padGetButtonsDown(&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);
|
||||
|
||||
if (kDown & HidNpadButton_Plus) break; // break in order to return to hbmenu
|
||||
if (kDown & KEY_PLUS) break; // break in order to return to hbmenu
|
||||
|
||||
consoleUpdate(NULL);
|
||||
}
|
||||
|
@ -20,13 +20,6 @@ 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;
|
||||
@ -60,17 +53,17 @@ int main(int argc, char* argv[])
|
||||
// Main loop
|
||||
while (appletMainLoop())
|
||||
{
|
||||
// Scan the gamepad. This should be done once for each frame
|
||||
padUpdate(&pad);
|
||||
// Scan all the inputs. This should be done once for each frame
|
||||
hidScanInput();
|
||||
|
||||
// padGetButtonsDown returns the set of buttons that have been
|
||||
// newly pressed in this frame compared to the previous one
|
||||
u64 kDown = padGetButtonsDown(&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);
|
||||
|
||||
if (kDown & HidNpadButton_Plus)
|
||||
if (kDown & KEY_PLUS)
|
||||
break; // break in order to return to hbmenu
|
||||
|
||||
if (kDown & HidNpadButton_A) {
|
||||
if (kDown & KEY_A) {
|
||||
// Use appletQueryApplicationPlayStatisticsByUid if you want playstats for a specific userID.
|
||||
|
||||
memset(stats, 0, sizeof(stats));
|
||||
@ -86,46 +79,46 @@ int main(int argc, char* argv[])
|
||||
printf("total_out: %d\n", total_out);
|
||||
for (i=0; i<total_out; i++) {
|
||||
printf("%d: ", i);
|
||||
printf("application_id = 0x%08lX, playtime = %lu (%lu seconds), total_launches = %lu\n", stats[i].application_id, stats[i].playtime, stats[i].playtime / 1000000000UL, stats[i].total_launches);
|
||||
printf("application_id = 0x%08lX, totalPlayTime = %lu (%llu seconds), totalLaunches = %lu\n", stats[i].application_id, stats[i].totalPlayTime, stats[i].totalPlayTime / 1000000000ULL, stats[i].totalLaunches);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (initflag && (kDown & HidNpadButton_X)) {
|
||||
if (initflag && (kDown & KEY_X)) {
|
||||
memset(events, 0, sizeof(events));
|
||||
total_out = 0;
|
||||
|
||||
// Get PdmAppletEvents.
|
||||
rc = pdmqryQueryAppletEvent(false, 0, events, sizeof(events)/sizeof(PdmAppletEvent), &total_out);
|
||||
rc = pdmqryQueryAppletEvent(0, events, sizeof(events)/sizeof(PdmAppletEvent), &total_out);
|
||||
printf("pdmqryQueryAppletEvent(): 0x%x\n", rc);
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
printf("total_out: %d\n", total_out);
|
||||
for (i=0; i<total_out; i++) {
|
||||
time_t tmptime = events[i].timestamp_user;
|
||||
time_t tmptime = pdmPlayTimestampToPosix(events[i].timestampUser);
|
||||
|
||||
printf("%d: ", i);
|
||||
printf("program_id = 0x%08lX, entry_index = 0x%x, timestamp_user = %lu, timestamp_network = %lu, event_type = %u, timestamp_user = %s\n", events[i].program_id, events[i].entry_index, events[i].timestamp_user, events[i].timestamp_network, events[i].event_type, ctime(&tmptime));
|
||||
printf("program_id = 0x%08lX, entry_index = 0x%x, timestampUser = %u, timestampNetwork = %u, eventType = %u, timestampUser = %s\n", events[i].program_id, events[i].entry_index, events[i].timestampUser, events[i].timestampNetwork, events[i].eventType, ctime(&tmptime));
|
||||
}
|
||||
}
|
||||
|
||||
// Get PdmPlayStatistics for the specified ApplicationId.
|
||||
PdmPlayStatistics playstats[1]={0};
|
||||
rc = pdmqryQueryPlayStatisticsByApplicationId(application_ids[0], false, &playstats[0]);
|
||||
rc = pdmqryQueryPlayStatisticsByApplicationId(application_ids[0], &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);
|
||||
if (R_SUCCEEDED(rc)) printf("application_id = 0x%08lX, playtimeMinutes = %u, totalLaunches = %u\n", playstats[0].application_id, playstats[0].playtimeMinutes, playstats[0].totalLaunches);
|
||||
|
||||
// Get PdmPlayStatistics for the specified ApplicationId and user.
|
||||
rc = pdmqryQueryPlayStatisticsByApplicationIdAndUserAccountId(application_ids[0], preselected_uid, false, &playstats[0]);
|
||||
rc = pdmqryQueryPlayStatisticsByApplicationIdAndUserAccountId(application_ids[0], preselected_uid, &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);
|
||||
if (R_SUCCEEDED(rc)) printf("application_id = 0x%08lX, playtimeMinutes = %u, totalLaunches = %u\n", playstats[0].application_id, playstats[0].playtimeMinutes, playstats[0].totalLaunches);
|
||||
|
||||
// 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);
|
||||
rc = pdmqryQueryLastPlayTime(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));
|
||||
printf("%d: timestampUser = %lu\n", i, pdmPlayTimestampToPosix(playtimes[i].timestampUser));
|
||||
}
|
||||
|
||||
// Get the available range for reading events, see pdm.h.
|
||||
@ -133,17 +126,12 @@ int main(int argc, char* argv[])
|
||||
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);
|
||||
// Get a listing of applications recently played by the specified user.
|
||||
rc = pdmqryQueryRecentlyPlayedApplication(preselected_uid, application_ids, 1, &total_out);
|
||||
printf("pdmqryQueryRecentlyPlayedApplication(): 0x%x, %d\n", rc, total_out);
|
||||
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 (i=0; i<total_out; i++)
|
||||
printf("%d: application_id = 0x%08lX\n", i, application_ids[i]);
|
||||
}
|
||||
|
||||
// For more cmds, see pdm.h.
|
||||
|
@ -24,13 +24,6 @@ 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);
|
||||
@ -41,17 +34,17 @@ int main(int argc, char* argv[])
|
||||
// Main loop
|
||||
while (appletMainLoop())
|
||||
{
|
||||
// Scan the gamepad. This should be done once for each frame
|
||||
padUpdate(&pad);
|
||||
// Scan all the inputs. This should be done once for each frame
|
||||
hidScanInput();
|
||||
|
||||
// padGetButtonsDown returns the set of buttons that have been
|
||||
// newly pressed in this frame compared to the previous one
|
||||
u64 kDown = padGetButtonsDown(&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);
|
||||
|
||||
if (kDown & HidNpadButton_Plus)
|
||||
if (kDown & KEY_PLUS)
|
||||
break; // break in order to return to hbmenu
|
||||
|
||||
if (kDown & HidNpadButton_A) {
|
||||
if (kDown & KEY_A) {
|
||||
printf("Running errorResultRecordShow()...\n");
|
||||
consoleUpdate(NULL);
|
||||
rc = errorResultRecordShow(MAKERESULT(16, 250), time(NULL));
|
||||
|
@ -23,13 +23,6 @@ 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);
|
||||
|
||||
@ -54,17 +47,17 @@ int main(int argc, char* argv[])
|
||||
// Main loop
|
||||
while (appletMainLoop())
|
||||
{
|
||||
// Scan the gamepad. This should be done once for each frame
|
||||
padUpdate(&pad);
|
||||
// Scan all the inputs. This should be done once for each frame
|
||||
hidScanInput();
|
||||
|
||||
// padGetButtonsDown returns the set of buttons that have been
|
||||
// newly pressed in this frame compared to the previous one
|
||||
u64 kDown = padGetButtonsDown(&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);
|
||||
|
||||
if (kDown & HidNpadButton_Plus)
|
||||
if (kDown & KEY_PLUS)
|
||||
break; // break in order to return to hbmenu
|
||||
|
||||
if (pctlflag && (kDown & HidNpadButton_A)) {
|
||||
if (pctlflag && (kDown & KEY_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 -lm
|
||||
LIBS := -lnx
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level containing
|
||||
|
@ -57,13 +57,6 @@ 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);
|
||||
@ -114,7 +107,6 @@ 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);
|
||||
@ -149,21 +141,21 @@ int main(int argc, char* argv[])
|
||||
// Main loop
|
||||
while (appletMainLoop())
|
||||
{
|
||||
// Scan the gamepad. This should be done once for each frame
|
||||
padUpdate(&pad);
|
||||
// Scan all the inputs. This should be done once for each frame
|
||||
hidScanInput();
|
||||
|
||||
// padGetButtonsDown returns the set of buttons that have been
|
||||
// newly pressed in this frame compared to the previous one
|
||||
u64 kDown = padGetButtonsDown(&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);
|
||||
|
||||
if (kDown & HidNpadButton_Plus)
|
||||
if (kDown & KEY_PLUS)
|
||||
break; // break in order to return to hbmenu
|
||||
|
||||
// Your code goes here
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
if (kDown & HidNpadButton_ZR) swkbdInlineDisappear(&kbdinline); //Optional, you can have swkbd (dis)appear whenever.
|
||||
if (kDown & HidNpadButton_Y) swkbdInlineAppear(&kbdinline, &appearArg); // If you use swkbdInlineAppear again after text was submitted, you may want to use swkbdInlineSetInputText since the current-text will be the same as when it was submitted otherwise.
|
||||
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.
|
||||
|
||||
rc = swkbdInlineUpdate(&kbdinline, NULL); // Handles updating SwkbdInline state, this should be called periodically.
|
||||
if (R_FAILED(rc)) printf("swkbdInlineUpdate(): 0x%x\n", rc);
|
||||
|
@ -23,13 +23,6 @@ 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);
|
||||
@ -40,17 +33,17 @@ int main(int argc, char* argv[])
|
||||
// Main loop
|
||||
while (appletMainLoop())
|
||||
{
|
||||
// Scan the gamepad. This should be done once for each frame
|
||||
padUpdate(&pad);
|
||||
// Scan all the inputs. This should be done once for each frame
|
||||
hidScanInput();
|
||||
|
||||
// padGetButtonsDown returns the set of buttons that have been
|
||||
// newly pressed in this frame compared to the previous one
|
||||
u64 kDown = padGetButtonsDown(&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);
|
||||
|
||||
if (kDown & HidNpadButton_Plus)
|
||||
if (kDown & KEY_PLUS)
|
||||
break; // break in order to return to hbmenu
|
||||
|
||||
if (kDown & HidNpadButton_A) {
|
||||
if (kDown & KEY_A) {
|
||||
WebCommonConfig config;
|
||||
WebCommonReply reply;
|
||||
WebExitReason exitReason=0;
|
||||
|
@ -18,13 +18,6 @@ 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");
|
||||
@ -32,17 +25,17 @@ int main(int argc, char* argv[])
|
||||
// Main loop
|
||||
while (appletMainLoop())
|
||||
{
|
||||
// Scan the gamepad. This should be done once for each frame
|
||||
padUpdate(&pad);
|
||||
// Scan all the inputs. This should be done once for each frame
|
||||
hidScanInput();
|
||||
|
||||
// padGetButtonsDown returns the set of buttons that have been
|
||||
// newly pressed in this frame compared to the previous one
|
||||
u64 kDown = padGetButtonsDown(&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);
|
||||
|
||||
if (kDown & HidNpadButton_Plus)
|
||||
if (kDown & KEY_PLUS)
|
||||
break; // break in order to return to hbmenu
|
||||
|
||||
if (kDown & HidNpadButton_A) {
|
||||
if (kDown & KEY_A) {
|
||||
Result rc = 0;
|
||||
float fLux=0;
|
||||
bool availableflag=0;
|
||||
|
@ -22,26 +22,19 @@ 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 the gamepad. This should be done once for each frame
|
||||
padUpdate(&pad);
|
||||
// Scan all the inputs. This should be done once for each frame
|
||||
hidScanInput();
|
||||
|
||||
// padGetButtonsDown returns the set of buttons that have been
|
||||
// newly pressed in this frame compared to the previous one
|
||||
u64 kDown = padGetButtonsDown(&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);
|
||||
|
||||
if (kDown & HidNpadButton_Plus)
|
||||
if (kDown & KEY_PLUS)
|
||||
break; // break in order to return to hbmenu
|
||||
|
||||
// Your code goes here
|
||||
|
@ -28,29 +28,22 @@ 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 the gamepad. This should be done once for each frame
|
||||
padUpdate(&pad);
|
||||
//Scan all the inputs. This should be done once for each frame
|
||||
hidScanInput();
|
||||
|
||||
// padGetButtonsDown returns the set of buttons that have been newly pressed in this frame compared to the previous one
|
||||
u64 kDown = padGetButtonsDown(&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);
|
||||
|
||||
if (kDown & HidNpadButton_Plus) break; // break in order to return to hbmenu
|
||||
if (kDown & KEY_PLUS) break; // break in order to return to hbmenu
|
||||
|
||||
if (kDown & HidNpadButton_Y) {
|
||||
if (kDown & KEY_Y) {
|
||||
appletSetGamePlayRecordingState(0);//Disable recording.
|
||||
}
|
||||
else if (kDown & HidNpadButton_X) {
|
||||
else if (kDown & KEY_X) {
|
||||
appletSetGamePlayRecordingState(1);//Enable recording.
|
||||
}
|
||||
|
||||
|
@ -18,13 +18,6 @@ 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.
|
||||
@ -63,14 +56,14 @@ int main(int argc, char* argv[])
|
||||
// Main loop
|
||||
while (appletMainLoop())
|
||||
{
|
||||
// Scan the gamepad. This should be done once for each frame
|
||||
padUpdate(&pad);
|
||||
// Scan all the inputs. This should be done once for each frame
|
||||
hidScanInput();
|
||||
|
||||
// padGetButtonsDown returns the set of buttons that have been
|
||||
// newly pressed in this frame compared to the previous one
|
||||
u64 kDown = padGetButtonsDown(&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);
|
||||
|
||||
if (kDown & HidNpadButton_Plus)
|
||||
if (kDown & KEY_PLUS)
|
||||
break; // break in order to return to hbmenu
|
||||
|
||||
// Update the console, sending a new frame to the display
|
||||
|
@ -24,13 +24,6 @@ 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.
|
||||
@ -50,18 +43,18 @@ int main(int argc, char* argv[])
|
||||
// Main loop
|
||||
while (appletMainLoop())
|
||||
{
|
||||
// Scan the gamepad. This should be done once for each frame
|
||||
padUpdate(&pad);
|
||||
// Scan all the inputs. This should be done once for each frame
|
||||
hidScanInput();
|
||||
|
||||
// padGetButtonsDown returns the set of buttons that have been
|
||||
// newly pressed in this frame compared to the previous one
|
||||
u64 kDown = padGetButtonsDown(&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);
|
||||
|
||||
if (kDown & HidNpadButton_Plus)
|
||||
if (kDown & KEY_PLUS)
|
||||
break; // break in order to return to hbmenu
|
||||
|
||||
if (pctlinit) {
|
||||
if (kDown & HidNpadButton_A) {
|
||||
if (kDown & KEY_A) {
|
||||
// Not sure what this is.
|
||||
/*rc = pctlConfirmStereoVisionPermission();
|
||||
if (R_FAILED(rc)) printf("pctlConfirmStereoVisionPermission(): 0x%x\n", rc);*/
|
||||
@ -77,7 +70,7 @@ int main(int argc, char* argv[])
|
||||
printf("appletSetVrModeEnabled(true): 0x%x\n", rc);
|
||||
}
|
||||
}
|
||||
else if (kDown & HidNpadButton_B) {
|
||||
else if (kDown & KEY_B) {
|
||||
rc = appletSetVrModeEnabled(false);
|
||||
printf("appletSetVrModeEnabled(false): 0x%x\n", rc);
|
||||
|
||||
@ -85,7 +78,7 @@ int main(int argc, char* argv[])
|
||||
/*rc = pctlResetConfirmedStereoVisionPermission();
|
||||
printf("pctlResetConfirmedStereoVisionPermission(): 0x%x\n", rc);*/
|
||||
}
|
||||
else if (kDown & HidNpadButton_X) {
|
||||
else if (kDown & KEY_X) {
|
||||
bool flag=0;
|
||||
rc = appletIsVrModeEnabled(&flag);
|
||||
printf("appletIsVrModeEnabled(): 0x%x", rc);
|
||||
|
@ -13,13 +13,6 @@ 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 =
|
||||
@ -64,7 +57,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);
|
||||
@ -84,16 +77,16 @@ int main(void)
|
||||
// Main loop
|
||||
while (appletMainLoop())
|
||||
{
|
||||
padUpdate(&pad);
|
||||
hidScanInput();
|
||||
|
||||
u64 kDown = padGetButtonsDown(&pad);
|
||||
u64 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
||||
|
||||
if (kDown & HidNpadButton_Plus)
|
||||
if (kDown & KEY_PLUS)
|
||||
break;
|
||||
|
||||
if (initedDriver)
|
||||
{
|
||||
if (kDown & HidNpadButton_A)
|
||||
if (kDown & KEY_A)
|
||||
{
|
||||
audrvVoiceStop(&drv, 0);
|
||||
audrvVoiceAddWaveBuf(&drv, 0, &wavebuf);
|
||||
|
@ -22,13 +22,6 @@ 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;
|
||||
@ -115,17 +108,17 @@ int main(int argc, char **argv)
|
||||
|
||||
while (appletMainLoop())
|
||||
{
|
||||
// Scan the gamepad. This should be done once for each frame
|
||||
padUpdate(&pad);
|
||||
//Scan all the inputs. This should be done once for each frame
|
||||
hidScanInput();
|
||||
|
||||
// padGetButtonsDown returns the set of buttons that have been newly pressed in this frame compared to the previous one
|
||||
u64 kDown = padGetButtonsDown(&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);
|
||||
|
||||
if (kDown & HidNpadButton_Plus) break; // break in order to return to hbmenu
|
||||
if (kDown & KEY_PLUS) break; // break in order to return to hbmenu
|
||||
|
||||
// Wait for audio capture and playback to finish.
|
||||
audinWaitCaptureFinish(&released_in_buffer, &released_in_count, UINT64_MAX);
|
||||
audoutWaitPlayFinish(&released_out_buffer, &released_out_count, UINT64_MAX);
|
||||
audinWaitCaptureFinish(&released_in_buffer, &released_in_count, U64_MAX);
|
||||
audoutWaitPlayFinish(&released_out_buffer, &released_out_count, U64_MAX);
|
||||
|
||||
// Copy the captured audio data into the playback buffer.
|
||||
if ((released_in_buffer != NULL) && (released_out_buffer != NULL))
|
||||
|
@ -53,13 +53,6 @@ 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 =
|
||||
@ -74,15 +67,15 @@ int main(void)
|
||||
|
||||
size_t num_channels = 1;
|
||||
size_t samplerate = 48000;
|
||||
size_t max_samples = samplerate/20;//each wavebuf can hold upto 1sec/20 = 50ms of audio data
|
||||
size_t max_samples = samplerate;
|
||||
size_t max_samples_datasize = max_samples*num_channels*sizeof(opus_int16);
|
||||
size_t mempool_size = (max_samples_datasize*4 + 0xFFF) &~ 0xFFF;//*4 for 4 wavebufs.
|
||||
size_t mempool_size = (max_samples_datasize*2 + 0xFFF) &~ 0xFFF;//*2 for 2 wavebufs.
|
||||
void* mempool_ptr = memalign(0x1000, mempool_size);
|
||||
void* tmpdata_ptr = malloc(max_samples_datasize);
|
||||
opuspkt_tmpbuf = (u8*)malloc(opuspkt_tmpbuf_size);
|
||||
opus_int16* curbuf = NULL;
|
||||
|
||||
AudioDriverWaveBuf wavebuf[4] = {0};
|
||||
AudioDriverWaveBuf wavebuf[2] = {0};
|
||||
int i, wavei;
|
||||
|
||||
HwopusDecoder hwdecoder = {0};
|
||||
@ -129,7 +122,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);
|
||||
@ -151,9 +144,9 @@ int main(void)
|
||||
}
|
||||
audrvVoiceStart(&drv, 0);
|
||||
|
||||
for(i=0; i<4; i++) {
|
||||
for(i=0; i<2; i++) {
|
||||
wavebuf[i].data_raw = mempool_ptr;
|
||||
wavebuf[i].size = max_samples_datasize*4;//*4 for 4 wavebufs.
|
||||
wavebuf[i].size = max_samples_datasize*2;//*2 for 2 wavebufs.
|
||||
wavebuf[i].start_sample_offset = i * max_samples;
|
||||
wavebuf[i].end_sample_offset = wavebuf[i].start_sample_offset + max_samples;
|
||||
}
|
||||
@ -170,16 +163,16 @@ int main(void)
|
||||
// Main loop
|
||||
while (appletMainLoop())
|
||||
{
|
||||
padUpdate(&pad);
|
||||
hidScanInput();
|
||||
|
||||
u64 kDown = padGetButtonsDown(&pad);
|
||||
u64 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
||||
|
||||
if (kDown & HidNpadButton_Plus)
|
||||
if (kDown & KEY_PLUS)
|
||||
break;
|
||||
|
||||
if (initedDriver)
|
||||
{
|
||||
if (kDown & HidNpadButton_A)
|
||||
if (kDown & KEY_A)
|
||||
{
|
||||
//Close the opus-file if needed and (re)open it, since libopusfile doesn't support seek-to-beginning.
|
||||
if (of)
|
||||
@ -198,7 +191,7 @@ int main(void)
|
||||
|
||||
if (audio_playing) {
|
||||
wavei = -1;
|
||||
for(i=0; i<4; i++) {
|
||||
for(i=0; i<2; i++) {
|
||||
if (wavebuf[i].state == AudioDriverWaveBufState_Free || wavebuf[i].state == AudioDriverWaveBufState_Done) {
|
||||
wavei = i;
|
||||
break;
|
||||
@ -206,7 +199,7 @@ int main(void)
|
||||
}
|
||||
|
||||
if (wavei >= 0) {
|
||||
curbuf = (opus_int16*)(mempool_ptr + wavebuf[wavei].start_sample_offset * sizeof(opus_int16));
|
||||
curbuf = (opus_int16*)(mempool_ptr + wavebuf[wavei].start_sample_offset);
|
||||
|
||||
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,13 +38,6 @@ 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;
|
||||
|
||||
@ -89,81 +82,81 @@ int main(int argc, char **argv)
|
||||
|
||||
while (appletMainLoop())
|
||||
{
|
||||
// Scan the gamepad. This should be done once for each frame
|
||||
padUpdate(&pad);
|
||||
//Scan all the inputs. This should be done once for each frame
|
||||
hidScanInput();
|
||||
|
||||
// padGetButtonsDown returns the set of buttons that have been newly pressed in this frame compared to the previous one
|
||||
u64 kDown = padGetButtonsDown(&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);
|
||||
|
||||
if (kDown & HidNpadButton_Plus) break; // break in order to return to hbmenu
|
||||
if (kDown & KEY_PLUS) break; // break in order to return to hbmenu
|
||||
|
||||
if (kDown & HidNpadButton_A)
|
||||
if (kDown & KEY_A)
|
||||
{
|
||||
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[0]);
|
||||
play_tone = true;
|
||||
}
|
||||
|
||||
if (kDown & HidNpadButton_B)
|
||||
if (kDown & KEY_B)
|
||||
{
|
||||
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[1]);
|
||||
play_tone = true;
|
||||
}
|
||||
|
||||
if (kDown & HidNpadButton_Y)
|
||||
if (kDown & KEY_Y)
|
||||
{
|
||||
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[2]);
|
||||
play_tone = true;
|
||||
}
|
||||
|
||||
if (kDown & HidNpadButton_X)
|
||||
if (kDown & KEY_X)
|
||||
{
|
||||
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[3]);
|
||||
play_tone = true;
|
||||
}
|
||||
|
||||
if (kDown & HidNpadButton_Left)
|
||||
if (kDown & KEY_DLEFT)
|
||||
{
|
||||
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[4]);
|
||||
play_tone = true;
|
||||
}
|
||||
|
||||
if (kDown & HidNpadButton_Up)
|
||||
if (kDown & KEY_DUP)
|
||||
{
|
||||
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[5]);
|
||||
play_tone = true;
|
||||
}
|
||||
|
||||
if (kDown & HidNpadButton_Right)
|
||||
if (kDown & KEY_DRIGHT)
|
||||
{
|
||||
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[6]);
|
||||
play_tone = true;
|
||||
}
|
||||
|
||||
if (kDown & HidNpadButton_Down)
|
||||
if (kDown & KEY_DDOWN)
|
||||
{
|
||||
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[7]);
|
||||
play_tone = true;
|
||||
}
|
||||
|
||||
if (kDown & HidNpadButton_L)
|
||||
if (kDown & KEY_L)
|
||||
{
|
||||
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[8]);
|
||||
play_tone = true;
|
||||
}
|
||||
|
||||
if (kDown & HidNpadButton_R)
|
||||
if (kDown & KEY_R)
|
||||
{
|
||||
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[9]);
|
||||
play_tone = true;
|
||||
}
|
||||
|
||||
if (kDown & HidNpadButton_ZL)
|
||||
if (kDown & KEY_ZL)
|
||||
{
|
||||
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[10]);
|
||||
play_tone = true;
|
||||
}
|
||||
|
||||
if (kDown & HidNpadButton_ZR)
|
||||
if (kDown & KEY_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) `$(PREFIX)pkg-config --cflags SDL2_mixer`
|
||||
CFLAGS += -D__SWITCH__ $(INCLUDE) `aarch64-none-elf-pkg-config --cflags SDL2_mixer`
|
||||
|
||||
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
|
||||
|
||||
ASFLAGS := -g $(ARCH)
|
||||
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
|
||||
|
||||
LIBS := `$(PREFIX)pkg-config --libs SDL2_mixer`
|
||||
LIBS := `aarch64-none-elf-pkg-config --libs SDL2_mixer`
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level containing
|
||||
# include and lib
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBDIRS := $(LIBNX)
|
||||
LIBDIRS := $(PORTLIBS) $(LIBNX)
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
|
@ -7,8 +7,8 @@
|
||||
#include <switch.h>
|
||||
|
||||
// Include sdl2 headers
|
||||
#include <SDL.h>
|
||||
#include <SDL_mixer.h>
|
||||
#include <SDL2/SDL.h>
|
||||
#include <SDL2/SDL_mixer.h>
|
||||
|
||||
// Main program entrypoint
|
||||
int main(int argc, char *argv[])
|
||||
@ -17,13 +17,6 @@ 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);
|
||||
@ -48,17 +41,17 @@ int main(int argc, char *argv[])
|
||||
// Main loop
|
||||
while (appletMainLoop())
|
||||
{
|
||||
// Scan the gamepad. This should be done once for each frame
|
||||
padUpdate(&pad);
|
||||
// Scan all the inputs. This should be done once for each frame
|
||||
hidScanInput();
|
||||
|
||||
// padGetButtonsDown returns the set of buttons that have been
|
||||
// newly pressed in this frame compared to the previous one
|
||||
u64 kDown = padGetButtonsDown(&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);
|
||||
|
||||
if (kDown & HidNpadButton_Plus)
|
||||
if (kDown & KEY_PLUS)
|
||||
break; // break in order to return to hbmenu
|
||||
|
||||
if (kDown & HidNpadButton_A)
|
||||
if (kDown & KEY_A)
|
||||
Mix_PlayMusic(audio, 1); //Play the audio file
|
||||
|
||||
// Update the console, sending a new frame to the display
|
||||
|
@ -47,13 +47,6 @@ 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);
|
||||
|
||||
@ -63,14 +56,14 @@ int main(int argc, char* argv[])
|
||||
// Main loop
|
||||
while (appletMainLoop())
|
||||
{
|
||||
// Scan the gamepad. This should be done once for each frame
|
||||
padUpdate(&pad);
|
||||
// Scan all the inputs. This should be done once for each frame
|
||||
hidScanInput();
|
||||
|
||||
// padGetButtonsDown returns the set of buttons that have been
|
||||
// newly pressed in this frame compared to the previous one
|
||||
u64 kDown = padGetButtonsDown(&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);
|
||||
|
||||
if (kDown & HidNpadButton_Plus)
|
||||
if (kDown & KEY_PLUS)
|
||||
break; // break in order to return to hbmenu
|
||||
|
||||
// Your code goes here
|
||||
|
@ -32,13 +32,6 @@ 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);
|
||||
@ -53,13 +46,13 @@ int main(int argc, char **argv)
|
||||
// Main loop
|
||||
while(appletMainLoop())
|
||||
{
|
||||
// Scan the gamepad. This should be done once for each frame
|
||||
padUpdate(&pad);
|
||||
//Scan all the inputs. This should be done once for each frame
|
||||
hidScanInput();
|
||||
|
||||
// padGetButtonsDown returns the set of buttons that have been newly pressed in this frame compared to the previous one
|
||||
u64 kDown = padGetButtonsDown(&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);
|
||||
|
||||
if (kDown & HidNpadButton_Plus) break; // break in order to return to hbmenu
|
||||
if (kDown & KEY_PLUS) break; // break in order to return to hbmenu
|
||||
|
||||
consoleUpdate(NULL);
|
||||
}
|
||||
|
@ -11,7 +11,6 @@ Result get_save(u64 *application_id, AccountUid *uid) {
|
||||
FsSaveDataInfoReader reader;
|
||||
s64 total_entries=0;
|
||||
FsSaveDataInfo info;
|
||||
bool found=0;
|
||||
|
||||
rc = fsOpenSaveDataInfoReader(&reader, FsSaveDataSpaceId_User);//See libnx fs.h.
|
||||
if (R_FAILED(rc)) {
|
||||
@ -27,14 +26,13 @@ Result get_save(u64 *application_id, AccountUid *uid) {
|
||||
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;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
fsSaveDataInfoReaderClose(&reader);
|
||||
|
||||
if (R_SUCCEEDED(rc) && !found) return MAKERESULT(Module_Libnx, LibnxError_NotFound);
|
||||
if (R_SUCCEEDED(rc)) return MAKERESULT(Module_Libnx, LibnxError_NotFound);
|
||||
|
||||
return rc;
|
||||
}
|
||||
@ -51,13 +49,6 @@ 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);
|
||||
|
||||
//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.
|
||||
@ -120,13 +111,13 @@ int main(int argc, char **argv)
|
||||
// Main loop
|
||||
while(appletMainLoop())
|
||||
{
|
||||
// Scan the gamepad. This should be done once for each frame
|
||||
padUpdate(&pad);
|
||||
//Scan all the inputs. This should be done once for each frame
|
||||
hidScanInput();
|
||||
|
||||
// padGetButtonsDown returns the set of buttons that have been newly pressed in this frame compared to the previous one
|
||||
u64 kDown = padGetButtonsDown(&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);
|
||||
|
||||
if (kDown & HidNpadButton_Plus) break; // break in order to return to hbmenu
|
||||
if (kDown & KEY_PLUS) break; // break in order to return to hbmenu
|
||||
|
||||
consoleUpdate(NULL);
|
||||
}
|
||||
|
@ -11,13 +11,6 @@ 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;
|
||||
|
||||
@ -40,13 +33,13 @@ int main(int argc, char **argv)
|
||||
// Main loop
|
||||
while(appletMainLoop())
|
||||
{
|
||||
// Scan the gamepad. This should be done once for each frame
|
||||
padUpdate(&pad);
|
||||
//Scan all the inputs. This should be done once for each frame
|
||||
hidScanInput();
|
||||
|
||||
// padGetButtonsDown returns the set of buttons that have been newly pressed in this frame compared to the previous one
|
||||
u64 kDown = padGetButtonsDown(&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);
|
||||
|
||||
if (kDown & HidNpadButton_Plus) break; // break in order to return to hbmenu
|
||||
if (kDown & KEY_PLUS) break; // break in order to return to hbmenu
|
||||
|
||||
consoleUpdate(NULL);
|
||||
}
|
||||
|
@ -1,271 +0,0 @@
|
||||
#---------------------------------------------------------------------------------
|
||||
.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
|
||||
#---------------------------------------------------------------------------------------
|
@ -1,9 +0,0 @@
|
||||
#version 460
|
||||
|
||||
layout (location = 0) in vec4 inColor;
|
||||
layout (location = 0) out vec4 outColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
outColor = inColor;
|
||||
}
|
@ -1,233 +0,0 @@
|
||||
#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;
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
#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];
|
||||
}
|
@ -1,271 +0,0 @@
|
||||
#---------------------------------------------------------------------------------
|
||||
.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
|
||||
#---------------------------------------------------------------------------------------
|
@ -1,15 +0,0 @@
|
||||
#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);
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
#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];
|
||||
}
|
@ -1,486 +0,0 @@
|
||||
#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;
|
||||
}
|
@ -1,86 +0,0 @@
|
||||
#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;
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
#include <switch.h>
|
||||
|
||||
void userAppInit(void)
|
||||
{
|
||||
Result res = romfsInit();
|
||||
if (R_FAILED(res))
|
||||
diagAbortWithResult(res);
|
||||
}
|
||||
|
||||
void userAppExit(void)
|
||||
{
|
||||
romfsExit();
|
||||
}
|
@ -1,271 +0,0 @@
|
||||
#---------------------------------------------------------------------------------
|
||||
.SUFFIXES:
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
ifeq ($(strip $(DEVKITPRO)),)
|
||||
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
|
||||
endif
|
||||
|
||||
TOPDIR ?= $(CURDIR)
|
||||
include $(DEVKITPRO)/libnx/switch_rules
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# TARGET is the name of the output
|
||||
# BUILD is the directory where object files & intermediate files will be placed
|
||||
# SOURCES is a list of directories containing source code
|
||||
# DATA is a list of directories containing data files
|
||||
# INCLUDES is a list of directories containing header files
|
||||
# ROMFS is the directory containing data to be added to RomFS, relative to the Makefile (Optional)
|
||||
#
|
||||
# NO_ICON: if set to anything, do not use icon.
|
||||
# NO_NACP: if set to anything, no .nacp file is generated.
|
||||
# APP_TITLE is the name of the app stored in the .nacp file (Optional)
|
||||
# APP_AUTHOR is the author of the app stored in the .nacp file (Optional)
|
||||
# APP_VERSION is the version of the app stored in the .nacp file (Optional)
|
||||
# APP_TITLEID is the titleID of the app stored in the .nacp file (Optional)
|
||||
# ICON is the filename of the icon (.jpg), relative to the project folder.
|
||||
# If not set, it attempts to use one of the following (in this order):
|
||||
# - <Project name>.jpg
|
||||
# - icon.jpg
|
||||
# - <libnx folder>/default_icon.jpg
|
||||
#
|
||||
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
|
||||
# If not set, it attempts to use one of the following (in this order):
|
||||
# - <Project name>.json
|
||||
# - config.json
|
||||
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
|
||||
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
|
||||
# NACP building is skipped as well.
|
||||
#---------------------------------------------------------------------------------
|
||||
TARGET := $(notdir $(CURDIR))
|
||||
BUILD := build
|
||||
SOURCES := source source/SampleFramework
|
||||
DATA := data
|
||||
INCLUDES := include
|
||||
ROMFS := romfs
|
||||
|
||||
# Output folders for autogenerated files in romfs
|
||||
OUT_SHADERS := shaders
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# options for code generation
|
||||
#---------------------------------------------------------------------------------
|
||||
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
|
||||
|
||||
CFLAGS := -g -Wall -O2 -ffunction-sections \
|
||||
$(ARCH) $(DEFINES)
|
||||
|
||||
CFLAGS += $(INCLUDE) -D__SWITCH__
|
||||
|
||||
CXXFLAGS := $(CFLAGS) -std=gnu++17 -fno-exceptions -fno-rtti
|
||||
|
||||
ASFLAGS := -g $(ARCH)
|
||||
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
|
||||
|
||||
LIBS := -ldeko3dd -lnx
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level containing
|
||||
# include and lib
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBDIRS := $(PORTLIBS) $(LIBNX)
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# no real need to edit anything past this point unless you need to add additional
|
||||
# rules for different file extensions
|
||||
#---------------------------------------------------------------------------------
|
||||
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OUTPUT := $(CURDIR)/$(TARGET)
|
||||
export TOPDIR := $(CURDIR)
|
||||
|
||||
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
|
||||
|
||||
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||
|
||||
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
|
||||
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
|
||||
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
||||
GLSLFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.glsl)))
|
||||
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# use CXX for linking C++ projects, CC for standard C
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(CPPFILES)),)
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CC)
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CXX)
|
||||
#---------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
|
||||
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
|
||||
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
|
||||
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
|
||||
|
||||
ifneq ($(strip $(ROMFS)),)
|
||||
ROMFS_TARGETS :=
|
||||
ROMFS_FOLDERS :=
|
||||
ifneq ($(strip $(OUT_SHADERS)),)
|
||||
ROMFS_SHADERS := $(ROMFS)/$(OUT_SHADERS)
|
||||
ROMFS_TARGETS += $(patsubst %.glsl, $(ROMFS_SHADERS)/%.dksh, $(GLSLFILES))
|
||||
ROMFS_FOLDERS += $(ROMFS_SHADERS)
|
||||
endif
|
||||
|
||||
export ROMFS_DEPS := $(foreach file,$(ROMFS_TARGETS),$(CURDIR)/$(file))
|
||||
endif
|
||||
|
||||
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||
-I$(CURDIR)/$(BUILD)
|
||||
|
||||
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||
|
||||
ifeq ($(strip $(CONFIG_JSON)),)
|
||||
jsons := $(wildcard *.json)
|
||||
ifneq (,$(findstring $(TARGET).json,$(jsons)))
|
||||
export APP_JSON := $(TOPDIR)/$(TARGET).json
|
||||
else
|
||||
ifneq (,$(findstring config.json,$(jsons)))
|
||||
export APP_JSON := $(TOPDIR)/config.json
|
||||
endif
|
||||
endif
|
||||
else
|
||||
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(ICON)),)
|
||||
icons := $(wildcard *.jpg)
|
||||
ifneq (,$(findstring $(TARGET).jpg,$(icons)))
|
||||
export APP_ICON := $(TOPDIR)/$(TARGET).jpg
|
||||
else
|
||||
ifneq (,$(findstring icon.jpg,$(icons)))
|
||||
export APP_ICON := $(TOPDIR)/icon.jpg
|
||||
endif
|
||||
endif
|
||||
else
|
||||
export APP_ICON := $(TOPDIR)/$(ICON)
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(NO_ICON)),)
|
||||
export NROFLAGS += --icon=$(APP_ICON)
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(NO_NACP)),)
|
||||
export NROFLAGS += --nacp=$(CURDIR)/$(TARGET).nacp
|
||||
endif
|
||||
|
||||
ifneq ($(APP_TITLEID),)
|
||||
export NACPFLAGS += --titleid=$(APP_TITLEID)
|
||||
endif
|
||||
|
||||
ifneq ($(ROMFS),)
|
||||
export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS)
|
||||
endif
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
all: $(ROMFS_TARGETS) | $(BUILD)
|
||||
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||
|
||||
$(BUILD):
|
||||
@mkdir -p $@
|
||||
|
||||
ifneq ($(strip $(ROMFS_TARGETS)),)
|
||||
|
||||
$(ROMFS_TARGETS): | $(ROMFS_FOLDERS)
|
||||
|
||||
$(ROMFS_FOLDERS):
|
||||
@mkdir -p $@
|
||||
|
||||
$(ROMFS_SHADERS)/%_vsh.dksh: %_vsh.glsl
|
||||
@echo {vert} $(notdir $<)
|
||||
@uam -s vert -o $@ $<
|
||||
|
||||
$(ROMFS_SHADERS)/%_tcsh.dksh: %_tcsh.glsl
|
||||
@echo {tess_ctrl} $(notdir $<)
|
||||
@uam -s tess_ctrl -o $@ $<
|
||||
|
||||
$(ROMFS_SHADERS)/%_tesh.dksh: %_tesh.glsl
|
||||
@echo {tess_eval} $(notdir $<)
|
||||
@uam -s tess_eval -o $@ $<
|
||||
|
||||
$(ROMFS_SHADERS)/%_gsh.dksh: %_gsh.glsl
|
||||
@echo {geom} $(notdir $<)
|
||||
@uam -s geom -o $@ $<
|
||||
|
||||
$(ROMFS_SHADERS)/%_fsh.dksh: %_fsh.glsl
|
||||
@echo {frag} $(notdir $<)
|
||||
@uam -s frag -o $@ $<
|
||||
|
||||
$(ROMFS_SHADERS)/%.dksh: %.glsl
|
||||
@echo {comp} $(notdir $<)
|
||||
@uam -s comp -o $@ $<
|
||||
|
||||
endif
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
clean:
|
||||
@echo clean ...
|
||||
ifeq ($(strip $(APP_JSON)),)
|
||||
@rm -fr $(BUILD) $(ROMFS_FOLDERS) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
|
||||
else
|
||||
@rm -fr $(BUILD) $(ROMFS_FOLDERS) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
|
||||
endif
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
.PHONY: all
|
||||
|
||||
DEPENDS := $(OFILES:.o=.d)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(APP_JSON)),)
|
||||
|
||||
all : $(OUTPUT).nro
|
||||
|
||||
ifeq ($(strip $(NO_NACP)),)
|
||||
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp $(ROMFS_DEPS)
|
||||
else
|
||||
$(OUTPUT).nro : $(OUTPUT).elf $(ROMFS_DEPS)
|
||||
endif
|
||||
|
||||
else
|
||||
|
||||
all : $(OUTPUT).nsp
|
||||
|
||||
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
|
||||
|
||||
$(OUTPUT).nso : $(OUTPUT).elf
|
||||
|
||||
endif
|
||||
|
||||
$(OUTPUT).elf : $(OFILES)
|
||||
|
||||
$(OFILES_SRC) : $(HFILES_BIN)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# you need a rule like this for each extension you use as binary data
|
||||
#---------------------------------------------------------------------------------
|
||||
%.bin.o %_bin.h : %.bin
|
||||
#---------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
-include $(DEPENDS)
|
||||
|
||||
#---------------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------------
|
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 14 KiB |
Binary file not shown.
@ -1,182 +0,0 @@
|
||||
/*
|
||||
** 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();
|
||||
}
|
@ -1,237 +0,0 @@
|
||||
/*
|
||||
** 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();
|
||||
}
|
@ -1,378 +0,0 @@
|
||||
/*
|
||||
** 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();
|
||||
}
|
@ -1,420 +0,0 @@
|
||||
/*
|
||||
** 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();
|
||||
}
|
@ -1,259 +0,0 @@
|
||||
/*
|
||||
** 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();
|
||||
}
|
@ -1,414 +0,0 @@
|
||||
/*
|
||||
** 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();
|
||||
}
|
@ -1,407 +0,0 @@
|
||||
/*
|
||||
** 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();
|
||||
}
|
@ -1,488 +0,0 @@
|
||||
/*
|
||||
** 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();
|
||||
}
|
@ -1,324 +0,0 @@
|
||||
/*
|
||||
** 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();
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
/*
|
||||
** 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;
|
||||
}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
/*
|
||||
** 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;
|
||||
}
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
/*
|
||||
** 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();
|
||||
}
|
||||
};
|
@ -1,76 +0,0 @@
|
||||
/*
|
||||
** 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);
|
||||
}
|
||||
};
|
@ -1,37 +0,0 @@
|
||||
/*
|
||||
** 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;
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
/*
|
||||
** 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);
|
||||
};
|
@ -1,119 +0,0 @@
|
||||
/*
|
||||
** 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);
|
||||
}
|
||||
}
|
||||
};
|
@ -1,214 +0,0 @@
|
||||
/*
|
||||
** 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);
|
||||
}
|
@ -1,230 +0,0 @@
|
||||
/*
|
||||
** 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));
|
||||
}
|
||||
};
|
@ -1,175 +0,0 @@
|
||||
/*
|
||||
** 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);
|
||||
}
|
@ -1,133 +0,0 @@
|
||||
/*
|
||||
** 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();
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
/*
|
||||
** 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;
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
/*
|
||||
** 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);
|
||||
};
|
@ -1,27 +0,0 @@
|
||||
/*
|
||||
** 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;
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
/*
|
||||
** 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);
|
@ -1,18 +0,0 @@
|
||||
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.
|
@ -1,12 +0,0 @@
|
||||
/*
|
||||
** 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>
|
@ -1,35 +0,0 @@
|
||||
/*
|
||||
** 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();
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
#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);
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
#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);
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
#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;
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
#version 460
|
||||
|
||||
layout (location = 0) in vec3 inColor;
|
||||
layout (location = 0) out vec4 outColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
outColor = vec4(inColor, 1.0);
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
#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);
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
#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);
|
||||
}
|
@ -1,143 +0,0 @@
|
||||
/*
|
||||
** 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;
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
#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);
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
#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];
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
#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]);
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
#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);
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
#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;
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
#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 & HidNpadButton_AnyLeft)
|
||||
if (kHeld & KEY_LEFT)
|
||||
s_cameraAngle = fract(s_cameraAngle - deltaTime/4);
|
||||
else if (kHeld & HidNpadButton_AnyRight)
|
||||
else if (kHeld & KEY_RIGHT)
|
||||
s_cameraAngle = fract(s_cameraAngle + deltaTime/4);
|
||||
if (kHeld & (HidNpadButton_AnyUp|HidNpadButton_AnyDown))
|
||||
if (kHeld & (KEY_UP|KEY_DOWN))
|
||||
{
|
||||
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 & HidNpadButton_AnyUp)
|
||||
if (kHeld & KEY_UP)
|
||||
s_cameraPos += front;
|
||||
else if (kHeld & HidNpadButton_AnyDown)
|
||||
else if (kHeld & KEY_DOWN)
|
||||
s_cameraPos -= front;
|
||||
}
|
||||
|
||||
@ -464,7 +464,7 @@ static void configureResolution(NWindow* win, bool halved)
|
||||
width = 1280;
|
||||
height = 720;
|
||||
break;
|
||||
case AppletOperationMode_Console:
|
||||
case AppletOperationMode_Docked:
|
||||
width = 1920;
|
||||
height = 1080;
|
||||
break;
|
||||
@ -525,24 +525,17 @@ 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
|
||||
padUpdate(&pad);
|
||||
u32 kDown = padGetButtonsDown(&pad);
|
||||
u32 kHeld = padGetButtons(&pad);
|
||||
if (kDown & HidNpadButton_Plus)
|
||||
hidScanInput();
|
||||
u32 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
||||
u32 kHeld = hidKeysHeld(CONTROLLER_P1_AUTO);
|
||||
if (kDown & KEY_PLUS)
|
||||
break;
|
||||
|
||||
bool shouldHalveResolution = !!(kHeld & HidNpadButton_A);
|
||||
bool shouldHalveResolution = !!(kHeld & KEY_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 = UINT64_MAX;
|
||||
static u64 origTicks = U64_MAX;
|
||||
|
||||
if (origTicks == UINT64_MAX)
|
||||
if (origTicks == U64_MAX)
|
||||
origTicks = armGetSystemTick();
|
||||
|
||||
u64 ticksElapsed = armGetSystemTick() - origTicks;
|
||||
@ -855,20 +855,13 @@ 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
|
||||
padUpdate(&pad);
|
||||
u32 kDown = padGetButtonsDown(&pad);
|
||||
if (kDown & HidNpadButton_Plus)
|
||||
hidScanInput();
|
||||
u32 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
||||
if (kDown & KEY_PLUS)
|
||||
break;
|
||||
|
||||
// Render stuff!
|
||||
|
@ -7,13 +7,6 @@ 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) );
|
||||
|
||||
@ -53,15 +46,15 @@ int main(int argc, char **argv)
|
||||
// Main loop
|
||||
while(appletMainLoop())
|
||||
{
|
||||
// Scan the gamepad. This should be done once for each frame
|
||||
padUpdate(&pad);
|
||||
//Scan all the inputs. This should be done once for each frame
|
||||
hidScanInput();
|
||||
|
||||
// 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);
|
||||
//hidKeysDown returns information about which buttons have been just pressed (and they weren't in the previous frame)
|
||||
u64 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
||||
|
||||
if (kDown & HidNpadButton_Plus) break; // break in order to return to hbmenu
|
||||
if (kDown & KEY_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,20 +414,13 @@ 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
|
||||
padUpdate(&pad);
|
||||
u32 kDown = padGetButtonsDown(&pad);
|
||||
if (kDown & HidNpadButton_Plus)
|
||||
hidScanInput();
|
||||
u32 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
||||
if (kDown & KEY_PLUS)
|
||||
break;
|
||||
|
||||
// Update our scene
|
||||
|
@ -325,20 +325,13 @@ 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
|
||||
padUpdate(&pad);
|
||||
u32 kDown = padGetButtonsDown(&pad);
|
||||
if (kDown & HidNpadButton_Plus)
|
||||
hidScanInput();
|
||||
u32 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
||||
if (kDown & KEY_PLUS)
|
||||
break;
|
||||
|
||||
// Render stuff!
|
||||
|
@ -514,20 +514,13 @@ 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
|
||||
padUpdate(&pad);
|
||||
u32 kDown = padGetButtonsDown(&pad);
|
||||
if (kDown & HidNpadButton_Plus)
|
||||
hidScanInput();
|
||||
u32 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
||||
if (kDown & KEY_PLUS)
|
||||
break;
|
||||
|
||||
// Update our scene
|
||||
|
@ -8,13 +8,6 @@ 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
|
||||
@ -22,13 +15,13 @@ int main(int argc, char **argv)
|
||||
|
||||
while(appletMainLoop())
|
||||
{
|
||||
// Scan the gamepad. This should be done once for each frame
|
||||
padUpdate(&pad);
|
||||
//Scan all the inputs. This should be done once for each frame
|
||||
hidScanInput();
|
||||
|
||||
// padGetButtonsDown returns the set of buttons that have been newly pressed in this frame compared to the previous one
|
||||
u64 kDown = padGetButtonsDown(&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);
|
||||
|
||||
if (kDown & HidNpadButton_Plus) break; // break in order to return to hbmenu
|
||||
if (kDown & KEY_PLUS) break; // break in order to return to hbmenu
|
||||
|
||||
consoleUpdate(NULL);
|
||||
}
|
||||
|
@ -7,13 +7,6 @@ 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) );
|
||||
|
||||
@ -68,15 +61,15 @@ int main(int argc, char **argv)
|
||||
// Main loop
|
||||
while(appletMainLoop())
|
||||
{
|
||||
// Scan the gamepad. This should be done once for each frame
|
||||
padUpdate(&pad);
|
||||
//Scan all the inputs. This should be done once for each frame
|
||||
hidScanInput();
|
||||
|
||||
// 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);
|
||||
//hidKeysDown returns information about which buttons have been just pressed (and they weren't in the previous frame)
|
||||
u64 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
||||
|
||||
if (kDown & HidNpadButton_Plus) break; // break in order to return to hbmenu
|
||||
if (kDown & KEY_PLUS) break; // break in order to return to hbmenu
|
||||
|
||||
consoleUpdate(NULL);
|
||||
}
|
||||
|
@ -1,217 +0,0 @@
|
||||
#---------------------------------------------------------------------------------
|
||||
.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
|
||||
#---------------------------------------------------------------------------------------
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 8.2 KiB |
Binary file not shown.
Before Width: | Height: | Size: 1.7 KiB |
@ -1,259 +0,0 @@
|
||||
/* 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,8 +18,6 @@
|
||||
|
||||
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)
|
||||
{
|
||||
@ -104,8 +102,8 @@ static int error_screen(const char* fmt, ...)
|
||||
printf("Press PLUS to exit\n");
|
||||
while (appletMainLoop())
|
||||
{
|
||||
padUpdate(&pad);
|
||||
if (padGetButtonsDown(&pad) & HidNpadButton_Plus)
|
||||
hidScanInput();
|
||||
if (hidKeysDown(CONTROLLER_P1_AUTO) & KEY_PLUS)
|
||||
break;
|
||||
consoleUpdate(NULL);
|
||||
}
|
||||
@ -134,9 +132,9 @@ void userAppInit(void)
|
||||
{
|
||||
Result rc;
|
||||
|
||||
rc = plInitialize(PlServiceType_User);
|
||||
rc = plInitialize();
|
||||
if (R_FAILED(rc))
|
||||
diagAbortWithResult(rc);
|
||||
fatalThrow(rc);
|
||||
|
||||
LanguageCode = getSystemLanguage();
|
||||
}
|
||||
@ -151,12 +149,6 @@ 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];
|
||||
@ -206,13 +198,13 @@ int main(int argc, char **argv)
|
||||
|
||||
while (appletMainLoop())
|
||||
{
|
||||
// Scan the gamepad. This should be done once for each frame
|
||||
padUpdate(&pad);
|
||||
//Scan all the inputs. This should be done once for each frame
|
||||
hidScanInput();
|
||||
|
||||
// padGetButtonsDown returns the set of buttons that have been newly pressed in this frame compared to the previous one
|
||||
u32 kDown = padGetButtonsDown(&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);
|
||||
|
||||
if (kDown & HidNpadButton_Plus) break; // break in order to return to hbmenu
|
||||
if (kDown & KEY_PLUS) break; // break in order to return to hbmenu
|
||||
|
||||
u32 stride;
|
||||
u32* framebuf = (u32*)framebufferBegin(&fb, &stride);
|
||||
|
@ -33,30 +33,21 @@ int main(int argc, char* argv[])
|
||||
|
||||
#ifdef DISPLAY_IMAGE
|
||||
u8* imageptr = (u8*)image_bin;
|
||||
const u32 image_width = 1280;
|
||||
const u32 image_height = 720;
|
||||
#endif
|
||||
|
||||
// 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;
|
||||
|
||||
// Main loop
|
||||
while (appletMainLoop())
|
||||
{
|
||||
// Scan the gamepad. This should be done once for each frame
|
||||
padUpdate(&pad);
|
||||
// Scan all the inputs. This should be done once for each frame
|
||||
hidScanInput();
|
||||
|
||||
// padGetButtonsDown returns the set of buttons that have been
|
||||
// newly pressed in this frame compared to the previous one
|
||||
u64 kDown = padGetButtonsDown(&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);
|
||||
|
||||
if (kDown & HidNpadButton_Plus)
|
||||
if (kDown & KEY_PLUS)
|
||||
break; // break in order to return to hbmenu
|
||||
|
||||
// Retrieve the framebuffer
|
||||
@ -75,9 +66,7 @@ int main(int argc, char* argv[])
|
||||
{
|
||||
u32 pos = y * stride / sizeof(u32) + x;
|
||||
#ifdef DISPLAY_IMAGE
|
||||
if (y >= image_height || x >= image_width) continue;
|
||||
u32 imagepos = y * image_width + x;
|
||||
framebuf[pos] = RGBA8_MAXALPHA(imageptr[imagepos*3+0]+(cnt*4), imageptr[imagepos*3+1], imageptr[imagepos*3+2]);
|
||||
framebuf[pos] = RGBA8_MAXALPHA(imageptr[pos*3+0]+(cnt*4), imageptr[pos*3+1], imageptr[pos*3+2]);
|
||||
#else
|
||||
framebuf[pos] = 0x01010101 * cnt * 4;//Set framebuf to different shades of grey.
|
||||
#endif
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user