mirror of
https://github.com/switchbrew/switch-examples.git
synced 2025-07-04 02:22:15 +02:00
Compare commits
68 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
bf9df10259 | ||
|
5e36dc2902 | ||
|
399aa26c6e | ||
|
b41c4957e7 | ||
|
d1b2175d58 | ||
|
4f6e700642 | ||
|
455ad6732c | ||
|
fbbe90507d | ||
|
454610b266 | ||
|
713404797b | ||
|
73960d720c | ||
|
0947425fc5 | ||
|
b55cb17676 | ||
|
950e99a1ab | ||
|
f03b9864ce | ||
|
5980937bdf | ||
|
e423a0ab0e | ||
|
78b1ed515e | ||
|
ce7c9a99ea | ||
|
84be02950a | ||
|
a5f53afed0 | ||
|
b924d358ec | ||
|
62885bbbff | ||
|
ac702280bc | ||
|
40ec713452 | ||
|
07863f0585 | ||
|
a6ea828801 | ||
|
00a0bb3899 | ||
|
29484f5c06 | ||
|
5a42b4766b | ||
|
2b0d5062a5 | ||
|
1e59bd0699 | ||
|
4272e286cc | ||
|
8487e07653 | ||
|
11fa984774 | ||
|
f252e65ebd | ||
|
847bb129e9 | ||
|
fc98393d50 | ||
|
9db0cfd567 | ||
|
c21700136e | ||
|
179e9a847b | ||
|
a6de2da842 | ||
|
cb01bf3c37 | ||
|
ea27fe123d | ||
|
73b50688ab | ||
|
4437fb814f | ||
|
1392e6cad0 | ||
|
7cce42673e | ||
|
a0ce16d0f7 | ||
|
25cdf12ab0 | ||
|
52bdce1eb0 | ||
|
1f30cbcd17 | ||
|
edb1506560 | ||
|
1a4cb0662a | ||
|
0175f8020b | ||
|
d8e5cd4d2b | ||
|
794a26392b | ||
|
c68fc5e826 | ||
|
bbd8727804 | ||
|
36dff0db63 | ||
|
554d75d499 | ||
|
a34aba3b72 | ||
|
948ce48e58 | ||
|
2e7d0c198e | ||
|
2ee7aff9e4 | ||
|
f82d955dfe | ||
|
d279271037 | ||
|
9da8e4a9f4 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -8,4 +8,5 @@ build
|
|||||||
*.nsp
|
*.nsp
|
||||||
*.npdm
|
*.npdm
|
||||||
*.bz2
|
*.bz2
|
||||||
|
*.dksh
|
||||||
*~
|
*~
|
||||||
|
14
Makefile
14
Makefile
@ -1,13 +1,19 @@
|
|||||||
MAKEFILES := $(shell find . -mindepth 2 -name Makefile)
|
MAKEFILES := $(subst ./,,$(shell find . -mindepth 2 -name Makefile))
|
||||||
|
|
||||||
|
TARGETS := $(dir $(MAKEFILES))
|
||||||
|
|
||||||
DATESTRING := $(shell date +%Y)$(shell date +%m)$(shell date +%d)
|
DATESTRING := $(shell date +%Y)$(shell date +%m)$(shell date +%d)
|
||||||
|
|
||||||
all:
|
all: $(TARGETS)
|
||||||
@for i in $(MAKEFILES); do $(MAKE) -C `dirname $$i` || exit 1; done;
|
|
||||||
|
.PHONY: $(TARGETS)
|
||||||
|
|
||||||
|
$(TARGETS):
|
||||||
|
@$(MAKE) -C $@
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
@rm -f *.bz2
|
@rm -f *.bz2
|
||||||
@for i in $(MAKEFILES); do $(MAKE) -C `dirname $$i` clean || exit 1; done;
|
@for i in $(TARGETS); do $(MAKE) -C $$i clean || exit 1; done;
|
||||||
|
|
||||||
dist: clean
|
dist: clean
|
||||||
@tar -cvjf switch-examples-$(DATESTRING).tar.bz2 *
|
@tar -cvjf switch-examples-$(DATESTRING).tar.bz2 *
|
||||||
|
@ -18,6 +18,13 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
consoleInit(NULL);
|
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(&userdata, 0, sizeof(userdata));
|
||||||
memset(&profilebase, 0, sizeof(profilebase));
|
memset(&profilebase, 0, sizeof(profilebase));
|
||||||
|
|
||||||
@ -30,7 +37,17 @@ int main(int argc, char **argv)
|
|||||||
rc = accountGetPreselectedUser(&userID);
|
rc = accountGetPreselectedUser(&userID);
|
||||||
|
|
||||||
if (R_FAILED(rc)) {
|
if (R_FAILED(rc)) {
|
||||||
printf("accountGetPreselectedUser() failed: 0x%x\n", 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (R_SUCCEEDED(rc)) {
|
if (R_SUCCEEDED(rc)) {
|
||||||
@ -68,13 +85,13 @@ int main(int argc, char **argv)
|
|||||||
// Main loop
|
// Main loop
|
||||||
while(appletMainLoop())
|
while(appletMainLoop())
|
||||||
{
|
{
|
||||||
//Scan all the inputs. This should be done once for each frame
|
// Scan the gamepad. This should be done once for each frame
|
||||||
hidScanInput();
|
padUpdate(&pad);
|
||||||
|
|
||||||
//hidKeysDown returns information about which buttons have been just pressed (and they weren't in the previous frame)
|
// padGetButtonsDown returns the set of buttons that have been newly pressed in this frame compared to the previous one
|
||||||
u64 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
u64 kDown = padGetButtonsDown(&pad);
|
||||||
|
|
||||||
if (kDown & KEY_PLUS) break; // break in order to return to hbmenu
|
if (kDown & HidNpadButton_Plus) break; // break in order to return to hbmenu
|
||||||
|
|
||||||
consoleUpdate(NULL);
|
consoleUpdate(NULL);
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,13 @@ int main(int argc, char* argv[])
|
|||||||
// take a look at the graphics/opengl set of examples, which uses EGL instead.
|
// take a look at the graphics/opengl set of examples, which uses EGL instead.
|
||||||
consoleInit(NULL);
|
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");
|
printf("alarm-notifications example\n");
|
||||||
|
|
||||||
Result rc=0, rc2=0;
|
Result rc=0, rc2=0;
|
||||||
@ -75,14 +82,14 @@ int main(int argc, char* argv[])
|
|||||||
// Main loop
|
// Main loop
|
||||||
while (appletMainLoop())
|
while (appletMainLoop())
|
||||||
{
|
{
|
||||||
// Scan all the inputs. This should be done once for each frame
|
// Scan the gamepad. This should be done once for each frame
|
||||||
hidScanInput();
|
padUpdate(&pad);
|
||||||
|
|
||||||
// hidKeysDown returns information about which buttons have been
|
// padGetButtonsDown returns the set of buttons that have been
|
||||||
// just pressed in this frame compared to the previous one
|
// newly pressed in this frame compared to the previous one
|
||||||
u64 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
u64 kDown = padGetButtonsDown(&pad);
|
||||||
|
|
||||||
if (kDown & KEY_PLUS)
|
if (kDown & HidNpadButton_Plus)
|
||||||
break; // break in order to return to hbmenu
|
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.
|
if (R_SUCCEEDED(rc) && R_SUCCEEDED(eventWait(&alarmevent, 0))) { // Some official apps don't use this. See libnx notif.h for this.
|
||||||
@ -96,7 +103,7 @@ int main(int argc, char* argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (R_SUCCEEDED(rc)) {
|
if (R_SUCCEEDED(rc)) {
|
||||||
if (kDown & KEY_A) {
|
if (kDown & HidNpadButton_A) {
|
||||||
// Setup an alarm with {current local-time} + {2 minutes}. You can use any weekday/time you want.
|
// 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.
|
// See libnx notif.h for more notifAlarmSetting*() funcs.
|
||||||
|
|
||||||
@ -121,7 +128,7 @@ int main(int argc, char* argv[])
|
|||||||
if (R_SUCCEEDED(rc)) printf("alarm_setting_id = 0x%x\n", alarm_setting_id);
|
if (R_SUCCEEDED(rc)) printf("alarm_setting_id = 0x%x\n", alarm_setting_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (kDown & KEY_B) {
|
else if (kDown & HidNpadButton_B) {
|
||||||
// List the Alarms.
|
// List the Alarms.
|
||||||
|
|
||||||
total_out=0;
|
total_out=0;
|
||||||
@ -160,8 +167,8 @@ int main(int argc, char* argv[])
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ((kDown & KEY_X) && alarm_setting_id) {
|
else if ((kDown & HidNpadButton_X) && alarm_setting_id) {
|
||||||
// Delete the AlarmSetting which was registered with the KEY_A block.
|
// Delete the AlarmSetting which was registered with the HidNpadButton_A block.
|
||||||
// If wanted, you can also use this with alarm_settings[alarmi].alarm_setting_id with the output from notifListAlarmSettings.
|
// If wanted, you can also use this with alarm_settings[alarmi].alarm_setting_id with the output from notifListAlarmSettings.
|
||||||
rc = notifDeleteAlarmSetting(alarm_setting_id);
|
rc = notifDeleteAlarmSetting(alarm_setting_id);
|
||||||
printf("notifDeleteAlarmSetting(): 0x%x\n", rc);
|
printf("notifDeleteAlarmSetting(): 0x%x\n", rc);
|
||||||
|
@ -19,6 +19,13 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
consoleInit(NULL);
|
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));
|
buf = (NsApplicationControlData*)malloc(sizeof(NsApplicationControlData));
|
||||||
if (buf==NULL) {
|
if (buf==NULL) {
|
||||||
rc = MAKERESULT(Module_Libnx, LibnxError_OutOfMemory);
|
rc = MAKERESULT(Module_Libnx, LibnxError_OutOfMemory);
|
||||||
@ -69,13 +76,13 @@ int main(int argc, char **argv)
|
|||||||
// Main loop
|
// Main loop
|
||||||
while(appletMainLoop())
|
while(appletMainLoop())
|
||||||
{
|
{
|
||||||
//Scan all the inputs. This should be done once for each frame
|
// Scan the gamepad. This should be done once for each frame
|
||||||
hidScanInput();
|
padUpdate(&pad);
|
||||||
|
|
||||||
//hidKeysDown returns information about which buttons have been just pressed (and they weren't in the previous frame)
|
// padGetButtonsDown returns the set of buttons that have been newly pressed in this frame compared to the previous one
|
||||||
u64 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
u64 kDown = padGetButtonsDown(&pad);
|
||||||
|
|
||||||
if (kDown & KEY_PLUS) break; // break in order to return to hbmenu
|
if (kDown & HidNpadButton_Plus) break; // break in order to return to hbmenu
|
||||||
|
|
||||||
consoleUpdate(NULL);
|
consoleUpdate(NULL);
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,13 @@ int main(int argc, char* argv[])
|
|||||||
// take a look at the graphics/opengl set of examples, which uses EGL instead.
|
// take a look at the graphics/opengl set of examples, which uses EGL instead.
|
||||||
consoleInit(NULL);
|
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");
|
printf("application play-stats example\n");
|
||||||
|
|
||||||
Result rc=0;
|
Result rc=0;
|
||||||
@ -53,17 +60,17 @@ int main(int argc, char* argv[])
|
|||||||
// Main loop
|
// Main loop
|
||||||
while (appletMainLoop())
|
while (appletMainLoop())
|
||||||
{
|
{
|
||||||
// Scan all the inputs. This should be done once for each frame
|
// Scan the gamepad. This should be done once for each frame
|
||||||
hidScanInput();
|
padUpdate(&pad);
|
||||||
|
|
||||||
// hidKeysDown returns information about which buttons have been
|
// padGetButtonsDown returns the set of buttons that have been
|
||||||
// just pressed in this frame compared to the previous one
|
// newly pressed in this frame compared to the previous one
|
||||||
u64 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
u64 kDown = padGetButtonsDown(&pad);
|
||||||
|
|
||||||
if (kDown & KEY_PLUS)
|
if (kDown & HidNpadButton_Plus)
|
||||||
break; // break in order to return to hbmenu
|
break; // break in order to return to hbmenu
|
||||||
|
|
||||||
if (kDown & KEY_A) {
|
if (kDown & HidNpadButton_A) {
|
||||||
// Use appletQueryApplicationPlayStatisticsByUid if you want playstats for a specific userID.
|
// Use appletQueryApplicationPlayStatisticsByUid if you want playstats for a specific userID.
|
||||||
|
|
||||||
memset(stats, 0, sizeof(stats));
|
memset(stats, 0, sizeof(stats));
|
||||||
@ -79,46 +86,46 @@ int main(int argc, char* argv[])
|
|||||||
printf("total_out: %d\n", total_out);
|
printf("total_out: %d\n", total_out);
|
||||||
for (i=0; i<total_out; i++) {
|
for (i=0; i<total_out; i++) {
|
||||||
printf("%d: ", i);
|
printf("%d: ", i);
|
||||||
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);
|
printf("application_id = 0x%08lX, playtime = %lu (%lu seconds), total_launches = %lu\n", stats[i].application_id, stats[i].playtime, stats[i].playtime / 1000000000UL, stats[i].total_launches);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (initflag && (kDown & KEY_X)) {
|
if (initflag && (kDown & HidNpadButton_X)) {
|
||||||
memset(events, 0, sizeof(events));
|
memset(events, 0, sizeof(events));
|
||||||
total_out = 0;
|
total_out = 0;
|
||||||
|
|
||||||
// Get PdmAppletEvents.
|
// Get PdmAppletEvents.
|
||||||
rc = pdmqryQueryAppletEvent(0, events, sizeof(events)/sizeof(PdmAppletEvent), &total_out);
|
rc = pdmqryQueryAppletEvent(false, 0, events, sizeof(events)/sizeof(PdmAppletEvent), &total_out);
|
||||||
printf("pdmqryQueryAppletEvent(): 0x%x\n", rc);
|
printf("pdmqryQueryAppletEvent(): 0x%x\n", rc);
|
||||||
if (R_SUCCEEDED(rc)) {
|
if (R_SUCCEEDED(rc)) {
|
||||||
printf("total_out: %d\n", total_out);
|
printf("total_out: %d\n", total_out);
|
||||||
for (i=0; i<total_out; i++) {
|
for (i=0; i<total_out; i++) {
|
||||||
time_t tmptime = pdmPlayTimestampToPosix(events[i].timestampUser);
|
time_t tmptime = events[i].timestamp_user;
|
||||||
|
|
||||||
printf("%d: ", i);
|
printf("%d: ", i);
|
||||||
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));
|
printf("program_id = 0x%08lX, entry_index = 0x%x, timestamp_user = %lu, timestamp_network = %lu, event_type = %u, timestamp_user = %s\n", events[i].program_id, events[i].entry_index, events[i].timestamp_user, events[i].timestamp_network, events[i].event_type, ctime(&tmptime));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get PdmPlayStatistics for the specified ApplicationId.
|
// Get PdmPlayStatistics for the specified ApplicationId.
|
||||||
PdmPlayStatistics playstats[1]={0};
|
PdmPlayStatistics playstats[1]={0};
|
||||||
rc = pdmqryQueryPlayStatisticsByApplicationId(application_ids[0], &playstats[0]);
|
rc = pdmqryQueryPlayStatisticsByApplicationId(application_ids[0], false, &playstats[0]);
|
||||||
printf("pdmqryQueryPlayStatisticsByApplicationId(): 0x%x\n", rc);
|
printf("pdmqryQueryPlayStatisticsByApplicationId(): 0x%x\n", rc);
|
||||||
if (R_SUCCEEDED(rc)) printf("application_id = 0x%08lX, playtimeMinutes = %u, totalLaunches = %u\n", playstats[0].application_id, playstats[0].playtimeMinutes, playstats[0].totalLaunches);
|
if (R_SUCCEEDED(rc)) printf("program_id = 0x%016lX, playtime = %lu (%lu seconds), total_launches = %u\n", playstats[0].program_id, playstats[0].playtime, playstats[0].playtime / 1000000000UL, playstats[0].total_launches);
|
||||||
|
|
||||||
// Get PdmPlayStatistics for the specified ApplicationId and user.
|
// Get PdmPlayStatistics for the specified ApplicationId and user.
|
||||||
rc = pdmqryQueryPlayStatisticsByApplicationIdAndUserAccountId(application_ids[0], preselected_uid, &playstats[0]);
|
rc = pdmqryQueryPlayStatisticsByApplicationIdAndUserAccountId(application_ids[0], preselected_uid, false, &playstats[0]);
|
||||||
printf("pdmqryQueryPlayStatisticsByApplicationIdAndUserAccountId(): 0x%x\n", rc);
|
printf("pdmqryQueryPlayStatisticsByApplicationIdAndUserAccountId(): 0x%x\n", rc);
|
||||||
if (R_SUCCEEDED(rc)) printf("application_id = 0x%08lX, playtimeMinutes = %u, totalLaunches = %u\n", playstats[0].application_id, playstats[0].playtimeMinutes, playstats[0].totalLaunches);
|
if (R_SUCCEEDED(rc)) printf("program_id = 0x%016lX, playtime = %lu (%lu seconds), total_launches = %u\n", playstats[0].program_id, playstats[0].playtime, playstats[0].playtime / 1000000000UL, playstats[0].total_launches);
|
||||||
|
|
||||||
// Get a listing of PdmLastPlayTime for the specified applications.
|
// Get a listing of PdmLastPlayTime for the specified applications.
|
||||||
PdmLastPlayTime playtimes[1]={0};
|
PdmLastPlayTime playtimes[1]={0};
|
||||||
rc = pdmqryQueryLastPlayTime(playtimes, application_ids, 1, &total_out);
|
rc = pdmqryQueryLastPlayTime(false, playtimes, application_ids, 1, &total_out);
|
||||||
printf("pdmqryQueryLastPlayTime(): 0x%x, %d\n, ", rc, total_out);
|
printf("pdmqryQueryLastPlayTime(): 0x%x, %d\n", rc, total_out);
|
||||||
if (R_SUCCEEDED(rc)) {
|
if (R_SUCCEEDED(rc)) {
|
||||||
for (i=0; i<total_out; i++)
|
for (i=0; i<total_out; i++)
|
||||||
printf("%d: timestampUser = %lu\n", i, pdmPlayTimestampToPosix(playtimes[i].timestampUser));
|
printf("%d: timestamp_user = %lu\n", i, pdmPlayTimestampToPosix(playtimes[i].timestamp_user));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the available range for reading events, see pdm.h.
|
// Get the available range for reading events, see pdm.h.
|
||||||
@ -126,12 +133,17 @@ int main(int argc, char* argv[])
|
|||||||
rc = pdmqryGetAvailablePlayEventRange(&total_entries, &start_entryindex, &end_entryindex);
|
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);
|
printf("pdmqryGetAvailablePlayEventRange(): 0x%x, 0x%x, 0x%x, 0x%x\n", rc, total_entries, start_entryindex, end_entryindex);
|
||||||
|
|
||||||
// Get a listing of applications recently played by the specified user.
|
// Get account events.
|
||||||
rc = pdmqryQueryRecentlyPlayedApplication(preselected_uid, application_ids, 1, &total_out);
|
PdmAccountEvent accevents[5]={};
|
||||||
printf("pdmqryQueryRecentlyPlayedApplication(): 0x%x, %d\n", rc, total_out);
|
rc = pdmqryQueryAccountEvent(0, accevents, 5, &total_out);
|
||||||
|
printf("pdmqryQueryAccountEvent(): 0x%x\n", rc);
|
||||||
if (R_SUCCEEDED(rc)) {
|
if (R_SUCCEEDED(rc)) {
|
||||||
for (i=0; i<total_out; i++)
|
printf("total_out: %d\n", total_out);
|
||||||
printf("%d: application_id = 0x%08lX\n", i, application_ids[i]);
|
for (i=0; i<total_out; i++) {
|
||||||
|
time_t tmptime = accevents[i].timestamp_user;
|
||||||
|
printf("%d: ", i);
|
||||||
|
printf("uid = 0x%lx 0x%lx, program_id = 0x%016lX, entry_index = 0x%x, timestamp_user = %lu, timestamp_network = %lu, event_type = %u, timestamp_user = %s\n", accevents[i].uid.uid[0], accevents[i].uid.uid[1], accevents[i].program_id, accevents[i].entry_index, accevents[i].timestamp_user, accevents[i].timestamp_network, accevents[i].type, ctime(&tmptime));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// For more cmds, see pdm.h.
|
// For more cmds, see pdm.h.
|
||||||
|
@ -24,6 +24,13 @@ int main(int argc, char* argv[])
|
|||||||
// take a look at the graphics/opengl set of examples, which uses EGL instead.
|
// take a look at the graphics/opengl set of examples, which uses EGL instead.
|
||||||
consoleInit(NULL);
|
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");
|
printf("error example\n");
|
||||||
|
|
||||||
consoleUpdate(NULL);
|
consoleUpdate(NULL);
|
||||||
@ -34,17 +41,17 @@ int main(int argc, char* argv[])
|
|||||||
// Main loop
|
// Main loop
|
||||||
while (appletMainLoop())
|
while (appletMainLoop())
|
||||||
{
|
{
|
||||||
// Scan all the inputs. This should be done once for each frame
|
// Scan the gamepad. This should be done once for each frame
|
||||||
hidScanInput();
|
padUpdate(&pad);
|
||||||
|
|
||||||
// hidKeysDown returns information about which buttons have been
|
// padGetButtonsDown returns the set of buttons that have been
|
||||||
// just pressed in this frame compared to the previous one
|
// newly pressed in this frame compared to the previous one
|
||||||
u64 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
u64 kDown = padGetButtonsDown(&pad);
|
||||||
|
|
||||||
if (kDown & KEY_PLUS)
|
if (kDown & HidNpadButton_Plus)
|
||||||
break; // break in order to return to hbmenu
|
break; // break in order to return to hbmenu
|
||||||
|
|
||||||
if (kDown & KEY_A) {
|
if (kDown & HidNpadButton_A) {
|
||||||
printf("Running errorResultRecordShow()...\n");
|
printf("Running errorResultRecordShow()...\n");
|
||||||
consoleUpdate(NULL);
|
consoleUpdate(NULL);
|
||||||
rc = errorResultRecordShow(MAKERESULT(16, 250), time(NULL));
|
rc = errorResultRecordShow(MAKERESULT(16, 250), time(NULL));
|
||||||
|
@ -23,6 +23,13 @@ int main(int argc, char* argv[])
|
|||||||
// take a look at the graphics/opengl set of examples, which uses EGL instead.
|
// take a look at the graphics/opengl set of examples, which uses EGL instead.
|
||||||
consoleInit(NULL);
|
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");
|
printf("pctlauth example\n");
|
||||||
consoleUpdate(NULL);
|
consoleUpdate(NULL);
|
||||||
|
|
||||||
@ -47,17 +54,17 @@ int main(int argc, char* argv[])
|
|||||||
// Main loop
|
// Main loop
|
||||||
while (appletMainLoop())
|
while (appletMainLoop())
|
||||||
{
|
{
|
||||||
// Scan all the inputs. This should be done once for each frame
|
// Scan the gamepad. This should be done once for each frame
|
||||||
hidScanInput();
|
padUpdate(&pad);
|
||||||
|
|
||||||
// hidKeysDown returns information about which buttons have been
|
// padGetButtonsDown returns the set of buttons that have been
|
||||||
// just pressed in this frame compared to the previous one
|
// newly pressed in this frame compared to the previous one
|
||||||
u64 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
u64 kDown = padGetButtonsDown(&pad);
|
||||||
|
|
||||||
if (kDown & KEY_PLUS)
|
if (kDown & HidNpadButton_Plus)
|
||||||
break; // break in order to return to hbmenu
|
break; // break in order to return to hbmenu
|
||||||
|
|
||||||
if (pctlflag && (kDown & KEY_A)) {
|
if (pctlflag && (kDown & HidNpadButton_A)) {
|
||||||
printf("Running pctlauthShow()...\n");
|
printf("Running pctlauthShow()...\n");
|
||||||
consoleUpdate(NULL);
|
consoleUpdate(NULL);
|
||||||
rc = pctlauthShow(true); // Launch the applet for validating the PIN.
|
rc = pctlauthShow(true); // Launch the applet for validating the PIN.
|
||||||
|
@ -59,7 +59,7 @@ CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
|
|||||||
ASFLAGS := -g $(ARCH)
|
ASFLAGS := -g $(ARCH)
|
||||||
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
|
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
|
||||||
|
|
||||||
LIBS := -lnx
|
LIBS := -lnx -lm
|
||||||
|
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
# list of directories containing libraries, this must be the top level containing
|
# list of directories containing libraries, this must be the top level containing
|
||||||
|
@ -57,6 +57,13 @@ int main(int argc, char* argv[])
|
|||||||
// take a look at the graphics/opengl set of examples, which uses EGL instead.
|
// take a look at the graphics/opengl set of examples, which uses EGL instead.
|
||||||
consoleInit(NULL);
|
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");
|
printf("swkbd example\n");
|
||||||
|
|
||||||
consoleUpdate(NULL);
|
consoleUpdate(NULL);
|
||||||
@ -107,6 +114,7 @@ int main(int argc, char* argv[])
|
|||||||
swkbdInlineSetFinishedInitializeCallback(&kbdinline, finishinit_cb);
|
swkbdInlineSetFinishedInitializeCallback(&kbdinline, finishinit_cb);
|
||||||
|
|
||||||
// Launch the applet.
|
// Launch the applet.
|
||||||
|
// If you want to display the image manually, you can also use swkbdInlineLaunch and swkbdInlineGetImageMemoryRequirement/swkbdInlineGetImage.
|
||||||
if (R_SUCCEEDED(rc)) {
|
if (R_SUCCEEDED(rc)) {
|
||||||
rc = swkbdInlineLaunchForLibraryApplet(&kbdinline, SwkbdInlineMode_AppletDisplay, 0);
|
rc = swkbdInlineLaunchForLibraryApplet(&kbdinline, SwkbdInlineMode_AppletDisplay, 0);
|
||||||
printf("swkbdInlineLaunch(): 0x%x\n", rc);
|
printf("swkbdInlineLaunch(): 0x%x\n", rc);
|
||||||
@ -141,21 +149,21 @@ int main(int argc, char* argv[])
|
|||||||
// Main loop
|
// Main loop
|
||||||
while (appletMainLoop())
|
while (appletMainLoop())
|
||||||
{
|
{
|
||||||
// Scan all the inputs. This should be done once for each frame
|
// Scan the gamepad. This should be done once for each frame
|
||||||
hidScanInput();
|
padUpdate(&pad);
|
||||||
|
|
||||||
// hidKeysDown returns information about which buttons have been
|
// padGetButtonsDown returns the set of buttons that have been
|
||||||
// just pressed in this frame compared to the previous one
|
// newly pressed in this frame compared to the previous one
|
||||||
u64 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
u64 kDown = padGetButtonsDown(&pad);
|
||||||
|
|
||||||
if (kDown & KEY_PLUS)
|
if (kDown & HidNpadButton_Plus)
|
||||||
break; // break in order to return to hbmenu
|
break; // break in order to return to hbmenu
|
||||||
|
|
||||||
// Your code goes here
|
// Your code goes here
|
||||||
|
|
||||||
if (R_SUCCEEDED(rc)) {
|
if (R_SUCCEEDED(rc)) {
|
||||||
if (kDown & KEY_ZR) swkbdInlineDisappear(&kbdinline); //Optional, you can have swkbd (dis)appear whenever.
|
if (kDown & HidNpadButton_ZR) swkbdInlineDisappear(&kbdinline); //Optional, you can have swkbd (dis)appear whenever.
|
||||||
if (kDown & KEY_Y) swkbdInlineAppear(&kbdinline, &appearArg); // If you use swkbdInlineAppear again after text was submitted, you may want to use swkbdInlineSetInputText since the current-text will be the same as when it was submitted otherwise.
|
if (kDown & HidNpadButton_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.
|
rc = swkbdInlineUpdate(&kbdinline, NULL); // Handles updating SwkbdInline state, this should be called periodically.
|
||||||
if (R_FAILED(rc)) printf("swkbdInlineUpdate(): 0x%x\n", rc);
|
if (R_FAILED(rc)) printf("swkbdInlineUpdate(): 0x%x\n", rc);
|
||||||
|
@ -23,6 +23,13 @@ int main(int argc, char* argv[])
|
|||||||
// take a look at the graphics/opengl set of examples, which uses EGL instead.
|
// take a look at the graphics/opengl set of examples, which uses EGL instead.
|
||||||
consoleInit(NULL);
|
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");
|
printf("web example\n");
|
||||||
|
|
||||||
consoleUpdate(NULL);
|
consoleUpdate(NULL);
|
||||||
@ -33,17 +40,17 @@ int main(int argc, char* argv[])
|
|||||||
// Main loop
|
// Main loop
|
||||||
while (appletMainLoop())
|
while (appletMainLoop())
|
||||||
{
|
{
|
||||||
// Scan all the inputs. This should be done once for each frame
|
// Scan the gamepad. This should be done once for each frame
|
||||||
hidScanInput();
|
padUpdate(&pad);
|
||||||
|
|
||||||
// hidKeysDown returns information about which buttons have been
|
// padGetButtonsDown returns the set of buttons that have been
|
||||||
// just pressed in this frame compared to the previous one
|
// newly pressed in this frame compared to the previous one
|
||||||
u64 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
u64 kDown = padGetButtonsDown(&pad);
|
||||||
|
|
||||||
if (kDown & KEY_PLUS)
|
if (kDown & HidNpadButton_Plus)
|
||||||
break; // break in order to return to hbmenu
|
break; // break in order to return to hbmenu
|
||||||
|
|
||||||
if (kDown & KEY_A) {
|
if (kDown & HidNpadButton_A) {
|
||||||
WebCommonConfig config;
|
WebCommonConfig config;
|
||||||
WebCommonReply reply;
|
WebCommonReply reply;
|
||||||
WebExitReason exitReason=0;
|
WebExitReason exitReason=0;
|
||||||
|
@ -18,6 +18,13 @@ int main(int argc, char* argv[])
|
|||||||
// take a look at the graphics/opengl set of examples, which uses EGL instead.
|
// take a look at the graphics/opengl set of examples, which uses EGL instead.
|
||||||
consoleInit(NULL);
|
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("light-sensor example\n");
|
||||||
printf("Press A to check light-sensor.\n");
|
printf("Press A to check light-sensor.\n");
|
||||||
printf("Press + to exit.\n");
|
printf("Press + to exit.\n");
|
||||||
@ -25,17 +32,17 @@ int main(int argc, char* argv[])
|
|||||||
// Main loop
|
// Main loop
|
||||||
while (appletMainLoop())
|
while (appletMainLoop())
|
||||||
{
|
{
|
||||||
// Scan all the inputs. This should be done once for each frame
|
// Scan the gamepad. This should be done once for each frame
|
||||||
hidScanInput();
|
padUpdate(&pad);
|
||||||
|
|
||||||
// hidKeysDown returns information about which buttons have been
|
// padGetButtonsDown returns the set of buttons that have been
|
||||||
// just pressed in this frame compared to the previous one
|
// newly pressed in this frame compared to the previous one
|
||||||
u64 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
u64 kDown = padGetButtonsDown(&pad);
|
||||||
|
|
||||||
if (kDown & KEY_PLUS)
|
if (kDown & HidNpadButton_Plus)
|
||||||
break; // break in order to return to hbmenu
|
break; // break in order to return to hbmenu
|
||||||
|
|
||||||
if (kDown & KEY_A) {
|
if (kDown & HidNpadButton_A) {
|
||||||
Result rc = 0;
|
Result rc = 0;
|
||||||
float fLux=0;
|
float fLux=0;
|
||||||
bool availableflag=0;
|
bool availableflag=0;
|
||||||
|
@ -22,19 +22,26 @@ int main(int argc, char* argv[])
|
|||||||
// take a look at the graphics/opengl set of examples, which uses EGL instead.
|
// take a look at the graphics/opengl set of examples, which uses EGL instead.
|
||||||
consoleInit(NULL);
|
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");
|
printf("applet exit-locking example.\n");
|
||||||
|
|
||||||
// Main loop
|
// Main loop
|
||||||
while (appletMainLoop())//This loop will automatically exit when applet requests exit.
|
while (appletMainLoop())//This loop will automatically exit when applet requests exit.
|
||||||
{
|
{
|
||||||
// Scan all the inputs. This should be done once for each frame
|
// Scan the gamepad. This should be done once for each frame
|
||||||
hidScanInput();
|
padUpdate(&pad);
|
||||||
|
|
||||||
// hidKeysDown returns information about which buttons have been
|
// padGetButtonsDown returns the set of buttons that have been
|
||||||
// just pressed in this frame compared to the previous one
|
// newly pressed in this frame compared to the previous one
|
||||||
u64 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
u64 kDown = padGetButtonsDown(&pad);
|
||||||
|
|
||||||
if (kDown & KEY_PLUS)
|
if (kDown & HidNpadButton_Plus)
|
||||||
break; // break in order to return to hbmenu
|
break; // break in order to return to hbmenu
|
||||||
|
|
||||||
// Your code goes here
|
// Your code goes here
|
||||||
|
@ -28,22 +28,29 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
appletInitializeGamePlayRecording();//Normally this is only recording func you need to call.
|
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;
|
u32 cnt = 0;
|
||||||
|
|
||||||
while(appletMainLoop())
|
while(appletMainLoop())
|
||||||
{
|
{
|
||||||
//Scan all the inputs. This should be done once for each frame
|
// Scan the gamepad. This should be done once for each frame
|
||||||
hidScanInput();
|
padUpdate(&pad);
|
||||||
|
|
||||||
//hidKeysDown returns information about which buttons have been just pressed (and they weren't in the previous frame)
|
// padGetButtonsDown returns the set of buttons that have been newly pressed in this frame compared to the previous one
|
||||||
u64 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
u64 kDown = padGetButtonsDown(&pad);
|
||||||
|
|
||||||
if (kDown & KEY_PLUS) break; // break in order to return to hbmenu
|
if (kDown & HidNpadButton_Plus) break; // break in order to return to hbmenu
|
||||||
|
|
||||||
if (kDown & KEY_Y) {
|
if (kDown & HidNpadButton_Y) {
|
||||||
appletSetGamePlayRecordingState(0);//Disable recording.
|
appletSetGamePlayRecordingState(0);//Disable recording.
|
||||||
}
|
}
|
||||||
else if (kDown & KEY_X) {
|
else if (kDown & HidNpadButton_X) {
|
||||||
appletSetGamePlayRecordingState(1);//Enable recording.
|
appletSetGamePlayRecordingState(1);//Enable recording.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,13 @@ int main(int argc, char* argv[])
|
|||||||
// take a look at the graphics/opengl set of examples, which uses EGL instead.
|
// take a look at the graphics/opengl set of examples, which uses EGL instead.
|
||||||
consoleInit(NULL);
|
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");
|
printf("screenshot-overlay example\n");
|
||||||
|
|
||||||
// Setup the RGBA8 image.
|
// Setup the RGBA8 image.
|
||||||
@ -56,14 +63,14 @@ int main(int argc, char* argv[])
|
|||||||
// Main loop
|
// Main loop
|
||||||
while (appletMainLoop())
|
while (appletMainLoop())
|
||||||
{
|
{
|
||||||
// Scan all the inputs. This should be done once for each frame
|
// Scan the gamepad. This should be done once for each frame
|
||||||
hidScanInput();
|
padUpdate(&pad);
|
||||||
|
|
||||||
// hidKeysDown returns information about which buttons have been
|
// padGetButtonsDown returns the set of buttons that have been
|
||||||
// just pressed in this frame compared to the previous one
|
// newly pressed in this frame compared to the previous one
|
||||||
u64 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
u64 kDown = padGetButtonsDown(&pad);
|
||||||
|
|
||||||
if (kDown & KEY_PLUS)
|
if (kDown & HidNpadButton_Plus)
|
||||||
break; // break in order to return to hbmenu
|
break; // break in order to return to hbmenu
|
||||||
|
|
||||||
// Update the console, sending a new frame to the display
|
// Update the console, sending a new frame to the display
|
||||||
|
@ -24,6 +24,13 @@ int main(int argc, char* argv[])
|
|||||||
// take a look at the graphics/opengl set of examples, which uses EGL instead.
|
// take a look at the graphics/opengl set of examples, which uses EGL instead.
|
||||||
consoleInit(NULL);
|
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");
|
printf("VrMode example\n");
|
||||||
|
|
||||||
// Using pctl is optional.
|
// Using pctl is optional.
|
||||||
@ -43,18 +50,18 @@ int main(int argc, char* argv[])
|
|||||||
// Main loop
|
// Main loop
|
||||||
while (appletMainLoop())
|
while (appletMainLoop())
|
||||||
{
|
{
|
||||||
// Scan all the inputs. This should be done once for each frame
|
// Scan the gamepad. This should be done once for each frame
|
||||||
hidScanInput();
|
padUpdate(&pad);
|
||||||
|
|
||||||
// hidKeysDown returns information about which buttons have been
|
// padGetButtonsDown returns the set of buttons that have been
|
||||||
// just pressed in this frame compared to the previous one
|
// newly pressed in this frame compared to the previous one
|
||||||
u64 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
u64 kDown = padGetButtonsDown(&pad);
|
||||||
|
|
||||||
if (kDown & KEY_PLUS)
|
if (kDown & HidNpadButton_Plus)
|
||||||
break; // break in order to return to hbmenu
|
break; // break in order to return to hbmenu
|
||||||
|
|
||||||
if (pctlinit) {
|
if (pctlinit) {
|
||||||
if (kDown & KEY_A) {
|
if (kDown & HidNpadButton_A) {
|
||||||
// Not sure what this is.
|
// Not sure what this is.
|
||||||
/*rc = pctlConfirmStereoVisionPermission();
|
/*rc = pctlConfirmStereoVisionPermission();
|
||||||
if (R_FAILED(rc)) printf("pctlConfirmStereoVisionPermission(): 0x%x\n", rc);*/
|
if (R_FAILED(rc)) printf("pctlConfirmStereoVisionPermission(): 0x%x\n", rc);*/
|
||||||
@ -70,7 +77,7 @@ int main(int argc, char* argv[])
|
|||||||
printf("appletSetVrModeEnabled(true): 0x%x\n", rc);
|
printf("appletSetVrModeEnabled(true): 0x%x\n", rc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (kDown & KEY_B) {
|
else if (kDown & HidNpadButton_B) {
|
||||||
rc = appletSetVrModeEnabled(false);
|
rc = appletSetVrModeEnabled(false);
|
||||||
printf("appletSetVrModeEnabled(false): 0x%x\n", rc);
|
printf("appletSetVrModeEnabled(false): 0x%x\n", rc);
|
||||||
|
|
||||||
@ -78,7 +85,7 @@ int main(int argc, char* argv[])
|
|||||||
/*rc = pctlResetConfirmedStereoVisionPermission();
|
/*rc = pctlResetConfirmedStereoVisionPermission();
|
||||||
printf("pctlResetConfirmedStereoVisionPermission(): 0x%x\n", rc);*/
|
printf("pctlResetConfirmedStereoVisionPermission(): 0x%x\n", rc);*/
|
||||||
}
|
}
|
||||||
else if (kDown & KEY_X) {
|
else if (kDown & HidNpadButton_X) {
|
||||||
bool flag=0;
|
bool flag=0;
|
||||||
rc = appletIsVrModeEnabled(&flag);
|
rc = appletIsVrModeEnabled(&flag);
|
||||||
printf("appletIsVrModeEnabled(): 0x%x", rc);
|
printf("appletIsVrModeEnabled(): 0x%x", rc);
|
||||||
|
@ -13,6 +13,13 @@ int main(void)
|
|||||||
{
|
{
|
||||||
consoleInit(NULL);
|
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");
|
printf("Simple audren demonstration program\n");
|
||||||
|
|
||||||
static const AudioRendererConfig arConfig =
|
static const AudioRendererConfig arConfig =
|
||||||
@ -57,7 +64,7 @@ int main(void)
|
|||||||
audrvMemPoolAttach(&drv, mpid);
|
audrvMemPoolAttach(&drv, mpid);
|
||||||
|
|
||||||
static const u8 sink_channels[] = { 0, 1 };
|
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);
|
res = audrvUpdate(&drv);
|
||||||
printf("audrvUpdate: %" PRIx32 "\n", res);
|
printf("audrvUpdate: %" PRIx32 "\n", res);
|
||||||
@ -77,16 +84,16 @@ int main(void)
|
|||||||
// Main loop
|
// Main loop
|
||||||
while (appletMainLoop())
|
while (appletMainLoop())
|
||||||
{
|
{
|
||||||
hidScanInput();
|
padUpdate(&pad);
|
||||||
|
|
||||||
u64 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
u64 kDown = padGetButtonsDown(&pad);
|
||||||
|
|
||||||
if (kDown & KEY_PLUS)
|
if (kDown & HidNpadButton_Plus)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (initedDriver)
|
if (initedDriver)
|
||||||
{
|
{
|
||||||
if (kDown & KEY_A)
|
if (kDown & HidNpadButton_A)
|
||||||
{
|
{
|
||||||
audrvVoiceStop(&drv, 0);
|
audrvVoiceStop(&drv, 0);
|
||||||
audrvVoiceAddWaveBuf(&drv, 0, &wavebuf);
|
audrvVoiceAddWaveBuf(&drv, 0, &wavebuf);
|
||||||
|
@ -22,6 +22,13 @@ int main(int argc, char **argv)
|
|||||||
// Initialize console. Using NULL as the second argument tells the console library to use the internal console structure as current one.
|
// Initialize console. Using NULL as the second argument tells the console library to use the internal console structure as current one.
|
||||||
consoleInit(NULL);
|
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;
|
AudioInBuffer audin_buf;
|
||||||
AudioOutBuffer audout_buf;
|
AudioOutBuffer audout_buf;
|
||||||
AudioInBuffer *released_in_buffer;
|
AudioInBuffer *released_in_buffer;
|
||||||
@ -108,17 +115,17 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
while (appletMainLoop())
|
while (appletMainLoop())
|
||||||
{
|
{
|
||||||
//Scan all the inputs. This should be done once for each frame
|
// Scan the gamepad. This should be done once for each frame
|
||||||
hidScanInput();
|
padUpdate(&pad);
|
||||||
|
|
||||||
//hidKeysDown returns information about which buttons have been just pressed (and they weren't in the previous frame)
|
// padGetButtonsDown returns the set of buttons that have been newly pressed in this frame compared to the previous one
|
||||||
u64 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
u64 kDown = padGetButtonsDown(&pad);
|
||||||
|
|
||||||
if (kDown & KEY_PLUS) break; // break in order to return to hbmenu
|
if (kDown & HidNpadButton_Plus) break; // break in order to return to hbmenu
|
||||||
|
|
||||||
// Wait for audio capture and playback to finish.
|
// Wait for audio capture and playback to finish.
|
||||||
audinWaitCaptureFinish(&released_in_buffer, &released_in_count, U64_MAX);
|
audinWaitCaptureFinish(&released_in_buffer, &released_in_count, UINT64_MAX);
|
||||||
audoutWaitPlayFinish(&released_out_buffer, &released_out_count, U64_MAX);
|
audoutWaitPlayFinish(&released_out_buffer, &released_out_count, UINT64_MAX);
|
||||||
|
|
||||||
// Copy the captured audio data into the playback buffer.
|
// Copy the captured audio data into the playback buffer.
|
||||||
if ((released_in_buffer != NULL) && (released_out_buffer != NULL))
|
if ((released_in_buffer != NULL) && (released_out_buffer != NULL))
|
||||||
@ -130,11 +137,11 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
consoleUpdate(NULL);
|
consoleUpdate(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop audio capture.
|
// Stop audio capture.
|
||||||
rc = audinStopAudioIn();
|
rc = audinStopAudioIn();
|
||||||
printf("audinStopAudioIn() returned 0x%x\n", rc);
|
printf("audinStopAudioIn() returned 0x%x\n", rc);
|
||||||
|
|
||||||
// Stop audio playback.
|
// Stop audio playback.
|
||||||
rc = audoutStopAudioOut();
|
rc = audoutStopAudioOut();
|
||||||
printf("audoutStopAudioOut() returned 0x%x\n", rc);
|
printf("audoutStopAudioOut() returned 0x%x\n", rc);
|
||||||
@ -142,7 +149,7 @@ int main(int argc, char **argv)
|
|||||||
// Terminate the default audio devices.
|
// Terminate the default audio devices.
|
||||||
audinExit();
|
audinExit();
|
||||||
audoutExit();
|
audoutExit();
|
||||||
|
|
||||||
consoleExit(NULL);
|
consoleExit(NULL);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -53,6 +53,13 @@ int main(void)
|
|||||||
{
|
{
|
||||||
consoleInit(NULL);
|
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");
|
printf("Simple hwopus-decoder example with audren\n");
|
||||||
|
|
||||||
static const AudioRendererConfig arConfig =
|
static const AudioRendererConfig arConfig =
|
||||||
@ -67,15 +74,15 @@ int main(void)
|
|||||||
|
|
||||||
size_t num_channels = 1;
|
size_t num_channels = 1;
|
||||||
size_t samplerate = 48000;
|
size_t samplerate = 48000;
|
||||||
size_t max_samples = samplerate;
|
size_t max_samples = samplerate/20;//each wavebuf can hold upto 1sec/20 = 50ms of audio data
|
||||||
size_t max_samples_datasize = max_samples*num_channels*sizeof(opus_int16);
|
size_t max_samples_datasize = max_samples*num_channels*sizeof(opus_int16);
|
||||||
size_t mempool_size = (max_samples_datasize*2 + 0xFFF) &~ 0xFFF;//*2 for 2 wavebufs.
|
size_t mempool_size = (max_samples_datasize*4 + 0xFFF) &~ 0xFFF;//*4 for 4 wavebufs.
|
||||||
void* mempool_ptr = memalign(0x1000, mempool_size);
|
void* mempool_ptr = memalign(0x1000, mempool_size);
|
||||||
void* tmpdata_ptr = malloc(max_samples_datasize);
|
void* tmpdata_ptr = malloc(max_samples_datasize);
|
||||||
opuspkt_tmpbuf = (u8*)malloc(opuspkt_tmpbuf_size);
|
opuspkt_tmpbuf = (u8*)malloc(opuspkt_tmpbuf_size);
|
||||||
opus_int16* curbuf = NULL;
|
opus_int16* curbuf = NULL;
|
||||||
|
|
||||||
AudioDriverWaveBuf wavebuf[2] = {0};
|
AudioDriverWaveBuf wavebuf[4] = {0};
|
||||||
int i, wavei;
|
int i, wavei;
|
||||||
|
|
||||||
HwopusDecoder hwdecoder = {0};
|
HwopusDecoder hwdecoder = {0};
|
||||||
@ -122,7 +129,7 @@ int main(void)
|
|||||||
audrvMemPoolAttach(&drv, mpid);
|
audrvMemPoolAttach(&drv, mpid);
|
||||||
|
|
||||||
static const u8 sink_channels[] = { 0, 1 };
|
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);
|
res = audrvUpdate(&drv);
|
||||||
printf("audrvUpdate: %" PRIx32 "\n", res);
|
printf("audrvUpdate: %" PRIx32 "\n", res);
|
||||||
@ -144,9 +151,9 @@ int main(void)
|
|||||||
}
|
}
|
||||||
audrvVoiceStart(&drv, 0);
|
audrvVoiceStart(&drv, 0);
|
||||||
|
|
||||||
for(i=0; i<2; i++) {
|
for(i=0; i<4; i++) {
|
||||||
wavebuf[i].data_raw = mempool_ptr;
|
wavebuf[i].data_raw = mempool_ptr;
|
||||||
wavebuf[i].size = max_samples_datasize*2;//*2 for 2 wavebufs.
|
wavebuf[i].size = max_samples_datasize*4;//*4 for 4 wavebufs.
|
||||||
wavebuf[i].start_sample_offset = i * max_samples;
|
wavebuf[i].start_sample_offset = i * max_samples;
|
||||||
wavebuf[i].end_sample_offset = wavebuf[i].start_sample_offset + max_samples;
|
wavebuf[i].end_sample_offset = wavebuf[i].start_sample_offset + max_samples;
|
||||||
}
|
}
|
||||||
@ -163,16 +170,16 @@ int main(void)
|
|||||||
// Main loop
|
// Main loop
|
||||||
while (appletMainLoop())
|
while (appletMainLoop())
|
||||||
{
|
{
|
||||||
hidScanInput();
|
padUpdate(&pad);
|
||||||
|
|
||||||
u64 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
u64 kDown = padGetButtonsDown(&pad);
|
||||||
|
|
||||||
if (kDown & KEY_PLUS)
|
if (kDown & HidNpadButton_Plus)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (initedDriver)
|
if (initedDriver)
|
||||||
{
|
{
|
||||||
if (kDown & KEY_A)
|
if (kDown & HidNpadButton_A)
|
||||||
{
|
{
|
||||||
//Close the opus-file if needed and (re)open it, since libopusfile doesn't support seek-to-beginning.
|
//Close the opus-file if needed and (re)open it, since libopusfile doesn't support seek-to-beginning.
|
||||||
if (of)
|
if (of)
|
||||||
@ -191,7 +198,7 @@ int main(void)
|
|||||||
|
|
||||||
if (audio_playing) {
|
if (audio_playing) {
|
||||||
wavei = -1;
|
wavei = -1;
|
||||||
for(i=0; i<2; i++) {
|
for(i=0; i<4; i++) {
|
||||||
if (wavebuf[i].state == AudioDriverWaveBufState_Free || wavebuf[i].state == AudioDriverWaveBufState_Done) {
|
if (wavebuf[i].state == AudioDriverWaveBufState_Free || wavebuf[i].state == AudioDriverWaveBufState_Done) {
|
||||||
wavei = i;
|
wavei = i;
|
||||||
break;
|
break;
|
||||||
@ -199,7 +206,7 @@ int main(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (wavei >= 0) {
|
if (wavei >= 0) {
|
||||||
curbuf = (opus_int16*)(mempool_ptr + wavebuf[wavei].start_sample_offset);
|
curbuf = (opus_int16*)(mempool_ptr + wavebuf[wavei].start_sample_offset * sizeof(opus_int16));
|
||||||
|
|
||||||
opret = op_read(of, tmpdata_ptr, max_samples * num_channels, NULL);//The buffer used here has to be seperate from mempool_ptr.
|
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)
|
if (opret < 0)
|
||||||
|
@ -38,6 +38,13 @@ int main(int argc, char **argv)
|
|||||||
// Initialize console. Using NULL as the second argument tells the console library to use the internal console structure as current one.
|
// Initialize console. Using NULL as the second argument tells the console library to use the internal console structure as current one.
|
||||||
consoleInit(NULL);
|
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_buf;
|
||||||
AudioOutBuffer *audout_released_buf;
|
AudioOutBuffer *audout_released_buf;
|
||||||
|
|
||||||
@ -82,81 +89,81 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
while (appletMainLoop())
|
while (appletMainLoop())
|
||||||
{
|
{
|
||||||
//Scan all the inputs. This should be done once for each frame
|
// Scan the gamepad. This should be done once for each frame
|
||||||
hidScanInput();
|
padUpdate(&pad);
|
||||||
|
|
||||||
//hidKeysDown returns information about which buttons have been just pressed (and they weren't in the previous frame)
|
// padGetButtonsDown returns the set of buttons that have been newly pressed in this frame compared to the previous one
|
||||||
u64 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
u64 kDown = padGetButtonsDown(&pad);
|
||||||
|
|
||||||
if (kDown & KEY_PLUS) break; // break in order to return to hbmenu
|
if (kDown & HidNpadButton_Plus) break; // break in order to return to hbmenu
|
||||||
|
|
||||||
if (kDown & KEY_A)
|
if (kDown & HidNpadButton_A)
|
||||||
{
|
{
|
||||||
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[0]);
|
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[0]);
|
||||||
play_tone = true;
|
play_tone = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (kDown & KEY_B)
|
if (kDown & HidNpadButton_B)
|
||||||
{
|
{
|
||||||
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[1]);
|
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[1]);
|
||||||
play_tone = true;
|
play_tone = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (kDown & KEY_Y)
|
if (kDown & HidNpadButton_Y)
|
||||||
{
|
{
|
||||||
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[2]);
|
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[2]);
|
||||||
play_tone = true;
|
play_tone = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (kDown & KEY_X)
|
if (kDown & HidNpadButton_X)
|
||||||
{
|
{
|
||||||
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[3]);
|
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[3]);
|
||||||
play_tone = true;
|
play_tone = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (kDown & KEY_DLEFT)
|
if (kDown & HidNpadButton_Left)
|
||||||
{
|
{
|
||||||
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[4]);
|
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[4]);
|
||||||
play_tone = true;
|
play_tone = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (kDown & KEY_DUP)
|
if (kDown & HidNpadButton_Up)
|
||||||
{
|
{
|
||||||
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[5]);
|
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[5]);
|
||||||
play_tone = true;
|
play_tone = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (kDown & KEY_DRIGHT)
|
if (kDown & HidNpadButton_Right)
|
||||||
{
|
{
|
||||||
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[6]);
|
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[6]);
|
||||||
play_tone = true;
|
play_tone = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (kDown & KEY_DDOWN)
|
if (kDown & HidNpadButton_Down)
|
||||||
{
|
{
|
||||||
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[7]);
|
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[7]);
|
||||||
play_tone = true;
|
play_tone = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (kDown & KEY_L)
|
if (kDown & HidNpadButton_L)
|
||||||
{
|
{
|
||||||
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[8]);
|
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[8]);
|
||||||
play_tone = true;
|
play_tone = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (kDown & KEY_R)
|
if (kDown & HidNpadButton_R)
|
||||||
{
|
{
|
||||||
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[9]);
|
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[9]);
|
||||||
play_tone = true;
|
play_tone = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (kDown & KEY_ZL)
|
if (kDown & HidNpadButton_ZL)
|
||||||
{
|
{
|
||||||
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[10]);
|
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[10]);
|
||||||
play_tone = true;
|
play_tone = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (kDown & KEY_ZR)
|
if (kDown & HidNpadButton_ZR)
|
||||||
{
|
{
|
||||||
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[11]);
|
fill_audio_buffer(out_buf_data, 0, data_size, notefreq[11]);
|
||||||
play_tone = true;
|
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 \
|
CFLAGS := -g -Wall -O2 -ffunction-sections \
|
||||||
$(ARCH) $(DEFINES)
|
$(ARCH) $(DEFINES)
|
||||||
|
|
||||||
CFLAGS += -D__SWITCH__ $(INCLUDE) `aarch64-none-elf-pkg-config --cflags SDL2_mixer`
|
CFLAGS += -D__SWITCH__ $(INCLUDE) `$(PREFIX)pkg-config --cflags SDL2_mixer`
|
||||||
|
|
||||||
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
|
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
|
||||||
|
|
||||||
ASFLAGS := -g $(ARCH)
|
ASFLAGS := -g $(ARCH)
|
||||||
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
|
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
|
||||||
|
|
||||||
LIBS := `aarch64-none-elf-pkg-config --libs SDL2_mixer`
|
LIBS := `$(PREFIX)pkg-config --libs SDL2_mixer`
|
||||||
|
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
# list of directories containing libraries, this must be the top level containing
|
# list of directories containing libraries, this must be the top level containing
|
||||||
# include and lib
|
# include and lib
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
LIBDIRS := $(PORTLIBS) $(LIBNX)
|
LIBDIRS := $(LIBNX)
|
||||||
|
|
||||||
|
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
|
@ -7,8 +7,8 @@
|
|||||||
#include <switch.h>
|
#include <switch.h>
|
||||||
|
|
||||||
// Include sdl2 headers
|
// Include sdl2 headers
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL.h>
|
||||||
#include <SDL2/SDL_mixer.h>
|
#include <SDL_mixer.h>
|
||||||
|
|
||||||
// Main program entrypoint
|
// Main program entrypoint
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
@ -17,6 +17,13 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
consoleInit(NULL);
|
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();
|
Result rc = romfsInit();
|
||||||
if (R_FAILED(rc))
|
if (R_FAILED(rc))
|
||||||
printf("romfsInit: %08X\n", rc);
|
printf("romfsInit: %08X\n", rc);
|
||||||
@ -41,17 +48,17 @@ int main(int argc, char *argv[])
|
|||||||
// Main loop
|
// Main loop
|
||||||
while (appletMainLoop())
|
while (appletMainLoop())
|
||||||
{
|
{
|
||||||
// Scan all the inputs. This should be done once for each frame
|
// Scan the gamepad. This should be done once for each frame
|
||||||
hidScanInput();
|
padUpdate(&pad);
|
||||||
|
|
||||||
// hidKeysDown returns information about which buttons have been
|
// padGetButtonsDown returns the set of buttons that have been
|
||||||
// just pressed in this frame compared to the previous one
|
// newly pressed in this frame compared to the previous one
|
||||||
u64 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
u64 kDown = padGetButtonsDown(&pad);
|
||||||
|
|
||||||
if (kDown & KEY_PLUS)
|
if (kDown & HidNpadButton_Plus)
|
||||||
break; // break in order to return to hbmenu
|
break; // break in order to return to hbmenu
|
||||||
|
|
||||||
if (kDown & KEY_A)
|
if (kDown & HidNpadButton_A)
|
||||||
Mix_PlayMusic(audio, 1); //Play the audio file
|
Mix_PlayMusic(audio, 1); //Play the audio file
|
||||||
|
|
||||||
// Update the console, sending a new frame to the display
|
// Update the console, sending a new frame to the display
|
||||||
|
@ -47,6 +47,13 @@ int main(int argc, char* argv[])
|
|||||||
// take a look at the graphics/opengl set of examples, which uses EGL instead.
|
// take a look at the graphics/opengl set of examples, which uses EGL instead.
|
||||||
consoleInit(NULL);
|
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");
|
printf("Triggering crash...\n");
|
||||||
consoleUpdate(NULL);
|
consoleUpdate(NULL);
|
||||||
|
|
||||||
@ -56,14 +63,14 @@ int main(int argc, char* argv[])
|
|||||||
// Main loop
|
// Main loop
|
||||||
while (appletMainLoop())
|
while (appletMainLoop())
|
||||||
{
|
{
|
||||||
// Scan all the inputs. This should be done once for each frame
|
// Scan the gamepad. This should be done once for each frame
|
||||||
hidScanInput();
|
padUpdate(&pad);
|
||||||
|
|
||||||
// hidKeysDown returns information about which buttons have been
|
// padGetButtonsDown returns the set of buttons that have been
|
||||||
// just pressed in this frame compared to the previous one
|
// newly pressed in this frame compared to the previous one
|
||||||
u64 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
u64 kDown = padGetButtonsDown(&pad);
|
||||||
|
|
||||||
if (kDown & KEY_PLUS)
|
if (kDown & HidNpadButton_Plus)
|
||||||
break; // break in order to return to hbmenu
|
break; // break in order to return to hbmenu
|
||||||
|
|
||||||
// Your code goes here
|
// Your code goes here
|
||||||
|
@ -32,6 +32,13 @@ int main(int argc, char **argv)
|
|||||||
{
|
{
|
||||||
consoleInit(NULL);
|
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();
|
Result rc = romfsInit();
|
||||||
if (R_FAILED(rc))
|
if (R_FAILED(rc))
|
||||||
printf("romfsInit: %08X\n", rc);
|
printf("romfsInit: %08X\n", rc);
|
||||||
@ -46,13 +53,13 @@ int main(int argc, char **argv)
|
|||||||
// Main loop
|
// Main loop
|
||||||
while(appletMainLoop())
|
while(appletMainLoop())
|
||||||
{
|
{
|
||||||
//Scan all the inputs. This should be done once for each frame
|
// Scan the gamepad. This should be done once for each frame
|
||||||
hidScanInput();
|
padUpdate(&pad);
|
||||||
|
|
||||||
//hidKeysDown returns information about which buttons have been just pressed (and they weren't in the previous frame)
|
// padGetButtonsDown returns the set of buttons that have been newly pressed in this frame compared to the previous one
|
||||||
u64 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
u64 kDown = padGetButtonsDown(&pad);
|
||||||
|
|
||||||
if (kDown & KEY_PLUS) break; // break in order to return to hbmenu
|
if (kDown & HidNpadButton_Plus) break; // break in order to return to hbmenu
|
||||||
|
|
||||||
consoleUpdate(NULL);
|
consoleUpdate(NULL);
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ Result get_save(u64 *application_id, AccountUid *uid) {
|
|||||||
FsSaveDataInfoReader reader;
|
FsSaveDataInfoReader reader;
|
||||||
s64 total_entries=0;
|
s64 total_entries=0;
|
||||||
FsSaveDataInfo info;
|
FsSaveDataInfo info;
|
||||||
|
bool found=0;
|
||||||
|
|
||||||
rc = fsOpenSaveDataInfoReader(&reader, FsSaveDataSpaceId_User);//See libnx fs.h.
|
rc = fsOpenSaveDataInfoReader(&reader, FsSaveDataSpaceId_User);//See libnx fs.h.
|
||||||
if (R_FAILED(rc)) {
|
if (R_FAILED(rc)) {
|
||||||
@ -26,13 +27,14 @@ 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.
|
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;
|
*application_id = info.application_id;
|
||||||
*uid = info.uid;
|
*uid = info.uid;
|
||||||
return 0;
|
found = 1;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fsSaveDataInfoReaderClose(&reader);
|
fsSaveDataInfoReaderClose(&reader);
|
||||||
|
|
||||||
if (R_SUCCEEDED(rc)) return MAKERESULT(Module_Libnx, LibnxError_NotFound);
|
if (R_SUCCEEDED(rc) && !found) return MAKERESULT(Module_Libnx, LibnxError_NotFound);
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
@ -49,6 +51,13 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
consoleInit(NULL);
|
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.
|
//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.
|
//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.
|
||||||
@ -111,13 +120,13 @@ int main(int argc, char **argv)
|
|||||||
// Main loop
|
// Main loop
|
||||||
while(appletMainLoop())
|
while(appletMainLoop())
|
||||||
{
|
{
|
||||||
//Scan all the inputs. This should be done once for each frame
|
// Scan the gamepad. This should be done once for each frame
|
||||||
hidScanInput();
|
padUpdate(&pad);
|
||||||
|
|
||||||
//hidKeysDown returns information about which buttons have been just pressed (and they weren't in the previous frame)
|
// padGetButtonsDown returns the set of buttons that have been newly pressed in this frame compared to the previous one
|
||||||
u64 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
u64 kDown = padGetButtonsDown(&pad);
|
||||||
|
|
||||||
if (kDown & KEY_PLUS) break; // break in order to return to hbmenu
|
if (kDown & HidNpadButton_Plus) break; // break in order to return to hbmenu
|
||||||
|
|
||||||
consoleUpdate(NULL);
|
consoleUpdate(NULL);
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,13 @@ int main(int argc, char **argv)
|
|||||||
{
|
{
|
||||||
consoleInit(NULL);
|
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;
|
DIR* dir;
|
||||||
struct dirent* ent;
|
struct dirent* ent;
|
||||||
|
|
||||||
@ -33,13 +40,13 @@ int main(int argc, char **argv)
|
|||||||
// Main loop
|
// Main loop
|
||||||
while(appletMainLoop())
|
while(appletMainLoop())
|
||||||
{
|
{
|
||||||
//Scan all the inputs. This should be done once for each frame
|
// Scan the gamepad. This should be done once for each frame
|
||||||
hidScanInput();
|
padUpdate(&pad);
|
||||||
|
|
||||||
//hidKeysDown returns information about which buttons have been just pressed (and they weren't in the previous frame)
|
// padGetButtonsDown returns the set of buttons that have been newly pressed in this frame compared to the previous one
|
||||||
u64 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
u64 kDown = padGetButtonsDown(&pad);
|
||||||
|
|
||||||
if (kDown & KEY_PLUS) break; // break in order to return to hbmenu
|
if (kDown & HidNpadButton_Plus) break; // break in order to return to hbmenu
|
||||||
|
|
||||||
consoleUpdate(NULL);
|
consoleUpdate(NULL);
|
||||||
}
|
}
|
||||||
|
271
graphics/deko3d/deko_basic/Makefile
Normal file
271
graphics/deko3d/deko_basic/Makefile
Normal file
@ -0,0 +1,271 @@
|
|||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
.SUFFIXES:
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
ifeq ($(strip $(DEVKITPRO)),)
|
||||||
|
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
|
||||||
|
endif
|
||||||
|
|
||||||
|
TOPDIR ?= $(CURDIR)
|
||||||
|
include $(DEVKITPRO)/libnx/switch_rules
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# TARGET is the name of the output
|
||||||
|
# BUILD is the directory where object files & intermediate files will be placed
|
||||||
|
# SOURCES is a list of directories containing source code
|
||||||
|
# DATA is a list of directories containing data files
|
||||||
|
# INCLUDES is a list of directories containing header files
|
||||||
|
# ROMFS is the directory containing data to be added to RomFS, relative to the Makefile (Optional)
|
||||||
|
#
|
||||||
|
# NO_ICON: if set to anything, do not use icon.
|
||||||
|
# NO_NACP: if set to anything, no .nacp file is generated.
|
||||||
|
# APP_TITLE is the name of the app stored in the .nacp file (Optional)
|
||||||
|
# APP_AUTHOR is the author of the app stored in the .nacp file (Optional)
|
||||||
|
# APP_VERSION is the version of the app stored in the .nacp file (Optional)
|
||||||
|
# APP_TITLEID is the titleID of the app stored in the .nacp file (Optional)
|
||||||
|
# ICON is the filename of the icon (.jpg), relative to the project folder.
|
||||||
|
# If not set, it attempts to use one of the following (in this order):
|
||||||
|
# - <Project name>.jpg
|
||||||
|
# - icon.jpg
|
||||||
|
# - <libnx folder>/default_icon.jpg
|
||||||
|
#
|
||||||
|
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
|
||||||
|
# If not set, it attempts to use one of the following (in this order):
|
||||||
|
# - <Project name>.json
|
||||||
|
# - config.json
|
||||||
|
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
|
||||||
|
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
|
||||||
|
# NACP building is skipped as well.
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
TARGET := $(notdir $(CURDIR))
|
||||||
|
BUILD := build
|
||||||
|
SOURCES := source
|
||||||
|
DATA := data
|
||||||
|
INCLUDES := include
|
||||||
|
ROMFS := romfs
|
||||||
|
|
||||||
|
# Output folders for autogenerated files in romfs
|
||||||
|
OUT_SHADERS := shaders
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# options for code generation
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
|
||||||
|
|
||||||
|
CFLAGS := -g -Wall -O2 -ffunction-sections \
|
||||||
|
$(ARCH) $(DEFINES)
|
||||||
|
|
||||||
|
CFLAGS += $(INCLUDE) -D__SWITCH__
|
||||||
|
|
||||||
|
CXXFLAGS := $(CFLAGS) -std=gnu++17 -fno-exceptions -fno-rtti
|
||||||
|
|
||||||
|
ASFLAGS := -g $(ARCH)
|
||||||
|
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
|
||||||
|
|
||||||
|
LIBS := -ldeko3dd -lnx
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# list of directories containing libraries, this must be the top level containing
|
||||||
|
# include and lib
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
LIBDIRS := $(PORTLIBS) $(LIBNX)
|
||||||
|
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# no real need to edit anything past this point unless you need to add additional
|
||||||
|
# rules for different file extensions
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export OUTPUT := $(CURDIR)/$(TARGET)
|
||||||
|
export TOPDIR := $(CURDIR)
|
||||||
|
|
||||||
|
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||||
|
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
|
||||||
|
|
||||||
|
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||||
|
|
||||||
|
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
|
||||||
|
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
|
||||||
|
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
||||||
|
GLSLFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.glsl)))
|
||||||
|
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# use CXX for linking C++ projects, CC for standard C
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
ifeq ($(strip $(CPPFILES)),)
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
export LD := $(CC)
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
else
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
export LD := $(CXX)
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
endif
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
|
||||||
|
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
|
||||||
|
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
|
||||||
|
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
|
||||||
|
|
||||||
|
ifneq ($(strip $(ROMFS)),)
|
||||||
|
ROMFS_TARGETS :=
|
||||||
|
ROMFS_FOLDERS :=
|
||||||
|
ifneq ($(strip $(OUT_SHADERS)),)
|
||||||
|
ROMFS_SHADERS := $(ROMFS)/$(OUT_SHADERS)
|
||||||
|
ROMFS_TARGETS += $(patsubst %.glsl, $(ROMFS_SHADERS)/%.dksh, $(GLSLFILES))
|
||||||
|
ROMFS_FOLDERS += $(ROMFS_SHADERS)
|
||||||
|
endif
|
||||||
|
|
||||||
|
export ROMFS_DEPS := $(foreach file,$(ROMFS_TARGETS),$(CURDIR)/$(file))
|
||||||
|
endif
|
||||||
|
|
||||||
|
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
||||||
|
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||||
|
-I$(CURDIR)/$(BUILD)
|
||||||
|
|
||||||
|
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||||
|
|
||||||
|
ifeq ($(strip $(CONFIG_JSON)),)
|
||||||
|
jsons := $(wildcard *.json)
|
||||||
|
ifneq (,$(findstring $(TARGET).json,$(jsons)))
|
||||||
|
export APP_JSON := $(TOPDIR)/$(TARGET).json
|
||||||
|
else
|
||||||
|
ifneq (,$(findstring config.json,$(jsons)))
|
||||||
|
export APP_JSON := $(TOPDIR)/config.json
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(strip $(ICON)),)
|
||||||
|
icons := $(wildcard *.jpg)
|
||||||
|
ifneq (,$(findstring $(TARGET).jpg,$(icons)))
|
||||||
|
export APP_ICON := $(TOPDIR)/$(TARGET).jpg
|
||||||
|
else
|
||||||
|
ifneq (,$(findstring icon.jpg,$(icons)))
|
||||||
|
export APP_ICON := $(TOPDIR)/icon.jpg
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
export APP_ICON := $(TOPDIR)/$(ICON)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(strip $(NO_ICON)),)
|
||||||
|
export NROFLAGS += --icon=$(APP_ICON)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(strip $(NO_NACP)),)
|
||||||
|
export NROFLAGS += --nacp=$(CURDIR)/$(TARGET).nacp
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq ($(APP_TITLEID),)
|
||||||
|
export NACPFLAGS += --titleid=$(APP_TITLEID)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq ($(ROMFS),)
|
||||||
|
export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS)
|
||||||
|
endif
|
||||||
|
|
||||||
|
.PHONY: all clean
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
all: $(ROMFS_TARGETS) | $(BUILD)
|
||||||
|
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||||
|
|
||||||
|
$(BUILD):
|
||||||
|
@mkdir -p $@
|
||||||
|
|
||||||
|
ifneq ($(strip $(ROMFS_TARGETS)),)
|
||||||
|
|
||||||
|
$(ROMFS_TARGETS): | $(ROMFS_FOLDERS)
|
||||||
|
|
||||||
|
$(ROMFS_FOLDERS):
|
||||||
|
@mkdir -p $@
|
||||||
|
|
||||||
|
$(ROMFS_SHADERS)/%_vsh.dksh: %_vsh.glsl
|
||||||
|
@echo {vert} $(notdir $<)
|
||||||
|
@uam -s vert -o $@ $<
|
||||||
|
|
||||||
|
$(ROMFS_SHADERS)/%_tcsh.dksh: %_tcsh.glsl
|
||||||
|
@echo {tess_ctrl} $(notdir $<)
|
||||||
|
@uam -s tess_ctrl -o $@ $<
|
||||||
|
|
||||||
|
$(ROMFS_SHADERS)/%_tesh.dksh: %_tesh.glsl
|
||||||
|
@echo {tess_eval} $(notdir $<)
|
||||||
|
@uam -s tess_eval -o $@ $<
|
||||||
|
|
||||||
|
$(ROMFS_SHADERS)/%_gsh.dksh: %_gsh.glsl
|
||||||
|
@echo {geom} $(notdir $<)
|
||||||
|
@uam -s geom -o $@ $<
|
||||||
|
|
||||||
|
$(ROMFS_SHADERS)/%_fsh.dksh: %_fsh.glsl
|
||||||
|
@echo {frag} $(notdir $<)
|
||||||
|
@uam -s frag -o $@ $<
|
||||||
|
|
||||||
|
$(ROMFS_SHADERS)/%.dksh: %.glsl
|
||||||
|
@echo {comp} $(notdir $<)
|
||||||
|
@uam -s comp -o $@ $<
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
clean:
|
||||||
|
@echo clean ...
|
||||||
|
ifeq ($(strip $(APP_JSON)),)
|
||||||
|
@rm -fr $(BUILD) $(ROMFS_FOLDERS) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
|
||||||
|
else
|
||||||
|
@rm -fr $(BUILD) $(ROMFS_FOLDERS) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
else
|
||||||
|
.PHONY: all
|
||||||
|
|
||||||
|
DEPENDS := $(OFILES:.o=.d)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# main targets
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
ifeq ($(strip $(APP_JSON)),)
|
||||||
|
|
||||||
|
all : $(OUTPUT).nro
|
||||||
|
|
||||||
|
ifeq ($(strip $(NO_NACP)),)
|
||||||
|
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp $(ROMFS_DEPS)
|
||||||
|
else
|
||||||
|
$(OUTPUT).nro : $(OUTPUT).elf $(ROMFS_DEPS)
|
||||||
|
endif
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
all : $(OUTPUT).nsp
|
||||||
|
|
||||||
|
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
|
||||||
|
|
||||||
|
$(OUTPUT).nso : $(OUTPUT).elf
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
|
$(OUTPUT).elf : $(OFILES)
|
||||||
|
|
||||||
|
$(OFILES_SRC) : $(HFILES_BIN)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# you need a rule like this for each extension you use as binary data
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
%.bin.o %_bin.h : %.bin
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
@echo $(notdir $<)
|
||||||
|
@$(bin2o)
|
||||||
|
|
||||||
|
-include $(DEPENDS)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------------
|
||||||
|
endif
|
||||||
|
#---------------------------------------------------------------------------------------
|
9
graphics/deko3d/deko_basic/source/color_fsh.glsl
Normal file
9
graphics/deko3d/deko_basic/source/color_fsh.glsl
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#version 460
|
||||||
|
|
||||||
|
layout (location = 0) in vec4 inColor;
|
||||||
|
layout (location = 0) out vec4 outColor;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
outColor = inColor;
|
||||||
|
}
|
233
graphics/deko3d/deko_basic/source/main.c
Normal file
233
graphics/deko3d/deko_basic/source/main.c
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <switch.h>
|
||||||
|
#include <deko3d.h>
|
||||||
|
|
||||||
|
// Define the desired number of framebuffers
|
||||||
|
#define FB_NUM 2
|
||||||
|
|
||||||
|
// Define the desired framebuffer resolution (here we set it to 720p).
|
||||||
|
#define FB_WIDTH 1280
|
||||||
|
#define FB_HEIGHT 720
|
||||||
|
|
||||||
|
// Remove above and uncomment below for 1080p
|
||||||
|
//#define FB_WIDTH 1920
|
||||||
|
//#define FB_HEIGHT 1080
|
||||||
|
|
||||||
|
// Define the size of the memory block that will hold code
|
||||||
|
#define CODEMEMSIZE (64*1024)
|
||||||
|
|
||||||
|
// Define the size of the memory block that will hold command lists
|
||||||
|
#define CMDMEMSIZE (16*1024)
|
||||||
|
|
||||||
|
static DkDevice g_device;
|
||||||
|
static DkMemBlock g_framebufferMemBlock;
|
||||||
|
static DkImage g_framebuffers[FB_NUM];
|
||||||
|
static DkSwapchain g_swapchain;
|
||||||
|
|
||||||
|
static DkMemBlock g_codeMemBlock;
|
||||||
|
static uint32_t g_codeMemOffset;
|
||||||
|
static DkShader g_vertexShader;
|
||||||
|
static DkShader g_fragmentShader;
|
||||||
|
|
||||||
|
static DkMemBlock g_cmdbufMemBlock;
|
||||||
|
static DkCmdBuf g_cmdbuf;
|
||||||
|
static DkCmdList g_cmdsBindFramebuffer[FB_NUM];
|
||||||
|
static DkCmdList g_cmdsRender;
|
||||||
|
|
||||||
|
static DkQueue g_renderQueue;
|
||||||
|
|
||||||
|
// Simple function for loading a shader from the filesystem
|
||||||
|
static void loadShader(DkShader* pShader, const char* path)
|
||||||
|
{
|
||||||
|
// Open the file, and retrieve its size
|
||||||
|
FILE* f = fopen(path, "rb");
|
||||||
|
fseek(f, 0, SEEK_END);
|
||||||
|
uint32_t size = ftell(f);
|
||||||
|
rewind(f);
|
||||||
|
|
||||||
|
// Look for a spot in the code memory block for loading this shader. Note that
|
||||||
|
// we are just using a simple incremental offset; this isn't a general purpose
|
||||||
|
// allocation algorithm.
|
||||||
|
uint32_t codeOffset = g_codeMemOffset;
|
||||||
|
g_codeMemOffset += (size + DK_SHADER_CODE_ALIGNMENT - 1) &~ (DK_SHADER_CODE_ALIGNMENT - 1);
|
||||||
|
|
||||||
|
// Read the file into memory, and close the file
|
||||||
|
fread((uint8_t*)dkMemBlockGetCpuAddr(g_codeMemBlock) + codeOffset, size, 1, f);
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
// Initialize the user provided shader object with the code we've just loaded
|
||||||
|
DkShaderMaker shaderMaker;
|
||||||
|
dkShaderMakerDefaults(&shaderMaker, g_codeMemBlock, codeOffset);
|
||||||
|
dkShaderInitialize(pShader, &shaderMaker);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function creates all the necessary graphical resources.
|
||||||
|
static void graphicsInitialize(void)
|
||||||
|
{
|
||||||
|
// Create the device, which is the root object
|
||||||
|
DkDeviceMaker deviceMaker;
|
||||||
|
dkDeviceMakerDefaults(&deviceMaker);
|
||||||
|
g_device = dkDeviceCreate(&deviceMaker);
|
||||||
|
|
||||||
|
// Calculate layout for the framebuffers
|
||||||
|
DkImageLayoutMaker imageLayoutMaker;
|
||||||
|
dkImageLayoutMakerDefaults(&imageLayoutMaker, g_device);
|
||||||
|
imageLayoutMaker.flags = DkImageFlags_UsageRender | DkImageFlags_UsagePresent | DkImageFlags_HwCompression;
|
||||||
|
imageLayoutMaker.format = DkImageFormat_RGBA8_Unorm;
|
||||||
|
imageLayoutMaker.dimensions[0] = FB_WIDTH;
|
||||||
|
imageLayoutMaker.dimensions[1] = FB_HEIGHT;
|
||||||
|
|
||||||
|
// Calculate layout for the framebuffers
|
||||||
|
DkImageLayout framebufferLayout;
|
||||||
|
dkImageLayoutInitialize(&framebufferLayout, &imageLayoutMaker);
|
||||||
|
|
||||||
|
// Retrieve necessary size and alignment for the framebuffers
|
||||||
|
uint32_t framebufferSize = dkImageLayoutGetSize(&framebufferLayout);
|
||||||
|
uint32_t framebufferAlign = dkImageLayoutGetAlignment(&framebufferLayout);
|
||||||
|
framebufferSize = (framebufferSize + framebufferAlign - 1) &~ (framebufferAlign - 1);
|
||||||
|
|
||||||
|
// Create a memory block that will host the framebuffers
|
||||||
|
DkMemBlockMaker memBlockMaker;
|
||||||
|
dkMemBlockMakerDefaults(&memBlockMaker, g_device, FB_NUM*framebufferSize);
|
||||||
|
memBlockMaker.flags = DkMemBlockFlags_GpuCached | DkMemBlockFlags_Image;
|
||||||
|
g_framebufferMemBlock = dkMemBlockCreate(&memBlockMaker);
|
||||||
|
|
||||||
|
// Initialize the framebuffers with the layout and backing memory we've just created
|
||||||
|
DkImage const* swapchainImages[FB_NUM];
|
||||||
|
for (unsigned i = 0; i < FB_NUM; i ++)
|
||||||
|
{
|
||||||
|
swapchainImages[i] = &g_framebuffers[i];
|
||||||
|
dkImageInitialize(&g_framebuffers[i], &framebufferLayout, g_framebufferMemBlock, i*framebufferSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a swapchain out of the framebuffers we've just initialized
|
||||||
|
DkSwapchainMaker swapchainMaker;
|
||||||
|
dkSwapchainMakerDefaults(&swapchainMaker, g_device, nwindowGetDefault(), swapchainImages, FB_NUM);
|
||||||
|
g_swapchain = dkSwapchainCreate(&swapchainMaker);
|
||||||
|
|
||||||
|
// Create a memory block onto which we will load shader code
|
||||||
|
dkMemBlockMakerDefaults(&memBlockMaker, g_device, CODEMEMSIZE);
|
||||||
|
memBlockMaker.flags = DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached | DkMemBlockFlags_Code;
|
||||||
|
g_codeMemBlock = dkMemBlockCreate(&memBlockMaker);
|
||||||
|
g_codeMemOffset = 0;
|
||||||
|
|
||||||
|
// Load our shaders (both vertex and fragment)
|
||||||
|
loadShader(&g_vertexShader, "romfs:/shaders/triangle_vsh.dksh");
|
||||||
|
loadShader(&g_fragmentShader, "romfs:/shaders/color_fsh.dksh");
|
||||||
|
|
||||||
|
// Create a memory block which will be used for recording command lists using a command buffer
|
||||||
|
dkMemBlockMakerDefaults(&memBlockMaker, g_device, CMDMEMSIZE);
|
||||||
|
memBlockMaker.flags = DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached;
|
||||||
|
g_cmdbufMemBlock = dkMemBlockCreate(&memBlockMaker);
|
||||||
|
|
||||||
|
// Create a command buffer object
|
||||||
|
DkCmdBufMaker cmdbufMaker;
|
||||||
|
dkCmdBufMakerDefaults(&cmdbufMaker, g_device);
|
||||||
|
g_cmdbuf = dkCmdBufCreate(&cmdbufMaker);
|
||||||
|
|
||||||
|
// Feed our memory to the command buffer so that we can start recording commands
|
||||||
|
dkCmdBufAddMemory(g_cmdbuf, g_cmdbufMemBlock, 0, CMDMEMSIZE);
|
||||||
|
|
||||||
|
// Generate a command list for each framebuffer, which will bind each of them as a render target
|
||||||
|
for (unsigned i = 0; i < FB_NUM; i ++)
|
||||||
|
{
|
||||||
|
DkImageView imageView;
|
||||||
|
dkImageViewDefaults(&imageView, &g_framebuffers[i]);
|
||||||
|
dkCmdBufBindRenderTarget(g_cmdbuf, &imageView, NULL);
|
||||||
|
g_cmdsBindFramebuffer[i] = dkCmdBufFinishList(g_cmdbuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Declare structs that will be used for binding state
|
||||||
|
DkViewport viewport = { 0.0f, 0.0f, (float)FB_WIDTH, (float)FB_HEIGHT, 0.0f, 1.0f };
|
||||||
|
DkScissor scissor = { 0, 0, FB_WIDTH, FB_HEIGHT };
|
||||||
|
DkShader const* shaders[] = { &g_vertexShader, &g_fragmentShader };
|
||||||
|
DkRasterizerState rasterizerState;
|
||||||
|
DkColorState colorState;
|
||||||
|
DkColorWriteState colorWriteState;
|
||||||
|
|
||||||
|
// Initialize state structs with the deko3d defaults
|
||||||
|
dkRasterizerStateDefaults(&rasterizerState);
|
||||||
|
dkColorStateDefaults(&colorState);
|
||||||
|
dkColorWriteStateDefaults(&colorWriteState);
|
||||||
|
|
||||||
|
// Generate the main rendering command list
|
||||||
|
dkCmdBufSetViewports(g_cmdbuf, 0, &viewport, 1);
|
||||||
|
dkCmdBufSetScissors(g_cmdbuf, 0, &scissor, 1);
|
||||||
|
dkCmdBufClearColorFloat(g_cmdbuf, 0, DkColorMask_RGBA, 0.125f, 0.294f, 0.478f, 1.0f);
|
||||||
|
dkCmdBufBindShaders(g_cmdbuf, DkStageFlag_GraphicsMask, shaders, sizeof(shaders)/sizeof(shaders[0]));
|
||||||
|
dkCmdBufBindRasterizerState(g_cmdbuf, &rasterizerState);
|
||||||
|
dkCmdBufBindColorState(g_cmdbuf, &colorState);
|
||||||
|
dkCmdBufBindColorWriteState(g_cmdbuf, &colorWriteState);
|
||||||
|
dkCmdBufDraw(g_cmdbuf, DkPrimitive_Triangles, 3, 1, 0, 0);
|
||||||
|
g_cmdsRender = dkCmdBufFinishList(g_cmdbuf);
|
||||||
|
|
||||||
|
// Create a queue, to which we will submit our command lists
|
||||||
|
DkQueueMaker queueMaker;
|
||||||
|
dkQueueMakerDefaults(&queueMaker, g_device);
|
||||||
|
queueMaker.flags = DkQueueFlags_Graphics;
|
||||||
|
g_renderQueue = dkQueueCreate(&queueMaker);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function is to be called at each frame, and it is in charge of rendering.
|
||||||
|
static void graphicsUpdate(void)
|
||||||
|
{
|
||||||
|
// Acquire a framebuffer from the swapchain (and wait for it to be available)
|
||||||
|
int slot = dkQueueAcquireImage(g_renderQueue, g_swapchain);
|
||||||
|
|
||||||
|
// Run the command list that binds said framebuffer as a render target
|
||||||
|
dkQueueSubmitCommands(g_renderQueue, g_cmdsBindFramebuffer[slot]);
|
||||||
|
|
||||||
|
// Run the main rendering command list
|
||||||
|
dkQueueSubmitCommands(g_renderQueue, g_cmdsRender);
|
||||||
|
|
||||||
|
// Now that we are done rendering, present it to the screen
|
||||||
|
dkQueuePresentImage(g_renderQueue, g_swapchain, slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function destroys the graphical resources created by graphicsInitialize.
|
||||||
|
static void graphicsExit(void)
|
||||||
|
{
|
||||||
|
// Make sure the rendering queue is idle before destroying anything
|
||||||
|
dkQueueWaitIdle(g_renderQueue);
|
||||||
|
|
||||||
|
// Destroy all the resources we've created
|
||||||
|
dkQueueDestroy(g_renderQueue);
|
||||||
|
dkCmdBufDestroy(g_cmdbuf);
|
||||||
|
dkMemBlockDestroy(g_cmdbufMemBlock);
|
||||||
|
dkMemBlockDestroy(g_codeMemBlock);
|
||||||
|
dkSwapchainDestroy(g_swapchain);
|
||||||
|
dkMemBlockDestroy(g_framebufferMemBlock);
|
||||||
|
dkDeviceDestroy(g_device);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main entrypoint
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
romfsInit();
|
||||||
|
graphicsInitialize();
|
||||||
|
|
||||||
|
// Configure our supported input layout: a single player with standard controller styles
|
||||||
|
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
|
||||||
|
|
||||||
|
// Initialize the default gamepad (which reads handheld mode inputs as well as the first connected controller)
|
||||||
|
PadState pad;
|
||||||
|
padInitializeDefault(&pad);
|
||||||
|
|
||||||
|
while (appletMainLoop())
|
||||||
|
{
|
||||||
|
padUpdate(&pad);
|
||||||
|
|
||||||
|
u64 kDown = padGetButtonsDown(&pad);
|
||||||
|
if (kDown & HidNpadButton_Plus)
|
||||||
|
break; // break in order to return to hbmenu
|
||||||
|
|
||||||
|
graphicsUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
graphicsExit();
|
||||||
|
romfsExit();
|
||||||
|
return 0;
|
||||||
|
}
|
23
graphics/deko3d/deko_basic/source/triangle_vsh.glsl
Normal file
23
graphics/deko3d/deko_basic/source/triangle_vsh.glsl
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#version 460
|
||||||
|
|
||||||
|
// Hardcoded array of vertex positions for our triangle
|
||||||
|
const vec4 positions[3] = vec4[](
|
||||||
|
vec4( 0.0, +1.0, 0.0, 1.0),
|
||||||
|
vec4(-1.0, -1.0, 0.0, 1.0),
|
||||||
|
vec4(+1.0, -1.0, 0.0, 1.0)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Hardcoded array of vertex colors for our triangle
|
||||||
|
const vec4 colors[3] = vec4[](
|
||||||
|
vec4(1.0, 0.0, 0.0, 1.0),
|
||||||
|
vec4(0.0, 1.0, 0.0, 1.0),
|
||||||
|
vec4(0.0, 0.0, 1.0, 1.0)
|
||||||
|
);
|
||||||
|
|
||||||
|
layout (location = 0) out vec4 outColor;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
gl_Position = positions[gl_VertexID];
|
||||||
|
outColor = colors[gl_VertexID];
|
||||||
|
}
|
271
graphics/deko3d/deko_console/Makefile
Normal file
271
graphics/deko3d/deko_console/Makefile
Normal file
@ -0,0 +1,271 @@
|
|||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
.SUFFIXES:
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
ifeq ($(strip $(DEVKITPRO)),)
|
||||||
|
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
|
||||||
|
endif
|
||||||
|
|
||||||
|
TOPDIR ?= $(CURDIR)
|
||||||
|
include $(DEVKITPRO)/libnx/switch_rules
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# TARGET is the name of the output
|
||||||
|
# BUILD is the directory where object files & intermediate files will be placed
|
||||||
|
# SOURCES is a list of directories containing source code
|
||||||
|
# DATA is a list of directories containing data files
|
||||||
|
# INCLUDES is a list of directories containing header files
|
||||||
|
# ROMFS is the directory containing data to be added to RomFS, relative to the Makefile (Optional)
|
||||||
|
#
|
||||||
|
# NO_ICON: if set to anything, do not use icon.
|
||||||
|
# NO_NACP: if set to anything, no .nacp file is generated.
|
||||||
|
# APP_TITLE is the name of the app stored in the .nacp file (Optional)
|
||||||
|
# APP_AUTHOR is the author of the app stored in the .nacp file (Optional)
|
||||||
|
# APP_VERSION is the version of the app stored in the .nacp file (Optional)
|
||||||
|
# APP_TITLEID is the titleID of the app stored in the .nacp file (Optional)
|
||||||
|
# ICON is the filename of the icon (.jpg), relative to the project folder.
|
||||||
|
# If not set, it attempts to use one of the following (in this order):
|
||||||
|
# - <Project name>.jpg
|
||||||
|
# - icon.jpg
|
||||||
|
# - <libnx folder>/default_icon.jpg
|
||||||
|
#
|
||||||
|
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
|
||||||
|
# If not set, it attempts to use one of the following (in this order):
|
||||||
|
# - <Project name>.json
|
||||||
|
# - config.json
|
||||||
|
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
|
||||||
|
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
|
||||||
|
# NACP building is skipped as well.
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
TARGET := $(notdir $(CURDIR))
|
||||||
|
BUILD := build
|
||||||
|
SOURCES := source
|
||||||
|
DATA := data
|
||||||
|
INCLUDES := include
|
||||||
|
ROMFS := romfs
|
||||||
|
|
||||||
|
# Output folders for autogenerated files in romfs
|
||||||
|
OUT_SHADERS := shaders
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# options for code generation
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
|
||||||
|
|
||||||
|
CFLAGS := -g -Wall -O2 -ffunction-sections \
|
||||||
|
$(ARCH) $(DEFINES)
|
||||||
|
|
||||||
|
CFLAGS += $(INCLUDE) -D__SWITCH__
|
||||||
|
|
||||||
|
CXXFLAGS := $(CFLAGS) -std=gnu++17 -fno-exceptions -fno-rtti
|
||||||
|
|
||||||
|
ASFLAGS := -g $(ARCH)
|
||||||
|
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
|
||||||
|
|
||||||
|
LIBS := -ldeko3d -lnx -lm
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# list of directories containing libraries, this must be the top level containing
|
||||||
|
# include and lib
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
LIBDIRS := $(PORTLIBS) $(LIBNX)
|
||||||
|
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# no real need to edit anything past this point unless you need to add additional
|
||||||
|
# rules for different file extensions
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export OUTPUT := $(CURDIR)/$(TARGET)
|
||||||
|
export TOPDIR := $(CURDIR)
|
||||||
|
|
||||||
|
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||||
|
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
|
||||||
|
|
||||||
|
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||||
|
|
||||||
|
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
|
||||||
|
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
|
||||||
|
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
||||||
|
GLSLFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.glsl)))
|
||||||
|
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# use CXX for linking C++ projects, CC for standard C
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
ifeq ($(strip $(CPPFILES)),)
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
export LD := $(CC)
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
else
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
export LD := $(CXX)
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
endif
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
|
||||||
|
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
|
||||||
|
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
|
||||||
|
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
|
||||||
|
|
||||||
|
ifneq ($(strip $(ROMFS)),)
|
||||||
|
ROMFS_TARGETS :=
|
||||||
|
ROMFS_FOLDERS :=
|
||||||
|
ifneq ($(strip $(OUT_SHADERS)),)
|
||||||
|
ROMFS_SHADERS := $(ROMFS)/$(OUT_SHADERS)
|
||||||
|
ROMFS_TARGETS += $(patsubst %.glsl, $(ROMFS_SHADERS)/%.dksh, $(GLSLFILES))
|
||||||
|
ROMFS_FOLDERS += $(ROMFS_SHADERS)
|
||||||
|
endif
|
||||||
|
|
||||||
|
export ROMFS_DEPS := $(foreach file,$(ROMFS_TARGETS),$(CURDIR)/$(file))
|
||||||
|
endif
|
||||||
|
|
||||||
|
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
||||||
|
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||||
|
-I$(CURDIR)/$(BUILD)
|
||||||
|
|
||||||
|
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||||
|
|
||||||
|
ifeq ($(strip $(CONFIG_JSON)),)
|
||||||
|
jsons := $(wildcard *.json)
|
||||||
|
ifneq (,$(findstring $(TARGET).json,$(jsons)))
|
||||||
|
export APP_JSON := $(TOPDIR)/$(TARGET).json
|
||||||
|
else
|
||||||
|
ifneq (,$(findstring config.json,$(jsons)))
|
||||||
|
export APP_JSON := $(TOPDIR)/config.json
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(strip $(ICON)),)
|
||||||
|
icons := $(wildcard *.jpg)
|
||||||
|
ifneq (,$(findstring $(TARGET).jpg,$(icons)))
|
||||||
|
export APP_ICON := $(TOPDIR)/$(TARGET).jpg
|
||||||
|
else
|
||||||
|
ifneq (,$(findstring icon.jpg,$(icons)))
|
||||||
|
export APP_ICON := $(TOPDIR)/icon.jpg
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
export APP_ICON := $(TOPDIR)/$(ICON)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(strip $(NO_ICON)),)
|
||||||
|
export NROFLAGS += --icon=$(APP_ICON)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(strip $(NO_NACP)),)
|
||||||
|
export NROFLAGS += --nacp=$(CURDIR)/$(TARGET).nacp
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq ($(APP_TITLEID),)
|
||||||
|
export NACPFLAGS += --titleid=$(APP_TITLEID)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq ($(ROMFS),)
|
||||||
|
export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS)
|
||||||
|
endif
|
||||||
|
|
||||||
|
.PHONY: all clean
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
all: $(ROMFS_TARGETS) | $(BUILD)
|
||||||
|
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||||
|
|
||||||
|
$(BUILD):
|
||||||
|
@mkdir -p $@
|
||||||
|
|
||||||
|
ifneq ($(strip $(ROMFS_TARGETS)),)
|
||||||
|
|
||||||
|
$(ROMFS_TARGETS): | $(ROMFS_FOLDERS)
|
||||||
|
|
||||||
|
$(ROMFS_FOLDERS):
|
||||||
|
@mkdir -p $@
|
||||||
|
|
||||||
|
$(ROMFS_SHADERS)/%_vsh.dksh: %_vsh.glsl
|
||||||
|
@echo {vert} $(notdir $<)
|
||||||
|
@uam -s vert -o $@ $<
|
||||||
|
|
||||||
|
$(ROMFS_SHADERS)/%_tcsh.dksh: %_tcsh.glsl
|
||||||
|
@echo {tess_ctrl} $(notdir $<)
|
||||||
|
@uam -s tess_ctrl -o $@ $<
|
||||||
|
|
||||||
|
$(ROMFS_SHADERS)/%_tesh.dksh: %_tesh.glsl
|
||||||
|
@echo {tess_eval} $(notdir $<)
|
||||||
|
@uam -s tess_eval -o $@ $<
|
||||||
|
|
||||||
|
$(ROMFS_SHADERS)/%_gsh.dksh: %_gsh.glsl
|
||||||
|
@echo {geom} $(notdir $<)
|
||||||
|
@uam -s geom -o $@ $<
|
||||||
|
|
||||||
|
$(ROMFS_SHADERS)/%_fsh.dksh: %_fsh.glsl
|
||||||
|
@echo {frag} $(notdir $<)
|
||||||
|
@uam -s frag -o $@ $<
|
||||||
|
|
||||||
|
$(ROMFS_SHADERS)/%.dksh: %.glsl
|
||||||
|
@echo {comp} $(notdir $<)
|
||||||
|
@uam -s comp -o $@ $<
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
clean:
|
||||||
|
@echo clean ...
|
||||||
|
ifeq ($(strip $(APP_JSON)),)
|
||||||
|
@rm -fr $(BUILD) $(ROMFS_FOLDERS) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
|
||||||
|
else
|
||||||
|
@rm -fr $(BUILD) $(ROMFS_FOLDERS) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
else
|
||||||
|
.PHONY: all
|
||||||
|
|
||||||
|
DEPENDS := $(OFILES:.o=.d)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# main targets
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
ifeq ($(strip $(APP_JSON)),)
|
||||||
|
|
||||||
|
all : $(OUTPUT).nro
|
||||||
|
|
||||||
|
ifeq ($(strip $(NO_NACP)),)
|
||||||
|
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp $(ROMFS_DEPS)
|
||||||
|
else
|
||||||
|
$(OUTPUT).nro : $(OUTPUT).elf $(ROMFS_DEPS)
|
||||||
|
endif
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
all : $(OUTPUT).nsp
|
||||||
|
|
||||||
|
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
|
||||||
|
|
||||||
|
$(OUTPUT).nso : $(OUTPUT).elf
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
|
$(OUTPUT).elf : $(OFILES)
|
||||||
|
|
||||||
|
$(OFILES_SRC) : $(HFILES_BIN)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# you need a rule like this for each extension you use as binary data
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
%.bin.o %_bin.h : %.bin
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
@echo $(notdir $<)
|
||||||
|
@$(bin2o)
|
||||||
|
|
||||||
|
-include $(DEPENDS)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------------
|
||||||
|
endif
|
||||||
|
#---------------------------------------------------------------------------------------
|
15
graphics/deko3d/deko_console/source/console_fsh.glsl
Normal file
15
graphics/deko3d/deko_console/source/console_fsh.glsl
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#version 460
|
||||||
|
|
||||||
|
layout (location = 0) noperspective in vec3 inTexCoord;
|
||||||
|
layout (location = 1) flat in vec4 inFrontPal;
|
||||||
|
layout (location = 2) flat in vec4 inBackPal;
|
||||||
|
|
||||||
|
layout (location = 0) out vec4 outColor;
|
||||||
|
|
||||||
|
layout (binding = 0) uniform sampler2DArray tileset;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
float value = texture(tileset, inTexCoord).r;
|
||||||
|
outColor = mix(inBackPal, inFrontPal, value);
|
||||||
|
}
|
35
graphics/deko3d/deko_console/source/console_vsh.glsl
Normal file
35
graphics/deko3d/deko_console/source/console_vsh.glsl
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#version 460
|
||||||
|
|
||||||
|
layout (location = 0) in float inTileId;
|
||||||
|
layout (location = 1) in uvec2 inColorId;
|
||||||
|
|
||||||
|
layout (location = 0) out vec3 outTexCoord;
|
||||||
|
layout (location = 1) out vec4 outFrontPal;
|
||||||
|
layout (location = 2) out vec4 outBackPal;
|
||||||
|
|
||||||
|
layout (std140, binding = 0) uniform Config
|
||||||
|
{
|
||||||
|
vec4 dimensions;
|
||||||
|
vec4 vertices[3];
|
||||||
|
vec4 palettes[24];
|
||||||
|
} u;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
float id = float(gl_InstanceID);
|
||||||
|
float tileRow = floor(id / u.dimensions.z);
|
||||||
|
float tileCol = id - tileRow * u.dimensions.z;
|
||||||
|
|
||||||
|
vec2 basePos;
|
||||||
|
basePos.x = 2.0 * (tileCol + 0.5) / u.dimensions.z - 1.0;
|
||||||
|
basePos.y = 2.0 * (1.0 - (tileRow + 0.5) / u.dimensions.w) - 1.0;
|
||||||
|
|
||||||
|
vec2 vtxData = u.vertices[gl_VertexID].xy;
|
||||||
|
vec2 scale = vec2(1.0) / u.dimensions.zw;
|
||||||
|
gl_Position.xy = vtxData * scale + basePos;
|
||||||
|
gl_Position.zw = vec2(0.5, 1.0);
|
||||||
|
|
||||||
|
outTexCoord = vec3(u.vertices[gl_VertexID].zw, inTileId);
|
||||||
|
outFrontPal = u.palettes[inColorId.x];
|
||||||
|
outBackPal = u.palettes[inColorId.y];
|
||||||
|
}
|
486
graphics/deko3d/deko_console/source/gpu_console.c
Normal file
486
graphics/deko3d/deko_console/source/gpu_console.c
Normal file
@ -0,0 +1,486 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/iosupport.h>
|
||||||
|
|
||||||
|
#include <switch.h>
|
||||||
|
#include <deko3d.h>
|
||||||
|
|
||||||
|
// Define the desired number of framebuffers
|
||||||
|
#define FB_NUM 2
|
||||||
|
|
||||||
|
// Define the size of the memory block that will hold code
|
||||||
|
#define CODEMEMSIZE (64*1024)
|
||||||
|
|
||||||
|
// Define the size of the memory block that will hold command lists
|
||||||
|
#define CMDMEMSIZE (64*1024)
|
||||||
|
|
||||||
|
#define NUM_IMAGE_SLOTS 1
|
||||||
|
#define NUM_SAMPLER_SLOTS 1
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
float pos[2];
|
||||||
|
float tex[2];
|
||||||
|
} VertexDef;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
float red;
|
||||||
|
float green;
|
||||||
|
float blue;
|
||||||
|
float alpha;
|
||||||
|
} PaletteColor;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
float dimensions[4];
|
||||||
|
VertexDef vertices[3];
|
||||||
|
PaletteColor palettes[24];
|
||||||
|
} ConsoleConfig;
|
||||||
|
|
||||||
|
static const VertexDef g_vertexData[3] = {
|
||||||
|
{ { 0.0f, +1.0f }, { 0.5f, 0.0f, } },
|
||||||
|
{ { -1.0f, -1.0f }, { 0.0f, 1.0f, } },
|
||||||
|
{ { +1.0f, -1.0f }, { 1.0f, 1.0f, } },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const PaletteColor g_paletteData[24] = {
|
||||||
|
{ 0.0f, 0.0f, 0.0f, 0.0f }, // black
|
||||||
|
{ 0.5f, 0.0f, 0.0f, 1.0f }, // red
|
||||||
|
{ 0.0f, 0.5f, 0.0f, 1.0f }, // green
|
||||||
|
{ 0.5f, 0.5f, 0.0f, 1.0f }, // yellow
|
||||||
|
{ 0.0f, 0.0f, 0.5f, 1.0f }, // blue
|
||||||
|
{ 0.5f, 0.0f, 0.5f, 1.0f }, // magenta
|
||||||
|
{ 0.0f, 0.5f, 0.5f, 1.0f }, // cyan
|
||||||
|
{ 0.75f, 0.75f, 0.75f, 1.0f }, // white
|
||||||
|
|
||||||
|
{ 0.5f, 0.5f, 0.5f, 1.0f }, // bright black
|
||||||
|
{ 1.0f, 0.0f, 0.0f, 1.0f }, // bright red
|
||||||
|
{ 0.0f, 1.0f, 0.0f, 1.0f }, // bright green
|
||||||
|
{ 1.0f, 1.0f, 0.0f, 1.0f }, // bright yellow
|
||||||
|
{ 0.0f, 0.0f, 1.0f, 1.0f }, // bright blue
|
||||||
|
{ 1.0f, 0.0f, 1.0f, 1.0f }, // bright magenta
|
||||||
|
{ 0.0f, 1.0f, 1.0f, 1.0f }, // bright cyan
|
||||||
|
{ 1.0f, 1.0f, 1.0f, 1.0f }, // bright white
|
||||||
|
|
||||||
|
{ 0.0f, 0.0f, 0.0f, 0.0f }, // faint black
|
||||||
|
{ 0.25f, 0.0f, 0.0f, 1.0f }, // faint red
|
||||||
|
{ 0.0f, 0.25f, 0.0f, 1.0f }, // faint green
|
||||||
|
{ 0.25f, 0.25f, 0.0f, 1.0f }, // faint yellow
|
||||||
|
{ 0.0f, 0.0f, 0.25f, 1.0f }, // faint blue
|
||||||
|
{ 0.25f, 0.0f, 0.25f, 1.0f }, // faint magenta
|
||||||
|
{ 0.0f, 0.25f, 0.25f, 1.0f }, // faint cyan
|
||||||
|
{ 0.375f, 0.375f, 0.375f, 1.0f }, // faint white
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint16_t tileId;
|
||||||
|
uint8_t frontPal;
|
||||||
|
uint8_t backPal;
|
||||||
|
} ConsoleChar;
|
||||||
|
|
||||||
|
static const DkVtxAttribState g_attribState[] = {
|
||||||
|
{ .bufferId=0, .isFixed=0, .offset=offsetof(ConsoleChar,tileId), .size=DkVtxAttribSize_1x16, .type=DkVtxAttribType_Uscaled, .isBgra=0 },
|
||||||
|
{ .bufferId=0, .isFixed=0, .offset=offsetof(ConsoleChar,frontPal), .size=DkVtxAttribSize_2x8, .type=DkVtxAttribType_Uint, .isBgra=0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const DkVtxBufferState g_vtxbufState[] = {
|
||||||
|
{ .stride=sizeof(ConsoleChar), .divisor=1 },
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GpuRenderer {
|
||||||
|
ConsoleRenderer base;
|
||||||
|
|
||||||
|
bool initialized;
|
||||||
|
|
||||||
|
DkDevice device;
|
||||||
|
DkQueue queue;
|
||||||
|
|
||||||
|
DkMemBlock imageMemBlock;
|
||||||
|
DkMemBlock codeMemBlock;
|
||||||
|
DkMemBlock dataMemBlock;
|
||||||
|
|
||||||
|
DkSwapchain swapchain;
|
||||||
|
DkImage framebuffers[FB_NUM];
|
||||||
|
DkImage tileset;
|
||||||
|
ConsoleChar* charBuf;
|
||||||
|
|
||||||
|
uint32_t codeMemOffset;
|
||||||
|
DkShader vertexShader;
|
||||||
|
DkShader fragmentShader;
|
||||||
|
|
||||||
|
DkCmdBuf cmdbuf;
|
||||||
|
DkCmdList cmdsBindFramebuffer[FB_NUM];
|
||||||
|
DkCmdList cmdsRender;
|
||||||
|
|
||||||
|
DkFence lastRenderFence;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct GpuRenderer* GpuRenderer(PrintConsole* con)
|
||||||
|
{
|
||||||
|
return (struct GpuRenderer*)con->renderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GpuRenderer_destroy(struct GpuRenderer* r)
|
||||||
|
{
|
||||||
|
// Make sure the queue is idle before destroying anything
|
||||||
|
dkQueueWaitIdle(r->queue);
|
||||||
|
|
||||||
|
// Destroy all the resources we've created
|
||||||
|
dkQueueDestroy(r->queue);
|
||||||
|
dkCmdBufDestroy(r->cmdbuf);
|
||||||
|
dkSwapchainDestroy(r->swapchain);
|
||||||
|
dkMemBlockDestroy(r->dataMemBlock);
|
||||||
|
dkMemBlockDestroy(r->codeMemBlock);
|
||||||
|
dkMemBlockDestroy(r->imageMemBlock);
|
||||||
|
dkDeviceDestroy(r->device);
|
||||||
|
|
||||||
|
// Clear out all state
|
||||||
|
memset(&r->initialized, 0, sizeof(*r) - offsetof(struct GpuRenderer, initialized));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple function for loading a shader from the filesystem
|
||||||
|
static void GpuRenderer_loadShader(struct GpuRenderer* r, DkShader* pShader, const char* path)
|
||||||
|
{
|
||||||
|
// Open the file, and retrieve its size
|
||||||
|
FILE* f = fopen(path, "rb");
|
||||||
|
fseek(f, 0, SEEK_END);
|
||||||
|
uint32_t size = ftell(f);
|
||||||
|
rewind(f);
|
||||||
|
|
||||||
|
// Look for a spot in the code memory block for loading this shader. Note that
|
||||||
|
// we are just using a simple incremental offset; this isn't a general purpose
|
||||||
|
// allocation algorithm.
|
||||||
|
uint32_t codeOffset = r->codeMemOffset;
|
||||||
|
r->codeMemOffset += (size + DK_SHADER_CODE_ALIGNMENT - 1) &~ (DK_SHADER_CODE_ALIGNMENT - 1);
|
||||||
|
|
||||||
|
// Read the file into memory, and close the file
|
||||||
|
fread((uint8_t*)dkMemBlockGetCpuAddr(r->codeMemBlock) + codeOffset, size, 1, f);
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
// Initialize the user provided shader object with the code we've just loaded
|
||||||
|
DkShaderMaker shaderMaker;
|
||||||
|
dkShaderMakerDefaults(&shaderMaker, r->codeMemBlock, codeOffset);
|
||||||
|
dkShaderInitialize(pShader, &shaderMaker);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool GpuRenderer_init(PrintConsole* con)
|
||||||
|
{
|
||||||
|
struct GpuRenderer* r = GpuRenderer(con);
|
||||||
|
|
||||||
|
if (r->initialized) {
|
||||||
|
// We're already initialized
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the deko3d device, which is the root object
|
||||||
|
DkDeviceMaker deviceMaker;
|
||||||
|
dkDeviceMakerDefaults(&deviceMaker);
|
||||||
|
r->device = dkDeviceCreate(&deviceMaker);
|
||||||
|
|
||||||
|
// Create the queue
|
||||||
|
DkQueueMaker queueMaker;
|
||||||
|
dkQueueMakerDefaults(&queueMaker, r->device);
|
||||||
|
queueMaker.flags = DkQueueFlags_Graphics;
|
||||||
|
r->queue = dkQueueCreate(&queueMaker);
|
||||||
|
|
||||||
|
// Calculate required width/height for the framebuffers
|
||||||
|
u32 width = con->font.tileWidth * con->consoleWidth;
|
||||||
|
u32 height = con->font.tileHeight * con->consoleHeight;
|
||||||
|
u32 totalConSize = con->consoleWidth * con->consoleHeight;
|
||||||
|
|
||||||
|
// Calculate layout for the framebuffers
|
||||||
|
DkImageLayoutMaker imageLayoutMaker;
|
||||||
|
dkImageLayoutMakerDefaults(&imageLayoutMaker, r->device);
|
||||||
|
imageLayoutMaker.flags = DkImageFlags_UsageRender | DkImageFlags_UsagePresent | DkImageFlags_HwCompression;
|
||||||
|
imageLayoutMaker.format = DkImageFormat_RGBA8_Unorm;
|
||||||
|
imageLayoutMaker.dimensions[0] = width;
|
||||||
|
imageLayoutMaker.dimensions[1] = height;
|
||||||
|
|
||||||
|
// Calculate layout for the framebuffers
|
||||||
|
DkImageLayout framebufferLayout;
|
||||||
|
dkImageLayoutInitialize(&framebufferLayout, &imageLayoutMaker);
|
||||||
|
|
||||||
|
// Calculate layout for the tileset
|
||||||
|
dkImageLayoutMakerDefaults(&imageLayoutMaker, r->device);
|
||||||
|
imageLayoutMaker.type = DkImageType_2DArray;
|
||||||
|
imageLayoutMaker.format = DkImageFormat_R32_Float;
|
||||||
|
imageLayoutMaker.dimensions[0] = con->font.tileWidth;
|
||||||
|
imageLayoutMaker.dimensions[1] = con->font.tileHeight;
|
||||||
|
imageLayoutMaker.dimensions[2] = con->font.numChars;
|
||||||
|
|
||||||
|
// Calculate layout for the tileset
|
||||||
|
DkImageLayout tilesetLayout;
|
||||||
|
dkImageLayoutInitialize(&tilesetLayout, &imageLayoutMaker);
|
||||||
|
|
||||||
|
// Retrieve necessary size and alignment for the framebuffers
|
||||||
|
uint32_t framebufferSize = dkImageLayoutGetSize(&framebufferLayout);
|
||||||
|
uint32_t framebufferAlign = dkImageLayoutGetAlignment(&framebufferLayout);
|
||||||
|
framebufferSize = (framebufferSize + framebufferAlign - 1) &~ (framebufferAlign - 1);
|
||||||
|
|
||||||
|
// Retrieve necessary size and alignment for the tileset
|
||||||
|
uint32_t tilesetSize = dkImageLayoutGetSize(&tilesetLayout);
|
||||||
|
uint32_t tilesetAlign = dkImageLayoutGetAlignment(&tilesetLayout);
|
||||||
|
tilesetSize = (tilesetSize + tilesetAlign - 1) &~ (tilesetAlign - 1);
|
||||||
|
|
||||||
|
// Create a memory block that will host the framebuffers and the tileset
|
||||||
|
DkMemBlockMaker memBlockMaker;
|
||||||
|
dkMemBlockMakerDefaults(&memBlockMaker, r->device, FB_NUM*framebufferSize + tilesetSize);
|
||||||
|
memBlockMaker.flags = DkMemBlockFlags_GpuCached | DkMemBlockFlags_Image;
|
||||||
|
r->imageMemBlock = dkMemBlockCreate(&memBlockMaker);
|
||||||
|
|
||||||
|
// Initialize the framebuffers with the layout and backing memory we've just created
|
||||||
|
DkImage const* swapchainImages[FB_NUM];
|
||||||
|
for (unsigned i = 0; i < FB_NUM; i ++) {
|
||||||
|
swapchainImages[i] = &r->framebuffers[i];
|
||||||
|
dkImageInitialize(&r->framebuffers[i], &framebufferLayout, r->imageMemBlock, i*framebufferSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a swapchain out of the framebuffers we've just initialized
|
||||||
|
DkSwapchainMaker swapchainMaker;
|
||||||
|
dkSwapchainMakerDefaults(&swapchainMaker, r->device, nwindowGetDefault(), swapchainImages, FB_NUM);
|
||||||
|
r->swapchain = dkSwapchainCreate(&swapchainMaker);
|
||||||
|
|
||||||
|
// Initialize the tileset
|
||||||
|
dkImageInitialize(&r->tileset, &tilesetLayout, r->imageMemBlock, FB_NUM*framebufferSize);
|
||||||
|
|
||||||
|
// Create a memory block onto which we will load shader code
|
||||||
|
dkMemBlockMakerDefaults(&memBlockMaker, r->device, CODEMEMSIZE);
|
||||||
|
memBlockMaker.flags = DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached | DkMemBlockFlags_Code;
|
||||||
|
r->codeMemBlock = dkMemBlockCreate(&memBlockMaker);
|
||||||
|
r->codeMemOffset = 0;
|
||||||
|
|
||||||
|
// Load our shaders (both vertex and fragment)
|
||||||
|
GpuRenderer_loadShader(r, &r->vertexShader, "romfs:/shaders/console_vsh.dksh");
|
||||||
|
GpuRenderer_loadShader(r, &r->fragmentShader, "romfs:/shaders/console_fsh.dksh");
|
||||||
|
|
||||||
|
// Generate the descriptors
|
||||||
|
struct {
|
||||||
|
DkImageDescriptor images[NUM_IMAGE_SLOTS];
|
||||||
|
DkSamplerDescriptor samplers[NUM_SAMPLER_SLOTS];
|
||||||
|
} descriptors;
|
||||||
|
|
||||||
|
// Generate a image descriptor for the tileset
|
||||||
|
DkImageView tilesetView;
|
||||||
|
dkImageViewDefaults(&tilesetView, &r->tileset);
|
||||||
|
dkImageDescriptorInitialize(&descriptors.images[0], &tilesetView, false, false);
|
||||||
|
|
||||||
|
// Generate a sampler descriptor for the tileset
|
||||||
|
DkSampler sampler;
|
||||||
|
dkSamplerDefaults(&sampler);
|
||||||
|
sampler.wrapMode[0] = DkWrapMode_ClampToEdge;
|
||||||
|
sampler.wrapMode[1] = DkWrapMode_ClampToEdge;
|
||||||
|
sampler.minFilter = DkFilter_Nearest;
|
||||||
|
sampler.magFilter = DkFilter_Nearest;
|
||||||
|
dkSamplerDescriptorInitialize(&descriptors.samplers[0], &sampler);
|
||||||
|
|
||||||
|
uint32_t descriptorsOffset = CMDMEMSIZE;
|
||||||
|
uint32_t configOffset = (descriptorsOffset + sizeof(descriptors) + DK_UNIFORM_BUF_ALIGNMENT - 1) &~ (DK_UNIFORM_BUF_ALIGNMENT - 1);
|
||||||
|
uint32_t configSize = (sizeof(ConsoleConfig) + DK_UNIFORM_BUF_ALIGNMENT - 1) &~ (DK_UNIFORM_BUF_ALIGNMENT - 1);
|
||||||
|
|
||||||
|
uint32_t charBufOffset = configOffset + configSize;
|
||||||
|
uint32_t charBufSize = totalConSize * sizeof(ConsoleChar);
|
||||||
|
|
||||||
|
// Create a memory block which will be used for recording command lists using a command buffer
|
||||||
|
dkMemBlockMakerDefaults(&memBlockMaker, r->device,
|
||||||
|
(charBufOffset + charBufSize + DK_MEMBLOCK_ALIGNMENT - 1) &~ (DK_MEMBLOCK_ALIGNMENT - 1)
|
||||||
|
);
|
||||||
|
memBlockMaker.flags = DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached;
|
||||||
|
r->dataMemBlock = dkMemBlockCreate(&memBlockMaker);
|
||||||
|
|
||||||
|
// Create a command buffer object
|
||||||
|
DkCmdBufMaker cmdbufMaker;
|
||||||
|
dkCmdBufMakerDefaults(&cmdbufMaker, r->device);
|
||||||
|
r->cmdbuf = dkCmdBufCreate(&cmdbufMaker);
|
||||||
|
|
||||||
|
// Feed our memory to the command buffer so that we can start recording commands
|
||||||
|
dkCmdBufAddMemory(r->cmdbuf, r->dataMemBlock, 0, CMDMEMSIZE);
|
||||||
|
|
||||||
|
// Create a temporary buffer that will hold the tileset
|
||||||
|
dkMemBlockMakerDefaults(&memBlockMaker, r->device,
|
||||||
|
(sizeof(float)*con->font.tileWidth*con->font.tileHeight*con->font.numChars + DK_MEMBLOCK_ALIGNMENT - 1) &~ (DK_MEMBLOCK_ALIGNMENT - 1)
|
||||||
|
);
|
||||||
|
memBlockMaker.flags = DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached;
|
||||||
|
DkMemBlock scratchMemBlock = dkMemBlockCreate(&memBlockMaker);
|
||||||
|
float* scratchMem = (float*)dkMemBlockGetCpuAddr(scratchMemBlock);
|
||||||
|
|
||||||
|
// Unpack 1bpp tileset into a texture image the GPU can read
|
||||||
|
unsigned packedTileWidth = (con->font.tileWidth+7)/8;
|
||||||
|
for (unsigned tile = 0; tile < con->font.numChars; tile ++) {
|
||||||
|
const uint8_t* data = (const uint8_t*)con->font.gfx + con->font.tileHeight*packedTileWidth*tile;
|
||||||
|
for (unsigned y = 0; y < con->font.tileHeight; y ++) {
|
||||||
|
const uint8_t* row = &data[packedTileWidth*(y+1)];
|
||||||
|
uint8_t c = 0;
|
||||||
|
for (unsigned x = 0; x < con->font.tileWidth; x ++) {
|
||||||
|
if (!(x & 7))
|
||||||
|
c = *--row;
|
||||||
|
*scratchMem++ = (c & 0x80) ? 1.0f : 0.0f;
|
||||||
|
c <<= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up configuration
|
||||||
|
DkGpuAddr configAddr = dkMemBlockGetGpuAddr(r->dataMemBlock) + configOffset;
|
||||||
|
ConsoleConfig consoleConfig = {};
|
||||||
|
consoleConfig.dimensions[0] = width;
|
||||||
|
consoleConfig.dimensions[1] = height;
|
||||||
|
consoleConfig.dimensions[2] = con->consoleWidth;
|
||||||
|
consoleConfig.dimensions[3] = con->consoleHeight;
|
||||||
|
memcpy(consoleConfig.vertices, g_vertexData, sizeof(g_vertexData));
|
||||||
|
memcpy(consoleConfig.palettes, g_paletteData, sizeof(g_paletteData));
|
||||||
|
|
||||||
|
// Generate a temporary command list for uploading stuff and run it
|
||||||
|
DkGpuAddr descriptorSet = dkMemBlockGetGpuAddr(r->dataMemBlock) + descriptorsOffset;
|
||||||
|
DkCopyBuf copySrc = { dkMemBlockGetGpuAddr(scratchMemBlock), 0, 0 };
|
||||||
|
DkImageRect copyDst = { 0, 0, 0, con->font.tileWidth, con->font.tileHeight, con->font.numChars };
|
||||||
|
dkCmdBufPushData(r->cmdbuf, descriptorSet, &descriptors, sizeof(descriptors));
|
||||||
|
dkCmdBufPushConstants(r->cmdbuf, configAddr, configSize, 0, sizeof(consoleConfig), &consoleConfig);
|
||||||
|
dkCmdBufBindImageDescriptorSet(r->cmdbuf, descriptorSet, NUM_IMAGE_SLOTS);
|
||||||
|
dkCmdBufBindSamplerDescriptorSet(r->cmdbuf, descriptorSet + NUM_IMAGE_SLOTS*sizeof(DkImageDescriptor), NUM_SAMPLER_SLOTS);
|
||||||
|
dkCmdBufCopyBufferToImage(r->cmdbuf, ©Src, &tilesetView, ©Dst, 0);
|
||||||
|
dkQueueSubmitCommands(r->queue, dkCmdBufFinishList(r->cmdbuf));
|
||||||
|
dkQueueFlush(r->queue);
|
||||||
|
dkQueueWaitIdle(r->queue);
|
||||||
|
dkCmdBufClear(r->cmdbuf);
|
||||||
|
|
||||||
|
// Destroy the scratch memory block since we don't need it anymore
|
||||||
|
dkMemBlockDestroy(scratchMemBlock);
|
||||||
|
|
||||||
|
// Retrieve the address of the character buffer
|
||||||
|
DkGpuAddr charBufAddr = dkMemBlockGetGpuAddr(r->dataMemBlock) + charBufOffset;
|
||||||
|
r->charBuf = (ConsoleChar*)((uint8_t*)dkMemBlockGetCpuAddr(r->dataMemBlock) + charBufOffset);
|
||||||
|
memset(r->charBuf, 0, charBufSize);
|
||||||
|
|
||||||
|
// Generate a command list for each framebuffer, which will bind each of them as a render target
|
||||||
|
for (unsigned i = 0; i < FB_NUM; i ++) {
|
||||||
|
DkImageView imageView;
|
||||||
|
dkImageViewDefaults(&imageView, &r->framebuffers[i]);
|
||||||
|
dkCmdBufBindRenderTarget(r->cmdbuf, &imageView, NULL);
|
||||||
|
r->cmdsBindFramebuffer[i] = dkCmdBufFinishList(r->cmdbuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Declare structs that will be used for binding state
|
||||||
|
DkViewport viewport = { 0.0f, 0.0f, (float)width, (float)height, 0.0f, 1.0f };
|
||||||
|
DkScissor scissor = { 0, 0, width, height };
|
||||||
|
DkShader const* shaders[] = { &r->vertexShader, &r->fragmentShader };
|
||||||
|
DkRasterizerState rasterizerState;
|
||||||
|
DkColorState colorState;
|
||||||
|
DkColorWriteState colorWriteState;
|
||||||
|
|
||||||
|
// Initialize state structs with the deko3d defaults
|
||||||
|
dkRasterizerStateDefaults(&rasterizerState);
|
||||||
|
dkColorStateDefaults(&colorState);
|
||||||
|
dkColorWriteStateDefaults(&colorWriteState);
|
||||||
|
|
||||||
|
rasterizerState.fillRectangleEnable = true;
|
||||||
|
colorState.alphaCompareOp = DkCompareOp_Greater;
|
||||||
|
|
||||||
|
// Generate the main rendering command list
|
||||||
|
dkCmdBufSetViewports(r->cmdbuf, 0, &viewport, 1);
|
||||||
|
dkCmdBufSetScissors(r->cmdbuf, 0, &scissor, 1);
|
||||||
|
//dkCmdBufClearColorFloat(r->cmdbuf, 0, DkColorMask_RGBA, 0.125f, 0.294f, 0.478f, 0.0f);
|
||||||
|
dkCmdBufClearColorFloat(r->cmdbuf, 0, DkColorMask_RGBA, 0.0f, 0.0f, 0.0f, 0.0f);
|
||||||
|
dkCmdBufBindShaders(r->cmdbuf, DkStageFlag_GraphicsMask, shaders, sizeof(shaders)/sizeof(shaders[0]));
|
||||||
|
dkCmdBufBindRasterizerState(r->cmdbuf, &rasterizerState);
|
||||||
|
dkCmdBufBindColorState(r->cmdbuf, &colorState);
|
||||||
|
dkCmdBufBindColorWriteState(r->cmdbuf, &colorWriteState);
|
||||||
|
dkCmdBufBindUniformBuffer(r->cmdbuf, DkStage_Vertex, 0, configAddr, configSize);
|
||||||
|
dkCmdBufBindTexture(r->cmdbuf, DkStage_Fragment, 0, dkMakeTextureHandle(0, 0));
|
||||||
|
dkCmdBufBindVtxAttribState(r->cmdbuf, g_attribState, sizeof(g_attribState)/sizeof(g_attribState[0]));
|
||||||
|
dkCmdBufBindVtxBufferState(r->cmdbuf, g_vtxbufState, sizeof(g_vtxbufState)/sizeof(g_vtxbufState[0]));
|
||||||
|
dkCmdBufBindVtxBuffer(r->cmdbuf, 0, charBufAddr, charBufSize);
|
||||||
|
dkCmdBufSetAlphaRef(r->cmdbuf, 0.0f);
|
||||||
|
dkCmdBufDraw(r->cmdbuf, DkPrimitive_Triangles, 3, totalConSize, 0, 0);
|
||||||
|
r->cmdsRender = dkCmdBufFinishList(r->cmdbuf);
|
||||||
|
|
||||||
|
r->initialized = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GpuRenderer_deinit(PrintConsole* con)
|
||||||
|
{
|
||||||
|
struct GpuRenderer* r = GpuRenderer(con);
|
||||||
|
|
||||||
|
if (r->initialized) {
|
||||||
|
GpuRenderer_destroy(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GpuRenderer_drawChar(PrintConsole* con, int x, int y, int c)
|
||||||
|
{
|
||||||
|
struct GpuRenderer* r = GpuRenderer(con);
|
||||||
|
|
||||||
|
int writingColor = con->fg;
|
||||||
|
int screenColor = con->bg;
|
||||||
|
|
||||||
|
if (con->flags & CONSOLE_COLOR_BOLD) {
|
||||||
|
writingColor += 8;
|
||||||
|
} else if (con->flags & CONSOLE_COLOR_FAINT) {
|
||||||
|
writingColor += 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (con->flags & CONSOLE_COLOR_REVERSE) {
|
||||||
|
int tmp = writingColor;
|
||||||
|
writingColor = screenColor;
|
||||||
|
screenColor = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the fence
|
||||||
|
dkFenceWait(&r->lastRenderFence, UINT64_MAX);
|
||||||
|
|
||||||
|
ConsoleChar* pos = &r->charBuf[y*con->consoleWidth+x];
|
||||||
|
pos->tileId = c;
|
||||||
|
pos->frontPal = writingColor;
|
||||||
|
pos->backPal = screenColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GpuRenderer_scrollWindow(PrintConsole* con)
|
||||||
|
{
|
||||||
|
struct GpuRenderer* r = GpuRenderer(con);
|
||||||
|
|
||||||
|
// Wait for the fence
|
||||||
|
dkFenceWait(&r->lastRenderFence, UINT64_MAX);
|
||||||
|
|
||||||
|
// Perform the scrolling
|
||||||
|
for (int y = 0; y < con->windowHeight-1; y ++) {
|
||||||
|
memcpy(
|
||||||
|
&r->charBuf[(con->windowY+y+0)*con->consoleWidth + con->windowX],
|
||||||
|
&r->charBuf[(con->windowY+y+1)*con->consoleWidth + con->windowX],
|
||||||
|
sizeof(ConsoleChar)*con->windowWidth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GpuRenderer_flushAndSwap(PrintConsole* con)
|
||||||
|
{
|
||||||
|
struct GpuRenderer* r = GpuRenderer(con);
|
||||||
|
|
||||||
|
// Acquire a framebuffer from the swapchain (and wait for it to be available)
|
||||||
|
int slot = dkQueueAcquireImage(r->queue, r->swapchain);
|
||||||
|
|
||||||
|
// Run the command list that binds said framebuffer as a render target
|
||||||
|
dkQueueSubmitCommands(r->queue, r->cmdsBindFramebuffer[slot]);
|
||||||
|
|
||||||
|
// Run the main rendering command list
|
||||||
|
dkQueueSubmitCommands(r->queue, r->cmdsRender);
|
||||||
|
|
||||||
|
// Signal the fence
|
||||||
|
dkQueueSignalFence(r->queue, &r->lastRenderFence, false);
|
||||||
|
|
||||||
|
// Now that we are done rendering, present it to the screen
|
||||||
|
dkQueuePresentImage(r->queue, r->swapchain, slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct GpuRenderer s_gpuRenderer =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
GpuRenderer_init,
|
||||||
|
GpuRenderer_deinit,
|
||||||
|
GpuRenderer_drawChar,
|
||||||
|
GpuRenderer_scrollWindow,
|
||||||
|
GpuRenderer_flushAndSwap,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ConsoleRenderer* getDefaultConsoleRenderer(void)
|
||||||
|
{
|
||||||
|
return &s_gpuRenderer.base;
|
||||||
|
}
|
86
graphics/deko3d/deko_console/source/main.c
Normal file
86
graphics/deko3d/deko_console/source/main.c
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <switch.h>
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
consoleInit(NULL);
|
||||||
|
|
||||||
|
// Configure our supported input layout: a single player with standard controller styles
|
||||||
|
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
|
||||||
|
|
||||||
|
// Initialize the default gamepad (which reads handheld mode inputs as well as the first connected controller)
|
||||||
|
PadState pad;
|
||||||
|
padInitializeDefault(&pad);
|
||||||
|
|
||||||
|
// clear screen and home cursor
|
||||||
|
printf( CONSOLE_ESC(2J) );
|
||||||
|
|
||||||
|
// Set print co-ordinates
|
||||||
|
// /x1b[row;columnH
|
||||||
|
printf(CONSOLE_ESC(42;37m));
|
||||||
|
printf(CONSOLE_ESC( 7;4H) "%s", " _ _ ____ _ ");
|
||||||
|
printf(CONSOLE_ESC( 8;4H) "%s", " | | | | |___ \\ | |");
|
||||||
|
printf(CONSOLE_ESC( 9;4H) "%s", " __| | ___| | _____ __) | __| |");
|
||||||
|
printf(CONSOLE_ESC(10;4H) "%s", " / _` |/ _ \\ |/ / _ \\|__ < / _` |");
|
||||||
|
printf(CONSOLE_ESC(11;4H) "%s", "| (_| | __/ < (_) |__) | (_| |");
|
||||||
|
printf(CONSOLE_ESC(12;4H) "%s", " \\__,_|\\___|_|\\_\\___/____/ \\__,_|");
|
||||||
|
printf(CONSOLE_ESC(0m));
|
||||||
|
|
||||||
|
// move cursor up
|
||||||
|
// /x1b[linesA
|
||||||
|
printf(CONSOLE_ESC(10A)"Line 0");
|
||||||
|
|
||||||
|
// move cursor left
|
||||||
|
// /x1b[columnsD
|
||||||
|
printf(CONSOLE_ESC(28D)"Column 0");
|
||||||
|
|
||||||
|
// move cursor down
|
||||||
|
// /x1b[linesB
|
||||||
|
printf(CONSOLE_ESC(19B)"Line 19");
|
||||||
|
|
||||||
|
// move cursor right
|
||||||
|
// /x1b[columnsC
|
||||||
|
printf(CONSOLE_ESC(5C)"Column 20");
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
// Color codes and attributes
|
||||||
|
for(int i=0; i<8; i++)
|
||||||
|
{
|
||||||
|
printf( CONSOLE_ESC(%1$dm) /* Set color */
|
||||||
|
"Default "
|
||||||
|
CONSOLE_ESC(1m) "Bold "
|
||||||
|
CONSOLE_ESC(7m) "Reversed "
|
||||||
|
|
||||||
|
CONSOLE_ESC(0m) /* revert attributes*/
|
||||||
|
CONSOLE_ESC(%1$dm)
|
||||||
|
|
||||||
|
CONSOLE_ESC(2m) "Light "
|
||||||
|
CONSOLE_ESC(7m) "Reversed "
|
||||||
|
|
||||||
|
"\n"
|
||||||
|
CONSOLE_ESC(0m) /* revert attributes*/
|
||||||
|
, i + 30);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main loop
|
||||||
|
while(appletMainLoop())
|
||||||
|
{
|
||||||
|
// Scan the gamepad. This should be done once for each frame
|
||||||
|
padUpdate(&pad);
|
||||||
|
|
||||||
|
// Your code goes here
|
||||||
|
|
||||||
|
// padGetButtonsDown returns the set of buttons that have been newly pressed in this frame compared to the previous one
|
||||||
|
u64 kDown = padGetButtonsDown(&pad);
|
||||||
|
|
||||||
|
if (kDown & HidNpadButton_Plus) break; // break in order to return to hbmenu
|
||||||
|
|
||||||
|
consoleUpdate(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
consoleExit(NULL);
|
||||||
|
return 0;
|
||||||
|
}
|
13
graphics/deko3d/deko_console/source/startup.c
Normal file
13
graphics/deko3d/deko_console/source/startup.c
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#include <switch.h>
|
||||||
|
|
||||||
|
void userAppInit(void)
|
||||||
|
{
|
||||||
|
Result res = romfsInit();
|
||||||
|
if (R_FAILED(res))
|
||||||
|
diagAbortWithResult(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
void userAppExit(void)
|
||||||
|
{
|
||||||
|
romfsExit();
|
||||||
|
}
|
271
graphics/deko3d/deko_examples/Makefile
Normal file
271
graphics/deko3d/deko_examples/Makefile
Normal file
@ -0,0 +1,271 @@
|
|||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
.SUFFIXES:
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
ifeq ($(strip $(DEVKITPRO)),)
|
||||||
|
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
|
||||||
|
endif
|
||||||
|
|
||||||
|
TOPDIR ?= $(CURDIR)
|
||||||
|
include $(DEVKITPRO)/libnx/switch_rules
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# TARGET is the name of the output
|
||||||
|
# BUILD is the directory where object files & intermediate files will be placed
|
||||||
|
# SOURCES is a list of directories containing source code
|
||||||
|
# DATA is a list of directories containing data files
|
||||||
|
# INCLUDES is a list of directories containing header files
|
||||||
|
# ROMFS is the directory containing data to be added to RomFS, relative to the Makefile (Optional)
|
||||||
|
#
|
||||||
|
# NO_ICON: if set to anything, do not use icon.
|
||||||
|
# NO_NACP: if set to anything, no .nacp file is generated.
|
||||||
|
# APP_TITLE is the name of the app stored in the .nacp file (Optional)
|
||||||
|
# APP_AUTHOR is the author of the app stored in the .nacp file (Optional)
|
||||||
|
# APP_VERSION is the version of the app stored in the .nacp file (Optional)
|
||||||
|
# APP_TITLEID is the titleID of the app stored in the .nacp file (Optional)
|
||||||
|
# ICON is the filename of the icon (.jpg), relative to the project folder.
|
||||||
|
# If not set, it attempts to use one of the following (in this order):
|
||||||
|
# - <Project name>.jpg
|
||||||
|
# - icon.jpg
|
||||||
|
# - <libnx folder>/default_icon.jpg
|
||||||
|
#
|
||||||
|
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
|
||||||
|
# If not set, it attempts to use one of the following (in this order):
|
||||||
|
# - <Project name>.json
|
||||||
|
# - config.json
|
||||||
|
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
|
||||||
|
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
|
||||||
|
# NACP building is skipped as well.
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
TARGET := $(notdir $(CURDIR))
|
||||||
|
BUILD := build
|
||||||
|
SOURCES := source source/SampleFramework
|
||||||
|
DATA := data
|
||||||
|
INCLUDES := include
|
||||||
|
ROMFS := romfs
|
||||||
|
|
||||||
|
# Output folders for autogenerated files in romfs
|
||||||
|
OUT_SHADERS := shaders
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# options for code generation
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
|
||||||
|
|
||||||
|
CFLAGS := -g -Wall -O2 -ffunction-sections \
|
||||||
|
$(ARCH) $(DEFINES)
|
||||||
|
|
||||||
|
CFLAGS += $(INCLUDE) -D__SWITCH__
|
||||||
|
|
||||||
|
CXXFLAGS := $(CFLAGS) -std=gnu++17 -fno-exceptions -fno-rtti
|
||||||
|
|
||||||
|
ASFLAGS := -g $(ARCH)
|
||||||
|
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
|
||||||
|
|
||||||
|
LIBS := -ldeko3dd -lnx
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# list of directories containing libraries, this must be the top level containing
|
||||||
|
# include and lib
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
LIBDIRS := $(PORTLIBS) $(LIBNX)
|
||||||
|
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# no real need to edit anything past this point unless you need to add additional
|
||||||
|
# rules for different file extensions
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export OUTPUT := $(CURDIR)/$(TARGET)
|
||||||
|
export TOPDIR := $(CURDIR)
|
||||||
|
|
||||||
|
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||||
|
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
|
||||||
|
|
||||||
|
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||||
|
|
||||||
|
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
|
||||||
|
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
|
||||||
|
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
||||||
|
GLSLFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.glsl)))
|
||||||
|
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# use CXX for linking C++ projects, CC for standard C
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
ifeq ($(strip $(CPPFILES)),)
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
export LD := $(CC)
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
else
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
export LD := $(CXX)
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
endif
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
|
||||||
|
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
|
||||||
|
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
|
||||||
|
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
|
||||||
|
|
||||||
|
ifneq ($(strip $(ROMFS)),)
|
||||||
|
ROMFS_TARGETS :=
|
||||||
|
ROMFS_FOLDERS :=
|
||||||
|
ifneq ($(strip $(OUT_SHADERS)),)
|
||||||
|
ROMFS_SHADERS := $(ROMFS)/$(OUT_SHADERS)
|
||||||
|
ROMFS_TARGETS += $(patsubst %.glsl, $(ROMFS_SHADERS)/%.dksh, $(GLSLFILES))
|
||||||
|
ROMFS_FOLDERS += $(ROMFS_SHADERS)
|
||||||
|
endif
|
||||||
|
|
||||||
|
export ROMFS_DEPS := $(foreach file,$(ROMFS_TARGETS),$(CURDIR)/$(file))
|
||||||
|
endif
|
||||||
|
|
||||||
|
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
||||||
|
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||||
|
-I$(CURDIR)/$(BUILD)
|
||||||
|
|
||||||
|
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||||
|
|
||||||
|
ifeq ($(strip $(CONFIG_JSON)),)
|
||||||
|
jsons := $(wildcard *.json)
|
||||||
|
ifneq (,$(findstring $(TARGET).json,$(jsons)))
|
||||||
|
export APP_JSON := $(TOPDIR)/$(TARGET).json
|
||||||
|
else
|
||||||
|
ifneq (,$(findstring config.json,$(jsons)))
|
||||||
|
export APP_JSON := $(TOPDIR)/config.json
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(strip $(ICON)),)
|
||||||
|
icons := $(wildcard *.jpg)
|
||||||
|
ifneq (,$(findstring $(TARGET).jpg,$(icons)))
|
||||||
|
export APP_ICON := $(TOPDIR)/$(TARGET).jpg
|
||||||
|
else
|
||||||
|
ifneq (,$(findstring icon.jpg,$(icons)))
|
||||||
|
export APP_ICON := $(TOPDIR)/icon.jpg
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
export APP_ICON := $(TOPDIR)/$(ICON)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(strip $(NO_ICON)),)
|
||||||
|
export NROFLAGS += --icon=$(APP_ICON)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(strip $(NO_NACP)),)
|
||||||
|
export NROFLAGS += --nacp=$(CURDIR)/$(TARGET).nacp
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq ($(APP_TITLEID),)
|
||||||
|
export NACPFLAGS += --titleid=$(APP_TITLEID)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq ($(ROMFS),)
|
||||||
|
export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS)
|
||||||
|
endif
|
||||||
|
|
||||||
|
.PHONY: all clean
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
all: $(ROMFS_TARGETS) | $(BUILD)
|
||||||
|
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||||
|
|
||||||
|
$(BUILD):
|
||||||
|
@mkdir -p $@
|
||||||
|
|
||||||
|
ifneq ($(strip $(ROMFS_TARGETS)),)
|
||||||
|
|
||||||
|
$(ROMFS_TARGETS): | $(ROMFS_FOLDERS)
|
||||||
|
|
||||||
|
$(ROMFS_FOLDERS):
|
||||||
|
@mkdir -p $@
|
||||||
|
|
||||||
|
$(ROMFS_SHADERS)/%_vsh.dksh: %_vsh.glsl
|
||||||
|
@echo {vert} $(notdir $<)
|
||||||
|
@uam -s vert -o $@ $<
|
||||||
|
|
||||||
|
$(ROMFS_SHADERS)/%_tcsh.dksh: %_tcsh.glsl
|
||||||
|
@echo {tess_ctrl} $(notdir $<)
|
||||||
|
@uam -s tess_ctrl -o $@ $<
|
||||||
|
|
||||||
|
$(ROMFS_SHADERS)/%_tesh.dksh: %_tesh.glsl
|
||||||
|
@echo {tess_eval} $(notdir $<)
|
||||||
|
@uam -s tess_eval -o $@ $<
|
||||||
|
|
||||||
|
$(ROMFS_SHADERS)/%_gsh.dksh: %_gsh.glsl
|
||||||
|
@echo {geom} $(notdir $<)
|
||||||
|
@uam -s geom -o $@ $<
|
||||||
|
|
||||||
|
$(ROMFS_SHADERS)/%_fsh.dksh: %_fsh.glsl
|
||||||
|
@echo {frag} $(notdir $<)
|
||||||
|
@uam -s frag -o $@ $<
|
||||||
|
|
||||||
|
$(ROMFS_SHADERS)/%.dksh: %.glsl
|
||||||
|
@echo {comp} $(notdir $<)
|
||||||
|
@uam -s comp -o $@ $<
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
clean:
|
||||||
|
@echo clean ...
|
||||||
|
ifeq ($(strip $(APP_JSON)),)
|
||||||
|
@rm -fr $(BUILD) $(ROMFS_FOLDERS) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
|
||||||
|
else
|
||||||
|
@rm -fr $(BUILD) $(ROMFS_FOLDERS) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
else
|
||||||
|
.PHONY: all
|
||||||
|
|
||||||
|
DEPENDS := $(OFILES:.o=.d)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# main targets
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
ifeq ($(strip $(APP_JSON)),)
|
||||||
|
|
||||||
|
all : $(OUTPUT).nro
|
||||||
|
|
||||||
|
ifeq ($(strip $(NO_NACP)),)
|
||||||
|
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp $(ROMFS_DEPS)
|
||||||
|
else
|
||||||
|
$(OUTPUT).nro : $(OUTPUT).elf $(ROMFS_DEPS)
|
||||||
|
endif
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
all : $(OUTPUT).nsp
|
||||||
|
|
||||||
|
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
|
||||||
|
|
||||||
|
$(OUTPUT).nso : $(OUTPUT).elf
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
|
$(OUTPUT).elf : $(OFILES)
|
||||||
|
|
||||||
|
$(OFILES_SRC) : $(HFILES_BIN)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# you need a rule like this for each extension you use as binary data
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
%.bin.o %_bin.h : %.bin
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
@echo $(notdir $<)
|
||||||
|
@$(bin2o)
|
||||||
|
|
||||||
|
-include $(DEPENDS)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------------
|
||||||
|
endif
|
||||||
|
#---------------------------------------------------------------------------------------
|
BIN
graphics/deko3d/deko_examples/romfs/cat-256x256.bc1
Normal file
BIN
graphics/deko3d/deko_examples/romfs/cat-256x256.bc1
Normal file
Binary file not shown.
BIN
graphics/deko3d/deko_examples/romfs/teapot-idx.bin
Normal file
BIN
graphics/deko3d/deko_examples/romfs/teapot-idx.bin
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
BIN
graphics/deko3d/deko_examples/romfs/teapot-vtx.bin
Normal file
BIN
graphics/deko3d/deko_examples/romfs/teapot-vtx.bin
Normal file
Binary file not shown.
182
graphics/deko3d/deko_examples/source/Example01_SimpleSetup.cpp
Normal file
182
graphics/deko3d/deko_examples/source/Example01_SimpleSetup.cpp
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
/*
|
||||||
|
** deko3d Example 01: Simple Setup
|
||||||
|
** This example shows how to setup deko3d for rendering scenes with the GPU.
|
||||||
|
** New concepts in this example:
|
||||||
|
** - Creating devices and queues
|
||||||
|
** - Basic memory management
|
||||||
|
** - Setting up framebuffers and swapchains
|
||||||
|
** - Recording a static command list with rendering commands
|
||||||
|
** - Acquiring and presenting images with the queue and swapchain
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Sample Framework headers
|
||||||
|
#include "SampleFramework/CApplication.h"
|
||||||
|
#include "SampleFramework/CMemPool.h"
|
||||||
|
|
||||||
|
// C++ standard library headers
|
||||||
|
#include <array>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
class CExample01 final : public CApplication
|
||||||
|
{
|
||||||
|
static constexpr unsigned NumFramebuffers = 2;
|
||||||
|
static constexpr uint32_t FramebufferWidth = 1280;
|
||||||
|
static constexpr uint32_t FramebufferHeight = 720;
|
||||||
|
static constexpr unsigned StaticCmdSize = 0x1000;
|
||||||
|
|
||||||
|
PadState pad;
|
||||||
|
|
||||||
|
dk::UniqueDevice device;
|
||||||
|
dk::UniqueQueue queue;
|
||||||
|
|
||||||
|
std::optional<CMemPool> pool_images;
|
||||||
|
std::optional<CMemPool> pool_data;
|
||||||
|
|
||||||
|
dk::UniqueCmdBuf cmdbuf;
|
||||||
|
|
||||||
|
CMemPool::Handle framebuffers_mem[NumFramebuffers];
|
||||||
|
dk::Image framebuffers[NumFramebuffers];
|
||||||
|
DkCmdList framebuffer_cmdlists[NumFramebuffers];
|
||||||
|
dk::UniqueSwapchain swapchain;
|
||||||
|
|
||||||
|
DkCmdList render_cmdlist;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CExample01()
|
||||||
|
{
|
||||||
|
// Create the deko3d device
|
||||||
|
device = dk::DeviceMaker{}.create();
|
||||||
|
|
||||||
|
// Create the main queue
|
||||||
|
queue = dk::QueueMaker{device}.setFlags(DkQueueFlags_Graphics).create();
|
||||||
|
|
||||||
|
// Create the memory pools
|
||||||
|
pool_images.emplace(device, DkMemBlockFlags_GpuCached | DkMemBlockFlags_Image, 16*1024*1024);
|
||||||
|
pool_data.emplace(device, DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached, 1*1024*1024);
|
||||||
|
|
||||||
|
// Create the static command buffer and feed it freshly allocated memory
|
||||||
|
cmdbuf = dk::CmdBufMaker{device}.create();
|
||||||
|
CMemPool::Handle cmdmem = pool_data->allocate(StaticCmdSize);
|
||||||
|
cmdbuf.addMemory(cmdmem.getMemBlock(), cmdmem.getOffset(), cmdmem.getSize());
|
||||||
|
|
||||||
|
// Create the framebuffer resources
|
||||||
|
createFramebufferResources();
|
||||||
|
|
||||||
|
// Initialize gamepad
|
||||||
|
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
|
||||||
|
padInitializeDefault(&pad);
|
||||||
|
}
|
||||||
|
|
||||||
|
~CExample01()
|
||||||
|
{
|
||||||
|
// Destroy the framebuffer resources
|
||||||
|
destroyFramebufferResources();
|
||||||
|
}
|
||||||
|
|
||||||
|
void createFramebufferResources()
|
||||||
|
{
|
||||||
|
// Create layout for the framebuffers
|
||||||
|
dk::ImageLayout layout_framebuffer;
|
||||||
|
dk::ImageLayoutMaker{device}
|
||||||
|
.setFlags(DkImageFlags_UsageRender | DkImageFlags_UsagePresent | DkImageFlags_HwCompression)
|
||||||
|
.setFormat(DkImageFormat_RGBA8_Unorm)
|
||||||
|
.setDimensions(FramebufferWidth, FramebufferHeight)
|
||||||
|
.initialize(layout_framebuffer);
|
||||||
|
|
||||||
|
// Create the framebuffers
|
||||||
|
std::array<DkImage const*, NumFramebuffers> fb_array;
|
||||||
|
uint64_t fb_size = layout_framebuffer.getSize();
|
||||||
|
uint32_t fb_align = layout_framebuffer.getAlignment();
|
||||||
|
for (unsigned i = 0; i < NumFramebuffers; i ++)
|
||||||
|
{
|
||||||
|
// Allocate a framebuffer
|
||||||
|
framebuffers_mem[i] = pool_images->allocate(fb_size, fb_align);
|
||||||
|
framebuffers[i].initialize(layout_framebuffer, framebuffers_mem[i].getMemBlock(), framebuffers_mem[i].getOffset());
|
||||||
|
|
||||||
|
// Generate a command list that binds it
|
||||||
|
dk::ImageView colorTarget{ framebuffers[i] };
|
||||||
|
cmdbuf.bindRenderTargets(&colorTarget);
|
||||||
|
framebuffer_cmdlists[i] = cmdbuf.finishList();
|
||||||
|
|
||||||
|
// Fill in the array for use later by the swapchain creation code
|
||||||
|
fb_array[i] = &framebuffers[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the swapchain using the framebuffers
|
||||||
|
swapchain = dk::SwapchainMaker{device, nwindowGetDefault(), fb_array}.create();
|
||||||
|
|
||||||
|
// Generate the main rendering cmdlist
|
||||||
|
recordStaticCommands();
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroyFramebufferResources()
|
||||||
|
{
|
||||||
|
// Return early if we have nothing to destroy
|
||||||
|
if (!swapchain) return;
|
||||||
|
|
||||||
|
// Make sure the queue is idle before destroying anything
|
||||||
|
queue.waitIdle();
|
||||||
|
|
||||||
|
// Clear the static cmdbuf, destroying the static cmdlists in the process
|
||||||
|
cmdbuf.clear();
|
||||||
|
|
||||||
|
// Destroy the swapchain
|
||||||
|
swapchain.destroy();
|
||||||
|
|
||||||
|
// Destroy the framebuffers
|
||||||
|
for (unsigned i = 0; i < NumFramebuffers; i ++)
|
||||||
|
framebuffers_mem[i].destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void recordStaticCommands()
|
||||||
|
{
|
||||||
|
// Calculate several measurements for the scene
|
||||||
|
unsigned HalfWidth = FramebufferWidth/2, HalfHeight = FramebufferHeight/2;
|
||||||
|
unsigned BoxSize = 400;
|
||||||
|
unsigned BoxX = HalfWidth - BoxSize/2, BoxY = HalfHeight - BoxSize/2;
|
||||||
|
unsigned TileWidth = BoxSize/5, TileHeight = BoxSize/4;
|
||||||
|
|
||||||
|
// Draw a scene using only scissors and clear colors
|
||||||
|
cmdbuf.setScissors(0, { { 0, 0, FramebufferWidth, FramebufferHeight } });
|
||||||
|
cmdbuf.clearColor(0, DkColorMask_RGBA, 0.0f, 0.25f, 0.0f, 1.0f);
|
||||||
|
cmdbuf.setScissors(0, { { BoxX, BoxY, BoxSize, BoxSize } });
|
||||||
|
cmdbuf.clearColor(0, DkColorMask_RGBA, 229/255.0f, 1.0f, 232/255.0f, 1.0f);
|
||||||
|
cmdbuf.setScissors(0, { { BoxX + 2*TileWidth, BoxY + 1*TileHeight, 1*TileWidth, 1*TileHeight } });
|
||||||
|
cmdbuf.clearColor(0, DkColorMask_RGBA, 0.0f, 0.5f, 0.0f, 1.0f);
|
||||||
|
cmdbuf.setScissors(0, { { BoxX + 1*TileWidth, BoxY + 2*TileHeight, 3*TileWidth, 1*TileHeight } });
|
||||||
|
cmdbuf.clearColor(0, DkColorMask_RGBA, 0.0f, 0.5f, 0.0f, 1.0f);
|
||||||
|
render_cmdlist = cmdbuf.finishList();
|
||||||
|
}
|
||||||
|
|
||||||
|
void render()
|
||||||
|
{
|
||||||
|
// Acquire a framebuffer from the swapchain (and wait for it to be available)
|
||||||
|
int slot = queue.acquireImage(swapchain);
|
||||||
|
|
||||||
|
// Run the command list that attaches said framebuffer to the queue
|
||||||
|
queue.submitCommands(framebuffer_cmdlists[slot]);
|
||||||
|
|
||||||
|
// Run the main rendering command list
|
||||||
|
queue.submitCommands(render_cmdlist);
|
||||||
|
|
||||||
|
// Now that we are done rendering, present it to the screen
|
||||||
|
queue.presentImage(swapchain, slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool onFrame(u64 ns) override
|
||||||
|
{
|
||||||
|
padUpdate(&pad);
|
||||||
|
u64 kDown = padGetButtonsDown(&pad);
|
||||||
|
if (kDown & HidNpadButton_Plus)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
render();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void Example01(void)
|
||||||
|
{
|
||||||
|
CExample01 app;
|
||||||
|
app.run();
|
||||||
|
}
|
237
graphics/deko3d/deko_examples/source/Example02_Triangle.cpp
Normal file
237
graphics/deko3d/deko_examples/source/Example02_Triangle.cpp
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
/*
|
||||||
|
** deko3d Example 02: Triangle
|
||||||
|
** This example shows how to draw a basic multi-colored triangle.
|
||||||
|
** New concepts in this example:
|
||||||
|
** - Loading and using shaders
|
||||||
|
** - Setting up basic 3D engine state
|
||||||
|
** - Setting up vertex attributes and vertex buffers
|
||||||
|
** - Drawing primitives
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Sample Framework headers
|
||||||
|
#include "SampleFramework/CApplication.h"
|
||||||
|
#include "SampleFramework/CMemPool.h"
|
||||||
|
#include "SampleFramework/CShader.h"
|
||||||
|
|
||||||
|
// C++ standard library headers
|
||||||
|
#include <array>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
struct Vertex
|
||||||
|
{
|
||||||
|
float position[3];
|
||||||
|
float color[3];
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr std::array VertexAttribState =
|
||||||
|
{
|
||||||
|
DkVtxAttribState{ 0, 0, offsetof(Vertex, position), DkVtxAttribSize_3x32, DkVtxAttribType_Float, 0 },
|
||||||
|
DkVtxAttribState{ 0, 0, offsetof(Vertex, color), DkVtxAttribSize_3x32, DkVtxAttribType_Float, 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr std::array VertexBufferState =
|
||||||
|
{
|
||||||
|
DkVtxBufferState{ sizeof(Vertex), 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr std::array TriangleVertexData =
|
||||||
|
{
|
||||||
|
Vertex{ { 0.0f, +1.0f, 0.0f }, { 1.0f, 0.0f, 0.0f } },
|
||||||
|
Vertex{ { -1.0f, -1.0f, 0.0f }, { 0.0f, 1.0f, 0.0f } },
|
||||||
|
Vertex{ { +1.0f, -1.0f, 0.0f }, { 0.0f, 0.0f, 1.0f } },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class CExample02 final : public CApplication
|
||||||
|
{
|
||||||
|
static constexpr unsigned NumFramebuffers = 2;
|
||||||
|
static constexpr uint32_t FramebufferWidth = 1280;
|
||||||
|
static constexpr uint32_t FramebufferHeight = 720;
|
||||||
|
static constexpr unsigned StaticCmdSize = 0x10000;
|
||||||
|
|
||||||
|
PadState pad;
|
||||||
|
|
||||||
|
dk::UniqueDevice device;
|
||||||
|
dk::UniqueQueue queue;
|
||||||
|
|
||||||
|
std::optional<CMemPool> pool_images;
|
||||||
|
std::optional<CMemPool> pool_code;
|
||||||
|
std::optional<CMemPool> pool_data;
|
||||||
|
|
||||||
|
dk::UniqueCmdBuf cmdbuf;
|
||||||
|
|
||||||
|
CShader vertexShader;
|
||||||
|
CShader fragmentShader;
|
||||||
|
|
||||||
|
CMemPool::Handle vertexBuffer;
|
||||||
|
|
||||||
|
CMemPool::Handle framebuffers_mem[NumFramebuffers];
|
||||||
|
dk::Image framebuffers[NumFramebuffers];
|
||||||
|
DkCmdList framebuffer_cmdlists[NumFramebuffers];
|
||||||
|
dk::UniqueSwapchain swapchain;
|
||||||
|
|
||||||
|
DkCmdList render_cmdlist;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CExample02()
|
||||||
|
{
|
||||||
|
// Create the deko3d device
|
||||||
|
device = dk::DeviceMaker{}.create();
|
||||||
|
|
||||||
|
// Create the main queue
|
||||||
|
queue = dk::QueueMaker{device}.setFlags(DkQueueFlags_Graphics).create();
|
||||||
|
|
||||||
|
// Create the memory pools
|
||||||
|
pool_images.emplace(device, DkMemBlockFlags_GpuCached | DkMemBlockFlags_Image, 16*1024*1024);
|
||||||
|
pool_code.emplace(device, DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached | DkMemBlockFlags_Code, 128*1024);
|
||||||
|
pool_data.emplace(device, DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached, 1*1024*1024);
|
||||||
|
|
||||||
|
// Create the static command buffer and feed it freshly allocated memory
|
||||||
|
cmdbuf = dk::CmdBufMaker{device}.create();
|
||||||
|
CMemPool::Handle cmdmem = pool_data->allocate(StaticCmdSize);
|
||||||
|
cmdbuf.addMemory(cmdmem.getMemBlock(), cmdmem.getOffset(), cmdmem.getSize());
|
||||||
|
|
||||||
|
// Load the shaders
|
||||||
|
vertexShader.load(*pool_code, "romfs:/shaders/basic_vsh.dksh");
|
||||||
|
fragmentShader.load(*pool_code, "romfs:/shaders/color_fsh.dksh");
|
||||||
|
|
||||||
|
// Load the vertex buffer
|
||||||
|
vertexBuffer = pool_data->allocate(sizeof(TriangleVertexData), alignof(Vertex));
|
||||||
|
memcpy(vertexBuffer.getCpuAddr(), TriangleVertexData.data(), vertexBuffer.getSize());
|
||||||
|
|
||||||
|
// Create the framebuffer resources
|
||||||
|
createFramebufferResources();
|
||||||
|
|
||||||
|
// Initialize gamepad
|
||||||
|
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
|
||||||
|
padInitializeDefault(&pad);
|
||||||
|
}
|
||||||
|
|
||||||
|
~CExample02()
|
||||||
|
{
|
||||||
|
// Destroy the framebuffer resources
|
||||||
|
destroyFramebufferResources();
|
||||||
|
|
||||||
|
// Destroy the vertex buffer (not strictly needed in this case)
|
||||||
|
vertexBuffer.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void createFramebufferResources()
|
||||||
|
{
|
||||||
|
// Create layout for the framebuffers
|
||||||
|
dk::ImageLayout layout_framebuffer;
|
||||||
|
dk::ImageLayoutMaker{device}
|
||||||
|
.setFlags(DkImageFlags_UsageRender | DkImageFlags_UsagePresent | DkImageFlags_HwCompression)
|
||||||
|
.setFormat(DkImageFormat_RGBA8_Unorm)
|
||||||
|
.setDimensions(FramebufferWidth, FramebufferHeight)
|
||||||
|
.initialize(layout_framebuffer);
|
||||||
|
|
||||||
|
// Create the framebuffers
|
||||||
|
std::array<DkImage const*, NumFramebuffers> fb_array;
|
||||||
|
uint64_t fb_size = layout_framebuffer.getSize();
|
||||||
|
uint32_t fb_align = layout_framebuffer.getAlignment();
|
||||||
|
for (unsigned i = 0; i < NumFramebuffers; i ++)
|
||||||
|
{
|
||||||
|
// Allocate a framebuffer
|
||||||
|
framebuffers_mem[i] = pool_images->allocate(fb_size, fb_align);
|
||||||
|
framebuffers[i].initialize(layout_framebuffer, framebuffers_mem[i].getMemBlock(), framebuffers_mem[i].getOffset());
|
||||||
|
|
||||||
|
// Generate a command list that binds it
|
||||||
|
dk::ImageView colorTarget{ framebuffers[i] };
|
||||||
|
cmdbuf.bindRenderTargets(&colorTarget);
|
||||||
|
framebuffer_cmdlists[i] = cmdbuf.finishList();
|
||||||
|
|
||||||
|
// Fill in the array for use later by the swapchain creation code
|
||||||
|
fb_array[i] = &framebuffers[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the swapchain using the framebuffers
|
||||||
|
swapchain = dk::SwapchainMaker{device, nwindowGetDefault(), fb_array}.create();
|
||||||
|
|
||||||
|
// Generate the main rendering cmdlist
|
||||||
|
recordStaticCommands();
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroyFramebufferResources()
|
||||||
|
{
|
||||||
|
// Return early if we have nothing to destroy
|
||||||
|
if (!swapchain) return;
|
||||||
|
|
||||||
|
// Make sure the queue is idle before destroying anything
|
||||||
|
queue.waitIdle();
|
||||||
|
|
||||||
|
// Clear the static cmdbuf, destroying the static cmdlists in the process
|
||||||
|
cmdbuf.clear();
|
||||||
|
|
||||||
|
// Destroy the swapchain
|
||||||
|
swapchain.destroy();
|
||||||
|
|
||||||
|
// Destroy the framebuffers
|
||||||
|
for (unsigned i = 0; i < NumFramebuffers; i ++)
|
||||||
|
framebuffers_mem[i].destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void recordStaticCommands()
|
||||||
|
{
|
||||||
|
// Initialize state structs with deko3d defaults
|
||||||
|
dk::RasterizerState rasterizerState;
|
||||||
|
dk::ColorState colorState;
|
||||||
|
dk::ColorWriteState colorWriteState;
|
||||||
|
|
||||||
|
// Configure viewport and scissor
|
||||||
|
cmdbuf.setViewports(0, { { 0.0f, 0.0f, FramebufferWidth, FramebufferHeight, 0.0f, 1.0f } });
|
||||||
|
cmdbuf.setScissors(0, { { 0, 0, FramebufferWidth, FramebufferHeight } });
|
||||||
|
|
||||||
|
// Clear the color buffer
|
||||||
|
cmdbuf.clearColor(0, DkColorMask_RGBA, 0.0f, 0.0f, 0.0f, 0.0f);
|
||||||
|
|
||||||
|
// Bind state required for drawing the triangle
|
||||||
|
cmdbuf.bindShaders(DkStageFlag_GraphicsMask, { vertexShader, fragmentShader });
|
||||||
|
cmdbuf.bindRasterizerState(rasterizerState);
|
||||||
|
cmdbuf.bindColorState(colorState);
|
||||||
|
cmdbuf.bindColorWriteState(colorWriteState);
|
||||||
|
cmdbuf.bindVtxBuffer(0, vertexBuffer.getGpuAddr(), vertexBuffer.getSize());
|
||||||
|
cmdbuf.bindVtxAttribState(VertexAttribState);
|
||||||
|
cmdbuf.bindVtxBufferState(VertexBufferState);
|
||||||
|
|
||||||
|
// Draw the triangle
|
||||||
|
cmdbuf.draw(DkPrimitive_Triangles, TriangleVertexData.size(), 1, 0, 0);
|
||||||
|
|
||||||
|
// Finish off this command list
|
||||||
|
render_cmdlist = cmdbuf.finishList();
|
||||||
|
}
|
||||||
|
|
||||||
|
void render()
|
||||||
|
{
|
||||||
|
// Acquire a framebuffer from the swapchain (and wait for it to be available)
|
||||||
|
int slot = queue.acquireImage(swapchain);
|
||||||
|
|
||||||
|
// Run the command list that attaches said framebuffer to the queue
|
||||||
|
queue.submitCommands(framebuffer_cmdlists[slot]);
|
||||||
|
|
||||||
|
// Run the main rendering command list
|
||||||
|
queue.submitCommands(render_cmdlist);
|
||||||
|
|
||||||
|
// Now that we are done rendering, present it to the screen
|
||||||
|
queue.presentImage(swapchain, slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool onFrame(u64 ns) override
|
||||||
|
{
|
||||||
|
padUpdate(&pad);
|
||||||
|
u64 kDown = padGetButtonsDown(&pad);
|
||||||
|
if (kDown & HidNpadButton_Plus)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
render();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void Example02(void)
|
||||||
|
{
|
||||||
|
CExample02 app;
|
||||||
|
app.run();
|
||||||
|
}
|
378
graphics/deko3d/deko_examples/source/Example03_Cube.cpp
Normal file
378
graphics/deko3d/deko_examples/source/Example03_Cube.cpp
Normal file
@ -0,0 +1,378 @@
|
|||||||
|
/*
|
||||||
|
** deko3d Example 03: Cube
|
||||||
|
** This example shows how to draw a basic rotating cube.
|
||||||
|
** New concepts in this example:
|
||||||
|
** - Setting up and using a depth buffer
|
||||||
|
** - Setting up uniform buffers
|
||||||
|
** - Basic 3D maths, including projection matrices
|
||||||
|
** - Updating uniforms with a dynamic command buffer
|
||||||
|
** - Adjusting resolution dynamically by recreating resources (720p handheld/1080p docked)
|
||||||
|
** - Depth buffer discard after a barrier
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Sample Framework headers
|
||||||
|
#include "SampleFramework/CApplication.h"
|
||||||
|
#include "SampleFramework/CMemPool.h"
|
||||||
|
#include "SampleFramework/CShader.h"
|
||||||
|
#include "SampleFramework/CCmdMemRing.h"
|
||||||
|
|
||||||
|
// C++ standard library headers
|
||||||
|
#include <array>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
// GLM headers
|
||||||
|
#define GLM_FORCE_DEFAULT_ALIGNED_GENTYPES // Enforces GLSL std140/std430 alignment rules for glm types
|
||||||
|
#define GLM_FORCE_INTRINSICS // Enables usage of SIMD CPU instructions (requiring the above as well)
|
||||||
|
#include <glm/vec3.hpp>
|
||||||
|
#include <glm/vec4.hpp>
|
||||||
|
#include <glm/mat4x4.hpp>
|
||||||
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
struct Vertex
|
||||||
|
{
|
||||||
|
float position[3];
|
||||||
|
float color[3];
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr std::array VertexAttribState =
|
||||||
|
{
|
||||||
|
DkVtxAttribState{ 0, 0, offsetof(Vertex, position), DkVtxAttribSize_3x32, DkVtxAttribType_Float, 0 },
|
||||||
|
DkVtxAttribState{ 0, 0, offsetof(Vertex, color), DkVtxAttribSize_3x32, DkVtxAttribType_Float, 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr std::array VertexBufferState =
|
||||||
|
{
|
||||||
|
DkVtxBufferState{ sizeof(Vertex), 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr std::array CubeVertexData =
|
||||||
|
{
|
||||||
|
// +X face
|
||||||
|
Vertex{ { +1.0f, +1.0f, +1.0f }, { 1.0f, 0.0f, 0.0f } },
|
||||||
|
Vertex{ { +1.0f, -1.0f, +1.0f }, { 0.0f, 1.0f, 0.0f } },
|
||||||
|
Vertex{ { +1.0f, -1.0f, -1.0f }, { 0.0f, 0.0f, 1.0f } },
|
||||||
|
Vertex{ { +1.0f, +1.0f, -1.0f }, { 1.0f, 1.0f, 0.0f } },
|
||||||
|
|
||||||
|
// -X face
|
||||||
|
Vertex{ { -1.0f, +1.0f, -1.0f }, { 1.0f, 0.0f, 0.0f } },
|
||||||
|
Vertex{ { -1.0f, -1.0f, -1.0f }, { 0.0f, 1.0f, 0.0f } },
|
||||||
|
Vertex{ { -1.0f, -1.0f, +1.0f }, { 0.0f, 0.0f, 1.0f } },
|
||||||
|
Vertex{ { -1.0f, +1.0f, +1.0f }, { 1.0f, 1.0f, 0.0f } },
|
||||||
|
|
||||||
|
// +Y face
|
||||||
|
Vertex{ { -1.0f, +1.0f, -1.0f }, { 1.0f, 0.0f, 0.0f } },
|
||||||
|
Vertex{ { -1.0f, +1.0f, +1.0f }, { 0.0f, 1.0f, 0.0f } },
|
||||||
|
Vertex{ { +1.0f, +1.0f, +1.0f }, { 0.0f, 0.0f, 1.0f } },
|
||||||
|
Vertex{ { +1.0f, +1.0f, -1.0f }, { 1.0f, 1.0f, 0.0f } },
|
||||||
|
|
||||||
|
// -Y face
|
||||||
|
Vertex{ { -1.0f, -1.0f, +1.0f }, { 1.0f, 0.0f, 0.0f } },
|
||||||
|
Vertex{ { -1.0f, -1.0f, -1.0f }, { 0.0f, 1.0f, 0.0f } },
|
||||||
|
Vertex{ { +1.0f, -1.0f, -1.0f }, { 0.0f, 0.0f, 1.0f } },
|
||||||
|
Vertex{ { +1.0f, -1.0f, +1.0f }, { 1.0f, 1.0f, 0.0f } },
|
||||||
|
|
||||||
|
// +Z face
|
||||||
|
Vertex{ { -1.0f, +1.0f, +1.0f }, { 1.0f, 0.0f, 0.0f } },
|
||||||
|
Vertex{ { -1.0f, -1.0f, +1.0f }, { 0.0f, 1.0f, 0.0f } },
|
||||||
|
Vertex{ { +1.0f, -1.0f, +1.0f }, { 0.0f, 0.0f, 1.0f } },
|
||||||
|
Vertex{ { +1.0f, +1.0f, +1.0f }, { 1.0f, 1.0f, 0.0f } },
|
||||||
|
|
||||||
|
// -Z face
|
||||||
|
Vertex{ { +1.0f, +1.0f, -1.0f }, { 1.0f, 0.0f, 0.0f } },
|
||||||
|
Vertex{ { +1.0f, -1.0f, -1.0f }, { 0.0f, 1.0f, 0.0f } },
|
||||||
|
Vertex{ { -1.0f, -1.0f, -1.0f }, { 0.0f, 0.0f, 1.0f } },
|
||||||
|
Vertex{ { -1.0f, +1.0f, -1.0f }, { 1.0f, 1.0f, 0.0f } },
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Transformation
|
||||||
|
{
|
||||||
|
glm::mat4 mdlvMtx;
|
||||||
|
glm::mat4 projMtx;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline float fractf(float x)
|
||||||
|
{
|
||||||
|
return x - floorf(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CExample03 final : public CApplication
|
||||||
|
{
|
||||||
|
static constexpr unsigned NumFramebuffers = 2;
|
||||||
|
static constexpr unsigned StaticCmdSize = 0x10000;
|
||||||
|
static constexpr unsigned DynamicCmdSize = 0x10000;
|
||||||
|
|
||||||
|
PadState pad;
|
||||||
|
|
||||||
|
dk::UniqueDevice device;
|
||||||
|
dk::UniqueQueue queue;
|
||||||
|
|
||||||
|
std::optional<CMemPool> pool_images;
|
||||||
|
std::optional<CMemPool> pool_code;
|
||||||
|
std::optional<CMemPool> pool_data;
|
||||||
|
|
||||||
|
dk::UniqueCmdBuf cmdbuf;
|
||||||
|
dk::UniqueCmdBuf dyncmd;
|
||||||
|
CCmdMemRing<NumFramebuffers> dynmem;
|
||||||
|
|
||||||
|
CShader vertexShader;
|
||||||
|
CShader fragmentShader;
|
||||||
|
|
||||||
|
Transformation transformState;
|
||||||
|
CMemPool::Handle transformUniformBuffer;
|
||||||
|
|
||||||
|
CMemPool::Handle vertexBuffer;
|
||||||
|
|
||||||
|
uint32_t framebufferWidth;
|
||||||
|
uint32_t framebufferHeight;
|
||||||
|
|
||||||
|
CMemPool::Handle depthBuffer_mem;
|
||||||
|
CMemPool::Handle framebuffers_mem[NumFramebuffers];
|
||||||
|
|
||||||
|
dk::Image depthBuffer;
|
||||||
|
dk::Image framebuffers[NumFramebuffers];
|
||||||
|
DkCmdList framebuffer_cmdlists[NumFramebuffers];
|
||||||
|
dk::UniqueSwapchain swapchain;
|
||||||
|
|
||||||
|
DkCmdList render_cmdlist;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CExample03()
|
||||||
|
{
|
||||||
|
// Create the deko3d device
|
||||||
|
device = dk::DeviceMaker{}.create();
|
||||||
|
|
||||||
|
// Create the main queue
|
||||||
|
queue = dk::QueueMaker{device}.setFlags(DkQueueFlags_Graphics).create();
|
||||||
|
|
||||||
|
// Create the memory pools
|
||||||
|
pool_images.emplace(device, DkMemBlockFlags_GpuCached | DkMemBlockFlags_Image, 16*1024*1024);
|
||||||
|
pool_code.emplace(device, DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached | DkMemBlockFlags_Code, 128*1024);
|
||||||
|
pool_data.emplace(device, DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached, 1*1024*1024);
|
||||||
|
|
||||||
|
// Create the static command buffer and feed it freshly allocated memory
|
||||||
|
cmdbuf = dk::CmdBufMaker{device}.create();
|
||||||
|
CMemPool::Handle cmdmem = pool_data->allocate(StaticCmdSize);
|
||||||
|
cmdbuf.addMemory(cmdmem.getMemBlock(), cmdmem.getOffset(), cmdmem.getSize());
|
||||||
|
|
||||||
|
// Create the dynamic command buffer and allocate memory for it
|
||||||
|
dyncmd = dk::CmdBufMaker{device}.create();
|
||||||
|
dynmem.allocate(*pool_data, DynamicCmdSize);
|
||||||
|
|
||||||
|
// Load the shaders
|
||||||
|
vertexShader.load(*pool_code, "romfs:/shaders/transform_vsh.dksh");
|
||||||
|
fragmentShader.load(*pool_code, "romfs:/shaders/color_fsh.dksh");
|
||||||
|
|
||||||
|
// Create the transformation uniform buffer
|
||||||
|
transformUniformBuffer = pool_data->allocate(sizeof(transformState), DK_UNIFORM_BUF_ALIGNMENT);
|
||||||
|
|
||||||
|
// Load the vertex buffer
|
||||||
|
vertexBuffer = pool_data->allocate(sizeof(CubeVertexData), alignof(Vertex));
|
||||||
|
memcpy(vertexBuffer.getCpuAddr(), CubeVertexData.data(), vertexBuffer.getSize());
|
||||||
|
|
||||||
|
// Initialize gamepad
|
||||||
|
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
|
||||||
|
padInitializeDefault(&pad);
|
||||||
|
}
|
||||||
|
|
||||||
|
~CExample03()
|
||||||
|
{
|
||||||
|
// Destroy the framebuffer resources
|
||||||
|
destroyFramebufferResources();
|
||||||
|
|
||||||
|
// Destroy the vertex buffer (not strictly needed in this case)
|
||||||
|
vertexBuffer.destroy();
|
||||||
|
|
||||||
|
// Destroy the uniform buffer (not strictly needed in this case)
|
||||||
|
transformUniformBuffer.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void createFramebufferResources()
|
||||||
|
{
|
||||||
|
// Create layout for the depth buffer
|
||||||
|
dk::ImageLayout layout_depthbuffer;
|
||||||
|
dk::ImageLayoutMaker{device}
|
||||||
|
.setFlags(DkImageFlags_UsageRender | DkImageFlags_HwCompression)
|
||||||
|
.setFormat(DkImageFormat_Z24S8)
|
||||||
|
.setDimensions(framebufferWidth, framebufferHeight)
|
||||||
|
.initialize(layout_depthbuffer);
|
||||||
|
|
||||||
|
// Create the depth buffer
|
||||||
|
depthBuffer_mem = pool_images->allocate(layout_depthbuffer.getSize(), layout_depthbuffer.getAlignment());
|
||||||
|
depthBuffer.initialize(layout_depthbuffer, depthBuffer_mem.getMemBlock(), depthBuffer_mem.getOffset());
|
||||||
|
|
||||||
|
// Create layout for the framebuffers
|
||||||
|
dk::ImageLayout layout_framebuffer;
|
||||||
|
dk::ImageLayoutMaker{device}
|
||||||
|
.setFlags(DkImageFlags_UsageRender | DkImageFlags_UsagePresent | DkImageFlags_HwCompression)
|
||||||
|
.setFormat(DkImageFormat_RGBA8_Unorm)
|
||||||
|
.setDimensions(framebufferWidth, framebufferHeight)
|
||||||
|
.initialize(layout_framebuffer);
|
||||||
|
|
||||||
|
// Create the framebuffers
|
||||||
|
std::array<DkImage const*, NumFramebuffers> fb_array;
|
||||||
|
uint64_t fb_size = layout_framebuffer.getSize();
|
||||||
|
uint32_t fb_align = layout_framebuffer.getAlignment();
|
||||||
|
for (unsigned i = 0; i < NumFramebuffers; i ++)
|
||||||
|
{
|
||||||
|
// Allocate a framebuffer
|
||||||
|
framebuffers_mem[i] = pool_images->allocate(fb_size, fb_align);
|
||||||
|
framebuffers[i].initialize(layout_framebuffer, framebuffers_mem[i].getMemBlock(), framebuffers_mem[i].getOffset());
|
||||||
|
|
||||||
|
// Generate a command list that binds it
|
||||||
|
dk::ImageView colorTarget{ framebuffers[i] }, depthTarget{ depthBuffer };
|
||||||
|
cmdbuf.bindRenderTargets(&colorTarget, &depthTarget);
|
||||||
|
framebuffer_cmdlists[i] = cmdbuf.finishList();
|
||||||
|
|
||||||
|
// Fill in the array for use later by the swapchain creation code
|
||||||
|
fb_array[i] = &framebuffers[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the swapchain using the framebuffers
|
||||||
|
swapchain = dk::SwapchainMaker{device, nwindowGetDefault(), fb_array}.create();
|
||||||
|
|
||||||
|
// Generate the main rendering cmdlist
|
||||||
|
recordStaticCommands();
|
||||||
|
|
||||||
|
// Initialize the projection matrix
|
||||||
|
transformState.projMtx = glm::perspectiveRH_ZO(
|
||||||
|
glm::radians(40.0f),
|
||||||
|
float(framebufferWidth)/float(framebufferHeight),
|
||||||
|
0.01f, 1000.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroyFramebufferResources()
|
||||||
|
{
|
||||||
|
// Return early if we have nothing to destroy
|
||||||
|
if (!swapchain) return;
|
||||||
|
|
||||||
|
// Make sure the queue is idle before destroying anything
|
||||||
|
queue.waitIdle();
|
||||||
|
|
||||||
|
// Clear the static cmdbuf, destroying the static cmdlists in the process
|
||||||
|
cmdbuf.clear();
|
||||||
|
|
||||||
|
// Destroy the swapchain
|
||||||
|
swapchain.destroy();
|
||||||
|
|
||||||
|
// Destroy the framebuffers
|
||||||
|
for (unsigned i = 0; i < NumFramebuffers; i ++)
|
||||||
|
framebuffers_mem[i].destroy();
|
||||||
|
|
||||||
|
// Destroy the depth buffer
|
||||||
|
depthBuffer_mem.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void recordStaticCommands()
|
||||||
|
{
|
||||||
|
// Initialize state structs with deko3d defaults
|
||||||
|
dk::RasterizerState rasterizerState;
|
||||||
|
dk::ColorState colorState;
|
||||||
|
dk::ColorWriteState colorWriteState;
|
||||||
|
dk::DepthStencilState depthStencilState;
|
||||||
|
|
||||||
|
// Configure viewport and scissor
|
||||||
|
cmdbuf.setViewports(0, { { 0.0f, 0.0f, (float)framebufferWidth, (float)framebufferHeight, 0.0f, 1.0f } });
|
||||||
|
cmdbuf.setScissors(0, { { 0, 0, framebufferWidth, framebufferHeight } });
|
||||||
|
|
||||||
|
// Clear the color and depth buffers
|
||||||
|
cmdbuf.clearColor(0, DkColorMask_RGBA, 0.0f, 0.0f, 0.0f, 0.0f);
|
||||||
|
cmdbuf.clearDepthStencil(true, 1.0f, 0xFF, 0);
|
||||||
|
|
||||||
|
// Bind state required for drawing the cube
|
||||||
|
cmdbuf.bindShaders(DkStageFlag_GraphicsMask, { vertexShader, fragmentShader });
|
||||||
|
cmdbuf.bindUniformBuffer(DkStage_Vertex, 0, transformUniformBuffer.getGpuAddr(), transformUniformBuffer.getSize());
|
||||||
|
cmdbuf.bindRasterizerState(rasterizerState);
|
||||||
|
cmdbuf.bindColorState(colorState);
|
||||||
|
cmdbuf.bindColorWriteState(colorWriteState);
|
||||||
|
cmdbuf.bindDepthStencilState(depthStencilState);
|
||||||
|
cmdbuf.bindVtxBuffer(0, vertexBuffer.getGpuAddr(), vertexBuffer.getSize());
|
||||||
|
cmdbuf.bindVtxAttribState(VertexAttribState);
|
||||||
|
cmdbuf.bindVtxBufferState(VertexBufferState);
|
||||||
|
|
||||||
|
// Draw the cube
|
||||||
|
cmdbuf.draw(DkPrimitive_Quads, CubeVertexData.size(), 1, 0, 0);
|
||||||
|
|
||||||
|
// Fragment barrier, to make sure we finish previous work before discarding the depth buffer
|
||||||
|
cmdbuf.barrier(DkBarrier_Fragments, 0);
|
||||||
|
|
||||||
|
// Discard the depth buffer since we don't need it anymore
|
||||||
|
cmdbuf.discardDepthStencil();
|
||||||
|
|
||||||
|
// Finish off this command list
|
||||||
|
render_cmdlist = cmdbuf.finishList();
|
||||||
|
}
|
||||||
|
|
||||||
|
void render()
|
||||||
|
{
|
||||||
|
// Begin generating the dynamic command list, for commands that need to be sent only this frame specifically
|
||||||
|
dynmem.begin(dyncmd);
|
||||||
|
|
||||||
|
// Update the uniform buffer with the new transformation state (this data gets inlined in the command list)
|
||||||
|
dyncmd.pushConstants(
|
||||||
|
transformUniformBuffer.getGpuAddr(), transformUniformBuffer.getSize(),
|
||||||
|
0, sizeof(transformState), &transformState);
|
||||||
|
|
||||||
|
// Finish off the dynamic command list, and submit it to the queue
|
||||||
|
queue.submitCommands(dynmem.end(dyncmd));
|
||||||
|
|
||||||
|
// Acquire a framebuffer from the swapchain (and wait for it to be available)
|
||||||
|
int slot = queue.acquireImage(swapchain);
|
||||||
|
|
||||||
|
// Run the command list that attaches said framebuffer to the queue
|
||||||
|
queue.submitCommands(framebuffer_cmdlists[slot]);
|
||||||
|
|
||||||
|
// Run the main rendering command list
|
||||||
|
queue.submitCommands(render_cmdlist);
|
||||||
|
|
||||||
|
// Now that we are done rendering, present it to the screen
|
||||||
|
queue.presentImage(swapchain, slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onOperationMode(AppletOperationMode mode) override
|
||||||
|
{
|
||||||
|
// Destroy the framebuffer resources
|
||||||
|
destroyFramebufferResources();
|
||||||
|
|
||||||
|
// Choose framebuffer size
|
||||||
|
chooseFramebufferSize(framebufferWidth, framebufferHeight, mode);
|
||||||
|
|
||||||
|
// Recreate the framebuffers and its associated resources
|
||||||
|
createFramebufferResources();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool onFrame(u64 ns) override
|
||||||
|
{
|
||||||
|
padUpdate(&pad);
|
||||||
|
u64 kDown = padGetButtonsDown(&pad);
|
||||||
|
if (kDown & HidNpadButton_Plus)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
float time = ns / 1000000000.0; // double precision division; followed by implicit cast to single precision
|
||||||
|
float tau = glm::two_pi<float>();
|
||||||
|
|
||||||
|
float period1 = fractf(time/8.0f);
|
||||||
|
float period2 = fractf(time/4.0f);
|
||||||
|
|
||||||
|
// Generate the model-view matrix for this frame
|
||||||
|
// Keep in mind that GLM transformation functions multiply to the right, so essentially we have:
|
||||||
|
// mdlvMtx = Translate * RotateX * RotateY * Scale
|
||||||
|
// This means that the Scale operation is applied first, then RotateY, and so on.
|
||||||
|
transformState.mdlvMtx = glm::mat4{1.0f};
|
||||||
|
transformState.mdlvMtx = glm::translate(transformState.mdlvMtx, glm::vec3{0.0f, 0.0f, -3.0f});
|
||||||
|
transformState.mdlvMtx = glm::rotate(transformState.mdlvMtx, sinf(period2 * tau) * tau / 8.0f, glm::vec3{1.0f, 0.0f, 0.0f});
|
||||||
|
transformState.mdlvMtx = glm::rotate(transformState.mdlvMtx, -period1 * tau, glm::vec3{0.0f, 1.0f, 0.0f});
|
||||||
|
transformState.mdlvMtx = glm::scale(transformState.mdlvMtx, glm::vec3{0.5f});
|
||||||
|
|
||||||
|
render();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void Example03(void)
|
||||||
|
{
|
||||||
|
CExample03 app;
|
||||||
|
app.run();
|
||||||
|
}
|
420
graphics/deko3d/deko_examples/source/Example04_TexturedCube.cpp
Normal file
420
graphics/deko3d/deko_examples/source/Example04_TexturedCube.cpp
Normal file
@ -0,0 +1,420 @@
|
|||||||
|
/*
|
||||||
|
** deko3d Example 04: Textured Cube
|
||||||
|
** This example shows how to render a textured cube.
|
||||||
|
** New concepts in this example:
|
||||||
|
** - Loading a texture image from the filesystem
|
||||||
|
** - Creating and using image descriptors
|
||||||
|
** - Creating and using samplers and sampler descriptors
|
||||||
|
** - Calculating combined image+sampler handles for use by shaders
|
||||||
|
** - Initializing persistent state in a queue
|
||||||
|
**
|
||||||
|
** The texture used in this example was borrowed from https://pixabay.com/photos/cat-animal-pet-cats-close-up-300572/
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Sample Framework headers
|
||||||
|
#include "SampleFramework/CApplication.h"
|
||||||
|
#include "SampleFramework/CMemPool.h"
|
||||||
|
#include "SampleFramework/CShader.h"
|
||||||
|
#include "SampleFramework/CCmdMemRing.h"
|
||||||
|
#include "SampleFramework/CDescriptorSet.h"
|
||||||
|
#include "SampleFramework/CExternalImage.h"
|
||||||
|
|
||||||
|
// C++ standard library headers
|
||||||
|
#include <array>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
// GLM headers
|
||||||
|
#define GLM_FORCE_DEFAULT_ALIGNED_GENTYPES // Enforces GLSL std140/std430 alignment rules for glm types
|
||||||
|
#define GLM_FORCE_INTRINSICS // Enables usage of SIMD CPU instructions (requiring the above as well)
|
||||||
|
#include <glm/vec3.hpp>
|
||||||
|
#include <glm/vec4.hpp>
|
||||||
|
#include <glm/mat4x4.hpp>
|
||||||
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
struct Vertex
|
||||||
|
{
|
||||||
|
float position[3];
|
||||||
|
float texcoord[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr std::array VertexAttribState =
|
||||||
|
{
|
||||||
|
DkVtxAttribState{ 0, 0, offsetof(Vertex, position), DkVtxAttribSize_3x32, DkVtxAttribType_Float, 0 },
|
||||||
|
DkVtxAttribState{ 0, 0, offsetof(Vertex, texcoord), DkVtxAttribSize_2x32, DkVtxAttribType_Float, 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr std::array VertexBufferState =
|
||||||
|
{
|
||||||
|
DkVtxBufferState{ sizeof(Vertex), 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr std::array CubeVertexData =
|
||||||
|
{
|
||||||
|
// +X face
|
||||||
|
Vertex{ { +1.0f, +1.0f, +1.0f }, { 0.0f, 0.0f } },
|
||||||
|
Vertex{ { +1.0f, -1.0f, +1.0f }, { 0.0f, 1.0f } },
|
||||||
|
Vertex{ { +1.0f, -1.0f, -1.0f }, { 1.0f, 1.0f } },
|
||||||
|
Vertex{ { +1.0f, +1.0f, -1.0f }, { 1.0f, 0.0f } },
|
||||||
|
|
||||||
|
// -X face
|
||||||
|
Vertex{ { -1.0f, +1.0f, -1.0f }, { 0.0f, 0.0f } },
|
||||||
|
Vertex{ { -1.0f, -1.0f, -1.0f }, { 0.0f, 1.0f } },
|
||||||
|
Vertex{ { -1.0f, -1.0f, +1.0f }, { 1.0f, 1.0f } },
|
||||||
|
Vertex{ { -1.0f, +1.0f, +1.0f }, { 1.0f, 0.0f } },
|
||||||
|
|
||||||
|
// +Y face
|
||||||
|
Vertex{ { -1.0f, +1.0f, -1.0f }, { 0.0f, 0.0f } },
|
||||||
|
Vertex{ { -1.0f, +1.0f, +1.0f }, { 0.0f, 1.0f } },
|
||||||
|
Vertex{ { +1.0f, +1.0f, +1.0f }, { 1.0f, 1.0f } },
|
||||||
|
Vertex{ { +1.0f, +1.0f, -1.0f }, { 1.0f, 0.0f } },
|
||||||
|
|
||||||
|
// -Y face
|
||||||
|
Vertex{ { -1.0f, -1.0f, +1.0f }, { 0.0f, 0.0f } },
|
||||||
|
Vertex{ { -1.0f, -1.0f, -1.0f }, { 0.0f, 1.0f } },
|
||||||
|
Vertex{ { +1.0f, -1.0f, -1.0f }, { 1.0f, 1.0f } },
|
||||||
|
Vertex{ { +1.0f, -1.0f, +1.0f }, { 1.0f, 0.0f } },
|
||||||
|
|
||||||
|
// +Z face
|
||||||
|
Vertex{ { -1.0f, +1.0f, +1.0f }, { 0.0f, 0.0f } },
|
||||||
|
Vertex{ { -1.0f, -1.0f, +1.0f }, { 0.0f, 1.0f } },
|
||||||
|
Vertex{ { +1.0f, -1.0f, +1.0f }, { 1.0f, 1.0f } },
|
||||||
|
Vertex{ { +1.0f, +1.0f, +1.0f }, { 1.0f, 0.0f } },
|
||||||
|
|
||||||
|
// -Z face
|
||||||
|
Vertex{ { +1.0f, +1.0f, -1.0f }, { 0.0f, 0.0f } },
|
||||||
|
Vertex{ { +1.0f, -1.0f, -1.0f }, { 0.0f, 1.0f } },
|
||||||
|
Vertex{ { -1.0f, -1.0f, -1.0f }, { 1.0f, 1.0f } },
|
||||||
|
Vertex{ { -1.0f, +1.0f, -1.0f }, { 1.0f, 0.0f } },
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Transformation
|
||||||
|
{
|
||||||
|
glm::mat4 mdlvMtx;
|
||||||
|
glm::mat4 projMtx;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline float fractf(float x)
|
||||||
|
{
|
||||||
|
return x - floorf(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CExample04 final : public CApplication
|
||||||
|
{
|
||||||
|
static constexpr unsigned NumFramebuffers = 2;
|
||||||
|
static constexpr unsigned StaticCmdSize = 0x10000;
|
||||||
|
static constexpr unsigned DynamicCmdSize = 0x10000;
|
||||||
|
static constexpr unsigned MaxImages = 1;
|
||||||
|
static constexpr unsigned MaxSamplers = 1;
|
||||||
|
|
||||||
|
PadState pad;
|
||||||
|
|
||||||
|
dk::UniqueDevice device;
|
||||||
|
dk::UniqueQueue queue;
|
||||||
|
|
||||||
|
std::optional<CMemPool> pool_images;
|
||||||
|
std::optional<CMemPool> pool_code;
|
||||||
|
std::optional<CMemPool> pool_data;
|
||||||
|
|
||||||
|
dk::UniqueCmdBuf cmdbuf;
|
||||||
|
dk::UniqueCmdBuf dyncmd;
|
||||||
|
CCmdMemRing<NumFramebuffers> dynmem;
|
||||||
|
|
||||||
|
CDescriptorSet<MaxImages> imageDescriptorSet;
|
||||||
|
CDescriptorSet<MaxSamplers> samplerDescriptorSet;
|
||||||
|
|
||||||
|
CShader vertexShader;
|
||||||
|
CShader fragmentShader;
|
||||||
|
|
||||||
|
Transformation transformState;
|
||||||
|
CMemPool::Handle transformUniformBuffer;
|
||||||
|
|
||||||
|
CMemPool::Handle vertexBuffer;
|
||||||
|
CExternalImage texImage;
|
||||||
|
|
||||||
|
uint32_t framebufferWidth;
|
||||||
|
uint32_t framebufferHeight;
|
||||||
|
|
||||||
|
CMemPool::Handle depthBuffer_mem;
|
||||||
|
CMemPool::Handle framebuffers_mem[NumFramebuffers];
|
||||||
|
|
||||||
|
dk::Image depthBuffer;
|
||||||
|
dk::Image framebuffers[NumFramebuffers];
|
||||||
|
DkCmdList framebuffer_cmdlists[NumFramebuffers];
|
||||||
|
dk::UniqueSwapchain swapchain;
|
||||||
|
|
||||||
|
DkCmdList render_cmdlist;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CExample04()
|
||||||
|
{
|
||||||
|
// Create the deko3d device
|
||||||
|
device = dk::DeviceMaker{}.create();
|
||||||
|
|
||||||
|
// Create the main queue
|
||||||
|
queue = dk::QueueMaker{device}.setFlags(DkQueueFlags_Graphics).create();
|
||||||
|
|
||||||
|
// Create the memory pools
|
||||||
|
pool_images.emplace(device, DkMemBlockFlags_GpuCached | DkMemBlockFlags_Image, 16*1024*1024);
|
||||||
|
pool_code.emplace(device, DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached | DkMemBlockFlags_Code, 128*1024);
|
||||||
|
pool_data.emplace(device, DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached, 1*1024*1024);
|
||||||
|
|
||||||
|
// Create the static command buffer and feed it freshly allocated memory
|
||||||
|
cmdbuf = dk::CmdBufMaker{device}.create();
|
||||||
|
CMemPool::Handle cmdmem = pool_data->allocate(StaticCmdSize);
|
||||||
|
cmdbuf.addMemory(cmdmem.getMemBlock(), cmdmem.getOffset(), cmdmem.getSize());
|
||||||
|
|
||||||
|
// Create the dynamic command buffer and allocate memory for it
|
||||||
|
dyncmd = dk::CmdBufMaker{device}.create();
|
||||||
|
dynmem.allocate(*pool_data, DynamicCmdSize);
|
||||||
|
|
||||||
|
// Create the image and sampler descriptor sets
|
||||||
|
imageDescriptorSet.allocate(*pool_data);
|
||||||
|
samplerDescriptorSet.allocate(*pool_data);
|
||||||
|
|
||||||
|
// Load the shaders
|
||||||
|
vertexShader.load(*pool_code, "romfs:/shaders/transform_vsh.dksh");
|
||||||
|
fragmentShader.load(*pool_code, "romfs:/shaders/texture_fsh.dksh");
|
||||||
|
|
||||||
|
// Create the transformation uniform buffer
|
||||||
|
transformUniformBuffer = pool_data->allocate(sizeof(transformState), DK_UNIFORM_BUF_ALIGNMENT);
|
||||||
|
|
||||||
|
// Load the vertex buffer
|
||||||
|
vertexBuffer = pool_data->allocate(sizeof(CubeVertexData), alignof(Vertex));
|
||||||
|
memcpy(vertexBuffer.getCpuAddr(), CubeVertexData.data(), vertexBuffer.getSize());
|
||||||
|
|
||||||
|
// Load the image
|
||||||
|
texImage.load(*pool_images, *pool_data, device, queue, "romfs:/cat-256x256.bc1", 256, 256, DkImageFormat_RGB_BC1);
|
||||||
|
|
||||||
|
// Configure persistent state in the queue
|
||||||
|
{
|
||||||
|
// Upload the image descriptor
|
||||||
|
imageDescriptorSet.update(cmdbuf, 0, texImage.getDescriptor());
|
||||||
|
|
||||||
|
// Configure a sampler
|
||||||
|
dk::Sampler sampler;
|
||||||
|
sampler.setFilter(DkFilter_Linear, DkFilter_Linear);
|
||||||
|
sampler.setWrapMode(DkWrapMode_ClampToEdge, DkWrapMode_ClampToEdge, DkWrapMode_ClampToEdge);
|
||||||
|
|
||||||
|
// Upload the sampler descriptor
|
||||||
|
dk::SamplerDescriptor samplerDescriptor;
|
||||||
|
samplerDescriptor.initialize(sampler);
|
||||||
|
samplerDescriptorSet.update(cmdbuf, 0, samplerDescriptor);
|
||||||
|
|
||||||
|
// Bind the image and sampler descriptor sets
|
||||||
|
imageDescriptorSet.bindForImages(cmdbuf);
|
||||||
|
samplerDescriptorSet.bindForSamplers(cmdbuf);
|
||||||
|
|
||||||
|
// Submit the configuration commands to the queue
|
||||||
|
queue.submitCommands(cmdbuf.finishList());
|
||||||
|
queue.waitIdle();
|
||||||
|
cmdbuf.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize gamepad
|
||||||
|
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
|
||||||
|
padInitializeDefault(&pad);
|
||||||
|
}
|
||||||
|
|
||||||
|
~CExample04()
|
||||||
|
{
|
||||||
|
// Destroy the framebuffer resources
|
||||||
|
destroyFramebufferResources();
|
||||||
|
|
||||||
|
// Destroy the vertex buffer (not strictly needed in this case)
|
||||||
|
vertexBuffer.destroy();
|
||||||
|
|
||||||
|
// Destroy the uniform buffer (not strictly needed in this case)
|
||||||
|
transformUniformBuffer.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void createFramebufferResources()
|
||||||
|
{
|
||||||
|
// Create layout for the depth buffer
|
||||||
|
dk::ImageLayout layout_depthbuffer;
|
||||||
|
dk::ImageLayoutMaker{device}
|
||||||
|
.setFlags(DkImageFlags_UsageRender | DkImageFlags_HwCompression)
|
||||||
|
.setFormat(DkImageFormat_Z24S8)
|
||||||
|
.setDimensions(framebufferWidth, framebufferHeight)
|
||||||
|
.initialize(layout_depthbuffer);
|
||||||
|
|
||||||
|
// Create the depth buffer
|
||||||
|
depthBuffer_mem = pool_images->allocate(layout_depthbuffer.getSize(), layout_depthbuffer.getAlignment());
|
||||||
|
depthBuffer.initialize(layout_depthbuffer, depthBuffer_mem.getMemBlock(), depthBuffer_mem.getOffset());
|
||||||
|
|
||||||
|
// Create layout for the framebuffers
|
||||||
|
dk::ImageLayout layout_framebuffer;
|
||||||
|
dk::ImageLayoutMaker{device}
|
||||||
|
.setFlags(DkImageFlags_UsageRender | DkImageFlags_UsagePresent | DkImageFlags_HwCompression)
|
||||||
|
.setFormat(DkImageFormat_RGBA8_Unorm)
|
||||||
|
.setDimensions(framebufferWidth, framebufferHeight)
|
||||||
|
.initialize(layout_framebuffer);
|
||||||
|
|
||||||
|
// Create the framebuffers
|
||||||
|
std::array<DkImage const*, NumFramebuffers> fb_array;
|
||||||
|
uint64_t fb_size = layout_framebuffer.getSize();
|
||||||
|
uint32_t fb_align = layout_framebuffer.getAlignment();
|
||||||
|
for (unsigned i = 0; i < NumFramebuffers; i ++)
|
||||||
|
{
|
||||||
|
// Allocate a framebuffer
|
||||||
|
framebuffers_mem[i] = pool_images->allocate(fb_size, fb_align);
|
||||||
|
framebuffers[i].initialize(layout_framebuffer, framebuffers_mem[i].getMemBlock(), framebuffers_mem[i].getOffset());
|
||||||
|
|
||||||
|
// Generate a command list that binds it
|
||||||
|
dk::ImageView colorTarget{ framebuffers[i] }, depthTarget { depthBuffer };
|
||||||
|
cmdbuf.bindRenderTargets(&colorTarget, &depthTarget);
|
||||||
|
framebuffer_cmdlists[i] = cmdbuf.finishList();
|
||||||
|
|
||||||
|
// Fill in the array for use later by the swapchain creation code
|
||||||
|
fb_array[i] = &framebuffers[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the swapchain using the framebuffers
|
||||||
|
swapchain = dk::SwapchainMaker{device, nwindowGetDefault(), fb_array}.create();
|
||||||
|
|
||||||
|
// Generate the main rendering cmdlist
|
||||||
|
recordStaticCommands();
|
||||||
|
|
||||||
|
// Initialize the projection matrix
|
||||||
|
transformState.projMtx = glm::perspectiveRH_ZO(
|
||||||
|
glm::radians(40.0f),
|
||||||
|
float(framebufferWidth)/float(framebufferHeight),
|
||||||
|
0.01f, 1000.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroyFramebufferResources()
|
||||||
|
{
|
||||||
|
// Return early if we have nothing to destroy
|
||||||
|
if (!swapchain) return;
|
||||||
|
|
||||||
|
// Make sure the queue is idle before destroying anything
|
||||||
|
queue.waitIdle();
|
||||||
|
|
||||||
|
// Clear the static cmdbuf, destroying the static cmdlists in the process
|
||||||
|
cmdbuf.clear();
|
||||||
|
|
||||||
|
// Destroy the swapchain
|
||||||
|
swapchain.destroy();
|
||||||
|
|
||||||
|
// Destroy the framebuffers
|
||||||
|
for (unsigned i = 0; i < NumFramebuffers; i ++)
|
||||||
|
framebuffers_mem[i].destroy();
|
||||||
|
|
||||||
|
// Destroy the depth buffer
|
||||||
|
depthBuffer_mem.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void recordStaticCommands()
|
||||||
|
{
|
||||||
|
// Initialize state structs with deko3d defaults
|
||||||
|
dk::RasterizerState rasterizerState;
|
||||||
|
dk::ColorState colorState;
|
||||||
|
dk::ColorWriteState colorWriteState;
|
||||||
|
dk::DepthStencilState depthStencilState;
|
||||||
|
|
||||||
|
// Configure viewport and scissor
|
||||||
|
cmdbuf.setViewports(0, { { 0.0f, 0.0f, (float)framebufferWidth, (float)framebufferHeight, 0.0f, 1.0f } });
|
||||||
|
cmdbuf.setScissors(0, { { 0, 0, framebufferWidth, framebufferHeight } });
|
||||||
|
|
||||||
|
// Clear the color and depth buffers
|
||||||
|
cmdbuf.clearColor(0, DkColorMask_RGBA, 0.0f, 0.0f, 0.0f, 0.0f);
|
||||||
|
cmdbuf.clearDepthStencil(true, 1.0f, 0xFF, 0);
|
||||||
|
|
||||||
|
// Bind state required for drawing the cube
|
||||||
|
cmdbuf.bindShaders(DkStageFlag_GraphicsMask, { vertexShader, fragmentShader });
|
||||||
|
cmdbuf.bindUniformBuffer(DkStage_Vertex, 0, transformUniformBuffer.getGpuAddr(), transformUniformBuffer.getSize());
|
||||||
|
cmdbuf.bindTextures(DkStage_Fragment, 0, dkMakeTextureHandle(0, 0));
|
||||||
|
cmdbuf.bindRasterizerState(rasterizerState);
|
||||||
|
cmdbuf.bindColorState(colorState);
|
||||||
|
cmdbuf.bindColorWriteState(colorWriteState);
|
||||||
|
cmdbuf.bindDepthStencilState(depthStencilState);
|
||||||
|
cmdbuf.bindVtxBuffer(0, vertexBuffer.getGpuAddr(), vertexBuffer.getSize());
|
||||||
|
cmdbuf.bindVtxAttribState(VertexAttribState);
|
||||||
|
cmdbuf.bindVtxBufferState(VertexBufferState);
|
||||||
|
|
||||||
|
// Draw the cube
|
||||||
|
cmdbuf.draw(DkPrimitive_Quads, CubeVertexData.size(), 1, 0, 0);
|
||||||
|
|
||||||
|
// Fragment barrier, to make sure we finish previous work before discarding the depth buffer
|
||||||
|
cmdbuf.barrier(DkBarrier_Fragments, 0);
|
||||||
|
|
||||||
|
// Discard the depth buffer since we don't need it anymore
|
||||||
|
cmdbuf.discardDepthStencil();
|
||||||
|
|
||||||
|
// Finish off this command list
|
||||||
|
render_cmdlist = cmdbuf.finishList();
|
||||||
|
}
|
||||||
|
|
||||||
|
void render()
|
||||||
|
{
|
||||||
|
// Begin generating the dynamic command list, for commands that need to be sent only this frame specifically
|
||||||
|
dynmem.begin(dyncmd);
|
||||||
|
|
||||||
|
// Update the uniform buffer with the new transformation state (this data gets inlined in the command list)
|
||||||
|
dyncmd.pushConstants(
|
||||||
|
transformUniformBuffer.getGpuAddr(), transformUniformBuffer.getSize(),
|
||||||
|
0, sizeof(transformState), &transformState);
|
||||||
|
|
||||||
|
// Finish off the dynamic command list (which also submits it to the queue)
|
||||||
|
queue.submitCommands(dynmem.end(dyncmd));
|
||||||
|
|
||||||
|
// Acquire a framebuffer from the swapchain (and wait for it to be available)
|
||||||
|
int slot = queue.acquireImage(swapchain);
|
||||||
|
|
||||||
|
// Run the command list that attaches said framebuffer to the queue
|
||||||
|
queue.submitCommands(framebuffer_cmdlists[slot]);
|
||||||
|
|
||||||
|
// Run the main rendering command list
|
||||||
|
queue.submitCommands(render_cmdlist);
|
||||||
|
|
||||||
|
// Now that we are done rendering, present it to the screen
|
||||||
|
queue.presentImage(swapchain, slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onOperationMode(AppletOperationMode mode) override
|
||||||
|
{
|
||||||
|
// Destroy the framebuffer resources
|
||||||
|
destroyFramebufferResources();
|
||||||
|
|
||||||
|
// Choose framebuffer size
|
||||||
|
chooseFramebufferSize(framebufferWidth, framebufferHeight, mode);
|
||||||
|
|
||||||
|
// Recreate the framebuffers and its associated resources
|
||||||
|
createFramebufferResources();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool onFrame(u64 ns) override
|
||||||
|
{
|
||||||
|
padUpdate(&pad);
|
||||||
|
u64 kDown = padGetButtonsDown(&pad);
|
||||||
|
if (kDown & HidNpadButton_Plus)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
float time = ns / 1000000000.0; // double precision division; followed by implicit cast to single precision
|
||||||
|
float tau = glm::two_pi<float>();
|
||||||
|
|
||||||
|
float period1 = fractf(time/8.0f);
|
||||||
|
float period2 = fractf(time/4.0f);
|
||||||
|
|
||||||
|
// Generate the model-view matrix for this frame
|
||||||
|
// Keep in mind that GLM transformation functions multiply to the right, so essentially we have:
|
||||||
|
// mdlvMtx = Translate * RotateX * RotateY * Scale
|
||||||
|
// This means that the Scale operation is applied first, then RotateY, and so on.
|
||||||
|
transformState.mdlvMtx = glm::mat4{1.0f};
|
||||||
|
transformState.mdlvMtx = glm::translate(transformState.mdlvMtx, glm::vec3{0.0f, 0.0f, -3.0f});
|
||||||
|
transformState.mdlvMtx = glm::rotate(transformState.mdlvMtx, sinf(period2 * tau) * tau / 8.0f, glm::vec3{1.0f, 0.0f, 0.0f});
|
||||||
|
transformState.mdlvMtx = glm::rotate(transformState.mdlvMtx, -period1 * tau, glm::vec3{0.0f, 1.0f, 0.0f});
|
||||||
|
transformState.mdlvMtx = glm::scale(transformState.mdlvMtx, glm::vec3{0.5f});
|
||||||
|
|
||||||
|
render();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void Example04(void)
|
||||||
|
{
|
||||||
|
CExample04 app;
|
||||||
|
app.run();
|
||||||
|
}
|
259
graphics/deko3d/deko_examples/source/Example05_Tessellation.cpp
Normal file
259
graphics/deko3d/deko_examples/source/Example05_Tessellation.cpp
Normal file
@ -0,0 +1,259 @@
|
|||||||
|
/*
|
||||||
|
** deko3d Example 05: Simple Tessellation
|
||||||
|
** This example shows how to use tessellation.
|
||||||
|
** New concepts in this example:
|
||||||
|
** - Using tessellation control and evaluation shaders
|
||||||
|
** - Controlling tessellation parameters
|
||||||
|
** - Configuring and using line polygon mode
|
||||||
|
** - Configuring and using built-in edge smoothing
|
||||||
|
** - Configuring and using blending (needed for obeying alpha generated by edge smoothing)
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Sample Framework headers
|
||||||
|
#include "SampleFramework/CApplication.h"
|
||||||
|
#include "SampleFramework/CMemPool.h"
|
||||||
|
#include "SampleFramework/CShader.h"
|
||||||
|
|
||||||
|
// C++ standard library headers
|
||||||
|
#include <array>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
struct Vertex
|
||||||
|
{
|
||||||
|
float position[3];
|
||||||
|
float color[3];
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr std::array VertexAttribState =
|
||||||
|
{
|
||||||
|
DkVtxAttribState{ 0, 0, offsetof(Vertex, position), DkVtxAttribSize_3x32, DkVtxAttribType_Float, 0 },
|
||||||
|
DkVtxAttribState{ 0, 0, offsetof(Vertex, color), DkVtxAttribSize_3x32, DkVtxAttribType_Float, 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr std::array VertexBufferState =
|
||||||
|
{
|
||||||
|
DkVtxBufferState{ sizeof(Vertex), 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr std::array TriangleVertexData =
|
||||||
|
{
|
||||||
|
Vertex{ { 0.0f, +1.0f, 0.0f }, { 1.0f, 0.0f, 0.0f } },
|
||||||
|
Vertex{ { -1.0f, -1.0f, 0.0f }, { 0.0f, 1.0f, 0.0f } },
|
||||||
|
Vertex{ { +1.0f, -1.0f, 0.0f }, { 0.0f, 0.0f, 1.0f } },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class CExample05 final : public CApplication
|
||||||
|
{
|
||||||
|
static constexpr unsigned NumFramebuffers = 2;
|
||||||
|
static constexpr uint32_t FramebufferWidth = 1280;
|
||||||
|
static constexpr uint32_t FramebufferHeight = 720;
|
||||||
|
static constexpr unsigned StaticCmdSize = 0x10000;
|
||||||
|
|
||||||
|
PadState pad;
|
||||||
|
|
||||||
|
dk::UniqueDevice device;
|
||||||
|
dk::UniqueQueue queue;
|
||||||
|
|
||||||
|
std::optional<CMemPool> pool_images;
|
||||||
|
std::optional<CMemPool> pool_code;
|
||||||
|
std::optional<CMemPool> pool_data;
|
||||||
|
|
||||||
|
dk::UniqueCmdBuf cmdbuf;
|
||||||
|
|
||||||
|
CShader vertexShader;
|
||||||
|
CShader tessCtrlShader;
|
||||||
|
CShader tessEvalShader;
|
||||||
|
CShader fragmentShader;
|
||||||
|
|
||||||
|
CMemPool::Handle vertexBuffer;
|
||||||
|
|
||||||
|
CMemPool::Handle framebuffers_mem[NumFramebuffers];
|
||||||
|
dk::Image framebuffers[NumFramebuffers];
|
||||||
|
DkCmdList framebuffer_cmdlists[NumFramebuffers];
|
||||||
|
dk::UniqueSwapchain swapchain;
|
||||||
|
|
||||||
|
DkCmdList render_cmdlist;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CExample05()
|
||||||
|
{
|
||||||
|
// Create the deko3d device
|
||||||
|
device = dk::DeviceMaker{}.create();
|
||||||
|
|
||||||
|
// Create the main queue
|
||||||
|
queue = dk::QueueMaker{device}.setFlags(DkQueueFlags_Graphics).create();
|
||||||
|
|
||||||
|
// Create the memory pools
|
||||||
|
pool_images.emplace(device, DkMemBlockFlags_GpuCached | DkMemBlockFlags_Image, 16*1024*1024);
|
||||||
|
pool_code.emplace(device, DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached | DkMemBlockFlags_Code, 128*1024);
|
||||||
|
pool_data.emplace(device, DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached, 1*1024*1024);
|
||||||
|
|
||||||
|
// Create the static command buffer and feed it freshly allocated memory
|
||||||
|
cmdbuf = dk::CmdBufMaker{device}.create();
|
||||||
|
CMemPool::Handle cmdmem = pool_data->allocate(StaticCmdSize);
|
||||||
|
cmdbuf.addMemory(cmdmem.getMemBlock(), cmdmem.getOffset(), cmdmem.getSize());
|
||||||
|
|
||||||
|
// Load the shaders
|
||||||
|
vertexShader.load(*pool_code, "romfs:/shaders/basic_vsh.dksh");
|
||||||
|
tessCtrlShader.load(*pool_code, "romfs:/shaders/tess_simple_tcsh.dksh");
|
||||||
|
tessEvalShader.load(*pool_code, "romfs:/shaders/tess_simple_tesh.dksh");
|
||||||
|
fragmentShader.load(*pool_code, "romfs:/shaders/color_fsh.dksh");
|
||||||
|
|
||||||
|
// Load the vertex buffer
|
||||||
|
vertexBuffer = pool_data->allocate(sizeof(TriangleVertexData), alignof(Vertex));
|
||||||
|
memcpy(vertexBuffer.getCpuAddr(), TriangleVertexData.data(), vertexBuffer.getSize());
|
||||||
|
|
||||||
|
// Create the framebuffer resources
|
||||||
|
createFramebufferResources();
|
||||||
|
|
||||||
|
// Initialize gamepad
|
||||||
|
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
|
||||||
|
padInitializeDefault(&pad);
|
||||||
|
}
|
||||||
|
|
||||||
|
~CExample05()
|
||||||
|
{
|
||||||
|
// Destroy the framebuffer resources
|
||||||
|
destroyFramebufferResources();
|
||||||
|
|
||||||
|
// Destroy the vertex buffer (not strictly needed in this case)
|
||||||
|
vertexBuffer.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void createFramebufferResources()
|
||||||
|
{
|
||||||
|
// Create layout for the framebuffers
|
||||||
|
dk::ImageLayout layout_framebuffer;
|
||||||
|
dk::ImageLayoutMaker{device}
|
||||||
|
.setFlags(DkImageFlags_UsageRender | DkImageFlags_UsagePresent | DkImageFlags_HwCompression)
|
||||||
|
.setFormat(DkImageFormat_RGBA8_Unorm)
|
||||||
|
.setDimensions(FramebufferWidth, FramebufferHeight)
|
||||||
|
.initialize(layout_framebuffer);
|
||||||
|
|
||||||
|
// Create the framebuffers
|
||||||
|
std::array<DkImage const*, NumFramebuffers> fb_array;
|
||||||
|
uint64_t fb_size = layout_framebuffer.getSize();
|
||||||
|
uint32_t fb_align = layout_framebuffer.getAlignment();
|
||||||
|
for (unsigned i = 0; i < NumFramebuffers; i ++)
|
||||||
|
{
|
||||||
|
// Allocate a framebuffer
|
||||||
|
framebuffers_mem[i] = pool_images->allocate(fb_size, fb_align);
|
||||||
|
framebuffers[i].initialize(layout_framebuffer, framebuffers_mem[i].getMemBlock(), framebuffers_mem[i].getOffset());
|
||||||
|
|
||||||
|
// Generate a command list that binds it
|
||||||
|
dk::ImageView colorTarget{ framebuffers[i] };
|
||||||
|
cmdbuf.bindRenderTargets(&colorTarget);
|
||||||
|
framebuffer_cmdlists[i] = cmdbuf.finishList();
|
||||||
|
|
||||||
|
// Fill in the array for use later by the swapchain creation code
|
||||||
|
fb_array[i] = &framebuffers[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the swapchain using the framebuffers
|
||||||
|
swapchain = dk::SwapchainMaker{device, nwindowGetDefault(), fb_array}.create();
|
||||||
|
|
||||||
|
// Generate the main rendering cmdlist
|
||||||
|
recordStaticCommands();
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroyFramebufferResources()
|
||||||
|
{
|
||||||
|
// Return early if we have nothing to destroy
|
||||||
|
if (!swapchain) return;
|
||||||
|
|
||||||
|
// Make sure the queue is idle before destroying anything
|
||||||
|
queue.waitIdle();
|
||||||
|
|
||||||
|
// Clear the static cmdbuf, destroying the static cmdlists in the process
|
||||||
|
cmdbuf.clear();
|
||||||
|
|
||||||
|
// Destroy the swapchain
|
||||||
|
swapchain.destroy();
|
||||||
|
|
||||||
|
// Destroy the framebuffers
|
||||||
|
for (unsigned i = 0; i < NumFramebuffers; i ++)
|
||||||
|
framebuffers_mem[i].destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void recordStaticCommands()
|
||||||
|
{
|
||||||
|
// Initialize state structs with deko3d defaults
|
||||||
|
dk::RasterizerState rasterizerState;
|
||||||
|
dk::ColorState colorState;
|
||||||
|
dk::ColorWriteState colorWriteState;
|
||||||
|
dk::BlendState blendState;
|
||||||
|
|
||||||
|
// Configure rasterizer state: draw polygons as lines, and enable polygon smoothing
|
||||||
|
rasterizerState.setPolygonMode(DkPolygonMode_Line);
|
||||||
|
rasterizerState.setPolygonSmoothEnable(true);
|
||||||
|
|
||||||
|
// Configure color state: enable blending (needed for polygon smoothing since it generates alpha values)
|
||||||
|
colorState.setBlendEnable(0, true);
|
||||||
|
|
||||||
|
// Configure viewport and scissor
|
||||||
|
cmdbuf.setViewports(0, { { 0.0f, 0.0f, FramebufferWidth, FramebufferHeight, 0.0f, 1.0f } });
|
||||||
|
cmdbuf.setScissors(0, { { 0, 0, FramebufferWidth, FramebufferHeight } });
|
||||||
|
|
||||||
|
// Clear the color buffer
|
||||||
|
cmdbuf.clearColor(0, DkColorMask_RGBA, 0.0f, 0.0f, 0.0f, 0.0f);
|
||||||
|
|
||||||
|
// Bind state required for drawing the triangle
|
||||||
|
cmdbuf.bindShaders(DkStageFlag_GraphicsMask, { vertexShader, tessCtrlShader, tessEvalShader, fragmentShader });
|
||||||
|
cmdbuf.bindRasterizerState(rasterizerState);
|
||||||
|
cmdbuf.bindColorState(colorState);
|
||||||
|
cmdbuf.bindColorWriteState(colorWriteState);
|
||||||
|
cmdbuf.bindBlendStates(0, blendState);
|
||||||
|
cmdbuf.bindVtxBuffer(0, vertexBuffer.getGpuAddr(), vertexBuffer.getSize());
|
||||||
|
cmdbuf.bindVtxAttribState(VertexAttribState);
|
||||||
|
cmdbuf.bindVtxBufferState(VertexBufferState);
|
||||||
|
cmdbuf.setLineWidth(4.0f);
|
||||||
|
cmdbuf.setPatchSize(3);
|
||||||
|
|
||||||
|
// Note that the tessellation control shader is optional. If no such shader is bound,
|
||||||
|
// the following commands can be used to control tessellation:
|
||||||
|
// (try it out! remove the "tessCtrlShader" from the bindShaders call and uncomment these)
|
||||||
|
//cmdbuf.setTessInnerLevels(5.0f);
|
||||||
|
//cmdbuf.setTessOuterLevels(7.0f, 3.0f, 5.0f);
|
||||||
|
|
||||||
|
// Draw the triangle
|
||||||
|
cmdbuf.draw(DkPrimitive_Patches, TriangleVertexData.size(), 1, 0, 0);
|
||||||
|
|
||||||
|
// Finish off this command list
|
||||||
|
render_cmdlist = cmdbuf.finishList();
|
||||||
|
}
|
||||||
|
|
||||||
|
void render()
|
||||||
|
{
|
||||||
|
// Acquire a framebuffer from the swapchain (and wait for it to be available)
|
||||||
|
int slot = queue.acquireImage(swapchain);
|
||||||
|
|
||||||
|
// Run the command list that attaches said framebuffer to the queue
|
||||||
|
queue.submitCommands(framebuffer_cmdlists[slot]);
|
||||||
|
|
||||||
|
// Run the main rendering command list
|
||||||
|
queue.submitCommands(render_cmdlist);
|
||||||
|
|
||||||
|
// Now that we are done rendering, present it to the screen
|
||||||
|
queue.presentImage(swapchain, slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool onFrame(u64 ns) override
|
||||||
|
{
|
||||||
|
padUpdate(&pad);
|
||||||
|
u64 kDown = padGetButtonsDown(&pad);
|
||||||
|
if (kDown & HidNpadButton_Plus)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
render();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void Example05(void)
|
||||||
|
{
|
||||||
|
CExample05 app;
|
||||||
|
app.run();
|
||||||
|
}
|
414
graphics/deko3d/deko_examples/source/Example06_Multisampling.cpp
Normal file
414
graphics/deko3d/deko_examples/source/Example06_Multisampling.cpp
Normal file
@ -0,0 +1,414 @@
|
|||||||
|
/*
|
||||||
|
** deko3d Example 06: Simple Multisampling
|
||||||
|
** This example shows how to use a multisampled render target, which is then resolved into the final framebuffer.
|
||||||
|
** New concepts in this example:
|
||||||
|
** - Creating multisampled render targets
|
||||||
|
** - Rendering to non-swapchain render targets
|
||||||
|
** - Configuring multisample state
|
||||||
|
** - Performing a resolve step
|
||||||
|
** - Discarding color/depth buffers that are not used for presentation
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Sample Framework headers
|
||||||
|
#include "SampleFramework/CApplication.h"
|
||||||
|
#include "SampleFramework/CMemPool.h"
|
||||||
|
#include "SampleFramework/CShader.h"
|
||||||
|
#include "SampleFramework/CCmdMemRing.h"
|
||||||
|
|
||||||
|
// C++ standard library headers
|
||||||
|
#include <array>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
// GLM headers
|
||||||
|
#define GLM_FORCE_DEFAULT_ALIGNED_GENTYPES // Enforces GLSL std140/std430 alignment rules for glm types
|
||||||
|
#define GLM_FORCE_INTRINSICS // Enables usage of SIMD CPU instructions (requiring the above as well)
|
||||||
|
#include <glm/vec3.hpp>
|
||||||
|
#include <glm/vec4.hpp>
|
||||||
|
#include <glm/mat4x4.hpp>
|
||||||
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
struct Vertex
|
||||||
|
{
|
||||||
|
float position[3];
|
||||||
|
float color[3];
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr std::array VertexAttribState =
|
||||||
|
{
|
||||||
|
DkVtxAttribState{ 0, 0, offsetof(Vertex, position), DkVtxAttribSize_3x32, DkVtxAttribType_Float, 0 },
|
||||||
|
DkVtxAttribState{ 0, 0, offsetof(Vertex, color), DkVtxAttribSize_3x32, DkVtxAttribType_Float, 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr std::array VertexBufferState =
|
||||||
|
{
|
||||||
|
DkVtxBufferState{ sizeof(Vertex), 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr std::array CubeVertexData =
|
||||||
|
{
|
||||||
|
// +X face
|
||||||
|
Vertex{ { +1.0f, +1.0f, +1.0f }, { 1.0f, 0.0f, 0.0f } },
|
||||||
|
Vertex{ { +1.0f, -1.0f, +1.0f }, { 0.0f, 1.0f, 0.0f } },
|
||||||
|
Vertex{ { +1.0f, -1.0f, -1.0f }, { 0.0f, 0.0f, 1.0f } },
|
||||||
|
Vertex{ { +1.0f, +1.0f, -1.0f }, { 1.0f, 1.0f, 0.0f } },
|
||||||
|
|
||||||
|
// -X face
|
||||||
|
Vertex{ { -1.0f, +1.0f, -1.0f }, { 1.0f, 0.0f, 0.0f } },
|
||||||
|
Vertex{ { -1.0f, -1.0f, -1.0f }, { 0.0f, 1.0f, 0.0f } },
|
||||||
|
Vertex{ { -1.0f, -1.0f, +1.0f }, { 0.0f, 0.0f, 1.0f } },
|
||||||
|
Vertex{ { -1.0f, +1.0f, +1.0f }, { 1.0f, 1.0f, 0.0f } },
|
||||||
|
|
||||||
|
// +Y face
|
||||||
|
Vertex{ { -1.0f, +1.0f, -1.0f }, { 1.0f, 0.0f, 0.0f } },
|
||||||
|
Vertex{ { -1.0f, +1.0f, +1.0f }, { 0.0f, 1.0f, 0.0f } },
|
||||||
|
Vertex{ { +1.0f, +1.0f, +1.0f }, { 0.0f, 0.0f, 1.0f } },
|
||||||
|
Vertex{ { +1.0f, +1.0f, -1.0f }, { 1.0f, 1.0f, 0.0f } },
|
||||||
|
|
||||||
|
// -Y face
|
||||||
|
Vertex{ { -1.0f, -1.0f, +1.0f }, { 1.0f, 0.0f, 0.0f } },
|
||||||
|
Vertex{ { -1.0f, -1.0f, -1.0f }, { 0.0f, 1.0f, 0.0f } },
|
||||||
|
Vertex{ { +1.0f, -1.0f, -1.0f }, { 0.0f, 0.0f, 1.0f } },
|
||||||
|
Vertex{ { +1.0f, -1.0f, +1.0f }, { 1.0f, 1.0f, 0.0f } },
|
||||||
|
|
||||||
|
// +Z face
|
||||||
|
Vertex{ { -1.0f, +1.0f, +1.0f }, { 1.0f, 0.0f, 0.0f } },
|
||||||
|
Vertex{ { -1.0f, -1.0f, +1.0f }, { 0.0f, 1.0f, 0.0f } },
|
||||||
|
Vertex{ { +1.0f, -1.0f, +1.0f }, { 0.0f, 0.0f, 1.0f } },
|
||||||
|
Vertex{ { +1.0f, +1.0f, +1.0f }, { 1.0f, 1.0f, 0.0f } },
|
||||||
|
|
||||||
|
// -Z face
|
||||||
|
Vertex{ { +1.0f, +1.0f, -1.0f }, { 1.0f, 0.0f, 0.0f } },
|
||||||
|
Vertex{ { +1.0f, -1.0f, -1.0f }, { 0.0f, 1.0f, 0.0f } },
|
||||||
|
Vertex{ { -1.0f, -1.0f, -1.0f }, { 0.0f, 0.0f, 1.0f } },
|
||||||
|
Vertex{ { -1.0f, +1.0f, -1.0f }, { 1.0f, 1.0f, 0.0f } },
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Transformation
|
||||||
|
{
|
||||||
|
glm::mat4 mdlvMtx;
|
||||||
|
glm::mat4 projMtx;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline float fractf(float x)
|
||||||
|
{
|
||||||
|
return x - floorf(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CExample06 final : public CApplication
|
||||||
|
{
|
||||||
|
static constexpr unsigned NumFramebuffers = 2;
|
||||||
|
static constexpr unsigned StaticCmdSize = 0x10000;
|
||||||
|
static constexpr unsigned DynamicCmdSize = 0x10000;
|
||||||
|
static constexpr DkMsMode MultisampleMode = DkMsMode_4x;
|
||||||
|
|
||||||
|
PadState pad;
|
||||||
|
|
||||||
|
dk::UniqueDevice device;
|
||||||
|
dk::UniqueQueue queue;
|
||||||
|
|
||||||
|
std::optional<CMemPool> pool_images;
|
||||||
|
std::optional<CMemPool> pool_code;
|
||||||
|
std::optional<CMemPool> pool_data;
|
||||||
|
|
||||||
|
dk::UniqueCmdBuf cmdbuf;
|
||||||
|
dk::UniqueCmdBuf dyncmd;
|
||||||
|
CCmdMemRing<NumFramebuffers> dynmem;
|
||||||
|
|
||||||
|
CShader vertexShader;
|
||||||
|
CShader fragmentShader;
|
||||||
|
|
||||||
|
Transformation transformState;
|
||||||
|
CMemPool::Handle transformUniformBuffer;
|
||||||
|
|
||||||
|
CMemPool::Handle vertexBuffer;
|
||||||
|
|
||||||
|
uint32_t framebufferWidth;
|
||||||
|
uint32_t framebufferHeight;
|
||||||
|
|
||||||
|
CMemPool::Handle colorBuffer_mem;
|
||||||
|
CMemPool::Handle depthBuffer_mem;
|
||||||
|
CMemPool::Handle framebuffers_mem[NumFramebuffers];
|
||||||
|
|
||||||
|
dk::Image colorBuffer;
|
||||||
|
dk::Image depthBuffer;
|
||||||
|
dk::Image framebuffers[NumFramebuffers];
|
||||||
|
DkCmdList framebuffer_cmdlists[NumFramebuffers];
|
||||||
|
dk::UniqueSwapchain swapchain;
|
||||||
|
|
||||||
|
DkCmdList render_cmdlist, discard_cmdlist;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CExample06()
|
||||||
|
{
|
||||||
|
// Create the deko3d device
|
||||||
|
device = dk::DeviceMaker{}.create();
|
||||||
|
|
||||||
|
// Create the main queue
|
||||||
|
queue = dk::QueueMaker{device}.setFlags(DkQueueFlags_Graphics).create();
|
||||||
|
|
||||||
|
// Create the memory pools
|
||||||
|
pool_images.emplace(device, DkMemBlockFlags_GpuCached | DkMemBlockFlags_Image, 64*1024*1024);
|
||||||
|
pool_code.emplace(device, DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached | DkMemBlockFlags_Code, 128*1024);
|
||||||
|
pool_data.emplace(device, DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached, 1*1024*1024);
|
||||||
|
|
||||||
|
// Create the static command buffer and feed it freshly allocated memory
|
||||||
|
cmdbuf = dk::CmdBufMaker{device}.create();
|
||||||
|
CMemPool::Handle cmdmem = pool_data->allocate(StaticCmdSize);
|
||||||
|
cmdbuf.addMemory(cmdmem.getMemBlock(), cmdmem.getOffset(), cmdmem.getSize());
|
||||||
|
|
||||||
|
// Create the dynamic command buffer and allocate memory for it
|
||||||
|
dyncmd = dk::CmdBufMaker{device}.create();
|
||||||
|
dynmem.allocate(*pool_data, DynamicCmdSize);
|
||||||
|
|
||||||
|
// Load the shaders
|
||||||
|
vertexShader.load(*pool_code, "romfs:/shaders/transform_vsh.dksh");
|
||||||
|
fragmentShader.load(*pool_code, "romfs:/shaders/color_fsh.dksh");
|
||||||
|
|
||||||
|
// Create the transformation uniform buffer
|
||||||
|
transformUniformBuffer = pool_data->allocate(sizeof(transformState), DK_UNIFORM_BUF_ALIGNMENT);
|
||||||
|
|
||||||
|
// Load the vertex buffer
|
||||||
|
vertexBuffer = pool_data->allocate(sizeof(CubeVertexData), alignof(Vertex));
|
||||||
|
memcpy(vertexBuffer.getCpuAddr(), CubeVertexData.data(), vertexBuffer.getSize());
|
||||||
|
|
||||||
|
// Initialize gamepad
|
||||||
|
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
|
||||||
|
padInitializeDefault(&pad);
|
||||||
|
}
|
||||||
|
|
||||||
|
~CExample06()
|
||||||
|
{
|
||||||
|
// Destroy the framebuffer resources
|
||||||
|
destroyFramebufferResources();
|
||||||
|
|
||||||
|
// Destroy the vertex buffer (not strictly needed in this case)
|
||||||
|
vertexBuffer.destroy();
|
||||||
|
|
||||||
|
// Destroy the uniform buffer (not strictly needed in this case)
|
||||||
|
transformUniformBuffer.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void createFramebufferResources()
|
||||||
|
{
|
||||||
|
// Create layout for the (multisampled) color buffer
|
||||||
|
dk::ImageLayout layout_colorbuffer;
|
||||||
|
dk::ImageLayoutMaker{device}
|
||||||
|
.setType(DkImageType_2DMS)
|
||||||
|
.setFlags(DkImageFlags_UsageRender | DkImageFlags_Usage2DEngine | DkImageFlags_HwCompression)
|
||||||
|
.setFormat(DkImageFormat_RGBA8_Unorm)
|
||||||
|
.setMsMode(MultisampleMode)
|
||||||
|
.setDimensions(framebufferWidth, framebufferHeight)
|
||||||
|
.initialize(layout_colorbuffer);
|
||||||
|
|
||||||
|
// Create layout for the (also multisampled) depth buffer
|
||||||
|
dk::ImageLayout layout_depthbuffer;
|
||||||
|
dk::ImageLayoutMaker{device}
|
||||||
|
.setType(DkImageType_2DMS)
|
||||||
|
.setFlags(DkImageFlags_UsageRender | DkImageFlags_HwCompression)
|
||||||
|
.setFormat(DkImageFormat_Z24S8)
|
||||||
|
.setMsMode(MultisampleMode)
|
||||||
|
.setDimensions(framebufferWidth, framebufferHeight)
|
||||||
|
.initialize(layout_depthbuffer);
|
||||||
|
|
||||||
|
// Create the color buffer
|
||||||
|
colorBuffer_mem = pool_images->allocate(layout_colorbuffer.getSize(), layout_colorbuffer.getAlignment());
|
||||||
|
colorBuffer.initialize(layout_colorbuffer, colorBuffer_mem.getMemBlock(), colorBuffer_mem.getOffset());
|
||||||
|
|
||||||
|
// Create the depth buffer
|
||||||
|
depthBuffer_mem = pool_images->allocate(layout_depthbuffer.getSize(), layout_depthbuffer.getAlignment());
|
||||||
|
depthBuffer.initialize(layout_depthbuffer, depthBuffer_mem.getMemBlock(), depthBuffer_mem.getOffset());
|
||||||
|
|
||||||
|
// Create layout for the framebuffers
|
||||||
|
dk::ImageLayout layout_framebuffer;
|
||||||
|
dk::ImageLayoutMaker{device}
|
||||||
|
.setFlags(DkImageFlags_Usage2DEngine | DkImageFlags_UsagePresent)
|
||||||
|
.setFormat(DkImageFormat_RGBA8_Unorm)
|
||||||
|
.setDimensions(framebufferWidth, framebufferHeight)
|
||||||
|
.initialize(layout_framebuffer);
|
||||||
|
|
||||||
|
// Create the framebuffers
|
||||||
|
std::array<DkImage const*, NumFramebuffers> fb_array;
|
||||||
|
uint64_t fb_size = layout_framebuffer.getSize();
|
||||||
|
uint32_t fb_align = layout_framebuffer.getAlignment();
|
||||||
|
for (unsigned i = 0; i < NumFramebuffers; i ++)
|
||||||
|
{
|
||||||
|
// Allocate a framebuffer
|
||||||
|
framebuffers_mem[i] = pool_images->allocate(fb_size, fb_align);
|
||||||
|
framebuffers[i].initialize(layout_framebuffer, framebuffers_mem[i].getMemBlock(), framebuffers_mem[i].getOffset());
|
||||||
|
|
||||||
|
// Generate a command list that resolves the color buffer into the framebuffer
|
||||||
|
dk::ImageView colorView { colorBuffer }, framebufferView { framebuffers[i] };
|
||||||
|
cmdbuf.resolveImage(colorView, framebufferView);
|
||||||
|
framebuffer_cmdlists[i] = cmdbuf.finishList();
|
||||||
|
|
||||||
|
// Fill in the array for use later by the swapchain creation code
|
||||||
|
fb_array[i] = &framebuffers[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the swapchain using the framebuffers
|
||||||
|
swapchain = dk::SwapchainMaker{device, nwindowGetDefault(), fb_array}.create();
|
||||||
|
|
||||||
|
// Generate the main command lists
|
||||||
|
recordStaticCommands();
|
||||||
|
|
||||||
|
// Initialize the projection matrix
|
||||||
|
transformState.projMtx = glm::perspectiveRH_ZO(
|
||||||
|
glm::radians(40.0f),
|
||||||
|
float(framebufferWidth)/float(framebufferHeight),
|
||||||
|
0.01f, 1000.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroyFramebufferResources()
|
||||||
|
{
|
||||||
|
// Return early if we have nothing to destroy
|
||||||
|
if (!swapchain) return;
|
||||||
|
|
||||||
|
// Make sure the queue is idle before destroying anything
|
||||||
|
queue.waitIdle();
|
||||||
|
|
||||||
|
// Clear the static cmdbuf, destroying the static cmdlists in the process
|
||||||
|
cmdbuf.clear();
|
||||||
|
|
||||||
|
// Destroy the swapchain
|
||||||
|
swapchain.destroy();
|
||||||
|
|
||||||
|
// Destroy the framebuffers
|
||||||
|
for (unsigned i = 0; i < NumFramebuffers; i ++)
|
||||||
|
framebuffers_mem[i].destroy();
|
||||||
|
|
||||||
|
// Destroy the depth buffer
|
||||||
|
depthBuffer_mem.destroy();
|
||||||
|
|
||||||
|
// Destroy the color buffer
|
||||||
|
colorBuffer_mem.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void recordStaticCommands()
|
||||||
|
{
|
||||||
|
// Initialize state structs with deko3d defaults
|
||||||
|
dk::RasterizerState rasterizerState;
|
||||||
|
dk::MultisampleState multisampleState;
|
||||||
|
dk::ColorState colorState;
|
||||||
|
dk::ColorWriteState colorWriteState;
|
||||||
|
dk::DepthStencilState depthStencilState;
|
||||||
|
|
||||||
|
// Configure multisample state
|
||||||
|
multisampleState.setMode(MultisampleMode);
|
||||||
|
multisampleState.setLocations();
|
||||||
|
|
||||||
|
// Bind color buffer and depth buffer
|
||||||
|
dk::ImageView colorTarget { colorBuffer }, depthTarget { depthBuffer };
|
||||||
|
cmdbuf.bindRenderTargets(&colorTarget, &depthTarget);
|
||||||
|
|
||||||
|
// Configure viewport and scissor
|
||||||
|
cmdbuf.setViewports(0, { { 0.0f, 0.0f, (float)framebufferWidth, (float)framebufferHeight, 0.0f, 1.0f } });
|
||||||
|
cmdbuf.setScissors(0, { { 0, 0, framebufferWidth, framebufferHeight } });
|
||||||
|
|
||||||
|
// Clear the color and depth buffers
|
||||||
|
cmdbuf.clearColor(0, DkColorMask_RGBA, 0.0f, 0.0f, 0.0f, 0.0f);
|
||||||
|
cmdbuf.clearDepthStencil(true, 1.0f, 0xFF, 0);
|
||||||
|
|
||||||
|
// Bind state required for drawing the cube
|
||||||
|
cmdbuf.bindShaders(DkStageFlag_GraphicsMask, { vertexShader, fragmentShader });
|
||||||
|
cmdbuf.bindUniformBuffer(DkStage_Vertex, 0, transformUniformBuffer.getGpuAddr(), transformUniformBuffer.getSize());
|
||||||
|
cmdbuf.bindRasterizerState(rasterizerState);
|
||||||
|
cmdbuf.bindMultisampleState(multisampleState);
|
||||||
|
cmdbuf.bindColorState(colorState);
|
||||||
|
cmdbuf.bindColorWriteState(colorWriteState);
|
||||||
|
cmdbuf.bindDepthStencilState(depthStencilState);
|
||||||
|
cmdbuf.bindVtxBuffer(0, vertexBuffer.getGpuAddr(), vertexBuffer.getSize());
|
||||||
|
cmdbuf.bindVtxAttribState(VertexAttribState);
|
||||||
|
cmdbuf.bindVtxBufferState(VertexBufferState);
|
||||||
|
|
||||||
|
// Draw the cube
|
||||||
|
cmdbuf.draw(DkPrimitive_Quads, CubeVertexData.size(), 1, 0, 0);
|
||||||
|
|
||||||
|
// Finish off this command list
|
||||||
|
render_cmdlist = cmdbuf.finishList();
|
||||||
|
|
||||||
|
// Discard the color and depth buffers since we don't need them anymore
|
||||||
|
cmdbuf.bindRenderTargets(&colorTarget, &depthTarget);
|
||||||
|
cmdbuf.discardColor(0);
|
||||||
|
cmdbuf.discardDepthStencil();
|
||||||
|
|
||||||
|
// Finish off this command list
|
||||||
|
discard_cmdlist = cmdbuf.finishList();
|
||||||
|
}
|
||||||
|
|
||||||
|
void render()
|
||||||
|
{
|
||||||
|
// Begin generating the dynamic command list, for commands that need to be sent only this frame specifically
|
||||||
|
dynmem.begin(dyncmd);
|
||||||
|
|
||||||
|
// Update the uniform buffer with the new transformation state (this data gets inlined in the command list)
|
||||||
|
dyncmd.pushConstants(
|
||||||
|
transformUniformBuffer.getGpuAddr(), transformUniformBuffer.getSize(),
|
||||||
|
0, sizeof(transformState), &transformState);
|
||||||
|
|
||||||
|
// Finish off the dynamic command list (which also submits it to the queue)
|
||||||
|
queue.submitCommands(dynmem.end(dyncmd));
|
||||||
|
|
||||||
|
// Run the main rendering command list
|
||||||
|
queue.submitCommands(render_cmdlist);
|
||||||
|
|
||||||
|
// Acquire a framebuffer from the swapchain
|
||||||
|
int slot = queue.acquireImage(swapchain);
|
||||||
|
|
||||||
|
// Submit the command list that resolves the color buffer to the framebuffer
|
||||||
|
queue.submitCommands(framebuffer_cmdlists[slot]);
|
||||||
|
|
||||||
|
// Submit the command list used for discarding the color and depth buffers
|
||||||
|
queue.submitCommands(discard_cmdlist);
|
||||||
|
|
||||||
|
// Now that we are done rendering, present it to the screen (this also flushes the queue)
|
||||||
|
queue.presentImage(swapchain, slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onOperationMode(AppletOperationMode mode) override
|
||||||
|
{
|
||||||
|
// Destroy the framebuffer resources
|
||||||
|
destroyFramebufferResources();
|
||||||
|
|
||||||
|
// Choose framebuffer size
|
||||||
|
chooseFramebufferSize(framebufferWidth, framebufferHeight, mode);
|
||||||
|
|
||||||
|
// Recreate the framebuffers and its associated resources
|
||||||
|
createFramebufferResources();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool onFrame(u64 ns) override
|
||||||
|
{
|
||||||
|
padUpdate(&pad);
|
||||||
|
u64 kDown = padGetButtonsDown(&pad);
|
||||||
|
if (kDown & HidNpadButton_Plus)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
float time = ns / 1000000000.0; // double precision division; followed by implicit cast to single precision
|
||||||
|
float tau = glm::two_pi<float>();
|
||||||
|
|
||||||
|
float period1 = fractf(time/8.0f);
|
||||||
|
float period2 = fractf(time/4.0f);
|
||||||
|
|
||||||
|
// Generate the model-view matrix for this frame
|
||||||
|
// Keep in mind that GLM transformation functions multiply to the right, so essentially we have:
|
||||||
|
// mdlvMtx = Translate * RotateX * RotateY * Scale
|
||||||
|
// This means that the Scale operation is applied first, then RotateY, and so on.
|
||||||
|
transformState.mdlvMtx = glm::mat4{1.0f};
|
||||||
|
transformState.mdlvMtx = glm::translate(transformState.mdlvMtx, glm::vec3{0.0f, 0.0f, -3.0f});
|
||||||
|
transformState.mdlvMtx = glm::rotate(transformState.mdlvMtx, sinf(period2 * tau) * tau / 8.0f, glm::vec3{1.0f, 0.0f, 0.0f});
|
||||||
|
transformState.mdlvMtx = glm::rotate(transformState.mdlvMtx, -period1 * tau, glm::vec3{0.0f, 1.0f, 0.0f});
|
||||||
|
transformState.mdlvMtx = glm::scale(transformState.mdlvMtx, glm::vec3{0.5f});
|
||||||
|
|
||||||
|
render();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void Example06(void)
|
||||||
|
{
|
||||||
|
CExample06 app;
|
||||||
|
app.run();
|
||||||
|
}
|
407
graphics/deko3d/deko_examples/source/Example07_MeshLighting.cpp
Normal file
407
graphics/deko3d/deko_examples/source/Example07_MeshLighting.cpp
Normal file
@ -0,0 +1,407 @@
|
|||||||
|
/*
|
||||||
|
** deko3d Example 07: Mesh Loading and Lighting (sRGB)
|
||||||
|
** This example shows how to load a mesh, and render it using per-fragment lighting.
|
||||||
|
** New concepts in this example:
|
||||||
|
** - Loading geometry data (mesh) from the filesystem
|
||||||
|
** - Configuring and using index buffers
|
||||||
|
** - Using sRGB framebuffers
|
||||||
|
** - Using multiple uniform buffers on different stages
|
||||||
|
** - Basic Blinn-Phong lighting with Reinhard tone mapping
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Sample Framework headers
|
||||||
|
#include "SampleFramework/CApplication.h"
|
||||||
|
#include "SampleFramework/CMemPool.h"
|
||||||
|
#include "SampleFramework/CShader.h"
|
||||||
|
#include "SampleFramework/CCmdMemRing.h"
|
||||||
|
#include "SampleFramework/FileLoader.h"
|
||||||
|
|
||||||
|
// C++ standard library headers
|
||||||
|
#include <array>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
// GLM headers
|
||||||
|
#define GLM_FORCE_DEFAULT_ALIGNED_GENTYPES // Enforces GLSL std140/std430 alignment rules for glm types
|
||||||
|
#define GLM_FORCE_INTRINSICS // Enables usage of SIMD CPU instructions (requiring the above as well)
|
||||||
|
#include <glm/vec3.hpp>
|
||||||
|
#include <glm/vec4.hpp>
|
||||||
|
#include <glm/mat4x4.hpp>
|
||||||
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
struct Vertex
|
||||||
|
{
|
||||||
|
float position[3];
|
||||||
|
float normal[3];
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr std::array VertexAttribState =
|
||||||
|
{
|
||||||
|
DkVtxAttribState{ 0, 0, offsetof(Vertex, position), DkVtxAttribSize_3x32, DkVtxAttribType_Float, 0 },
|
||||||
|
DkVtxAttribState{ 0, 0, offsetof(Vertex, normal), DkVtxAttribSize_3x32, DkVtxAttribType_Float, 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr std::array VertexBufferState =
|
||||||
|
{
|
||||||
|
DkVtxBufferState{ sizeof(Vertex), 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Transformation
|
||||||
|
{
|
||||||
|
glm::mat4 mdlvMtx;
|
||||||
|
glm::mat4 projMtx;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Lighting
|
||||||
|
{
|
||||||
|
glm::vec4 lightPos; // if w=0 this is lightDir
|
||||||
|
glm::vec3 ambient;
|
||||||
|
glm::vec3 diffuse;
|
||||||
|
glm::vec4 specular; // w is shininess
|
||||||
|
};
|
||||||
|
|
||||||
|
inline float fractf(float x)
|
||||||
|
{
|
||||||
|
return x - floorf(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CExample07 final : public CApplication
|
||||||
|
{
|
||||||
|
static constexpr unsigned NumFramebuffers = 2;
|
||||||
|
static constexpr unsigned StaticCmdSize = 0x10000;
|
||||||
|
static constexpr unsigned DynamicCmdSize = 0x10000;
|
||||||
|
static constexpr DkMsMode MultisampleMode = DkMsMode_4x;
|
||||||
|
|
||||||
|
PadState pad;
|
||||||
|
|
||||||
|
dk::UniqueDevice device;
|
||||||
|
dk::UniqueQueue queue;
|
||||||
|
|
||||||
|
std::optional<CMemPool> pool_images;
|
||||||
|
std::optional<CMemPool> pool_code;
|
||||||
|
std::optional<CMemPool> pool_data;
|
||||||
|
|
||||||
|
dk::UniqueCmdBuf cmdbuf;
|
||||||
|
dk::UniqueCmdBuf dyncmd;
|
||||||
|
CCmdMemRing<NumFramebuffers> dynmem;
|
||||||
|
|
||||||
|
CShader vertexShader;
|
||||||
|
CShader fragmentShader;
|
||||||
|
|
||||||
|
Transformation transformState;
|
||||||
|
CMemPool::Handle transformUniformBuffer;
|
||||||
|
|
||||||
|
Lighting lightingState;
|
||||||
|
CMemPool::Handle lightingUniformBuffer;
|
||||||
|
|
||||||
|
CMemPool::Handle vertexBuffer;
|
||||||
|
CMemPool::Handle indexBuffer;
|
||||||
|
|
||||||
|
uint32_t framebufferWidth;
|
||||||
|
uint32_t framebufferHeight;
|
||||||
|
|
||||||
|
CMemPool::Handle colorBuffer_mem;
|
||||||
|
CMemPool::Handle depthBuffer_mem;
|
||||||
|
CMemPool::Handle framebuffers_mem[NumFramebuffers];
|
||||||
|
|
||||||
|
dk::Image colorBuffer;
|
||||||
|
dk::Image depthBuffer;
|
||||||
|
dk::Image framebuffers[NumFramebuffers];
|
||||||
|
DkCmdList framebuffer_cmdlists[NumFramebuffers];
|
||||||
|
dk::UniqueSwapchain swapchain;
|
||||||
|
|
||||||
|
DkCmdList render_cmdlist, discard_cmdlist;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CExample07()
|
||||||
|
{
|
||||||
|
// Create the deko3d device
|
||||||
|
device = dk::DeviceMaker{}.create();
|
||||||
|
|
||||||
|
// Create the main queue
|
||||||
|
queue = dk::QueueMaker{device}.setFlags(DkQueueFlags_Graphics).create();
|
||||||
|
|
||||||
|
// Create the memory pools
|
||||||
|
pool_images.emplace(device, DkMemBlockFlags_GpuCached | DkMemBlockFlags_Image, 64*1024*1024);
|
||||||
|
pool_code.emplace(device, DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached | DkMemBlockFlags_Code, 128*1024);
|
||||||
|
pool_data.emplace(device, DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached, 1*1024*1024);
|
||||||
|
|
||||||
|
// Create the static command buffer and feed it freshly allocated memory
|
||||||
|
cmdbuf = dk::CmdBufMaker{device}.create();
|
||||||
|
CMemPool::Handle cmdmem = pool_data->allocate(StaticCmdSize);
|
||||||
|
cmdbuf.addMemory(cmdmem.getMemBlock(), cmdmem.getOffset(), cmdmem.getSize());
|
||||||
|
|
||||||
|
// Create the dynamic command buffer and allocate memory for it
|
||||||
|
dyncmd = dk::CmdBufMaker{device}.create();
|
||||||
|
dynmem.allocate(*pool_data, DynamicCmdSize);
|
||||||
|
|
||||||
|
// Load the shaders
|
||||||
|
vertexShader.load(*pool_code, "romfs:/shaders/transform_normal_vsh.dksh");
|
||||||
|
fragmentShader.load(*pool_code, "romfs:/shaders/basic_lighting_fsh.dksh");
|
||||||
|
|
||||||
|
// Create the transformation uniform buffer
|
||||||
|
transformUniformBuffer = pool_data->allocate(sizeof(transformState), DK_UNIFORM_BUF_ALIGNMENT);
|
||||||
|
|
||||||
|
// Create the lighting uniform buffer
|
||||||
|
lightingUniformBuffer = pool_data->allocate(sizeof(lightingState), DK_UNIFORM_BUF_ALIGNMENT);
|
||||||
|
|
||||||
|
// Initialize the lighting state
|
||||||
|
lightingState.lightPos = glm::vec4{0.0f, 4.0f, 1.0f, 1.0f};
|
||||||
|
lightingState.ambient = glm::vec3{0.046227f,0.028832f,0.003302f};
|
||||||
|
lightingState.diffuse = glm::vec3{0.564963f,0.367818f,0.051293f};
|
||||||
|
lightingState.specular = glm::vec4{24.0f*glm::vec3{0.394737f,0.308916f,0.134004f}, 64.0f};
|
||||||
|
|
||||||
|
// Load the teapot mesh
|
||||||
|
vertexBuffer = LoadFile(*pool_data, "romfs:/teapot-vtx.bin", alignof(Vertex));
|
||||||
|
indexBuffer = LoadFile(*pool_data, "romfs:/teapot-idx.bin", alignof(u16));
|
||||||
|
|
||||||
|
// Initialize gamepad
|
||||||
|
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
|
||||||
|
padInitializeDefault(&pad);
|
||||||
|
}
|
||||||
|
|
||||||
|
~CExample07()
|
||||||
|
{
|
||||||
|
// Destroy the framebuffer resources
|
||||||
|
destroyFramebufferResources();
|
||||||
|
|
||||||
|
// Destroy the index buffer (not strictly needed in this case)
|
||||||
|
indexBuffer.destroy();
|
||||||
|
|
||||||
|
// Destroy the vertex buffer (not strictly needed in this case)
|
||||||
|
vertexBuffer.destroy();
|
||||||
|
|
||||||
|
// Destroy the uniform buffer (not strictly needed in this case)
|
||||||
|
transformUniformBuffer.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void createFramebufferResources()
|
||||||
|
{
|
||||||
|
// Create layout for the (multisampled) color buffer
|
||||||
|
dk::ImageLayout layout_colorbuffer;
|
||||||
|
dk::ImageLayoutMaker{device}
|
||||||
|
.setType(DkImageType_2DMS)
|
||||||
|
.setFlags(DkImageFlags_UsageRender | DkImageFlags_Usage2DEngine | DkImageFlags_HwCompression)
|
||||||
|
.setFormat(DkImageFormat_RGBA8_Unorm_sRGB)
|
||||||
|
.setMsMode(MultisampleMode)
|
||||||
|
.setDimensions(framebufferWidth, framebufferHeight)
|
||||||
|
.initialize(layout_colorbuffer);
|
||||||
|
|
||||||
|
// Create layout for the (also multisampled) depth buffer
|
||||||
|
dk::ImageLayout layout_depthbuffer;
|
||||||
|
dk::ImageLayoutMaker{device}
|
||||||
|
.setType(DkImageType_2DMS)
|
||||||
|
.setFlags(DkImageFlags_UsageRender | DkImageFlags_HwCompression)
|
||||||
|
.setFormat(DkImageFormat_Z24S8)
|
||||||
|
.setMsMode(MultisampleMode)
|
||||||
|
.setDimensions(framebufferWidth, framebufferHeight)
|
||||||
|
.initialize(layout_depthbuffer);
|
||||||
|
|
||||||
|
// Create the color buffer
|
||||||
|
colorBuffer_mem = pool_images->allocate(layout_colorbuffer.getSize(), layout_colorbuffer.getAlignment());
|
||||||
|
colorBuffer.initialize(layout_colorbuffer, colorBuffer_mem.getMemBlock(), colorBuffer_mem.getOffset());
|
||||||
|
|
||||||
|
// Create the depth buffer
|
||||||
|
depthBuffer_mem = pool_images->allocate(layout_depthbuffer.getSize(), layout_depthbuffer.getAlignment());
|
||||||
|
depthBuffer.initialize(layout_depthbuffer, depthBuffer_mem.getMemBlock(), depthBuffer_mem.getOffset());
|
||||||
|
|
||||||
|
// Create layout for the framebuffers
|
||||||
|
dk::ImageLayout layout_framebuffer;
|
||||||
|
dk::ImageLayoutMaker{device}
|
||||||
|
.setFlags(DkImageFlags_Usage2DEngine | DkImageFlags_UsagePresent)
|
||||||
|
.setFormat(DkImageFormat_RGBA8_Unorm_sRGB)
|
||||||
|
.setDimensions(framebufferWidth, framebufferHeight)
|
||||||
|
.initialize(layout_framebuffer);
|
||||||
|
|
||||||
|
// Create the framebuffers
|
||||||
|
std::array<DkImage const*, NumFramebuffers> fb_array;
|
||||||
|
uint64_t fb_size = layout_framebuffer.getSize();
|
||||||
|
uint32_t fb_align = layout_framebuffer.getAlignment();
|
||||||
|
for (unsigned i = 0; i < NumFramebuffers; i ++)
|
||||||
|
{
|
||||||
|
// Allocate a framebuffer
|
||||||
|
framebuffers_mem[i] = pool_images->allocate(fb_size, fb_align);
|
||||||
|
framebuffers[i].initialize(layout_framebuffer, framebuffers_mem[i].getMemBlock(), framebuffers_mem[i].getOffset());
|
||||||
|
|
||||||
|
// Generate a command list that resolves the color buffer into the framebuffer
|
||||||
|
dk::ImageView colorView { colorBuffer }, framebufferView { framebuffers[i] };
|
||||||
|
cmdbuf.resolveImage(colorView, framebufferView);
|
||||||
|
framebuffer_cmdlists[i] = cmdbuf.finishList();
|
||||||
|
|
||||||
|
// Fill in the array for use later by the swapchain creation code
|
||||||
|
fb_array[i] = &framebuffers[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the swapchain using the framebuffers
|
||||||
|
swapchain = dk::SwapchainMaker{device, nwindowGetDefault(), fb_array}.create();
|
||||||
|
|
||||||
|
// Generate the main command lists
|
||||||
|
recordStaticCommands();
|
||||||
|
|
||||||
|
// Initialize the projection matrix
|
||||||
|
transformState.projMtx = glm::perspectiveRH_ZO(
|
||||||
|
glm::radians(40.0f),
|
||||||
|
float(framebufferWidth)/float(framebufferHeight),
|
||||||
|
0.01f, 1000.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroyFramebufferResources()
|
||||||
|
{
|
||||||
|
// Return early if we have nothing to destroy
|
||||||
|
if (!swapchain) return;
|
||||||
|
|
||||||
|
// Make sure the queue is idle before destroying anything
|
||||||
|
queue.waitIdle();
|
||||||
|
|
||||||
|
// Clear the static cmdbuf, destroying the static cmdlists in the process
|
||||||
|
cmdbuf.clear();
|
||||||
|
|
||||||
|
// Destroy the swapchain
|
||||||
|
swapchain.destroy();
|
||||||
|
|
||||||
|
// Destroy the framebuffers
|
||||||
|
for (unsigned i = 0; i < NumFramebuffers; i ++)
|
||||||
|
framebuffers_mem[i].destroy();
|
||||||
|
|
||||||
|
// Destroy the depth buffer
|
||||||
|
depthBuffer_mem.destroy();
|
||||||
|
|
||||||
|
// Destroy the color buffer
|
||||||
|
colorBuffer_mem.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void recordStaticCommands()
|
||||||
|
{
|
||||||
|
// Initialize state structs with deko3d defaults
|
||||||
|
dk::RasterizerState rasterizerState;
|
||||||
|
dk::MultisampleState multisampleState;
|
||||||
|
dk::ColorState colorState;
|
||||||
|
dk::ColorWriteState colorWriteState;
|
||||||
|
dk::DepthStencilState depthStencilState;
|
||||||
|
|
||||||
|
// Configure multisample state
|
||||||
|
multisampleState.setMode(MultisampleMode);
|
||||||
|
multisampleState.setLocations();
|
||||||
|
|
||||||
|
// Bind color buffer and depth buffer
|
||||||
|
dk::ImageView colorTarget { colorBuffer }, depthTarget { depthBuffer };
|
||||||
|
cmdbuf.bindRenderTargets(&colorTarget, &depthTarget);
|
||||||
|
|
||||||
|
// Configure viewport and scissor
|
||||||
|
cmdbuf.setViewports(0, { { 0.0f, 0.0f, (float)framebufferWidth, (float)framebufferHeight, 0.0f, 1.0f } });
|
||||||
|
cmdbuf.setScissors(0, { { 0, 0, framebufferWidth, framebufferHeight } });
|
||||||
|
|
||||||
|
// Clear the color and depth buffers
|
||||||
|
cmdbuf.clearColor(0, DkColorMask_RGBA, 0.0f, 0.0f, 0.0f, 0.0f);
|
||||||
|
cmdbuf.clearDepthStencil(true, 1.0f, 0xFF, 0);
|
||||||
|
|
||||||
|
// Bind state required for drawing the mesh
|
||||||
|
cmdbuf.bindShaders(DkStageFlag_GraphicsMask, { vertexShader, fragmentShader });
|
||||||
|
cmdbuf.bindUniformBuffer(DkStage_Vertex, 0, transformUniformBuffer.getGpuAddr(), transformUniformBuffer.getSize());
|
||||||
|
cmdbuf.bindUniformBuffer(DkStage_Fragment, 0, lightingUniformBuffer.getGpuAddr(), lightingUniformBuffer.getSize());
|
||||||
|
cmdbuf.bindRasterizerState(rasterizerState);
|
||||||
|
cmdbuf.bindMultisampleState(multisampleState);
|
||||||
|
cmdbuf.bindColorState(colorState);
|
||||||
|
cmdbuf.bindColorWriteState(colorWriteState);
|
||||||
|
cmdbuf.bindDepthStencilState(depthStencilState);
|
||||||
|
cmdbuf.bindVtxBuffer(0, vertexBuffer.getGpuAddr(), vertexBuffer.getSize());
|
||||||
|
cmdbuf.bindVtxAttribState(VertexAttribState);
|
||||||
|
cmdbuf.bindVtxBufferState(VertexBufferState);
|
||||||
|
cmdbuf.bindIdxBuffer(DkIdxFormat_Uint16, indexBuffer.getGpuAddr());
|
||||||
|
|
||||||
|
// Draw the mesh
|
||||||
|
cmdbuf.drawIndexed(DkPrimitive_Triangles, indexBuffer.getSize() / sizeof(u16), 1, 0, 0, 0);
|
||||||
|
|
||||||
|
// Finish off this command list
|
||||||
|
render_cmdlist = cmdbuf.finishList();
|
||||||
|
|
||||||
|
// Discard the color and depth buffers since we don't need them anymore
|
||||||
|
cmdbuf.bindRenderTargets(&colorTarget, &depthTarget);
|
||||||
|
cmdbuf.discardColor(0);
|
||||||
|
cmdbuf.discardDepthStencil();
|
||||||
|
|
||||||
|
// Finish off this command list
|
||||||
|
discard_cmdlist = cmdbuf.finishList();
|
||||||
|
}
|
||||||
|
|
||||||
|
void render()
|
||||||
|
{
|
||||||
|
// Begin generating the dynamic command list, for commands that need to be sent only this frame specifically
|
||||||
|
dynmem.begin(dyncmd);
|
||||||
|
|
||||||
|
// Update the transformation uniform buffer with the new state (this data gets inlined in the command list)
|
||||||
|
dyncmd.pushConstants(
|
||||||
|
transformUniformBuffer.getGpuAddr(), transformUniformBuffer.getSize(),
|
||||||
|
0, sizeof(transformState), &transformState);
|
||||||
|
|
||||||
|
// Update the lighting uniform buffer with the new state
|
||||||
|
dyncmd.pushConstants(
|
||||||
|
lightingUniformBuffer.getGpuAddr(), lightingUniformBuffer.getSize(),
|
||||||
|
0, sizeof(lightingState), &lightingState);
|
||||||
|
|
||||||
|
// Finish off the dynamic command list (which also submits it to the queue)
|
||||||
|
queue.submitCommands(dynmem.end(dyncmd));
|
||||||
|
|
||||||
|
// Run the main rendering command list
|
||||||
|
queue.submitCommands(render_cmdlist);
|
||||||
|
|
||||||
|
// Acquire a framebuffer from the swapchain
|
||||||
|
int slot = queue.acquireImage(swapchain);
|
||||||
|
|
||||||
|
// Submit the command list that resolves the color buffer to the framebuffer
|
||||||
|
queue.submitCommands(framebuffer_cmdlists[slot]);
|
||||||
|
|
||||||
|
// Submit the command list used for discarding the color and depth buffers
|
||||||
|
queue.submitCommands(discard_cmdlist);
|
||||||
|
|
||||||
|
// Now that we are done rendering, present it to the screen (this also flushes the queue)
|
||||||
|
queue.presentImage(swapchain, slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onOperationMode(AppletOperationMode mode) override
|
||||||
|
{
|
||||||
|
// Destroy the framebuffer resources
|
||||||
|
destroyFramebufferResources();
|
||||||
|
|
||||||
|
// Choose framebuffer size
|
||||||
|
chooseFramebufferSize(framebufferWidth, framebufferHeight, mode);
|
||||||
|
|
||||||
|
// Recreate the framebuffers and its associated resources
|
||||||
|
createFramebufferResources();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool onFrame(u64 ns) override
|
||||||
|
{
|
||||||
|
padUpdate(&pad);
|
||||||
|
u64 kDown = padGetButtonsDown(&pad);
|
||||||
|
if (kDown & HidNpadButton_Plus)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
float time = ns / 1000000000.0; // double precision division; followed by implicit cast to single precision
|
||||||
|
float tau = glm::two_pi<float>();
|
||||||
|
|
||||||
|
float period1 = fractf(time/8.0f);
|
||||||
|
float period2 = fractf(time/4.0f);
|
||||||
|
|
||||||
|
// Generate the model-view matrix for this frame
|
||||||
|
// Keep in mind that GLM transformation functions multiply to the right, so essentially we have:
|
||||||
|
// mdlvMtx = Translate1 * RotateX * RotateY * Translate2
|
||||||
|
// This means that the Translate2 operation is applied first, then RotateY, and so on.
|
||||||
|
transformState.mdlvMtx = glm::mat4{1.0f};
|
||||||
|
transformState.mdlvMtx = glm::translate(transformState.mdlvMtx, glm::vec3{0.0f, 0.0f, -3.0f});
|
||||||
|
transformState.mdlvMtx = glm::rotate(transformState.mdlvMtx, sinf(period2 * tau) * tau / 8.0f, glm::vec3{1.0f, 0.0f, 0.0f});
|
||||||
|
transformState.mdlvMtx = glm::rotate(transformState.mdlvMtx, -period1 * tau, glm::vec3{0.0f, 1.0f, 0.0f});
|
||||||
|
transformState.mdlvMtx = glm::translate(transformState.mdlvMtx, glm::vec3{0.0f, -0.5f, 0.0f});
|
||||||
|
|
||||||
|
render();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void Example07(void)
|
||||||
|
{
|
||||||
|
CExample07 app;
|
||||||
|
app.run();
|
||||||
|
}
|
@ -0,0 +1,488 @@
|
|||||||
|
/*
|
||||||
|
** deko3d Example 08: Deferred Shading (Multipass Rendering with Tiled Cache)
|
||||||
|
** This example shows how to perform deferred shading, a multipass rendering technique that goes well with the tiled cache.
|
||||||
|
** New concepts in this example:
|
||||||
|
** - Rendering to multiple render targets (MRT) at once
|
||||||
|
** - Floating point render targets
|
||||||
|
** - Enabling and configuring the tiled cache
|
||||||
|
** - Using the tiled barrier for relaxing ordering to the tiles generated by the binner (as opposed to a full fragment barrier)
|
||||||
|
** - Custom composition step reading the output of previous rendering passes as textures
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Sample Framework headers
|
||||||
|
#include "SampleFramework/CApplication.h"
|
||||||
|
#include "SampleFramework/CMemPool.h"
|
||||||
|
#include "SampleFramework/CShader.h"
|
||||||
|
#include "SampleFramework/CCmdMemRing.h"
|
||||||
|
#include "SampleFramework/CDescriptorSet.h"
|
||||||
|
#include "SampleFramework/FileLoader.h"
|
||||||
|
|
||||||
|
// C++ standard library headers
|
||||||
|
#include <array>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
// GLM headers
|
||||||
|
#define GLM_FORCE_DEFAULT_ALIGNED_GENTYPES // Enforces GLSL std140/std430 alignment rules for glm types
|
||||||
|
#define GLM_FORCE_INTRINSICS // Enables usage of SIMD CPU instructions (requiring the above as well)
|
||||||
|
#include <glm/vec3.hpp>
|
||||||
|
#include <glm/vec4.hpp>
|
||||||
|
#include <glm/mat4x4.hpp>
|
||||||
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
struct Vertex
|
||||||
|
{
|
||||||
|
float position[3];
|
||||||
|
float normal[3];
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr std::array VertexAttribState =
|
||||||
|
{
|
||||||
|
DkVtxAttribState{ 0, 0, offsetof(Vertex, position), DkVtxAttribSize_3x32, DkVtxAttribType_Float, 0 },
|
||||||
|
DkVtxAttribState{ 0, 0, offsetof(Vertex, normal), DkVtxAttribSize_3x32, DkVtxAttribType_Float, 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr std::array VertexBufferState =
|
||||||
|
{
|
||||||
|
DkVtxBufferState{ sizeof(Vertex), 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Transformation
|
||||||
|
{
|
||||||
|
glm::mat4 mdlvMtx;
|
||||||
|
glm::mat4 projMtx;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Lighting
|
||||||
|
{
|
||||||
|
glm::vec4 lightPos; // if w=0 this is lightDir
|
||||||
|
glm::vec3 ambient;
|
||||||
|
glm::vec3 diffuse;
|
||||||
|
glm::vec4 specular; // w is shininess
|
||||||
|
};
|
||||||
|
|
||||||
|
inline float fractf(float x)
|
||||||
|
{
|
||||||
|
return x - floorf(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CExample08 final : public CApplication
|
||||||
|
{
|
||||||
|
static constexpr unsigned NumFramebuffers = 2;
|
||||||
|
static constexpr unsigned StaticCmdSize = 0x10000;
|
||||||
|
static constexpr unsigned DynamicCmdSize = 0x10000;
|
||||||
|
static constexpr unsigned MaxImages = 3;
|
||||||
|
static constexpr unsigned MaxSamplers = 1;
|
||||||
|
|
||||||
|
PadState pad;
|
||||||
|
|
||||||
|
dk::UniqueDevice device;
|
||||||
|
dk::UniqueQueue queue;
|
||||||
|
|
||||||
|
std::optional<CMemPool> pool_images;
|
||||||
|
std::optional<CMemPool> pool_code;
|
||||||
|
std::optional<CMemPool> pool_data;
|
||||||
|
|
||||||
|
dk::UniqueCmdBuf cmdbuf;
|
||||||
|
dk::UniqueCmdBuf dyncmd;
|
||||||
|
CCmdMemRing<NumFramebuffers> dynmem;
|
||||||
|
|
||||||
|
CDescriptorSet<MaxImages> imageDescriptorSet;
|
||||||
|
CDescriptorSet<MaxSamplers> samplerDescriptorSet;
|
||||||
|
|
||||||
|
CShader vertexShader;
|
||||||
|
CShader fragmentShader;
|
||||||
|
|
||||||
|
CShader compositionVertexShader;
|
||||||
|
CShader compositionFragmentShader;
|
||||||
|
|
||||||
|
Transformation transformState;
|
||||||
|
CMemPool::Handle transformUniformBuffer;
|
||||||
|
|
||||||
|
Lighting lightingState;
|
||||||
|
CMemPool::Handle lightingUniformBuffer;
|
||||||
|
|
||||||
|
CMemPool::Handle vertexBuffer;
|
||||||
|
CMemPool::Handle indexBuffer;
|
||||||
|
|
||||||
|
uint32_t framebufferWidth;
|
||||||
|
uint32_t framebufferHeight;
|
||||||
|
|
||||||
|
CMemPool::Handle albedoBuffer_mem;
|
||||||
|
CMemPool::Handle normalBuffer_mem;
|
||||||
|
CMemPool::Handle viewDirBuffer_mem;
|
||||||
|
CMemPool::Handle depthBuffer_mem;
|
||||||
|
CMemPool::Handle framebuffers_mem[NumFramebuffers];
|
||||||
|
|
||||||
|
dk::Image albedoBuffer;
|
||||||
|
dk::Image normalBuffer;
|
||||||
|
dk::Image viewDirBuffer;
|
||||||
|
dk::Image depthBuffer;
|
||||||
|
dk::Image framebuffers[NumFramebuffers];
|
||||||
|
DkCmdList framebuffer_cmdlists[NumFramebuffers];
|
||||||
|
dk::UniqueSwapchain swapchain;
|
||||||
|
|
||||||
|
DkCmdList render_cmdlist, composition_cmdlist;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CExample08()
|
||||||
|
{
|
||||||
|
// Create the deko3d device
|
||||||
|
device = dk::DeviceMaker{}.create();
|
||||||
|
|
||||||
|
// Create the main queue
|
||||||
|
queue = dk::QueueMaker{device}.setFlags(DkQueueFlags_Graphics).create();
|
||||||
|
|
||||||
|
// Create the memory pools
|
||||||
|
pool_images.emplace(device, DkMemBlockFlags_GpuCached | DkMemBlockFlags_Image, 64*1024*1024);
|
||||||
|
pool_code.emplace(device, DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached | DkMemBlockFlags_Code, 1*1024*1024);
|
||||||
|
pool_data.emplace(device, DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached, 1*1024*1024);
|
||||||
|
|
||||||
|
// Create the static command buffer and feed it freshly allocated memory
|
||||||
|
cmdbuf = dk::CmdBufMaker{device}.create();
|
||||||
|
CMemPool::Handle cmdmem = pool_data->allocate(StaticCmdSize);
|
||||||
|
cmdbuf.addMemory(cmdmem.getMemBlock(), cmdmem.getOffset(), cmdmem.getSize());
|
||||||
|
|
||||||
|
// Create the dynamic command buffer and allocate memory for it
|
||||||
|
dyncmd = dk::CmdBufMaker{device}.create();
|
||||||
|
dynmem.allocate(*pool_data, DynamicCmdSize);
|
||||||
|
|
||||||
|
// Create the image and sampler descriptor sets
|
||||||
|
imageDescriptorSet.allocate(*pool_data);
|
||||||
|
samplerDescriptorSet.allocate(*pool_data);
|
||||||
|
|
||||||
|
// Load the shaders
|
||||||
|
vertexShader.load(*pool_code, "romfs:/shaders/transform_normal_vsh.dksh");
|
||||||
|
fragmentShader.load(*pool_code, "romfs:/shaders/basic_deferred_fsh.dksh");
|
||||||
|
compositionVertexShader.load(*pool_code, "romfs:/shaders/composition_vsh.dksh");
|
||||||
|
compositionFragmentShader.load(*pool_code, "romfs:/shaders/composition_fsh.dksh");
|
||||||
|
|
||||||
|
// Create the transformation uniform buffer
|
||||||
|
transformUniformBuffer = pool_data->allocate(sizeof(transformState), DK_UNIFORM_BUF_ALIGNMENT);
|
||||||
|
|
||||||
|
// Create the lighting uniform buffer
|
||||||
|
lightingUniformBuffer = pool_data->allocate(sizeof(lightingState), DK_UNIFORM_BUF_ALIGNMENT);
|
||||||
|
|
||||||
|
// Initialize the lighting state
|
||||||
|
lightingState.lightPos = glm::vec4{0.0f, 4.0f, 1.0f, 1.0f};
|
||||||
|
lightingState.ambient = glm::vec3{0.046227f,0.028832f,0.003302f};
|
||||||
|
lightingState.diffuse = glm::vec3{0.564963f,0.367818f,0.051293f};
|
||||||
|
lightingState.specular = glm::vec4{24.0f*glm::vec3{0.394737f,0.308916f,0.134004f}, 64.0f};
|
||||||
|
|
||||||
|
// Load the teapot mesh
|
||||||
|
vertexBuffer = LoadFile(*pool_data, "romfs:/teapot-vtx.bin", alignof(Vertex));
|
||||||
|
indexBuffer = LoadFile(*pool_data, "romfs:/teapot-idx.bin", alignof(u16));
|
||||||
|
|
||||||
|
// Configure persistent state in the queue
|
||||||
|
{
|
||||||
|
// Bind the image and sampler descriptor sets
|
||||||
|
imageDescriptorSet.bindForImages(cmdbuf);
|
||||||
|
samplerDescriptorSet.bindForSamplers(cmdbuf);
|
||||||
|
|
||||||
|
// Enable the tiled cache
|
||||||
|
cmdbuf.setTileSize(64, 64); // example size, please experiment with this
|
||||||
|
cmdbuf.tiledCacheOp(DkTiledCacheOp_Enable);
|
||||||
|
|
||||||
|
// Submit the configuration commands to the queue
|
||||||
|
queue.submitCommands(cmdbuf.finishList());
|
||||||
|
queue.waitIdle();
|
||||||
|
cmdbuf.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize gamepad
|
||||||
|
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
|
||||||
|
padInitializeDefault(&pad);
|
||||||
|
}
|
||||||
|
|
||||||
|
void createFramebufferResources()
|
||||||
|
{
|
||||||
|
// Calculate layout for the different buffers part of the g-buffer
|
||||||
|
dk::ImageLayout layout_gbuffer;
|
||||||
|
dk::ImageLayoutMaker{device}
|
||||||
|
.setFlags(DkImageFlags_UsageRender | DkImageFlags_HwCompression)
|
||||||
|
.setFormat(DkImageFormat_RGBA16_Float)
|
||||||
|
.setDimensions(framebufferWidth, framebufferHeight)
|
||||||
|
.initialize(layout_gbuffer);
|
||||||
|
|
||||||
|
// Calculate layout for the depth buffer
|
||||||
|
dk::ImageLayout layout_depthbuffer;
|
||||||
|
dk::ImageLayoutMaker{device}
|
||||||
|
.setFlags(DkImageFlags_UsageRender | DkImageFlags_HwCompression)
|
||||||
|
.setFormat(DkImageFormat_Z24S8)
|
||||||
|
.setDimensions(framebufferWidth, framebufferHeight)
|
||||||
|
.initialize(layout_depthbuffer);
|
||||||
|
|
||||||
|
// Calculate layout for the framebuffers
|
||||||
|
dk::ImageLayout layout_framebuffer;
|
||||||
|
dk::ImageLayoutMaker{device}
|
||||||
|
.setFlags(DkImageFlags_UsageRender | DkImageFlags_UsagePresent)
|
||||||
|
.setFormat(DkImageFormat_RGBA8_Unorm_sRGB)
|
||||||
|
.setDimensions(framebufferWidth, framebufferHeight)
|
||||||
|
.initialize(layout_framebuffer);
|
||||||
|
|
||||||
|
// Create the albedo buffer
|
||||||
|
albedoBuffer_mem = pool_images->allocate(layout_gbuffer.getSize(), layout_gbuffer.getAlignment());
|
||||||
|
albedoBuffer.initialize(layout_gbuffer, albedoBuffer_mem.getMemBlock(), albedoBuffer_mem.getOffset());
|
||||||
|
|
||||||
|
// Create the normal buffer
|
||||||
|
normalBuffer_mem = pool_images->allocate(layout_gbuffer.getSize(), layout_gbuffer.getAlignment());
|
||||||
|
normalBuffer.initialize(layout_gbuffer, normalBuffer_mem.getMemBlock(), normalBuffer_mem.getOffset());
|
||||||
|
|
||||||
|
// Create the view direction buffer
|
||||||
|
viewDirBuffer_mem = pool_images->allocate(layout_gbuffer.getSize(), layout_gbuffer.getAlignment());
|
||||||
|
viewDirBuffer.initialize(layout_gbuffer, viewDirBuffer_mem.getMemBlock(), viewDirBuffer_mem.getOffset());
|
||||||
|
|
||||||
|
// Create the depth buffer
|
||||||
|
depthBuffer_mem = pool_images->allocate(layout_depthbuffer.getSize(), layout_depthbuffer.getAlignment());
|
||||||
|
depthBuffer.initialize(layout_depthbuffer, depthBuffer_mem.getMemBlock(), depthBuffer_mem.getOffset());
|
||||||
|
|
||||||
|
// Create the framebuffers
|
||||||
|
uint64_t fb_size = layout_framebuffer.getSize();
|
||||||
|
uint32_t fb_align = layout_framebuffer.getAlignment();
|
||||||
|
DkImage const* fb_array[NumFramebuffers];
|
||||||
|
for (unsigned i = 0; i < NumFramebuffers; i ++)
|
||||||
|
{
|
||||||
|
// Allocate a framebuffer
|
||||||
|
framebuffers_mem[i] = pool_images->allocate(fb_size, fb_align);
|
||||||
|
framebuffers[i].initialize(layout_framebuffer, framebuffers_mem[i].getMemBlock(), framebuffers_mem[i].getOffset());
|
||||||
|
|
||||||
|
// Generate a command list that binds the framebuffer
|
||||||
|
dk::ImageView framebufferView { framebuffers[i] };
|
||||||
|
cmdbuf.bindRenderTargets(&framebufferView);
|
||||||
|
framebuffer_cmdlists[i] = cmdbuf.finishList();
|
||||||
|
|
||||||
|
// Fill in the array for use later by the swapchain creation code
|
||||||
|
fb_array[i] = &framebuffers[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the swapchain using the framebuffers
|
||||||
|
swapchain = dk::SwapchainMaker{device, nwindowGetDefault(), fb_array, NumFramebuffers}.create();
|
||||||
|
|
||||||
|
// Generate the static command lists
|
||||||
|
recordStaticCommands();
|
||||||
|
|
||||||
|
// Initialize the projection matrix
|
||||||
|
transformState.projMtx = glm::perspectiveRH_ZO(
|
||||||
|
glm::radians(40.0f),
|
||||||
|
float(framebufferWidth)/float(framebufferHeight),
|
||||||
|
0.01f, 1000.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroyFramebufferResources()
|
||||||
|
{
|
||||||
|
// Return early if we have nothing to destroy
|
||||||
|
if (!swapchain) return;
|
||||||
|
|
||||||
|
// Make sure the queue is idle before destroying anything
|
||||||
|
queue.waitIdle();
|
||||||
|
|
||||||
|
// Clear the static cmdbuf, destroying the static cmdlists in the process
|
||||||
|
cmdbuf.clear();
|
||||||
|
|
||||||
|
// Destroy the swapchain
|
||||||
|
swapchain.destroy();
|
||||||
|
|
||||||
|
// Destroy the framebuffers
|
||||||
|
for (unsigned i = 0; i < NumFramebuffers; i ++)
|
||||||
|
framebuffers_mem[i].destroy();
|
||||||
|
|
||||||
|
// Destroy the rendertargets
|
||||||
|
depthBuffer_mem.destroy();
|
||||||
|
viewDirBuffer_mem.destroy();
|
||||||
|
normalBuffer_mem.destroy();
|
||||||
|
albedoBuffer_mem.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
~CExample08()
|
||||||
|
{
|
||||||
|
// Destory the framebuffer resources
|
||||||
|
destroyFramebufferResources();
|
||||||
|
|
||||||
|
// Destroy the index buffer (not strictly needed in this case)
|
||||||
|
indexBuffer.destroy();
|
||||||
|
|
||||||
|
// Destroy the vertex buffer (not strictly needed in this case)
|
||||||
|
vertexBuffer.destroy();
|
||||||
|
|
||||||
|
// Destroy the uniform buffers (not strictly needed in this case)
|
||||||
|
lightingUniformBuffer.destroy();
|
||||||
|
transformUniformBuffer.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void recordStaticCommands()
|
||||||
|
{
|
||||||
|
// Initialize state structs with deko3d defaults
|
||||||
|
dk::RasterizerState rasterizerState;
|
||||||
|
dk::ColorState colorState;
|
||||||
|
dk::ColorWriteState colorWriteState;
|
||||||
|
dk::DepthStencilState depthStencilState;
|
||||||
|
|
||||||
|
// Bind g-buffer and depth buffer
|
||||||
|
dk::ImageView albedoTarget { albedoBuffer }, normalTarget { normalBuffer }, viewDirTarget { viewDirBuffer }, depthTarget { depthBuffer };
|
||||||
|
cmdbuf.bindRenderTargets({ &albedoTarget, &normalTarget, &viewDirTarget }, &depthTarget);
|
||||||
|
|
||||||
|
// Configure viewport and scissor
|
||||||
|
const DkViewport viewport = { 0.0f, 0.0f, float(framebufferWidth), float(framebufferHeight), 0.0f, 1.0f };
|
||||||
|
const DkScissor scissor = { 0, 0, framebufferWidth, framebufferHeight };
|
||||||
|
cmdbuf.setViewports(0, { viewport, viewport, viewport });
|
||||||
|
cmdbuf.setScissors(0, { scissor, scissor, scissor });
|
||||||
|
|
||||||
|
// Clear the g-buffer and the depth buffer
|
||||||
|
cmdbuf.clearColor(0, DkColorMask_RGBA, 0.0f, 0.0f, 0.0f, 0.0f);
|
||||||
|
cmdbuf.clearColor(1, DkColorMask_RGBA, 0.0f, 0.0f, 0.0f, 0.0f);
|
||||||
|
cmdbuf.clearColor(2, DkColorMask_RGBA, 0.0f, 0.0f, 0.0f, 0.0f);
|
||||||
|
cmdbuf.clearDepthStencil(true, 1.0f, 0xFF, 0);
|
||||||
|
|
||||||
|
// Bind state required for drawing the mesh
|
||||||
|
cmdbuf.bindShaders(DkStageFlag_GraphicsMask, { vertexShader, fragmentShader });
|
||||||
|
cmdbuf.bindUniformBuffer(DkStage_Vertex, 0, transformUniformBuffer.getGpuAddr(), transformUniformBuffer.getSize());
|
||||||
|
cmdbuf.bindRasterizerState(rasterizerState);
|
||||||
|
cmdbuf.bindColorState(colorState);
|
||||||
|
cmdbuf.bindColorWriteState(colorWriteState);
|
||||||
|
cmdbuf.bindDepthStencilState(depthStencilState);
|
||||||
|
cmdbuf.bindVtxBuffer(0, vertexBuffer.getGpuAddr(), vertexBuffer.getSize());
|
||||||
|
cmdbuf.bindVtxAttribState(VertexAttribState);
|
||||||
|
cmdbuf.bindVtxBufferState(VertexBufferState);
|
||||||
|
cmdbuf.bindIdxBuffer(DkIdxFormat_Uint16, indexBuffer.getGpuAddr());
|
||||||
|
|
||||||
|
// Draw the mesh
|
||||||
|
cmdbuf.drawIndexed(DkPrimitive_Triangles, indexBuffer.getSize() / sizeof(u16), 1, 0, 0, 0);
|
||||||
|
|
||||||
|
// Tiled barrier (similar to using Vulkan's vkCmdNextSubpass) + image cache
|
||||||
|
// flush so that the next rendering step can access the output from this step
|
||||||
|
cmdbuf.barrier(DkBarrier_Tiles, DkInvalidateFlags_Image);
|
||||||
|
|
||||||
|
// Discard the depth buffer since we don't need it anymore
|
||||||
|
cmdbuf.discardDepthStencil();
|
||||||
|
|
||||||
|
// End of the main rendering command list
|
||||||
|
render_cmdlist = cmdbuf.finishList();
|
||||||
|
|
||||||
|
// Upload image descriptors
|
||||||
|
std::array<dk::ImageDescriptor, MaxImages> descriptors;
|
||||||
|
descriptors[0].initialize(albedoTarget);
|
||||||
|
descriptors[1].initialize(normalTarget);
|
||||||
|
descriptors[2].initialize(viewDirTarget);
|
||||||
|
imageDescriptorSet.update(cmdbuf, 0, descriptors);
|
||||||
|
|
||||||
|
// Upload sampler descriptor
|
||||||
|
dk::Sampler sampler;
|
||||||
|
dk::SamplerDescriptor samplerDescriptor;
|
||||||
|
samplerDescriptor.initialize(sampler);
|
||||||
|
samplerDescriptorSet.update(cmdbuf, 0, samplerDescriptor);
|
||||||
|
|
||||||
|
// Flush the descriptor cache
|
||||||
|
cmdbuf.barrier(DkBarrier_None, DkInvalidateFlags_Descriptors);
|
||||||
|
|
||||||
|
// Bind state required for doing the composition
|
||||||
|
cmdbuf.setViewports(0, viewport);
|
||||||
|
cmdbuf.setScissors(0, scissor);
|
||||||
|
cmdbuf.bindShaders(DkStageFlag_GraphicsMask, { compositionVertexShader, compositionFragmentShader });
|
||||||
|
cmdbuf.bindUniformBuffer(DkStage_Fragment, 0, lightingUniformBuffer.getGpuAddr(), lightingUniformBuffer.getSize());
|
||||||
|
cmdbuf.bindTextures(DkStage_Fragment, 0, {
|
||||||
|
dkMakeTextureHandle(0, 0),
|
||||||
|
dkMakeTextureHandle(1, 0),
|
||||||
|
dkMakeTextureHandle(2, 0),
|
||||||
|
});
|
||||||
|
cmdbuf.bindRasterizerState(dk::RasterizerState{});
|
||||||
|
cmdbuf.bindColorState(dk::ColorState{});
|
||||||
|
cmdbuf.bindColorWriteState(dk::ColorWriteState{});
|
||||||
|
cmdbuf.bindVtxAttribState({});
|
||||||
|
|
||||||
|
// Draw the full screen quad
|
||||||
|
cmdbuf.draw(DkPrimitive_Quads, 4, 1, 0, 0);
|
||||||
|
|
||||||
|
// Tiled barrier
|
||||||
|
cmdbuf.barrier(DkBarrier_Tiles, 0);
|
||||||
|
|
||||||
|
// Discard the g-buffer since we don't need it anymore
|
||||||
|
cmdbuf.bindRenderTargets({ &albedoTarget, &normalTarget, &viewDirTarget });
|
||||||
|
cmdbuf.discardColor(0);
|
||||||
|
cmdbuf.discardColor(1);
|
||||||
|
cmdbuf.discardColor(2);
|
||||||
|
|
||||||
|
// End of the composition cmdlist
|
||||||
|
composition_cmdlist = cmdbuf.finishList();
|
||||||
|
}
|
||||||
|
|
||||||
|
void render()
|
||||||
|
{
|
||||||
|
// Begin generating the dynamic command list, for commands that need to be sent only this frame specifically
|
||||||
|
dynmem.begin(dyncmd);
|
||||||
|
|
||||||
|
// Update the transformation uniform buffer with the new state (this data gets inlined in the command list)
|
||||||
|
dyncmd.pushConstants(
|
||||||
|
transformUniformBuffer.getGpuAddr(), transformUniformBuffer.getSize(),
|
||||||
|
0, sizeof(transformState), &transformState);
|
||||||
|
|
||||||
|
// Update the lighting uniform buffer with the new state
|
||||||
|
dyncmd.pushConstants(
|
||||||
|
lightingUniformBuffer.getGpuAddr(), lightingUniformBuffer.getSize(),
|
||||||
|
0, sizeof(lightingState), &lightingState);
|
||||||
|
|
||||||
|
// Finish off the dynamic command list (which also submits it to the queue)
|
||||||
|
queue.submitCommands(dynmem.end(dyncmd));
|
||||||
|
|
||||||
|
// Run the main rendering command list
|
||||||
|
queue.submitCommands(render_cmdlist);
|
||||||
|
|
||||||
|
// Acquire a framebuffer from the swapchain
|
||||||
|
int slot = queue.acquireImage(swapchain);
|
||||||
|
|
||||||
|
// Submit the command list that binds the correct framebuffer
|
||||||
|
queue.submitCommands(framebuffer_cmdlists[slot]);
|
||||||
|
|
||||||
|
// Submit the command list used for performing the composition
|
||||||
|
queue.submitCommands(composition_cmdlist);
|
||||||
|
|
||||||
|
// Now that we are done rendering, present it to the screen (this also flushes the queue)
|
||||||
|
queue.presentImage(swapchain, slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onOperationMode(AppletOperationMode mode) override
|
||||||
|
{
|
||||||
|
// Destroy the framebuffer resources
|
||||||
|
destroyFramebufferResources();
|
||||||
|
|
||||||
|
// Choose framebuffer size
|
||||||
|
chooseFramebufferSize(framebufferWidth, framebufferHeight, mode);
|
||||||
|
|
||||||
|
// Recreate the framebuffers and its associated resources
|
||||||
|
createFramebufferResources();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool onFrame(u64 ns) override
|
||||||
|
{
|
||||||
|
padUpdate(&pad);
|
||||||
|
u64 kDown = padGetButtonsDown(&pad);
|
||||||
|
if (kDown & HidNpadButton_Plus)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
float time = ns / 1000000000.0; // double precision division; followed by implicit cast to single precision
|
||||||
|
float tau = glm::two_pi<float>();
|
||||||
|
|
||||||
|
float period1 = fractf(time/8.0f);
|
||||||
|
float period2 = fractf(time/4.0f);
|
||||||
|
|
||||||
|
// Generate the model-view matrix for this frame
|
||||||
|
// Keep in mind that GLM transformation functions multiply to the right, so essentially we have:
|
||||||
|
// mdlvMtx = Translate * RotateX * RotateY * Translate
|
||||||
|
// This means that the Scale operation is applied first, then RotateY, and so on.
|
||||||
|
transformState.mdlvMtx = glm::mat4{1.0f};
|
||||||
|
transformState.mdlvMtx = glm::translate(transformState.mdlvMtx, glm::vec3{sinf(period1*tau), 0.0f, -3.0f});
|
||||||
|
transformState.mdlvMtx = glm::rotate(transformState.mdlvMtx, sinf(period2 * tau) * tau / 8.0f, glm::vec3{1.0f, 0.0f, 0.0f});
|
||||||
|
transformState.mdlvMtx = glm::rotate(transformState.mdlvMtx, -period1 * tau, glm::vec3{0.0f, 1.0f, 0.0f});
|
||||||
|
transformState.mdlvMtx = glm::translate(transformState.mdlvMtx, glm::vec3{0.0f, -0.5f, 0.0f});
|
||||||
|
|
||||||
|
render();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void Example08(void)
|
||||||
|
{
|
||||||
|
CExample08 app;
|
||||||
|
app.run();
|
||||||
|
}
|
324
graphics/deko3d/deko_examples/source/Example09_SimpleCompute.cpp
Normal file
324
graphics/deko3d/deko_examples/source/Example09_SimpleCompute.cpp
Normal file
@ -0,0 +1,324 @@
|
|||||||
|
/*
|
||||||
|
** deko3d Example 09: Simple Compute Shader (Geometry Generation)
|
||||||
|
** This example shows how to use a compute shader to dynamically generate geometry.
|
||||||
|
** New concepts in this example:
|
||||||
|
** - Enabling compute support in a queue
|
||||||
|
** - Configuring and using compute shaders
|
||||||
|
** - Setting up shader storage buffers (SSBOs)
|
||||||
|
** - Dispatching compute jobs
|
||||||
|
** - Using a primitive barrier to ensure ordering of items
|
||||||
|
** - Drawing geometry generated dynamically by the GPU itself
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Sample Framework headers
|
||||||
|
#include "SampleFramework/CApplication.h"
|
||||||
|
#include "SampleFramework/CMemPool.h"
|
||||||
|
#include "SampleFramework/CShader.h"
|
||||||
|
#include "SampleFramework/CCmdMemRing.h"
|
||||||
|
|
||||||
|
// C++ standard library headers
|
||||||
|
#include <array>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
// GLM headers
|
||||||
|
#define GLM_FORCE_DEFAULT_ALIGNED_GENTYPES // Enforces GLSL std140/std430 alignment rules for glm types
|
||||||
|
#define GLM_FORCE_INTRINSICS // Enables usage of SIMD CPU instructions (requiring the above as well)
|
||||||
|
#include <glm/vec4.hpp>
|
||||||
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
struct Vertex
|
||||||
|
{
|
||||||
|
float position[4];
|
||||||
|
float color[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr std::array VertexAttribState =
|
||||||
|
{
|
||||||
|
DkVtxAttribState{ 0, 0, offsetof(Vertex, position), DkVtxAttribSize_4x32, DkVtxAttribType_Float, 0 },
|
||||||
|
DkVtxAttribState{ 0, 0, offsetof(Vertex, color), DkVtxAttribSize_4x32, DkVtxAttribType_Float, 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr std::array VertexBufferState =
|
||||||
|
{
|
||||||
|
DkVtxBufferState{ sizeof(Vertex), 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GeneratorParams
|
||||||
|
{
|
||||||
|
glm::vec4 colorA;
|
||||||
|
glm::vec4 colorB;
|
||||||
|
float offset;
|
||||||
|
float scale;
|
||||||
|
float padding[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
inline float fractf(float x)
|
||||||
|
{
|
||||||
|
return x - floorf(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CExample09 final : public CApplication
|
||||||
|
{
|
||||||
|
static constexpr unsigned NumFramebuffers = 2;
|
||||||
|
static constexpr uint32_t FramebufferWidth = 1280;
|
||||||
|
static constexpr uint32_t FramebufferHeight = 720;
|
||||||
|
static constexpr unsigned StaticCmdSize = 0x10000;
|
||||||
|
static constexpr unsigned DynamicCmdSize = 0x10000;
|
||||||
|
static constexpr unsigned NumVertices = 256;
|
||||||
|
|
||||||
|
PadState pad;
|
||||||
|
|
||||||
|
dk::UniqueDevice device;
|
||||||
|
dk::UniqueQueue queue;
|
||||||
|
|
||||||
|
std::optional<CMemPool> pool_images;
|
||||||
|
std::optional<CMemPool> pool_code;
|
||||||
|
std::optional<CMemPool> pool_data;
|
||||||
|
|
||||||
|
dk::UniqueCmdBuf cmdbuf;
|
||||||
|
dk::UniqueCmdBuf dyncmd;
|
||||||
|
CCmdMemRing<NumFramebuffers> dynmem;
|
||||||
|
|
||||||
|
GeneratorParams params;
|
||||||
|
CMemPool::Handle paramsUniformBuffer;
|
||||||
|
|
||||||
|
CShader computeShader;
|
||||||
|
CShader vertexShader;
|
||||||
|
CShader fragmentShader;
|
||||||
|
|
||||||
|
CMemPool::Handle vertexBuffer;
|
||||||
|
|
||||||
|
CMemPool::Handle framebuffers_mem[NumFramebuffers];
|
||||||
|
dk::Image framebuffers[NumFramebuffers];
|
||||||
|
DkCmdList framebuffer_cmdlists[NumFramebuffers];
|
||||||
|
dk::UniqueSwapchain swapchain;
|
||||||
|
|
||||||
|
DkCmdList compute_cmdlist, render_cmdlist;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CExample09()
|
||||||
|
{
|
||||||
|
// Create the deko3d device
|
||||||
|
device = dk::DeviceMaker{}.create();
|
||||||
|
|
||||||
|
// Create the main queue
|
||||||
|
queue = dk::QueueMaker{device}.setFlags(DkQueueFlags_Graphics | DkQueueFlags_Compute).create();
|
||||||
|
|
||||||
|
// Create the memory pools
|
||||||
|
pool_images.emplace(device, DkMemBlockFlags_GpuCached | DkMemBlockFlags_Image, 16*1024*1024);
|
||||||
|
pool_code.emplace(device, DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached | DkMemBlockFlags_Code, 128*1024);
|
||||||
|
pool_data.emplace(device, DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached, 1*1024*1024);
|
||||||
|
|
||||||
|
// Create the static command buffer and feed it freshly allocated memory
|
||||||
|
cmdbuf = dk::CmdBufMaker{device}.create();
|
||||||
|
CMemPool::Handle cmdmem = pool_data->allocate(StaticCmdSize);
|
||||||
|
cmdbuf.addMemory(cmdmem.getMemBlock(), cmdmem.getOffset(), cmdmem.getSize());
|
||||||
|
|
||||||
|
// Create the dynamic command buffer and allocate memory for it
|
||||||
|
dyncmd = dk::CmdBufMaker{device}.create();
|
||||||
|
dynmem.allocate(*pool_data, DynamicCmdSize);
|
||||||
|
|
||||||
|
// Load the shaders
|
||||||
|
computeShader.load(*pool_code, "romfs:/shaders/sinewave.dksh");
|
||||||
|
vertexShader.load(*pool_code, "romfs:/shaders/basic_vsh.dksh");
|
||||||
|
fragmentShader.load(*pool_code, "romfs:/shaders/color_fsh.dksh");
|
||||||
|
|
||||||
|
// Create the uniform buffer
|
||||||
|
paramsUniformBuffer = pool_data->allocate(sizeof(params), DK_UNIFORM_BUF_ALIGNMENT);
|
||||||
|
|
||||||
|
// Initialize the params
|
||||||
|
params.colorA = glm::vec4 { 1.0f, 0.0f, 1.0f, 1.0f };
|
||||||
|
params.colorB = glm::vec4 { 0.0f, 1.0f, 0.0f, 1.0f };
|
||||||
|
params.offset = 0.0f;
|
||||||
|
params.scale = 1.0f;
|
||||||
|
|
||||||
|
// Allocate memory for the vertex buffer
|
||||||
|
vertexBuffer = pool_data->allocate(sizeof(Vertex)*NumVertices, alignof(Vertex));
|
||||||
|
|
||||||
|
// Create the framebuffer resources
|
||||||
|
createFramebufferResources();
|
||||||
|
|
||||||
|
// Initialize gamepad
|
||||||
|
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
|
||||||
|
padInitializeDefault(&pad);
|
||||||
|
}
|
||||||
|
|
||||||
|
~CExample09()
|
||||||
|
{
|
||||||
|
// Destroy the framebuffer resources
|
||||||
|
destroyFramebufferResources();
|
||||||
|
|
||||||
|
// Destroy the vertex buffer (not strictly needed in this case)
|
||||||
|
vertexBuffer.destroy();
|
||||||
|
|
||||||
|
// Destroy the uniform buffer (not strictly needed in this case)
|
||||||
|
paramsUniformBuffer.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void createFramebufferResources()
|
||||||
|
{
|
||||||
|
// Create layout for the framebuffers
|
||||||
|
dk::ImageLayout layout_framebuffer;
|
||||||
|
dk::ImageLayoutMaker{device}
|
||||||
|
.setFlags(DkImageFlags_UsageRender | DkImageFlags_UsagePresent | DkImageFlags_HwCompression)
|
||||||
|
.setFormat(DkImageFormat_RGBA8_Unorm)
|
||||||
|
.setDimensions(FramebufferWidth, FramebufferHeight)
|
||||||
|
.initialize(layout_framebuffer);
|
||||||
|
|
||||||
|
// Create the framebuffers
|
||||||
|
std::array<DkImage const*, NumFramebuffers> fb_array;
|
||||||
|
uint64_t fb_size = layout_framebuffer.getSize();
|
||||||
|
uint32_t fb_align = layout_framebuffer.getAlignment();
|
||||||
|
for (unsigned i = 0; i < NumFramebuffers; i ++)
|
||||||
|
{
|
||||||
|
// Allocate a framebuffer
|
||||||
|
framebuffers_mem[i] = pool_images->allocate(fb_size, fb_align);
|
||||||
|
framebuffers[i].initialize(layout_framebuffer, framebuffers_mem[i].getMemBlock(), framebuffers_mem[i].getOffset());
|
||||||
|
|
||||||
|
// Generate a command list that binds it
|
||||||
|
dk::ImageView colorTarget{ framebuffers[i] };
|
||||||
|
cmdbuf.bindRenderTargets(&colorTarget);
|
||||||
|
framebuffer_cmdlists[i] = cmdbuf.finishList();
|
||||||
|
|
||||||
|
// Fill in the array for use later by the swapchain creation code
|
||||||
|
fb_array[i] = &framebuffers[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the swapchain using the framebuffers
|
||||||
|
swapchain = dk::SwapchainMaker{device, nwindowGetDefault(), fb_array}.create();
|
||||||
|
|
||||||
|
// Generate the main rendering cmdlist
|
||||||
|
recordStaticCommands();
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroyFramebufferResources()
|
||||||
|
{
|
||||||
|
// Return early if we have nothing to destroy
|
||||||
|
if (!swapchain) return;
|
||||||
|
|
||||||
|
// Make sure the queue is idle before destroying anything
|
||||||
|
queue.waitIdle();
|
||||||
|
|
||||||
|
// Clear the static cmdbuf, destroying the static cmdlists in the process
|
||||||
|
cmdbuf.clear();
|
||||||
|
|
||||||
|
// Destroy the swapchain
|
||||||
|
swapchain.destroy();
|
||||||
|
|
||||||
|
// Destroy the framebuffers
|
||||||
|
for (unsigned i = 0; i < NumFramebuffers; i ++)
|
||||||
|
framebuffers_mem[i].destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void recordStaticCommands()
|
||||||
|
{
|
||||||
|
// Bind state required for running the compute job
|
||||||
|
cmdbuf.bindShaders(DkStageFlag_Compute, { computeShader });
|
||||||
|
cmdbuf.bindUniformBuffer(DkStage_Compute, 0, paramsUniformBuffer.getGpuAddr(), paramsUniformBuffer.getSize());
|
||||||
|
cmdbuf.bindStorageBuffer(DkStage_Compute, 0, vertexBuffer.getGpuAddr(), vertexBuffer.getSize());
|
||||||
|
|
||||||
|
// Run the compute job
|
||||||
|
cmdbuf.dispatchCompute(NumVertices/32, 1, 1);
|
||||||
|
|
||||||
|
// Place a barrier
|
||||||
|
cmdbuf.barrier(DkBarrier_Primitives, 0);
|
||||||
|
|
||||||
|
// Finish off this command list
|
||||||
|
compute_cmdlist = cmdbuf.finishList();
|
||||||
|
|
||||||
|
// Initialize state structs with deko3d defaults
|
||||||
|
dk::RasterizerState rasterizerState;
|
||||||
|
dk::ColorState colorState;
|
||||||
|
dk::ColorWriteState colorWriteState;
|
||||||
|
dk::BlendState blendState;
|
||||||
|
|
||||||
|
// Configure rasterizer state: enable polygon smoothing
|
||||||
|
rasterizerState.setPolygonSmoothEnable(true);
|
||||||
|
|
||||||
|
// Configure color state: enable blending (needed for polygon smoothing since it generates alpha values)
|
||||||
|
colorState.setBlendEnable(0, true);
|
||||||
|
|
||||||
|
// Configure viewport and scissor
|
||||||
|
cmdbuf.setViewports(0, { { 0.0f, 0.0f, FramebufferWidth, FramebufferHeight, 0.0f, 1.0f } });
|
||||||
|
cmdbuf.setScissors(0, { { 0, 0, FramebufferWidth, FramebufferHeight } });
|
||||||
|
|
||||||
|
// Clear the color buffer
|
||||||
|
cmdbuf.clearColor(0, DkColorMask_RGBA, 0.0f, 0.0f, 0.0f, 0.0f);
|
||||||
|
|
||||||
|
// Bind state required for drawing the triangle
|
||||||
|
cmdbuf.bindShaders(DkStageFlag_GraphicsMask, { vertexShader, fragmentShader });
|
||||||
|
cmdbuf.bindRasterizerState(rasterizerState);
|
||||||
|
cmdbuf.bindColorState(colorState);
|
||||||
|
cmdbuf.bindColorWriteState(colorWriteState);
|
||||||
|
cmdbuf.bindBlendStates(0, blendState);
|
||||||
|
cmdbuf.bindVtxBuffer(0, vertexBuffer.getGpuAddr(), vertexBuffer.getSize());
|
||||||
|
cmdbuf.bindVtxAttribState(VertexAttribState);
|
||||||
|
cmdbuf.bindVtxBufferState(VertexBufferState);
|
||||||
|
cmdbuf.setLineWidth(16.0f);
|
||||||
|
|
||||||
|
// Draw the line
|
||||||
|
cmdbuf.draw(DkPrimitive_LineStrip, NumVertices, 1, 0, 0);
|
||||||
|
|
||||||
|
// Finish off this command list
|
||||||
|
render_cmdlist = cmdbuf.finishList();
|
||||||
|
}
|
||||||
|
|
||||||
|
void render()
|
||||||
|
{
|
||||||
|
// Begin generating the dynamic command list, for commands that need to be sent only this frame specifically
|
||||||
|
dynmem.begin(dyncmd);
|
||||||
|
|
||||||
|
// Update the uniform buffer with the new state (this data gets inlined in the command list)
|
||||||
|
dyncmd.pushConstants(
|
||||||
|
paramsUniformBuffer.getGpuAddr(), paramsUniformBuffer.getSize(),
|
||||||
|
0, sizeof(params), ¶ms);
|
||||||
|
|
||||||
|
// Finish off the dynamic command list (which also submits it to the queue)
|
||||||
|
queue.submitCommands(dynmem.end(dyncmd));
|
||||||
|
|
||||||
|
// Run the compute command list
|
||||||
|
queue.submitCommands(compute_cmdlist);
|
||||||
|
|
||||||
|
// Acquire a framebuffer from the swapchain (and wait for it to be available)
|
||||||
|
int slot = queue.acquireImage(swapchain);
|
||||||
|
|
||||||
|
// Run the command list that attaches said framebuffer to the queue
|
||||||
|
queue.submitCommands(framebuffer_cmdlists[slot]);
|
||||||
|
|
||||||
|
// Run the main rendering command list
|
||||||
|
queue.submitCommands(render_cmdlist);
|
||||||
|
|
||||||
|
// Now that we are done rendering, present it to the screen
|
||||||
|
queue.presentImage(swapchain, slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool onFrame(u64 ns) override
|
||||||
|
{
|
||||||
|
padUpdate(&pad);
|
||||||
|
u64 kDown = padGetButtonsDown(&pad);
|
||||||
|
if (kDown & HidNpadButton_Plus)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
float time = ns / 1000000000.0; // double precision division; followed by implicit cast to single precision
|
||||||
|
float tau = glm::two_pi<float>();
|
||||||
|
|
||||||
|
params.offset = fractf(time/4.0f);
|
||||||
|
|
||||||
|
float xx = fractf(time * 135.0f / 60.0f / 2.0f);
|
||||||
|
params.scale = cosf(xx*tau);
|
||||||
|
params.colorA.g = powf(fabsf(params.scale), 4.0f);
|
||||||
|
params.colorB.g = 1.0f - params.colorA.g;
|
||||||
|
|
||||||
|
render();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void Example09(void)
|
||||||
|
{
|
||||||
|
CExample09 app;
|
||||||
|
app.run();
|
||||||
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
** Sample Framework for deko3d Applications
|
||||||
|
** CApplication.cpp: Wrapper class containing common application boilerplate
|
||||||
|
*/
|
||||||
|
#include "CApplication.h"
|
||||||
|
|
||||||
|
CApplication::CApplication()
|
||||||
|
{
|
||||||
|
appletLockExit();
|
||||||
|
appletSetFocusHandlingMode(AppletFocusHandlingMode_NoSuspend);
|
||||||
|
}
|
||||||
|
|
||||||
|
CApplication::~CApplication()
|
||||||
|
{
|
||||||
|
appletSetFocusHandlingMode(AppletFocusHandlingMode_SuspendHomeSleep);
|
||||||
|
appletUnlockExit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CApplication::run()
|
||||||
|
{
|
||||||
|
u64 tick_ref = armGetSystemTick();
|
||||||
|
u64 tick_saved = tick_ref;
|
||||||
|
bool focused = appletGetFocusState() == AppletFocusState_InFocus;
|
||||||
|
|
||||||
|
onOperationMode(appletGetOperationMode());
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
u32 msg = 0;
|
||||||
|
Result rc = appletGetMessage(&msg);
|
||||||
|
if (R_SUCCEEDED(rc))
|
||||||
|
{
|
||||||
|
bool should_close = !appletProcessMessage(msg);
|
||||||
|
if (should_close)
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch (msg)
|
||||||
|
{
|
||||||
|
case AppletMessage_FocusStateChanged:
|
||||||
|
{
|
||||||
|
bool old_focused = focused;
|
||||||
|
AppletFocusState state = appletGetFocusState();
|
||||||
|
focused = state == AppletFocusState_InFocus;
|
||||||
|
|
||||||
|
onFocusState(state);
|
||||||
|
if (focused == old_focused)
|
||||||
|
break;
|
||||||
|
if (focused)
|
||||||
|
{
|
||||||
|
appletSetFocusHandlingMode(AppletFocusHandlingMode_NoSuspend);
|
||||||
|
tick_ref += armGetSystemTick() - tick_saved;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tick_saved = armGetSystemTick();
|
||||||
|
appletSetFocusHandlingMode(AppletFocusHandlingMode_SuspendHomeSleepNotify);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AppletMessage_OperationModeChanged:
|
||||||
|
onOperationMode(appletGetOperationMode());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (focused && !onFrame(armTicksToNs(armGetSystemTick() - tick_ref)))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
** Sample Framework for deko3d Applications
|
||||||
|
** CApplication.h: Wrapper class containing common application boilerplate
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
class CApplication
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
virtual void onFocusState(AppletFocusState) { }
|
||||||
|
virtual void onOperationMode(AppletOperationMode) { }
|
||||||
|
virtual bool onFrame(u64) { return true; }
|
||||||
|
|
||||||
|
public:
|
||||||
|
CApplication();
|
||||||
|
~CApplication();
|
||||||
|
|
||||||
|
void run();
|
||||||
|
|
||||||
|
static constexpr void chooseFramebufferSize(uint32_t& width, uint32_t& height, AppletOperationMode mode);
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr void CApplication::chooseFramebufferSize(uint32_t& width, uint32_t& height, AppletOperationMode mode)
|
||||||
|
{
|
||||||
|
switch (mode)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case AppletOperationMode_Handheld:
|
||||||
|
width = 1280;
|
||||||
|
height = 720;
|
||||||
|
break;
|
||||||
|
case AppletOperationMode_Console:
|
||||||
|
width = 1920;
|
||||||
|
height = 1080;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
** Sample Framework for deko3d Applications
|
||||||
|
** CCmdMemRing.h: Memory provider class for dynamic command buffers
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "common.h"
|
||||||
|
#include "CMemPool.h"
|
||||||
|
|
||||||
|
template <unsigned NumSlices>
|
||||||
|
class CCmdMemRing
|
||||||
|
{
|
||||||
|
static_assert(NumSlices > 0, "Need a non-zero number of slices...");
|
||||||
|
CMemPool::Handle m_mem;
|
||||||
|
unsigned m_curSlice;
|
||||||
|
dk::Fence m_fences[NumSlices];
|
||||||
|
public:
|
||||||
|
CCmdMemRing() : m_mem{}, m_curSlice{}, m_fences{} { }
|
||||||
|
|
||||||
|
CCmdMemRing(const CCmdMemRing&) = delete;
|
||||||
|
|
||||||
|
CCmdMemRing& operator=(const CCmdMemRing&) = delete;
|
||||||
|
|
||||||
|
~CCmdMemRing()
|
||||||
|
{
|
||||||
|
m_mem.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool allocate(CMemPool& pool, uint32_t sliceSize)
|
||||||
|
{
|
||||||
|
sliceSize = (sliceSize + DK_CMDMEM_ALIGNMENT - 1) &~ (DK_CMDMEM_ALIGNMENT - 1);
|
||||||
|
m_mem = pool.allocate(NumSlices*sliceSize);
|
||||||
|
return m_mem;
|
||||||
|
}
|
||||||
|
|
||||||
|
void begin(dk::CmdBuf cmdbuf)
|
||||||
|
{
|
||||||
|
// Clear/reset the command buffer, which also destroys all command list handles
|
||||||
|
// (but remember: it does *not* in fact destroy the command data)
|
||||||
|
cmdbuf.clear();
|
||||||
|
|
||||||
|
// Wait for the current slice of memory to be available, and feed it to the command buffer
|
||||||
|
uint32_t sliceSize = m_mem.getSize() / NumSlices;
|
||||||
|
m_fences[m_curSlice].wait();
|
||||||
|
|
||||||
|
// Feed the memory to the command buffer
|
||||||
|
cmdbuf.addMemory(m_mem.getMemBlock(), m_mem.getOffset() + m_curSlice * sliceSize, sliceSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
DkCmdList end(dk::CmdBuf cmdbuf)
|
||||||
|
{
|
||||||
|
// Signal the fence corresponding to the current slice; so that in the future when we want
|
||||||
|
// to use it again, we can wait for the completion of the commands we've just submitted
|
||||||
|
// (and as such we don't overwrite in-flight command data with new one)
|
||||||
|
cmdbuf.signalFence(m_fences[m_curSlice]);
|
||||||
|
|
||||||
|
// Advance the current slice counter; wrapping around when we reach the end
|
||||||
|
m_curSlice = (m_curSlice + 1) % NumSlices;
|
||||||
|
|
||||||
|
// Finish off the command list, returning it to the caller
|
||||||
|
return cmdbuf.finishList();
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
** Sample Framework for deko3d Applications
|
||||||
|
** CDescriptorSet.h: Image/Sampler descriptor set class
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "common.h"
|
||||||
|
#include "CMemPool.h"
|
||||||
|
|
||||||
|
template <unsigned NumDescriptors>
|
||||||
|
class CDescriptorSet
|
||||||
|
{
|
||||||
|
static_assert(NumDescriptors > 0, "Need a non-zero number of descriptors...");
|
||||||
|
static_assert(sizeof(DkImageDescriptor) == sizeof(DkSamplerDescriptor), "shouldn't happen");
|
||||||
|
static_assert(DK_IMAGE_DESCRIPTOR_ALIGNMENT == DK_SAMPLER_DESCRIPTOR_ALIGNMENT, "shouldn't happen");
|
||||||
|
static constexpr size_t DescriptorSize = sizeof(DkImageDescriptor);
|
||||||
|
static constexpr size_t DescriptorAlign = DK_IMAGE_DESCRIPTOR_ALIGNMENT;
|
||||||
|
|
||||||
|
CMemPool::Handle m_mem;
|
||||||
|
public:
|
||||||
|
CDescriptorSet() : m_mem{} { }
|
||||||
|
|
||||||
|
CDescriptorSet(const CDescriptorSet&) = delete;
|
||||||
|
|
||||||
|
CDescriptorSet& operator=(const CDescriptorSet&) = delete;
|
||||||
|
|
||||||
|
~CDescriptorSet()
|
||||||
|
{
|
||||||
|
m_mem.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool allocate(CMemPool& pool)
|
||||||
|
{
|
||||||
|
m_mem = pool.allocate(NumDescriptors*DescriptorSize, DescriptorAlign);
|
||||||
|
return m_mem;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bindForImages(dk::CmdBuf cmdbuf)
|
||||||
|
{
|
||||||
|
cmdbuf.bindImageDescriptorSet(m_mem.getGpuAddr(), NumDescriptors);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bindForSamplers(dk::CmdBuf cmdbuf)
|
||||||
|
{
|
||||||
|
cmdbuf.bindSamplerDescriptorSet(m_mem.getGpuAddr(), NumDescriptors);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void update(dk::CmdBuf cmdbuf, uint32_t id, T const& descriptor)
|
||||||
|
{
|
||||||
|
static_assert(sizeof(T) == DescriptorSize);
|
||||||
|
cmdbuf.pushData(m_mem.getGpuAddr() + id*DescriptorSize, &descriptor, DescriptorSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, size_t N>
|
||||||
|
void update(dk::CmdBuf cmdbuf, uint32_t id, std::array<T, N> const& descriptors)
|
||||||
|
{
|
||||||
|
static_assert(sizeof(T) == DescriptorSize);
|
||||||
|
cmdbuf.pushData(m_mem.getGpuAddr() + id*DescriptorSize, descriptors.data(), descriptors.size()*DescriptorSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DK_HPP_SUPPORT_VECTOR
|
||||||
|
template <typename T, typename Allocator = std::allocator<T>>
|
||||||
|
void update(dk::CmdBuf cmdbuf, uint32_t id, std::vector<T,Allocator> const& descriptors)
|
||||||
|
{
|
||||||
|
static_assert(sizeof(T) == DescriptorSize);
|
||||||
|
cmdbuf.pushData(m_mem.getGpuAddr() + id*DescriptorSize, descriptors.data(), descriptors.size()*DescriptorSize);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void update(dk::CmdBuf cmdbuf, uint32_t id, std::initializer_list<T const> const& descriptors)
|
||||||
|
{
|
||||||
|
static_assert(sizeof(T) == DescriptorSize);
|
||||||
|
cmdbuf.pushData(m_mem.getGpuAddr() + id*DescriptorSize, descriptors.data(), descriptors.size()*DescriptorSize);
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
** Sample Framework for deko3d Applications
|
||||||
|
** CExternalImage.cpp: Utility class for loading images from the filesystem
|
||||||
|
*/
|
||||||
|
#include "CExternalImage.h"
|
||||||
|
#include "FileLoader.h"
|
||||||
|
|
||||||
|
bool CExternalImage::load(CMemPool& imagePool, CMemPool& scratchPool, dk::Device device, dk::Queue transferQueue, const char* path, uint32_t width, uint32_t height, DkImageFormat format, uint32_t flags)
|
||||||
|
{
|
||||||
|
CMemPool::Handle tempimgmem = LoadFile(scratchPool, path, DK_IMAGE_LINEAR_STRIDE_ALIGNMENT);
|
||||||
|
if (!tempimgmem)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
dk::UniqueCmdBuf tempcmdbuf = dk::CmdBufMaker{device}.create();
|
||||||
|
CMemPool::Handle tempcmdmem = scratchPool.allocate(DK_MEMBLOCK_ALIGNMENT);
|
||||||
|
tempcmdbuf.addMemory(tempcmdmem.getMemBlock(), tempcmdmem.getOffset(), tempcmdmem.getSize());
|
||||||
|
|
||||||
|
dk::ImageLayout layout;
|
||||||
|
dk::ImageLayoutMaker{device}
|
||||||
|
.setFlags(flags)
|
||||||
|
.setFormat(format)
|
||||||
|
.setDimensions(width, height)
|
||||||
|
.initialize(layout);
|
||||||
|
|
||||||
|
m_mem = imagePool.allocate(layout.getSize(), layout.getAlignment());
|
||||||
|
m_image.initialize(layout, m_mem.getMemBlock(), m_mem.getOffset());
|
||||||
|
m_descriptor.initialize(m_image);
|
||||||
|
|
||||||
|
dk::ImageView imageView{m_image};
|
||||||
|
tempcmdbuf.copyBufferToImage({ tempimgmem.getGpuAddr() }, imageView, { 0, 0, 0, width, height, 1 });
|
||||||
|
transferQueue.submitCommands(tempcmdbuf.finishList());
|
||||||
|
transferQueue.waitIdle();
|
||||||
|
|
||||||
|
tempcmdmem.destroy();
|
||||||
|
tempimgmem.destroy();
|
||||||
|
return true;
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
** Sample Framework for deko3d Applications
|
||||||
|
** CExternalImage.h: Utility class for loading images from the filesystem
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "common.h"
|
||||||
|
#include "CMemPool.h"
|
||||||
|
|
||||||
|
class CExternalImage
|
||||||
|
{
|
||||||
|
dk::Image m_image;
|
||||||
|
dk::ImageDescriptor m_descriptor;
|
||||||
|
CMemPool::Handle m_mem;
|
||||||
|
public:
|
||||||
|
CExternalImage() : m_image{}, m_descriptor{}, m_mem{} { }
|
||||||
|
|
||||||
|
CExternalImage(const CExternalImage&) = delete;
|
||||||
|
|
||||||
|
CExternalImage& operator=(const CExternalImage&) = delete;
|
||||||
|
|
||||||
|
~CExternalImage()
|
||||||
|
{
|
||||||
|
m_mem.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr operator bool() const
|
||||||
|
{
|
||||||
|
return m_mem;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr dk::Image& get()
|
||||||
|
{
|
||||||
|
return m_image;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr dk::ImageDescriptor const& getDescriptor() const
|
||||||
|
{
|
||||||
|
return m_descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool load(CMemPool& imagePool, CMemPool& scratchPool, dk::Device device, dk::Queue transferQueue, const char* path, uint32_t width, uint32_t height, DkImageFormat format, uint32_t flags = 0);
|
||||||
|
};
|
@ -0,0 +1,119 @@
|
|||||||
|
/*
|
||||||
|
** Sample Framework for deko3d Applications
|
||||||
|
** CIntrusiveList.h: Intrusive doubly-linked list helper class
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct CIntrusiveListNode
|
||||||
|
{
|
||||||
|
T *m_next, *m_prev;
|
||||||
|
|
||||||
|
constexpr CIntrusiveListNode() : m_next{}, m_prev{} { }
|
||||||
|
constexpr operator bool() const { return m_next || m_prev; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, CIntrusiveListNode<T> T::* node_ptr>
|
||||||
|
class CIntrusiveList
|
||||||
|
{
|
||||||
|
T *m_first, *m_last;
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr CIntrusiveList() : m_first{}, m_last{} { }
|
||||||
|
|
||||||
|
constexpr T* first() const { return m_first; }
|
||||||
|
constexpr T* last() const { return m_last; }
|
||||||
|
constexpr bool empty() const { return !m_first; }
|
||||||
|
constexpr void clear() { m_first = m_last = nullptr; }
|
||||||
|
|
||||||
|
constexpr bool isLinked(T* obj) const { return obj->*node_ptr || m_first == obj; }
|
||||||
|
constexpr T* prev(T* obj) const { return (obj->*node_ptr).m_prev; }
|
||||||
|
constexpr T* next(T* obj) const { return (obj->*node_ptr).m_next; }
|
||||||
|
|
||||||
|
void add(T* obj)
|
||||||
|
{
|
||||||
|
return addBefore(nullptr, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addBefore(T* pos, T* obj)
|
||||||
|
{
|
||||||
|
auto& node = obj->*node_ptr;
|
||||||
|
node.m_next = pos;
|
||||||
|
node.m_prev = pos ? (pos->*node_ptr).m_prev : m_last;
|
||||||
|
|
||||||
|
if (pos)
|
||||||
|
(pos->*node_ptr).m_prev = obj;
|
||||||
|
else
|
||||||
|
m_last = obj;
|
||||||
|
|
||||||
|
if (node.m_prev)
|
||||||
|
(node.m_prev->*node_ptr).m_next = obj;
|
||||||
|
else
|
||||||
|
m_first = obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addAfter(T* pos, T* obj)
|
||||||
|
{
|
||||||
|
auto& node = obj->*node_ptr;
|
||||||
|
node.m_next = pos ? (pos->*node_ptr).m_next : m_first;
|
||||||
|
node.m_prev = pos;
|
||||||
|
|
||||||
|
if (pos)
|
||||||
|
(pos->*node_ptr).m_next = obj;
|
||||||
|
else
|
||||||
|
m_first = obj;
|
||||||
|
|
||||||
|
if (node.m_next)
|
||||||
|
(node.m_next->*node_ptr).m_prev = obj;
|
||||||
|
else
|
||||||
|
m_last = obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
T* pop()
|
||||||
|
{
|
||||||
|
T* ret = m_first;
|
||||||
|
if (ret)
|
||||||
|
{
|
||||||
|
m_first = (ret->*node_ptr).m_next;
|
||||||
|
if (m_first)
|
||||||
|
(m_first->*node_ptr).m_prev = nullptr;
|
||||||
|
else
|
||||||
|
m_last = nullptr;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove(T* obj)
|
||||||
|
{
|
||||||
|
auto& node = obj->*node_ptr;
|
||||||
|
if (node.m_prev)
|
||||||
|
{
|
||||||
|
(node.m_prev->*node_ptr).m_next = node.m_next;
|
||||||
|
if (node.m_next)
|
||||||
|
(node.m_next->*node_ptr).m_prev = node.m_prev;
|
||||||
|
else
|
||||||
|
m_last = node.m_prev;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
m_first = node.m_next;
|
||||||
|
if (m_first)
|
||||||
|
(m_first->*node_ptr).m_prev = nullptr;
|
||||||
|
else
|
||||||
|
m_last = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
node.m_next = node.m_prev = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename L>
|
||||||
|
void iterate(L lambda) const
|
||||||
|
{
|
||||||
|
T* next = nullptr;
|
||||||
|
for (T* cur = m_first; cur; cur = next)
|
||||||
|
{
|
||||||
|
next = (cur->*node_ptr).m_next;
|
||||||
|
lambda(cur);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,214 @@
|
|||||||
|
/*
|
||||||
|
** Sample Framework for deko3d Applications
|
||||||
|
** CIntrusiveTree.cpp: Intrusive red-black tree helper class
|
||||||
|
*/
|
||||||
|
#include "CIntrusiveTree.h"
|
||||||
|
|
||||||
|
// This red-black tree implementation is mostly based on mtheall's work,
|
||||||
|
// which can be found here:
|
||||||
|
// https://github.com/smealum/ctrulib/tree/master/libctru/source/util/rbtree
|
||||||
|
|
||||||
|
void CIntrusiveTreeBase::rotate(N* node, N::Leaf leaf)
|
||||||
|
{
|
||||||
|
N *tmp = node->child(leaf);
|
||||||
|
N *parent = node->getParent();
|
||||||
|
|
||||||
|
node->child(leaf) = tmp->child(!leaf);
|
||||||
|
if (tmp->child(!leaf))
|
||||||
|
tmp->child(!leaf)->setParent(node);
|
||||||
|
|
||||||
|
tmp->child(!leaf) = node;
|
||||||
|
tmp->setParent(parent);
|
||||||
|
|
||||||
|
if (parent)
|
||||||
|
{
|
||||||
|
if (node == parent->child(!leaf))
|
||||||
|
parent->child(!leaf) = tmp;
|
||||||
|
else
|
||||||
|
parent->child(leaf) = tmp;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
m_root = tmp;
|
||||||
|
|
||||||
|
node->setParent(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CIntrusiveTreeBase::recolor(N* parent, N* node)
|
||||||
|
{
|
||||||
|
N *sibling;
|
||||||
|
|
||||||
|
while ((!node || node->isBlack()) && node != m_root)
|
||||||
|
{
|
||||||
|
N::Leaf leaf = node == parent->left() ? N::Right : N::Left;
|
||||||
|
sibling = parent->child(leaf);
|
||||||
|
|
||||||
|
if (sibling->isRed())
|
||||||
|
{
|
||||||
|
sibling->setBlack();
|
||||||
|
parent->setRed();
|
||||||
|
rotate(parent, leaf);
|
||||||
|
sibling = parent->child(leaf);
|
||||||
|
}
|
||||||
|
|
||||||
|
N::Color clr[2];
|
||||||
|
clr[N::Left] = sibling->left() ? sibling->left()->getColor() : N::Black;
|
||||||
|
clr[N::Right] = sibling->right() ? sibling->right()->getColor() : N::Black;
|
||||||
|
|
||||||
|
if (clr[N::Left] == N::Black && clr[N::Right] == N::Black)
|
||||||
|
{
|
||||||
|
sibling->setRed();
|
||||||
|
node = parent;
|
||||||
|
parent = node->getParent();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (clr[leaf] == N::Black)
|
||||||
|
{
|
||||||
|
sibling->child(!leaf)->setBlack();
|
||||||
|
sibling->setRed();
|
||||||
|
rotate(sibling, !leaf);
|
||||||
|
sibling = parent->child(leaf);
|
||||||
|
}
|
||||||
|
|
||||||
|
sibling->setColor(parent->getColor());
|
||||||
|
parent->setBlack();
|
||||||
|
sibling->child(leaf)->setBlack();
|
||||||
|
rotate(parent, leaf);
|
||||||
|
|
||||||
|
node = m_root;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node)
|
||||||
|
node->setBlack();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto CIntrusiveTreeBase::walk(N* node, N::Leaf leaf) const -> N*
|
||||||
|
{
|
||||||
|
if (node->child(leaf))
|
||||||
|
{
|
||||||
|
node = node->child(leaf);
|
||||||
|
while (node->child(!leaf))
|
||||||
|
node = node->child(!leaf);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
N *parent = node->getParent();
|
||||||
|
while (parent && node == parent->child(leaf))
|
||||||
|
{
|
||||||
|
node = parent;
|
||||||
|
parent = node->getParent();
|
||||||
|
}
|
||||||
|
node = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CIntrusiveTreeBase::insert(N* node, N* parent)
|
||||||
|
{
|
||||||
|
node->left() = node->right() = nullptr;
|
||||||
|
node->setParent(parent);
|
||||||
|
node->setRed();
|
||||||
|
|
||||||
|
while ((parent = node->getParent()) && parent->isRed())
|
||||||
|
{
|
||||||
|
N *grandparent = parent->getParent();
|
||||||
|
N::Leaf leaf = parent == grandparent->left() ? N::Right : N::Left;
|
||||||
|
N *uncle = grandparent->child(leaf);
|
||||||
|
|
||||||
|
if (uncle && uncle->isRed())
|
||||||
|
{
|
||||||
|
uncle->setBlack();
|
||||||
|
parent->setBlack();
|
||||||
|
grandparent->setRed();
|
||||||
|
|
||||||
|
node = grandparent;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (parent->child(leaf) == node)
|
||||||
|
{
|
||||||
|
rotate(parent, leaf);
|
||||||
|
|
||||||
|
N* tmp = parent;
|
||||||
|
parent = node;
|
||||||
|
node = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
parent->setBlack();
|
||||||
|
grandparent->setRed();
|
||||||
|
rotate(grandparent, !leaf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_root->setBlack();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CIntrusiveTreeBase::remove(N* node)
|
||||||
|
{
|
||||||
|
N::Color color;
|
||||||
|
N *child, *parent;
|
||||||
|
|
||||||
|
if (node->left() && node->right())
|
||||||
|
{
|
||||||
|
N *old = node;
|
||||||
|
|
||||||
|
node = node->right();
|
||||||
|
while (node->left())
|
||||||
|
node = node->left();
|
||||||
|
|
||||||
|
parent = old->getParent();
|
||||||
|
if (parent)
|
||||||
|
{
|
||||||
|
if (parent->left() == old)
|
||||||
|
parent->left() = node;
|
||||||
|
else
|
||||||
|
parent->right() = node;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
m_root = node;
|
||||||
|
|
||||||
|
child = node->right();
|
||||||
|
parent = node->getParent();
|
||||||
|
color = node->getColor();
|
||||||
|
|
||||||
|
if (parent == old)
|
||||||
|
parent = node;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (child)
|
||||||
|
child->setParent(parent);
|
||||||
|
parent->left() = child;
|
||||||
|
|
||||||
|
node->right() = old->right();
|
||||||
|
old->right()->setParent(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
node->setParent(old->getParent());
|
||||||
|
node->setColor(old->getColor());
|
||||||
|
node->left() = old->left();
|
||||||
|
old->left()->setParent(node);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
child = node->left() ? node->left() : node->right();
|
||||||
|
parent = node->getParent();
|
||||||
|
color = node->getColor();
|
||||||
|
|
||||||
|
if (child)
|
||||||
|
child->setParent(parent);
|
||||||
|
if (parent)
|
||||||
|
{
|
||||||
|
if (parent->left() == node)
|
||||||
|
parent->left() = child;
|
||||||
|
else
|
||||||
|
parent->right() = child;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
m_root = child;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (color == N::Black)
|
||||||
|
recolor(parent, child);
|
||||||
|
}
|
@ -0,0 +1,230 @@
|
|||||||
|
/*
|
||||||
|
** Sample Framework for deko3d Applications
|
||||||
|
** CIntrusiveTree.h: Intrusive red-black tree helper class
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
struct CIntrusiveTreeNode
|
||||||
|
{
|
||||||
|
enum Color
|
||||||
|
{
|
||||||
|
Red,
|
||||||
|
Black,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Leaf
|
||||||
|
{
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
uintptr_t m_parent_color;
|
||||||
|
CIntrusiveTreeNode* m_children[2];
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr CIntrusiveTreeNode() : m_parent_color{}, m_children{} { }
|
||||||
|
|
||||||
|
constexpr CIntrusiveTreeNode* getParent() const
|
||||||
|
{
|
||||||
|
return reinterpret_cast<CIntrusiveTreeNode*>(m_parent_color &~ 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setParent(CIntrusiveTreeNode* parent)
|
||||||
|
{
|
||||||
|
m_parent_color = (m_parent_color & 1) | reinterpret_cast<uintptr_t>(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Color getColor() const
|
||||||
|
{
|
||||||
|
return static_cast<Color>(m_parent_color & 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setColor(Color color)
|
||||||
|
{
|
||||||
|
m_parent_color = (m_parent_color &~ 1) | static_cast<uintptr_t>(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr CIntrusiveTreeNode*& child(Leaf leaf)
|
||||||
|
{
|
||||||
|
return m_children[leaf];
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr CIntrusiveTreeNode* const& child(Leaf leaf) const
|
||||||
|
{
|
||||||
|
return m_children[leaf];
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------
|
||||||
|
|
||||||
|
constexpr bool isRed() const { return getColor() == Red; }
|
||||||
|
constexpr bool isBlack() const { return getColor() == Black; }
|
||||||
|
void setRed() { setColor(Red); }
|
||||||
|
void setBlack() { setColor(Black); }
|
||||||
|
|
||||||
|
constexpr CIntrusiveTreeNode*& left() { return child(Left); }
|
||||||
|
constexpr CIntrusiveTreeNode*& right() { return child(Right); }
|
||||||
|
constexpr CIntrusiveTreeNode* const& left() const { return child(Left); }
|
||||||
|
constexpr CIntrusiveTreeNode* const& right() const { return child(Right); }
|
||||||
|
};
|
||||||
|
|
||||||
|
NX_CONSTEXPR CIntrusiveTreeNode::Leaf operator!(CIntrusiveTreeNode::Leaf val) noexcept
|
||||||
|
{
|
||||||
|
return static_cast<CIntrusiveTreeNode::Leaf>(!static_cast<unsigned>(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
class CIntrusiveTreeBase
|
||||||
|
{
|
||||||
|
using N = CIntrusiveTreeNode;
|
||||||
|
|
||||||
|
void rotate(N* node, N::Leaf leaf);
|
||||||
|
void recolor(N* parent, N* node);
|
||||||
|
protected:
|
||||||
|
N* m_root;
|
||||||
|
|
||||||
|
constexpr CIntrusiveTreeBase() : m_root{} { }
|
||||||
|
|
||||||
|
N* walk(N* node, N::Leaf leaf) const;
|
||||||
|
void insert(N* node, N* parent);
|
||||||
|
void remove(N* node);
|
||||||
|
|
||||||
|
N* minmax(N::Leaf leaf) const
|
||||||
|
{
|
||||||
|
N* p = m_root;
|
||||||
|
if (!p)
|
||||||
|
return nullptr;
|
||||||
|
while (p->child(leaf))
|
||||||
|
p = p->child(leaf);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename H>
|
||||||
|
N*& navigate(N*& node, N*& parent, N::Leaf leafOnEqual, H helm) const
|
||||||
|
{
|
||||||
|
node = nullptr;
|
||||||
|
parent = nullptr;
|
||||||
|
|
||||||
|
N** point = const_cast<N**>(&m_root);
|
||||||
|
while (*point)
|
||||||
|
{
|
||||||
|
int direction = helm(*point);
|
||||||
|
parent = *point;
|
||||||
|
if (direction < 0)
|
||||||
|
point = &(*point)->left();
|
||||||
|
else if (direction > 0)
|
||||||
|
point = &(*point)->right();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
node = *point;
|
||||||
|
point = &(*point)->child(leafOnEqual);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return *point;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename ClassT, typename MemberT>
|
||||||
|
constexpr ClassT* parent_obj(MemberT* member, MemberT ClassT::* ptr)
|
||||||
|
{
|
||||||
|
union whatever
|
||||||
|
{
|
||||||
|
MemberT ClassT::* ptr;
|
||||||
|
intptr_t offset;
|
||||||
|
};
|
||||||
|
// This is technically UB, but basically every compiler worth using admits it as an extension
|
||||||
|
return (ClassT*)((intptr_t)member - whatever{ptr}.offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename T,
|
||||||
|
CIntrusiveTreeNode T::* node_ptr,
|
||||||
|
typename Comparator = std::less<>
|
||||||
|
>
|
||||||
|
class CIntrusiveTree final : protected CIntrusiveTreeBase
|
||||||
|
{
|
||||||
|
using N = CIntrusiveTreeNode;
|
||||||
|
|
||||||
|
static constexpr T* toType(N* m)
|
||||||
|
{
|
||||||
|
return m ? parent_obj(m, node_ptr) : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr N* toNode(T* m)
|
||||||
|
{
|
||||||
|
return m ? &(m->*node_ptr) : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename A, typename B>
|
||||||
|
static int compare(A const& a, B const& b)
|
||||||
|
{
|
||||||
|
Comparator comp;
|
||||||
|
if (comp(a, b))
|
||||||
|
return -1;
|
||||||
|
if (comp(b, a))
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr CIntrusiveTree() : CIntrusiveTreeBase{} { }
|
||||||
|
|
||||||
|
T* first() const { return toType(minmax(N::Left)); }
|
||||||
|
T* last() const { return toType(minmax(N::Right)); }
|
||||||
|
bool empty() const { return m_root == nullptr; }
|
||||||
|
void clear() { m_root = nullptr; }
|
||||||
|
|
||||||
|
T* prev(T* node) const { return toType(walk(toNode(node), N::Left)); }
|
||||||
|
T* next(T* node) const { return toType(walk(toNode(node), N::Right)); }
|
||||||
|
|
||||||
|
enum SearchMode
|
||||||
|
{
|
||||||
|
Exact = 0,
|
||||||
|
LowerBound = 1,
|
||||||
|
UpperBound = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Lambda>
|
||||||
|
T* search(SearchMode mode, Lambda lambda) const
|
||||||
|
{
|
||||||
|
N *node, *parent;
|
||||||
|
N*& point = navigate(node, parent,
|
||||||
|
mode != UpperBound ? N::Left : N::Right,
|
||||||
|
[&lambda](N* curnode) { return lambda(toType(curnode)); });
|
||||||
|
|
||||||
|
if (mode != Exact) {
|
||||||
|
if (mode == UpperBound && node)
|
||||||
|
node = walk(node, N::Right);
|
||||||
|
else if (!node && parent)
|
||||||
|
node = &parent->left() == &point ? parent : walk(parent, N::Right);
|
||||||
|
}
|
||||||
|
return toType(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename K>
|
||||||
|
T* find(K const& key, SearchMode mode = Exact) const
|
||||||
|
{
|
||||||
|
return search(mode, [&key](T* obj) { return compare(key, *obj); });
|
||||||
|
}
|
||||||
|
|
||||||
|
T* insert(T* obj, bool allow_dupes = false)
|
||||||
|
{
|
||||||
|
N *node, *parent;
|
||||||
|
N*& point = navigate(node, parent, N::Right,
|
||||||
|
[obj](N* curnode) { return compare(*obj, *toType(curnode)); });
|
||||||
|
|
||||||
|
if (node && !allow_dupes)
|
||||||
|
return toType(node);
|
||||||
|
|
||||||
|
point = toNode(obj);
|
||||||
|
CIntrusiveTreeBase::insert(point, parent);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove(T* obj)
|
||||||
|
{
|
||||||
|
CIntrusiveTreeBase::remove(toNode(obj));
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,175 @@
|
|||||||
|
/*
|
||||||
|
** Sample Framework for deko3d Applications
|
||||||
|
** CMemPool.cpp: Pooled dynamic memory allocation manager class
|
||||||
|
*/
|
||||||
|
#include "CMemPool.h"
|
||||||
|
|
||||||
|
inline auto CMemPool::_newSlice() -> Slice*
|
||||||
|
{
|
||||||
|
Slice* ret = m_sliceHeap.pop();
|
||||||
|
if (!ret) ret = (Slice*)::malloc(sizeof(Slice));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void CMemPool::_deleteSlice(Slice* s)
|
||||||
|
{
|
||||||
|
if (!s) return;
|
||||||
|
m_sliceHeap.add(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
CMemPool::~CMemPool()
|
||||||
|
{
|
||||||
|
m_memMap.iterate([](Slice* s) { ::free(s); });
|
||||||
|
m_sliceHeap.iterate([](Slice* s) { ::free(s); });
|
||||||
|
m_blocks.iterate([](Block* blk) {
|
||||||
|
blk->m_obj.destroy();
|
||||||
|
::free(blk);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
auto CMemPool::allocate(uint32_t size, uint32_t alignment) -> Handle
|
||||||
|
{
|
||||||
|
if (!size) return nullptr;
|
||||||
|
if (alignment & (alignment - 1)) return nullptr;
|
||||||
|
size = (size + alignment - 1) &~ (alignment - 1);
|
||||||
|
#ifdef DEBUG_CMEMPOOL
|
||||||
|
printf("Allocating size=%u alignment=0x%x\n", size, alignment);
|
||||||
|
{
|
||||||
|
Slice* temp = /*m_freeList*/m_memMap.first();
|
||||||
|
while (temp)
|
||||||
|
{
|
||||||
|
printf("-- blk %p | 0x%08x-0x%08x | %s used\n", temp->m_block, temp->m_start, temp->m_end, temp->m_pool ? " " : "not");
|
||||||
|
temp = /*m_freeList*/m_memMap.next(temp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uint32_t start_offset = 0;
|
||||||
|
uint32_t end_offset = 0;
|
||||||
|
Slice* slice = m_freeList.find(size, decltype(m_freeList)::LowerBound);
|
||||||
|
while (slice)
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_CMEMPOOL
|
||||||
|
printf(" * Checking slice 0x%x - 0x%x\n", slice->m_start, slice->m_end);
|
||||||
|
#endif
|
||||||
|
start_offset = (slice->m_start + alignment - 1) &~ (alignment - 1);
|
||||||
|
end_offset = start_offset + size;
|
||||||
|
if (end_offset <= slice->m_end)
|
||||||
|
break;
|
||||||
|
slice = m_freeList.next(slice);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!slice)
|
||||||
|
{
|
||||||
|
Block* blk = (Block*)::malloc(sizeof(Block));
|
||||||
|
if (!blk)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
uint32_t unusableSize = (m_flags & DkMemBlockFlags_Code) ? DK_SHADER_CODE_UNUSABLE_SIZE : 0;
|
||||||
|
uint32_t blkSize = m_blockSize - unusableSize;
|
||||||
|
blkSize = size > blkSize ? size : blkSize;
|
||||||
|
blkSize = (blkSize + unusableSize + DK_MEMBLOCK_ALIGNMENT - 1) &~ (DK_MEMBLOCK_ALIGNMENT - 1);
|
||||||
|
#ifdef DEBUG_CMEMPOOL
|
||||||
|
printf(" ! Allocating block of size 0x%x\n", blkSize);
|
||||||
|
#endif
|
||||||
|
blk->m_obj = dk::MemBlockMaker{m_dev, blkSize}.setFlags(m_flags).create();
|
||||||
|
if (!blk->m_obj)
|
||||||
|
{
|
||||||
|
::free(blk);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
slice = _newSlice();
|
||||||
|
if (!slice)
|
||||||
|
{
|
||||||
|
blk->m_obj.destroy();
|
||||||
|
::free(blk);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
slice->m_pool = nullptr;
|
||||||
|
slice->m_block = blk;
|
||||||
|
slice->m_start = 0;
|
||||||
|
slice->m_end = blkSize - unusableSize;
|
||||||
|
m_memMap.add(slice);
|
||||||
|
|
||||||
|
blk->m_cpuAddr = blk->m_obj.getCpuAddr();
|
||||||
|
blk->m_gpuAddr = blk->m_obj.getGpuAddr();
|
||||||
|
m_blocks.add(blk);
|
||||||
|
|
||||||
|
start_offset = 0;
|
||||||
|
end_offset = size;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_CMEMPOOL
|
||||||
|
printf(" * found it\n");
|
||||||
|
#endif
|
||||||
|
m_freeList.remove(slice);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start_offset != slice->m_start)
|
||||||
|
{
|
||||||
|
Slice* t = _newSlice();
|
||||||
|
if (!t) goto _bad;
|
||||||
|
t->m_pool = nullptr;
|
||||||
|
t->m_block = slice->m_block;
|
||||||
|
t->m_start = slice->m_start;
|
||||||
|
t->m_end = start_offset;
|
||||||
|
#ifdef DEBUG_CMEMPOOL
|
||||||
|
printf("-> subdivide left: %08x-%08x\n", t->m_start, t->m_end);
|
||||||
|
#endif
|
||||||
|
m_memMap.addBefore(slice, t);
|
||||||
|
m_freeList.insert(t, true);
|
||||||
|
slice->m_start = start_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end_offset != slice->m_end)
|
||||||
|
{
|
||||||
|
Slice* t = _newSlice();
|
||||||
|
if (!t) goto _bad;
|
||||||
|
t->m_pool = nullptr;
|
||||||
|
t->m_block = slice->m_block;
|
||||||
|
t->m_start = end_offset;
|
||||||
|
t->m_end = slice->m_end;
|
||||||
|
#ifdef DEBUG_CMEMPOOL
|
||||||
|
printf("-> subdivide right: %08x-%08x\n", t->m_start, t->m_end);
|
||||||
|
#endif
|
||||||
|
m_memMap.addAfter(slice, t);
|
||||||
|
m_freeList.insert(t, true);
|
||||||
|
slice->m_end = end_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
slice->m_pool = this;
|
||||||
|
return slice;
|
||||||
|
|
||||||
|
_bad:
|
||||||
|
m_freeList.insert(slice, true);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMemPool::_destroy(Slice* slice)
|
||||||
|
{
|
||||||
|
slice->m_pool = nullptr;
|
||||||
|
|
||||||
|
Slice* left = m_memMap.prev(slice);
|
||||||
|
Slice* right = m_memMap.next(slice);
|
||||||
|
|
||||||
|
if (left && left->canCoalesce(*slice))
|
||||||
|
{
|
||||||
|
slice->m_start = left->m_start;
|
||||||
|
m_freeList.remove(left);
|
||||||
|
m_memMap.remove(left);
|
||||||
|
_deleteSlice(left);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (right && slice->canCoalesce(*right))
|
||||||
|
{
|
||||||
|
slice->m_end = right->m_end;
|
||||||
|
m_freeList.remove(right);
|
||||||
|
m_memMap.remove(right);
|
||||||
|
_deleteSlice(right);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_freeList.insert(slice, true);
|
||||||
|
}
|
133
graphics/deko3d/deko_examples/source/SampleFramework/CMemPool.h
Normal file
133
graphics/deko3d/deko_examples/source/SampleFramework/CMemPool.h
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
/*
|
||||||
|
** Sample Framework for deko3d Applications
|
||||||
|
** CMemPool.h: Pooled dynamic memory allocation manager class
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "common.h"
|
||||||
|
#include "CIntrusiveList.h"
|
||||||
|
#include "CIntrusiveTree.h"
|
||||||
|
|
||||||
|
class CMemPool
|
||||||
|
{
|
||||||
|
dk::Device m_dev;
|
||||||
|
uint32_t m_flags;
|
||||||
|
uint32_t m_blockSize;
|
||||||
|
|
||||||
|
struct Block
|
||||||
|
{
|
||||||
|
CIntrusiveListNode<Block> m_node;
|
||||||
|
dk::MemBlock m_obj;
|
||||||
|
void* m_cpuAddr;
|
||||||
|
DkGpuAddr m_gpuAddr;
|
||||||
|
|
||||||
|
Block(const Block&) = delete;
|
||||||
|
|
||||||
|
Block& operator=(const Block&) = delete;
|
||||||
|
|
||||||
|
constexpr void* cpuOffset(uint32_t offset) const
|
||||||
|
{
|
||||||
|
return m_cpuAddr ? ((u8*)m_cpuAddr + offset) : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr DkGpuAddr gpuOffset(uint32_t offset) const
|
||||||
|
{
|
||||||
|
return m_gpuAddr != DK_GPU_ADDR_INVALID ? (m_gpuAddr + offset) : DK_GPU_ADDR_INVALID;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
CIntrusiveList<Block, &Block::m_node> m_blocks;
|
||||||
|
|
||||||
|
struct Slice
|
||||||
|
{
|
||||||
|
CIntrusiveListNode<Slice> m_node;
|
||||||
|
CIntrusiveTreeNode m_treenode;
|
||||||
|
CMemPool* m_pool;
|
||||||
|
Block* m_block;
|
||||||
|
uint32_t m_start;
|
||||||
|
uint32_t m_end;
|
||||||
|
|
||||||
|
Slice(const Slice&) = delete;
|
||||||
|
|
||||||
|
Slice& operator=(const Slice&) = delete;
|
||||||
|
|
||||||
|
constexpr uint32_t getSize() const { return m_end - m_start; }
|
||||||
|
constexpr bool canCoalesce(Slice const& rhs) const { return m_pool == rhs.m_pool && m_block == rhs.m_block && m_end == rhs.m_start; }
|
||||||
|
|
||||||
|
constexpr bool operator<(Slice const& rhs) const { return getSize() < rhs.getSize(); }
|
||||||
|
constexpr bool operator<(uint32_t rhs) const { return getSize() < rhs; }
|
||||||
|
};
|
||||||
|
|
||||||
|
friend constexpr bool operator<(uint32_t lhs, Slice const& rhs);
|
||||||
|
|
||||||
|
CIntrusiveList<Slice, &Slice::m_node> m_memMap, m_sliceHeap;
|
||||||
|
CIntrusiveTree<Slice, &Slice::m_treenode> m_freeList;
|
||||||
|
|
||||||
|
Slice* _newSlice();
|
||||||
|
void _deleteSlice(Slice*);
|
||||||
|
|
||||||
|
void _destroy(Slice* slice);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static constexpr uint32_t DefaultBlockSize = 0x800000;
|
||||||
|
class Handle
|
||||||
|
{
|
||||||
|
Slice* m_slice;
|
||||||
|
public:
|
||||||
|
constexpr Handle(Slice* slice = nullptr) : m_slice{slice} { }
|
||||||
|
constexpr operator bool() const { return m_slice != nullptr; }
|
||||||
|
constexpr operator Slice*() const { return m_slice; }
|
||||||
|
constexpr bool operator!() const { return !m_slice; }
|
||||||
|
constexpr bool operator==(Handle const& rhs) const { return m_slice == rhs.m_slice; }
|
||||||
|
constexpr bool operator!=(Handle const& rhs) const { return m_slice != rhs.m_slice; }
|
||||||
|
|
||||||
|
void destroy()
|
||||||
|
{
|
||||||
|
if (m_slice)
|
||||||
|
{
|
||||||
|
m_slice->m_pool->_destroy(m_slice);
|
||||||
|
m_slice = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr dk::MemBlock getMemBlock() const
|
||||||
|
{
|
||||||
|
return m_slice->m_block->m_obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr uint32_t getOffset() const
|
||||||
|
{
|
||||||
|
return m_slice->m_start;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr uint32_t getSize() const
|
||||||
|
{
|
||||||
|
return m_slice->getSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void* getCpuAddr() const
|
||||||
|
{
|
||||||
|
return m_slice->m_block->cpuOffset(m_slice->m_start);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr DkGpuAddr getGpuAddr() const
|
||||||
|
{
|
||||||
|
return m_slice->m_block->gpuOffset(m_slice->m_start);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
CMemPool(dk::Device dev, uint32_t flags = DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached, uint32_t blockSize = DefaultBlockSize) :
|
||||||
|
m_dev{dev}, m_flags{flags}, m_blockSize{blockSize}, m_blocks{}, m_memMap{}, m_sliceHeap{}, m_freeList{} { }
|
||||||
|
|
||||||
|
~CMemPool();
|
||||||
|
|
||||||
|
Handle allocate(uint32_t size, uint32_t alignment = DK_CMDMEM_ALIGNMENT);
|
||||||
|
|
||||||
|
CMemPool(const CMemPool&) = delete;
|
||||||
|
|
||||||
|
CMemPool& operator=(const CMemPool&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr bool operator<(uint32_t lhs, CMemPool::Slice const& rhs)
|
||||||
|
{
|
||||||
|
return lhs < rhs.getSize();
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
** Sample Framework for deko3d Applications
|
||||||
|
** CShader.cpp: Utility class for loading shaders from the filesystem
|
||||||
|
*/
|
||||||
|
#include "CShader.h"
|
||||||
|
|
||||||
|
struct DkshHeader
|
||||||
|
{
|
||||||
|
uint32_t magic; // DKSH_MAGIC
|
||||||
|
uint32_t header_sz; // sizeof(DkshHeader)
|
||||||
|
uint32_t control_sz;
|
||||||
|
uint32_t code_sz;
|
||||||
|
uint32_t programs_off;
|
||||||
|
uint32_t num_programs;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool CShader::load(CMemPool& pool, const char* path)
|
||||||
|
{
|
||||||
|
FILE* f;
|
||||||
|
DkshHeader hdr;
|
||||||
|
void* ctrlmem;
|
||||||
|
|
||||||
|
m_codemem.destroy();
|
||||||
|
|
||||||
|
f = fopen(path, "rb");
|
||||||
|
if (!f) return false;
|
||||||
|
|
||||||
|
if (!fread(&hdr, sizeof(hdr), 1, f))
|
||||||
|
goto _fail0;
|
||||||
|
|
||||||
|
ctrlmem = malloc(hdr.control_sz);
|
||||||
|
if (!ctrlmem)
|
||||||
|
goto _fail0;
|
||||||
|
|
||||||
|
rewind(f);
|
||||||
|
if (!fread(ctrlmem, hdr.control_sz, 1, f))
|
||||||
|
goto _fail1;
|
||||||
|
|
||||||
|
m_codemem = pool.allocate(hdr.code_sz, DK_SHADER_CODE_ALIGNMENT);
|
||||||
|
if (!m_codemem)
|
||||||
|
goto _fail1;
|
||||||
|
|
||||||
|
if (!fread(m_codemem.getCpuAddr(), hdr.code_sz, 1, f))
|
||||||
|
goto _fail2;
|
||||||
|
|
||||||
|
dk::ShaderMaker{m_codemem.getMemBlock(), m_codemem.getOffset()}
|
||||||
|
.setControl(ctrlmem)
|
||||||
|
.setProgramId(0)
|
||||||
|
.initialize(m_shader);
|
||||||
|
|
||||||
|
free(ctrlmem);
|
||||||
|
fclose(f);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
_fail2:
|
||||||
|
m_codemem.destroy();
|
||||||
|
_fail1:
|
||||||
|
free(ctrlmem);
|
||||||
|
_fail0:
|
||||||
|
fclose(f);
|
||||||
|
return false;
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
** Sample Framework for deko3d Applications
|
||||||
|
** CShader.h: Utility class for loading shaders from the filesystem
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "common.h"
|
||||||
|
#include "CMemPool.h"
|
||||||
|
|
||||||
|
class CShader
|
||||||
|
{
|
||||||
|
dk::Shader m_shader;
|
||||||
|
CMemPool::Handle m_codemem;
|
||||||
|
public:
|
||||||
|
CShader() : m_shader{}, m_codemem{} { }
|
||||||
|
|
||||||
|
CShader(const CShader&) = delete;
|
||||||
|
|
||||||
|
CShader& operator=(const CShader&) = delete;
|
||||||
|
|
||||||
|
~CShader()
|
||||||
|
{
|
||||||
|
m_codemem.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr operator bool() const
|
||||||
|
{
|
||||||
|
return m_codemem;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr operator dk::Shader const*() const
|
||||||
|
{
|
||||||
|
return &m_shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool load(CMemPool& pool, const char* path);
|
||||||
|
};
|
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
** Sample Framework for deko3d Applications
|
||||||
|
** FileLoader.cpp: Helpers for loading data from the filesystem directly into GPU memory
|
||||||
|
*/
|
||||||
|
#include "FileLoader.h"
|
||||||
|
|
||||||
|
CMemPool::Handle LoadFile(CMemPool& pool, const char* path, uint32_t alignment)
|
||||||
|
{
|
||||||
|
FILE *f = fopen(path, "rb");
|
||||||
|
if (!f) return nullptr;
|
||||||
|
|
||||||
|
fseek(f, 0, SEEK_END);
|
||||||
|
uint32_t fsize = ftell(f);
|
||||||
|
rewind(f);
|
||||||
|
|
||||||
|
CMemPool::Handle mem = pool.allocate(fsize, alignment);
|
||||||
|
if (!mem)
|
||||||
|
{
|
||||||
|
fclose(f);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
fread(mem.getCpuAddr(), fsize, 1, f);
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
return mem;
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
/*
|
||||||
|
** Sample Framework for deko3d Applications
|
||||||
|
** FileLoader.h: Helpers for loading data from the filesystem directly into GPU memory
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "common.h"
|
||||||
|
#include "CMemPool.h"
|
||||||
|
|
||||||
|
CMemPool::Handle LoadFile(CMemPool& pool, const char* path, uint32_t alignment = DK_CMDMEM_ALIGNMENT);
|
18
graphics/deko3d/deko_examples/source/SampleFramework/LICENSE
Normal file
18
graphics/deko3d/deko_examples/source/SampleFramework/LICENSE
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
Copyright (C) 2020 fincs
|
||||||
|
|
||||||
|
This software is provided 'as-is', without any express or implied
|
||||||
|
warranty. In no event will the authors be held liable for any
|
||||||
|
damages arising from the use of this software.
|
||||||
|
|
||||||
|
Permission is granted to anyone to use this software for any
|
||||||
|
purpose, including commercial applications, and to alter it and
|
||||||
|
redistribute it freely, subject to the following restrictions:
|
||||||
|
|
||||||
|
1. The origin of this software must not be misrepresented; you
|
||||||
|
must not claim that you wrote the original software. If you use
|
||||||
|
this software in a product, an acknowledgment in the product
|
||||||
|
documentation would be appreciated but is not required.
|
||||||
|
2. Altered source versions must be plainly marked as such, and
|
||||||
|
must not be misrepresented as being the original software.
|
||||||
|
3. This notice may not be removed or altered from any source
|
||||||
|
distribution.
|
@ -0,0 +1,12 @@
|
|||||||
|
/*
|
||||||
|
** Sample Framework for deko3d Applications
|
||||||
|
** common.h: Common includes
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <switch.h>
|
||||||
|
|
||||||
|
#include <deko3d.hpp>
|
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
** Sample Framework for deko3d Applications
|
||||||
|
** startup.cpp: Automatic initialization/deinitialization
|
||||||
|
*/
|
||||||
|
#include "common.h"
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
//#define DEBUG_NXLINK
|
||||||
|
|
||||||
|
#ifdef DEBUG_NXLINK
|
||||||
|
static int nxlink_sock = -1;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern "C" void userAppInit(void)
|
||||||
|
{
|
||||||
|
Result res = romfsInit();
|
||||||
|
if (R_FAILED(res))
|
||||||
|
diagAbortWithResult(res);
|
||||||
|
|
||||||
|
#ifdef DEBUG_NXLINK
|
||||||
|
socketInitializeDefault();
|
||||||
|
nxlink_sock = nxlinkStdioForDebug();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" void userAppExit(void)
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_NXLINK
|
||||||
|
if (nxlink_sock != -1)
|
||||||
|
close(nxlink_sock);
|
||||||
|
socketExit();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
romfsExit();
|
||||||
|
}
|
15
graphics/deko3d/deko_examples/source/basic_deferred_fsh.glsl
Normal file
15
graphics/deko3d/deko_examples/source/basic_deferred_fsh.glsl
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#version 460
|
||||||
|
|
||||||
|
layout (location = 0) in vec3 inWorldPos;
|
||||||
|
layout (location = 1) in vec3 inNormal;
|
||||||
|
|
||||||
|
layout (location = 0) out vec4 outAlbedo;
|
||||||
|
layout (location = 1) out vec4 outNormal;
|
||||||
|
layout (location = 2) out vec4 outViewDir;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
outAlbedo = vec4(1.0, 1.0, 1.0, 1.0);
|
||||||
|
outNormal = vec4(normalize(inNormal), 0.0);
|
||||||
|
outViewDir = vec4(-inWorldPos, 0.0);
|
||||||
|
}
|
46
graphics/deko3d/deko_examples/source/basic_lighting_fsh.glsl
Normal file
46
graphics/deko3d/deko_examples/source/basic_lighting_fsh.glsl
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#version 460
|
||||||
|
|
||||||
|
layout (location = 0) in vec3 inWorldPos;
|
||||||
|
layout (location = 1) in vec3 inNormal;
|
||||||
|
layout (location = 0) out vec4 outColor;
|
||||||
|
|
||||||
|
layout (std140, binding = 0) uniform Lighting
|
||||||
|
{
|
||||||
|
vec4 lightPos; // if w=0 this is lightDir
|
||||||
|
vec3 ambient;
|
||||||
|
vec3 diffuse;
|
||||||
|
vec4 specular; // w is shininess
|
||||||
|
} u;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
// Renormalize the normal after interpolation
|
||||||
|
vec3 normal = normalize(inNormal);
|
||||||
|
|
||||||
|
// Calculate light direction (i.e. vector that points *towards* the light source)
|
||||||
|
vec3 lightDir;
|
||||||
|
if (u.lightPos.w != 0.0)
|
||||||
|
lightDir = normalize(u.lightPos.xyz - inWorldPos);
|
||||||
|
else
|
||||||
|
lightDir = -u.lightPos.xyz;
|
||||||
|
vec3 viewDir = normalize(-inWorldPos);
|
||||||
|
|
||||||
|
// Calculate diffuse factor
|
||||||
|
float diffuse = max(0.0, dot(normal,lightDir));
|
||||||
|
|
||||||
|
// Calculate specular factor (Blinn-Phong)
|
||||||
|
vec3 halfwayDir = normalize(lightDir + viewDir);
|
||||||
|
float specular = pow(max(0.0, dot(normal,halfwayDir)), u.specular.w);
|
||||||
|
|
||||||
|
// Calculate the color
|
||||||
|
vec3 color =
|
||||||
|
u.ambient +
|
||||||
|
u.diffuse*vec3(diffuse) +
|
||||||
|
u.specular.xyz*vec3(specular);
|
||||||
|
|
||||||
|
// Reinhard tone mapping
|
||||||
|
vec3 mappedColor = color / (vec3(1.0) + color);
|
||||||
|
|
||||||
|
// Output this color (no need to gamma adjust since the framebuffer is sRGB)
|
||||||
|
outColor = vec4(mappedColor, 1.0);
|
||||||
|
}
|
12
graphics/deko3d/deko_examples/source/basic_vsh.glsl
Normal file
12
graphics/deko3d/deko_examples/source/basic_vsh.glsl
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#version 460
|
||||||
|
|
||||||
|
layout (location = 0) in vec3 inPos;
|
||||||
|
layout (location = 1) in vec4 inAttrib;
|
||||||
|
|
||||||
|
layout (location = 0) out vec4 outAttrib;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
gl_Position = vec4(inPos, 1.0);
|
||||||
|
outAttrib = inAttrib;
|
||||||
|
}
|
9
graphics/deko3d/deko_examples/source/color_fsh.glsl
Normal file
9
graphics/deko3d/deko_examples/source/color_fsh.glsl
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#version 460
|
||||||
|
|
||||||
|
layout (location = 0) in vec3 inColor;
|
||||||
|
layout (location = 0) out vec4 outColor;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
outColor = vec4(inColor, 1.0);
|
||||||
|
}
|
53
graphics/deko3d/deko_examples/source/composition_fsh.glsl
Normal file
53
graphics/deko3d/deko_examples/source/composition_fsh.glsl
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
#version 460
|
||||||
|
|
||||||
|
layout (location = 0) out vec4 outColor;
|
||||||
|
|
||||||
|
layout (binding = 0) uniform sampler2D texAlbedo;
|
||||||
|
layout (binding = 1) uniform sampler2D texNormal;
|
||||||
|
layout (binding = 2) uniform sampler2D texViewDir;
|
||||||
|
|
||||||
|
layout (std140, binding = 0) uniform Lighting
|
||||||
|
{
|
||||||
|
vec4 lightPos; // if w=0 this is lightDir
|
||||||
|
vec3 ambient;
|
||||||
|
vec3 diffuse;
|
||||||
|
vec4 specular; // w is shininess
|
||||||
|
} u;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
// Uncomment the coordinate reversion below to observe the effects of tiled corruption
|
||||||
|
ivec2 coord = /*textureSize(texAlbedo, 0) - ivec2(1,1) -*/ ivec2(gl_FragCoord.xy);
|
||||||
|
|
||||||
|
// Retrieve values from the g-buffer
|
||||||
|
vec4 albedo = texelFetch(texAlbedo, coord, 0);
|
||||||
|
vec3 normal = texelFetch(texNormal, coord, 0).xyz;
|
||||||
|
vec3 viewDir = texelFetch(texViewDir, coord, 0).xyz;
|
||||||
|
|
||||||
|
// Calculate light direction (i.e. vector that points *towards* the light source)
|
||||||
|
vec3 lightDir;
|
||||||
|
if (u.lightPos.w != 0.0)
|
||||||
|
lightDir = normalize(u.lightPos.xyz + viewDir);
|
||||||
|
else
|
||||||
|
lightDir = -u.lightPos.xyz;
|
||||||
|
viewDir = normalize(viewDir);
|
||||||
|
|
||||||
|
// Calculate diffuse factor
|
||||||
|
float diffuse = max(0.0, dot(normal,lightDir));
|
||||||
|
|
||||||
|
// Calculate specular factor (Blinn-Phong)
|
||||||
|
vec3 halfwayDir = normalize(lightDir + viewDir);
|
||||||
|
float specular = pow(max(0.0, dot(normal,halfwayDir)), u.specular.w);
|
||||||
|
|
||||||
|
// Calculate the color
|
||||||
|
vec3 color =
|
||||||
|
u.ambient +
|
||||||
|
albedo.rgb*u.diffuse*vec3(diffuse) +
|
||||||
|
u.specular.xyz*vec3(specular);
|
||||||
|
|
||||||
|
// Reinhard tone mapping
|
||||||
|
vec3 mappedColor = albedo.a * color / (vec3(1.0) + color);
|
||||||
|
|
||||||
|
// Output this color (no need to gamma adjust since the framebuffer is sRGB)
|
||||||
|
outColor = vec4(mappedColor, albedo.a);
|
||||||
|
}
|
24
graphics/deko3d/deko_examples/source/composition_vsh.glsl
Normal file
24
graphics/deko3d/deko_examples/source/composition_vsh.glsl
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#version 460
|
||||||
|
|
||||||
|
/*
|
||||||
|
ID | gl_Position.xy
|
||||||
|
0 | -1.0 +1.0
|
||||||
|
1 | -1.0 -1.0
|
||||||
|
2 | +1.0 -1.0
|
||||||
|
3 | +1.0 +1.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
if ((gl_VertexID & 2) == 0)
|
||||||
|
gl_Position.x = -1.0;
|
||||||
|
else
|
||||||
|
gl_Position.x = +1.0;
|
||||||
|
|
||||||
|
if (((gl_VertexID+1) & 2) == 0)
|
||||||
|
gl_Position.y = +1.0;
|
||||||
|
else
|
||||||
|
gl_Position.y = -1.0;
|
||||||
|
|
||||||
|
gl_Position.zw = vec2(0.5, 1.0);
|
||||||
|
}
|
143
graphics/deko3d/deko_examples/source/main.cpp
Normal file
143
graphics/deko3d/deko_examples/source/main.cpp
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
/*
|
||||||
|
** deko3d Examples - Main Menu
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Sample Framework headers
|
||||||
|
#include "SampleFramework/CApplication.h"
|
||||||
|
|
||||||
|
// C++ standard library headers
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
void Example01(void);
|
||||||
|
void Example02(void);
|
||||||
|
void Example03(void);
|
||||||
|
void Example04(void);
|
||||||
|
void Example05(void);
|
||||||
|
void Example06(void);
|
||||||
|
void Example07(void);
|
||||||
|
void Example08(void);
|
||||||
|
void Example09(void);
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
using ExampleFunc = void(*)(void);
|
||||||
|
struct Example
|
||||||
|
{
|
||||||
|
ExampleFunc mainfunc;
|
||||||
|
const char* name;
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr std::array Examples =
|
||||||
|
{
|
||||||
|
Example{ Example01, "01: Simple Setup" },
|
||||||
|
Example{ Example02, "02: Triangle" },
|
||||||
|
Example{ Example03, "03: Cube" },
|
||||||
|
Example{ Example04, "04: Textured Cube" },
|
||||||
|
Example{ Example05, "05: Simple Tessellation" },
|
||||||
|
Example{ Example06, "06: Simple Multisampling" },
|
||||||
|
Example{ Example07, "07: Mesh Loading and Lighting (sRGB)" },
|
||||||
|
Example{ Example08, "08: Deferred Shading (Multipass Rendering with Tiled Cache)" },
|
||||||
|
Example{ Example09, "09: Simple Compute Shader (Geometry Generation)" },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class CMainMenu final : public CApplication
|
||||||
|
{
|
||||||
|
static constexpr unsigned EntriesPerScreen = 39;
|
||||||
|
static constexpr unsigned EntryPageLength = 10;
|
||||||
|
|
||||||
|
PadState pad;
|
||||||
|
|
||||||
|
int screenPos;
|
||||||
|
int selectPos;
|
||||||
|
|
||||||
|
void renderMenu()
|
||||||
|
{
|
||||||
|
printf("\x1b[2J\n");
|
||||||
|
printf(" deko3d Examples\n");
|
||||||
|
printf(" Press PLUS(+) to exit; A to select an example to run\n");
|
||||||
|
printf("\n");
|
||||||
|
printf("--------------------------------------------------------------------------------");
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < (Examples.size() - screenPos) && i < EntriesPerScreen; i ++)
|
||||||
|
{
|
||||||
|
unsigned id = screenPos+i;
|
||||||
|
printf(" %c %s\n", id==unsigned(selectPos) ? '*' : ' ', Examples[id].name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CMainMenu() : screenPos{}, selectPos{}
|
||||||
|
{
|
||||||
|
consoleInit(NULL);
|
||||||
|
renderMenu();
|
||||||
|
|
||||||
|
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
|
||||||
|
padInitializeDefault(&pad);
|
||||||
|
padUpdate(&pad);
|
||||||
|
}
|
||||||
|
|
||||||
|
~CMainMenu()
|
||||||
|
{
|
||||||
|
consoleExit(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool onFrame(u64 ns) override
|
||||||
|
{
|
||||||
|
int oldPos = selectPos;
|
||||||
|
padUpdate(&pad);
|
||||||
|
|
||||||
|
u64 kDown = padGetButtonsDown(&pad);
|
||||||
|
if (kDown & HidNpadButton_Plus)
|
||||||
|
{
|
||||||
|
selectPos = -1;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (kDown & HidNpadButton_A)
|
||||||
|
return false;
|
||||||
|
if (kDown & HidNpadButton_AnyUp)
|
||||||
|
selectPos -= 1;
|
||||||
|
if (kDown & HidNpadButton_AnyDown)
|
||||||
|
selectPos += 1;
|
||||||
|
if (kDown & HidNpadButton_AnyLeft)
|
||||||
|
selectPos -= EntryPageLength;
|
||||||
|
if (kDown & HidNpadButton_AnyRight)
|
||||||
|
selectPos += EntryPageLength;
|
||||||
|
|
||||||
|
if (selectPos < 0)
|
||||||
|
selectPos = 0;
|
||||||
|
if (unsigned(selectPos) >= Examples.size())
|
||||||
|
selectPos = Examples.size()-1;
|
||||||
|
|
||||||
|
if (selectPos != oldPos)
|
||||||
|
{
|
||||||
|
if (selectPos < screenPos)
|
||||||
|
screenPos = selectPos;
|
||||||
|
else if (selectPos >= screenPos + int(EntriesPerScreen))
|
||||||
|
screenPos = selectPos - EntriesPerScreen + 1;
|
||||||
|
renderMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
consoleUpdate(NULL);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
static ExampleFunc Display()
|
||||||
|
{
|
||||||
|
CMainMenu app;
|
||||||
|
app.run();
|
||||||
|
return app.selectPos >= 0 ? Examples[app.selectPos].mainfunc : nullptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
ExampleFunc func = CMainMenu::Display();
|
||||||
|
if (!func) break;
|
||||||
|
func();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
38
graphics/deko3d/deko_examples/source/sinewave.glsl
Normal file
38
graphics/deko3d/deko_examples/source/sinewave.glsl
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#version 460
|
||||||
|
|
||||||
|
layout (local_size_x = 32) in;
|
||||||
|
|
||||||
|
struct Vertex
|
||||||
|
{
|
||||||
|
vec4 position;
|
||||||
|
vec4 color;
|
||||||
|
};
|
||||||
|
|
||||||
|
layout (std140, binding = 0) uniform Params
|
||||||
|
{
|
||||||
|
vec4 colorA;
|
||||||
|
vec4 colorB;
|
||||||
|
float offset;
|
||||||
|
float scale;
|
||||||
|
} u;
|
||||||
|
|
||||||
|
layout (std430, binding = 0) buffer Output
|
||||||
|
{
|
||||||
|
Vertex vertices[];
|
||||||
|
} o;
|
||||||
|
|
||||||
|
const float TAU = 6.2831853071795;
|
||||||
|
|
||||||
|
void calcVertex(out Vertex vtx, float x)
|
||||||
|
{
|
||||||
|
vtx.position = vec4(x * 2.0 - 1.0, u.scale * sin((u.offset + x)*TAU), 0.5, 1.0);
|
||||||
|
vtx.color = mix(u.colorA, u.colorB, x);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
uint id = gl_GlobalInvocationID.x;
|
||||||
|
uint maxid = gl_WorkGroupSize.x * gl_NumWorkGroups.x - 1;
|
||||||
|
float x = float(id) / float(maxid);
|
||||||
|
calcVertex(o.vertices[id], x);
|
||||||
|
}
|
21
graphics/deko3d/deko_examples/source/tess_simple_tcsh.glsl
Normal file
21
graphics/deko3d/deko_examples/source/tess_simple_tcsh.glsl
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#version 460
|
||||||
|
|
||||||
|
layout (vertices = 3) out;
|
||||||
|
|
||||||
|
layout (location = 0) in vec4 inAttrib[];
|
||||||
|
|
||||||
|
layout (location = 0) out vec4 outAttrib[];
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
if (gl_InvocationID == 0)
|
||||||
|
{
|
||||||
|
gl_TessLevelInner[0] = 5.0; // i.e. 2 concentric triangles with the center being a triangle
|
||||||
|
gl_TessLevelOuter[0] = 2.0;
|
||||||
|
gl_TessLevelOuter[1] = 3.0;
|
||||||
|
gl_TessLevelOuter[2] = 5.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;
|
||||||
|
outAttrib[gl_InvocationID] = inAttrib[gl_InvocationID];
|
||||||
|
}
|
21
graphics/deko3d/deko_examples/source/tess_simple_tesh.glsl
Normal file
21
graphics/deko3d/deko_examples/source/tess_simple_tesh.glsl
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#version 460
|
||||||
|
|
||||||
|
layout (triangles, equal_spacing, ccw) in;
|
||||||
|
|
||||||
|
layout (location = 0) in vec4 inAttrib[];
|
||||||
|
|
||||||
|
layout (location = 0) out vec4 outAttrib;
|
||||||
|
|
||||||
|
vec4 interpolate(in vec4 v0, in vec4 v1, in vec4 v2)
|
||||||
|
{
|
||||||
|
vec4 a0 = gl_TessCoord.x * v0;
|
||||||
|
vec4 a1 = gl_TessCoord.y * v1;
|
||||||
|
vec4 a2 = gl_TessCoord.z * v2;
|
||||||
|
return a0 + a1 + a2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
gl_Position = interpolate(gl_in[0].gl_Position, gl_in[1].gl_Position, gl_in[2].gl_Position);
|
||||||
|
outAttrib = interpolate(inAttrib[0], inAttrib[1], inAttrib[2]);
|
||||||
|
}
|
11
graphics/deko3d/deko_examples/source/texture_fsh.glsl
Normal file
11
graphics/deko3d/deko_examples/source/texture_fsh.glsl
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#version 460
|
||||||
|
|
||||||
|
layout (location = 0) in vec2 inTexCoord;
|
||||||
|
layout (location = 0) out vec4 outColor;
|
||||||
|
|
||||||
|
layout (binding = 0) uniform sampler2D texture0;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
outColor = texture(texture0, inTexCoord);
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
#version 460
|
||||||
|
|
||||||
|
layout (location = 0) in vec3 inPos;
|
||||||
|
layout (location = 1) in vec3 inNormal;
|
||||||
|
layout (location = 2) in vec4 inAttrib;
|
||||||
|
|
||||||
|
layout (location = 0) out vec3 outWorldPos;
|
||||||
|
layout (location = 1) out vec3 outNormal;
|
||||||
|
layout (location = 2) out vec4 outAttrib;
|
||||||
|
|
||||||
|
layout (std140, binding = 0) uniform Transformation
|
||||||
|
{
|
||||||
|
mat4 mdlvMtx;
|
||||||
|
mat4 projMtx;
|
||||||
|
} u;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec4 worldPos = u.mdlvMtx * vec4(inPos, 1.0);
|
||||||
|
gl_Position = u.projMtx * worldPos;
|
||||||
|
|
||||||
|
outWorldPos = worldPos.xyz;
|
||||||
|
|
||||||
|
outNormal = normalize(mat3(u.mdlvMtx) * inNormal);
|
||||||
|
|
||||||
|
// Pass through the user-defined attribute
|
||||||
|
outAttrib = inAttrib;
|
||||||
|
}
|
20
graphics/deko3d/deko_examples/source/transform_vsh.glsl
Normal file
20
graphics/deko3d/deko_examples/source/transform_vsh.glsl
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#version 460
|
||||||
|
|
||||||
|
layout (location = 0) in vec3 inPos;
|
||||||
|
layout (location = 1) in vec4 inAttrib;
|
||||||
|
|
||||||
|
layout (location = 0) out vec4 outAttrib;
|
||||||
|
|
||||||
|
layout (std140, binding = 0) uniform Transformation
|
||||||
|
{
|
||||||
|
mat4 mdlvMtx;
|
||||||
|
mat4 projMtx;
|
||||||
|
} u;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec4 pos = u.mdlvMtx * vec4(inPos, 1.0);
|
||||||
|
gl_Position = u.projMtx * pos;
|
||||||
|
|
||||||
|
outAttrib = inAttrib;
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <switch.h>
|
#include <switch.h>
|
||||||
@ -430,16 +430,16 @@ static void sceneUpdate(u32 kHeld)
|
|||||||
float deltaTime = curTime - s_updTime;
|
float deltaTime = curTime - s_updTime;
|
||||||
s_updTime = curTime;
|
s_updTime = curTime;
|
||||||
|
|
||||||
if (kHeld & KEY_LEFT)
|
if (kHeld & HidNpadButton_AnyLeft)
|
||||||
s_cameraAngle = fract(s_cameraAngle - deltaTime/4);
|
s_cameraAngle = fract(s_cameraAngle - deltaTime/4);
|
||||||
else if (kHeld & KEY_RIGHT)
|
else if (kHeld & HidNpadButton_AnyRight)
|
||||||
s_cameraAngle = fract(s_cameraAngle + deltaTime/4);
|
s_cameraAngle = fract(s_cameraAngle + deltaTime/4);
|
||||||
if (kHeld & (KEY_UP|KEY_DOWN))
|
if (kHeld & (HidNpadButton_AnyUp|HidNpadButton_AnyDown))
|
||||||
{
|
{
|
||||||
glm::vec3 front = deltaTime * glm::rotate(glm::vec3{0.0f, 0.0f, -1.0f}, s_cameraAngle * TAU, glm::vec3{0.0f, -1.0f, 0.0f});
|
glm::vec3 front = deltaTime * glm::rotate(glm::vec3{0.0f, 0.0f, -1.0f}, s_cameraAngle * TAU, glm::vec3{0.0f, -1.0f, 0.0f});
|
||||||
if (kHeld & KEY_UP)
|
if (kHeld & HidNpadButton_AnyUp)
|
||||||
s_cameraPos += front;
|
s_cameraPos += front;
|
||||||
else if (kHeld & KEY_DOWN)
|
else if (kHeld & HidNpadButton_AnyDown)
|
||||||
s_cameraPos -= front;
|
s_cameraPos -= front;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -464,7 +464,7 @@ static void configureResolution(NWindow* win, bool halved)
|
|||||||
width = 1280;
|
width = 1280;
|
||||||
height = 720;
|
height = 720;
|
||||||
break;
|
break;
|
||||||
case AppletOperationMode_Docked:
|
case AppletOperationMode_Console:
|
||||||
width = 1920;
|
width = 1920;
|
||||||
height = 1080;
|
height = 1080;
|
||||||
break;
|
break;
|
||||||
@ -525,17 +525,24 @@ int main(int argc, char* argv[])
|
|||||||
// Initialize our scene
|
// Initialize our scene
|
||||||
sceneInit();
|
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
|
// Main graphics loop
|
||||||
while (appletMainLoop())
|
while (appletMainLoop())
|
||||||
{
|
{
|
||||||
// Get and process input
|
// Get and process input
|
||||||
hidScanInput();
|
padUpdate(&pad);
|
||||||
u32 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
u32 kDown = padGetButtonsDown(&pad);
|
||||||
u32 kHeld = hidKeysHeld(CONTROLLER_P1_AUTO);
|
u32 kHeld = padGetButtons(&pad);
|
||||||
if (kDown & KEY_PLUS)
|
if (kDown & HidNpadButton_Plus)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
bool shouldHalveResolution = !!(kHeld & KEY_A);
|
bool shouldHalveResolution = !!(kHeld & HidNpadButton_A);
|
||||||
|
|
||||||
// Configure the resolution used to render the scene, which
|
// Configure the resolution used to render the scene, which
|
||||||
// will be different in handheld mode/docked mode.
|
// will be different in handheld mode/docked mode.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 1999-2001 Brian Paul All Rights Reserved.
|
* Copyright (C) 1999-2001 Brian Paul All Rights Reserved.
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
@ -718,9 +718,9 @@ gears_idle(void)
|
|||||||
static int frames = 0;
|
static int frames = 0;
|
||||||
static double tRot0 = -1.0, tRate0 = -1.0;
|
static double tRot0 = -1.0, tRate0 = -1.0;
|
||||||
double dt, t;
|
double dt, t;
|
||||||
static u64 origTicks = U64_MAX;
|
static u64 origTicks = UINT64_MAX;
|
||||||
|
|
||||||
if (origTicks == U64_MAX)
|
if (origTicks == UINT64_MAX)
|
||||||
origTicks = armGetSystemTick();
|
origTicks = armGetSystemTick();
|
||||||
|
|
||||||
u64 ticksElapsed = armGetSystemTick() - origTicks;
|
u64 ticksElapsed = armGetSystemTick() - origTicks;
|
||||||
@ -855,13 +855,20 @@ int main(int argc, char* argv[])
|
|||||||
gears_init();
|
gears_init();
|
||||||
gears_reshape(1280, 720);
|
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
|
// Main graphics loop
|
||||||
while (appletMainLoop())
|
while (appletMainLoop())
|
||||||
{
|
{
|
||||||
// Get and process input
|
// Get and process input
|
||||||
hidScanInput();
|
padUpdate(&pad);
|
||||||
u32 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
u32 kDown = padGetButtonsDown(&pad);
|
||||||
if (kDown & KEY_PLUS)
|
if (kDown & HidNpadButton_Plus)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Render stuff!
|
// Render stuff!
|
||||||
|
@ -7,6 +7,13 @@ int main(int argc, char **argv)
|
|||||||
{
|
{
|
||||||
consoleInit(NULL);
|
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
|
// clear screen and home cursor
|
||||||
printf( CONSOLE_ESC(2J) );
|
printf( CONSOLE_ESC(2J) );
|
||||||
|
|
||||||
@ -46,15 +53,15 @@ int main(int argc, char **argv)
|
|||||||
// Main loop
|
// Main loop
|
||||||
while(appletMainLoop())
|
while(appletMainLoop())
|
||||||
{
|
{
|
||||||
//Scan all the inputs. This should be done once for each frame
|
// Scan the gamepad. This should be done once for each frame
|
||||||
hidScanInput();
|
padUpdate(&pad);
|
||||||
|
|
||||||
// Your code goes here
|
// Your code goes here
|
||||||
|
|
||||||
//hidKeysDown returns information about which buttons have been just pressed (and they weren't in the previous frame)
|
// padGetButtonsDown returns the set of buttons that have been newly pressed in this frame compared to the previous one
|
||||||
u64 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
u64 kDown = padGetButtonsDown(&pad);
|
||||||
|
|
||||||
if (kDown & KEY_PLUS) break; // break in order to return to hbmenu
|
if (kDown & HidNpadButton_Plus) break; // break in order to return to hbmenu
|
||||||
|
|
||||||
consoleUpdate(NULL);
|
consoleUpdate(NULL);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <switch.h>
|
#include <switch.h>
|
||||||
@ -414,13 +414,20 @@ int main(int argc, char* argv[])
|
|||||||
// Initialize our scene
|
// Initialize our scene
|
||||||
sceneInit();
|
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
|
// Main graphics loop
|
||||||
while (appletMainLoop())
|
while (appletMainLoop())
|
||||||
{
|
{
|
||||||
// Get and process input
|
// Get and process input
|
||||||
hidScanInput();
|
padUpdate(&pad);
|
||||||
u32 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
u32 kDown = padGetButtonsDown(&pad);
|
||||||
if (kDown & KEY_PLUS)
|
if (kDown & HidNpadButton_Plus)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Update our scene
|
// Update our scene
|
||||||
|
@ -325,13 +325,20 @@ int main(int argc, char* argv[])
|
|||||||
// Initialize our scene
|
// Initialize our scene
|
||||||
sceneInit();
|
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
|
// Main graphics loop
|
||||||
while (appletMainLoop())
|
while (appletMainLoop())
|
||||||
{
|
{
|
||||||
// Get and process input
|
// Get and process input
|
||||||
hidScanInput();
|
padUpdate(&pad);
|
||||||
u32 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
u32 kDown = padGetButtonsDown(&pad);
|
||||||
if (kDown & KEY_PLUS)
|
if (kDown & HidNpadButton_Plus)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Render stuff!
|
// Render stuff!
|
||||||
|
@ -514,13 +514,20 @@ int main(int argc, char* argv[])
|
|||||||
// Initialize our scene
|
// Initialize our scene
|
||||||
sceneInit();
|
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
|
// Main graphics loop
|
||||||
while (appletMainLoop())
|
while (appletMainLoop())
|
||||||
{
|
{
|
||||||
// Get and process input
|
// Get and process input
|
||||||
hidScanInput();
|
padUpdate(&pad);
|
||||||
u32 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
u32 kDown = padGetButtonsDown(&pad);
|
||||||
if (kDown & KEY_PLUS)
|
if (kDown & HidNpadButton_Plus)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Update our scene
|
// Update our scene
|
||||||
|
@ -8,6 +8,13 @@ int main(int argc, char **argv)
|
|||||||
//Initialize console. Using NULL as the second argument tells the console library to use the internal console structure as current one.
|
//Initialize console. Using NULL as the second argument tells the console library to use the internal console structure as current one.
|
||||||
consoleInit(NULL);
|
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!"
|
//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
|
//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
|
//the row and column where you want your cursor to move
|
||||||
@ -15,13 +22,13 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
while(appletMainLoop())
|
while(appletMainLoop())
|
||||||
{
|
{
|
||||||
//Scan all the inputs. This should be done once for each frame
|
// Scan the gamepad. This should be done once for each frame
|
||||||
hidScanInput();
|
padUpdate(&pad);
|
||||||
|
|
||||||
//hidKeysDown returns information about which buttons have been just pressed (and they weren't in the previous frame)
|
// padGetButtonsDown returns the set of buttons that have been newly pressed in this frame compared to the previous one
|
||||||
u64 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
u64 kDown = padGetButtonsDown(&pad);
|
||||||
|
|
||||||
if (kDown & KEY_PLUS) break; // break in order to return to hbmenu
|
if (kDown & HidNpadButton_Plus) break; // break in order to return to hbmenu
|
||||||
|
|
||||||
consoleUpdate(NULL);
|
consoleUpdate(NULL);
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,13 @@ int main(int argc, char **argv)
|
|||||||
{
|
{
|
||||||
consoleInit(NULL);
|
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
|
// clear screen and home cursor
|
||||||
printf( CONSOLE_ESC(2J) );
|
printf( CONSOLE_ESC(2J) );
|
||||||
|
|
||||||
@ -61,15 +68,15 @@ int main(int argc, char **argv)
|
|||||||
// Main loop
|
// Main loop
|
||||||
while(appletMainLoop())
|
while(appletMainLoop())
|
||||||
{
|
{
|
||||||
//Scan all the inputs. This should be done once for each frame
|
// Scan the gamepad. This should be done once for each frame
|
||||||
hidScanInput();
|
padUpdate(&pad);
|
||||||
|
|
||||||
// Your code goes here
|
// Your code goes here
|
||||||
|
|
||||||
//hidKeysDown returns information about which buttons have been just pressed (and they weren't in the previous frame)
|
// padGetButtonsDown returns the set of buttons that have been newly pressed in this frame compared to the previous one
|
||||||
u64 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
u64 kDown = padGetButtonsDown(&pad);
|
||||||
|
|
||||||
if (kDown & KEY_PLUS) break; // break in order to return to hbmenu
|
if (kDown & HidNpadButton_Plus) break; // break in order to return to hbmenu
|
||||||
|
|
||||||
consoleUpdate(NULL);
|
consoleUpdate(NULL);
|
||||||
}
|
}
|
||||||
|
217
graphics/sdl2/sdl2-demo/Makefile
Normal file
217
graphics/sdl2/sdl2-demo/Makefile
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
.SUFFIXES:
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
ifeq ($(strip $(DEVKITPRO)),)
|
||||||
|
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
|
||||||
|
endif
|
||||||
|
|
||||||
|
TOPDIR ?= $(CURDIR)
|
||||||
|
include $(DEVKITPRO)/libnx/switch_rules
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# TARGET is the name of the output
|
||||||
|
# BUILD is the directory where object files & intermediate files will be placed
|
||||||
|
# SOURCES is a list of directories containing source code
|
||||||
|
# DATA is a list of directories containing data files
|
||||||
|
# INCLUDES is a list of directories containing header files
|
||||||
|
# ROMFS is the directory containing data to be added to RomFS, relative to the Makefile (Optional)
|
||||||
|
#
|
||||||
|
# NO_ICON: if set to anything, do not use icon.
|
||||||
|
# NO_NACP: if set to anything, no .nacp file is generated.
|
||||||
|
# APP_TITLE is the name of the app stored in the .nacp file (Optional)
|
||||||
|
# APP_AUTHOR is the author of the app stored in the .nacp file (Optional)
|
||||||
|
# APP_VERSION is the version of the app stored in the .nacp file (Optional)
|
||||||
|
# APP_TITLEID is the titleID of the app stored in the .nacp file (Optional)
|
||||||
|
# ICON is the filename of the icon (.jpg), relative to the project folder.
|
||||||
|
# If not set, it attempts to use one of the following (in this order):
|
||||||
|
# - <Project name>.jpg
|
||||||
|
# - icon.jpg
|
||||||
|
# - <libnx folder>/default_icon.jpg
|
||||||
|
#
|
||||||
|
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
|
||||||
|
# If not set, it attempts to use one of the following (in this order):
|
||||||
|
# - <Project name>.json
|
||||||
|
# - config.json
|
||||||
|
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
|
||||||
|
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
|
||||||
|
# NACP building is skipped as well.
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
TARGET := $(notdir $(CURDIR))
|
||||||
|
BUILD := build
|
||||||
|
SOURCES := source
|
||||||
|
DATA := data
|
||||||
|
INCLUDES := include
|
||||||
|
ROMFS := romfs
|
||||||
|
|
||||||
|
APP_TITLE := SDL2+mixer+image Demo
|
||||||
|
APP_AUTHOR := carstene1ns
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# options for code generation
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
|
||||||
|
|
||||||
|
CFLAGS := `$(PREFIX)pkg-config --cflags sdl2 SDL2_mixer SDL2_image` -Wall -O2 -ffunction-sections \
|
||||||
|
$(ARCH) $(DEFINES)
|
||||||
|
|
||||||
|
CFLAGS += $(INCLUDE) -D__SWITCH__
|
||||||
|
|
||||||
|
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
|
||||||
|
|
||||||
|
ASFLAGS := -g $(ARCH)
|
||||||
|
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
|
||||||
|
|
||||||
|
LIBS := `$(PREFIX)pkg-config --libs sdl2 SDL2_mixer SDL2_image SDL2_ttf` \
|
||||||
|
-lnx
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# list of directories containing libraries, this must be the top level containing
|
||||||
|
# include and lib
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
LIBDIRS := $(PORTLIBS) $(LIBNX)
|
||||||
|
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# no real need to edit anything past this point unless you need to add additional
|
||||||
|
# rules for different file extensions
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export OUTPUT := $(CURDIR)/$(TARGET)
|
||||||
|
export TOPDIR := $(CURDIR)
|
||||||
|
|
||||||
|
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||||
|
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
|
||||||
|
|
||||||
|
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||||
|
|
||||||
|
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
|
||||||
|
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
|
||||||
|
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
||||||
|
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# use CXX for linking C++ and libEGL dependent projects
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
export LD := $(CXX)
|
||||||
|
|
||||||
|
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
|
||||||
|
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
|
||||||
|
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
|
||||||
|
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
|
||||||
|
|
||||||
|
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
||||||
|
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||||
|
-I$(CURDIR)/$(BUILD)
|
||||||
|
|
||||||
|
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||||
|
|
||||||
|
ifeq ($(strip $(CONFIG_JSON)),)
|
||||||
|
jsons := $(wildcard *.json)
|
||||||
|
ifneq (,$(findstring $(TARGET).json,$(jsons)))
|
||||||
|
export APP_JSON := $(TOPDIR)/$(TARGET).json
|
||||||
|
else
|
||||||
|
ifneq (,$(findstring config.json,$(jsons)))
|
||||||
|
export APP_JSON := $(TOPDIR)/config.json
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(strip $(ICON)),)
|
||||||
|
icons := $(wildcard *.jpg)
|
||||||
|
ifneq (,$(findstring $(TARGET).jpg,$(icons)))
|
||||||
|
export APP_ICON := $(TOPDIR)/$(TARGET).jpg
|
||||||
|
else
|
||||||
|
ifneq (,$(findstring icon.jpg,$(icons)))
|
||||||
|
export APP_ICON := $(TOPDIR)/icon.jpg
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
export APP_ICON := $(TOPDIR)/$(ICON)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(strip $(NO_ICON)),)
|
||||||
|
export NROFLAGS += --icon=$(APP_ICON)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(strip $(NO_NACP)),)
|
||||||
|
export NROFLAGS += --nacp=$(CURDIR)/$(TARGET).nacp
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq ($(APP_TITLEID),)
|
||||||
|
export NACPFLAGS += --titleid=$(APP_TITLEID)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq ($(ROMFS),)
|
||||||
|
export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS)
|
||||||
|
endif
|
||||||
|
|
||||||
|
.PHONY: $(BUILD) clean all
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
all: $(BUILD)
|
||||||
|
|
||||||
|
$(BUILD):
|
||||||
|
@[ -d $@ ] || mkdir -p $@
|
||||||
|
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
clean:
|
||||||
|
@echo clean ...
|
||||||
|
ifeq ($(strip $(APP_JSON)),)
|
||||||
|
@rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
|
||||||
|
else
|
||||||
|
@rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
else
|
||||||
|
.PHONY: all
|
||||||
|
|
||||||
|
DEPENDS := $(OFILES:.o=.d)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# main targets
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
ifeq ($(strip $(APP_JSON)),)
|
||||||
|
|
||||||
|
all : $(OUTPUT).nro
|
||||||
|
|
||||||
|
ifeq ($(strip $(NO_NACP)),)
|
||||||
|
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp
|
||||||
|
else
|
||||||
|
$(OUTPUT).nro : $(OUTPUT).elf
|
||||||
|
endif
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
all : $(OUTPUT).nsp
|
||||||
|
|
||||||
|
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
|
||||||
|
|
||||||
|
$(OUTPUT).nso : $(OUTPUT).elf
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
|
$(OUTPUT).elf : $(OFILES)
|
||||||
|
|
||||||
|
$(OFILES_SRC) : $(HFILES_BIN)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# you need a rule like this for each extension you use as binary data
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
%.bin.o %_bin.h : %.bin
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
@echo $(notdir $<)
|
||||||
|
@$(bin2o)
|
||||||
|
|
||||||
|
-include $(DEPENDS)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------------
|
||||||
|
endif
|
||||||
|
#---------------------------------------------------------------------------------------
|
BIN
graphics/sdl2/sdl2-demo/romfs/data/LeroyLetteringLightBeta01.ttf
Normal file
BIN
graphics/sdl2/sdl2-demo/romfs/data/LeroyLetteringLightBeta01.ttf
Normal file
Binary file not shown.
BIN
graphics/sdl2/sdl2-demo/romfs/data/background.ogg
Normal file
BIN
graphics/sdl2/sdl2-demo/romfs/data/background.ogg
Normal file
Binary file not shown.
BIN
graphics/sdl2/sdl2-demo/romfs/data/pop1.wav
Normal file
BIN
graphics/sdl2/sdl2-demo/romfs/data/pop1.wav
Normal file
Binary file not shown.
BIN
graphics/sdl2/sdl2-demo/romfs/data/pop2.wav
Normal file
BIN
graphics/sdl2/sdl2-demo/romfs/data/pop2.wav
Normal file
Binary file not shown.
BIN
graphics/sdl2/sdl2-demo/romfs/data/pop3.wav
Normal file
BIN
graphics/sdl2/sdl2-demo/romfs/data/pop3.wav
Normal file
Binary file not shown.
BIN
graphics/sdl2/sdl2-demo/romfs/data/pop4.wav
Normal file
BIN
graphics/sdl2/sdl2-demo/romfs/data/pop4.wav
Normal file
Binary file not shown.
BIN
graphics/sdl2/sdl2-demo/romfs/data/sdl.png
Normal file
BIN
graphics/sdl2/sdl2-demo/romfs/data/sdl.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.2 KiB |
BIN
graphics/sdl2/sdl2-demo/romfs/data/switch.png
Normal file
BIN
graphics/sdl2/sdl2-demo/romfs/data/switch.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
259
graphics/sdl2/sdl2-demo/source/main.c
Normal file
259
graphics/sdl2/sdl2-demo/source/main.c
Normal file
@ -0,0 +1,259 @@
|
|||||||
|
/* Mini SDL Demo
|
||||||
|
* featuring SDL2 + SDL2_mixer + SDL2_image + SDL2_ttf
|
||||||
|
* on Nintendo Switch using libnx
|
||||||
|
*
|
||||||
|
* Copyright 2018 carsten1ns
|
||||||
|
* 2020 WinterMute
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <SDL.h>
|
||||||
|
#include <SDL_mixer.h>
|
||||||
|
#include <SDL_image.h>
|
||||||
|
#include <SDL_ttf.h>
|
||||||
|
#include <switch.h>
|
||||||
|
|
||||||
|
// some switch buttons
|
||||||
|
#define JOY_A 0
|
||||||
|
#define JOY_B 1
|
||||||
|
#define JOY_X 2
|
||||||
|
#define JOY_Y 3
|
||||||
|
#define JOY_PLUS 10
|
||||||
|
#define JOY_MINUS 11
|
||||||
|
#define JOY_LEFT 12
|
||||||
|
#define JOY_UP 13
|
||||||
|
#define JOY_RIGHT 14
|
||||||
|
#define JOY_DOWN 15
|
||||||
|
|
||||||
|
#define SCREEN_W 1280
|
||||||
|
#define SCREEN_H 720
|
||||||
|
|
||||||
|
SDL_Texture * render_text(SDL_Renderer *renderer, const char* text, TTF_Font *font, SDL_Color color, SDL_Rect *rect)
|
||||||
|
{
|
||||||
|
SDL_Surface *surface;
|
||||||
|
SDL_Texture *texture;
|
||||||
|
|
||||||
|
surface = TTF_RenderText_Solid(font, text, color);
|
||||||
|
texture = SDL_CreateTextureFromSurface(renderer, surface);
|
||||||
|
rect->w = surface->w;
|
||||||
|
rect->h = surface->h;
|
||||||
|
|
||||||
|
SDL_FreeSurface(surface);
|
||||||
|
|
||||||
|
return texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rand_range(int min, int max){
|
||||||
|
return min + rand() / (RAND_MAX / (max - min + 1) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
|
||||||
|
romfsInit();
|
||||||
|
chdir("romfs:/");
|
||||||
|
|
||||||
|
int exit_requested = 0;
|
||||||
|
int trail = 0;
|
||||||
|
int wait = 25;
|
||||||
|
|
||||||
|
SDL_Texture *switchlogo_tex = NULL, *sdllogo_tex = NULL, *helloworld_tex = NULL;
|
||||||
|
SDL_Rect pos = { 0, 0, 0, 0 }, sdl_pos = { 0, 0, 0, 0 };
|
||||||
|
Mix_Music *music = NULL;
|
||||||
|
Mix_Chunk *sound[4] = { NULL };
|
||||||
|
SDL_Event event;
|
||||||
|
|
||||||
|
SDL_Color colors[] = {
|
||||||
|
{ 128, 128, 128, 0 }, // gray
|
||||||
|
{ 255, 255, 255, 0 }, // white
|
||||||
|
{ 255, 0, 0, 0 }, // red
|
||||||
|
{ 0, 255, 0, 0 }, // green
|
||||||
|
{ 0, 0, 255, 0 }, // blue
|
||||||
|
{ 255, 255, 0, 0 }, // brown
|
||||||
|
{ 0, 255, 255, 0 }, // cyan
|
||||||
|
{ 255, 0, 255, 0 }, // purple
|
||||||
|
};
|
||||||
|
int col = 0, snd = 0;
|
||||||
|
|
||||||
|
srand(time(NULL));
|
||||||
|
int vel_x = rand_range(1, 5);
|
||||||
|
int vel_y = rand_range(1, 5);
|
||||||
|
|
||||||
|
SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER);
|
||||||
|
Mix_Init(MIX_INIT_OGG);
|
||||||
|
IMG_Init(IMG_INIT_PNG);
|
||||||
|
TTF_Init();
|
||||||
|
|
||||||
|
SDL_Window* window = SDL_CreateWindow("sdl2+mixer+image+ttf demo", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SCREEN_W, SCREEN_H, SDL_WINDOW_SHOWN);
|
||||||
|
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
|
||||||
|
|
||||||
|
// load logos from file
|
||||||
|
SDL_Surface *sdllogo = IMG_Load("data/sdl.png");
|
||||||
|
if (sdllogo) {
|
||||||
|
sdl_pos.w = sdllogo->w;
|
||||||
|
sdl_pos.h = sdllogo->h;
|
||||||
|
sdllogo_tex = SDL_CreateTextureFromSurface(renderer, sdllogo);
|
||||||
|
SDL_FreeSurface(sdllogo);
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Surface *switchlogo = IMG_Load("data/switch.png");
|
||||||
|
if (switchlogo) {
|
||||||
|
pos.x = SCREEN_W / 2 - switchlogo->w / 2;
|
||||||
|
pos.y = SCREEN_H / 2 - switchlogo->h / 2;
|
||||||
|
pos.w = switchlogo->w;
|
||||||
|
pos.h = switchlogo->h;
|
||||||
|
switchlogo_tex = SDL_CreateTextureFromSurface(renderer, switchlogo);
|
||||||
|
SDL_FreeSurface(switchlogo);
|
||||||
|
}
|
||||||
|
|
||||||
|
col = rand_range(0, 7);
|
||||||
|
|
||||||
|
SDL_InitSubSystem(SDL_INIT_JOYSTICK);
|
||||||
|
SDL_JoystickEventState(SDL_ENABLE);
|
||||||
|
SDL_JoystickOpen(0);
|
||||||
|
|
||||||
|
// load font from romfs
|
||||||
|
TTF_Font* font = TTF_OpenFont("data/LeroyLetteringLightBeta01.ttf", 36);
|
||||||
|
|
||||||
|
// render text as texture
|
||||||
|
SDL_Rect helloworld_rect = { 0, SCREEN_H - 36, 0, 0 };
|
||||||
|
helloworld_tex = render_text(renderer, "Hello, world!", font, colors[1], &helloworld_rect);
|
||||||
|
|
||||||
|
// no need to keep the font loaded
|
||||||
|
TTF_CloseFont(font);
|
||||||
|
|
||||||
|
SDL_InitSubSystem(SDL_INIT_AUDIO);
|
||||||
|
Mix_AllocateChannels(5);
|
||||||
|
Mix_OpenAudio(48000, AUDIO_S16, 2, 4096);
|
||||||
|
|
||||||
|
// load music and sounds from files
|
||||||
|
music = Mix_LoadMUS("data/background.ogg");
|
||||||
|
sound[0] = Mix_LoadWAV("data/pop1.wav");
|
||||||
|
sound[1] = Mix_LoadWAV("data/pop2.wav");
|
||||||
|
sound[2] = Mix_LoadWAV("data/pop3.wav");
|
||||||
|
sound[3] = Mix_LoadWAV("data/pop4.wav");
|
||||||
|
if (music)
|
||||||
|
Mix_PlayMusic(music, -1);
|
||||||
|
|
||||||
|
while (!exit_requested
|
||||||
|
&& appletMainLoop()
|
||||||
|
) {
|
||||||
|
while (SDL_PollEvent(&event)) {
|
||||||
|
if (event.type == SDL_QUIT)
|
||||||
|
exit_requested = 1;
|
||||||
|
|
||||||
|
// use joystick
|
||||||
|
if (event.type == SDL_JOYBUTTONDOWN) {
|
||||||
|
if (event.jbutton.button == JOY_UP)
|
||||||
|
if (wait > 0)
|
||||||
|
wait--;
|
||||||
|
if (event.jbutton.button == JOY_DOWN)
|
||||||
|
if (wait < 100)
|
||||||
|
wait++;
|
||||||
|
|
||||||
|
if (event.jbutton.button == JOY_PLUS)
|
||||||
|
exit_requested = 1;
|
||||||
|
|
||||||
|
if (event.jbutton.button == JOY_B)
|
||||||
|
trail =! trail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set position and bounce on the walls
|
||||||
|
pos.y += vel_y;
|
||||||
|
pos.x += vel_x;
|
||||||
|
if (pos.x + pos.w > SCREEN_W) {
|
||||||
|
pos.x = SCREEN_W - pos.w;
|
||||||
|
vel_x = -rand_range(1, 5);
|
||||||
|
col = rand_range(0, 4);
|
||||||
|
snd = rand_range(0, 3);
|
||||||
|
if (sound[snd])
|
||||||
|
Mix_PlayChannel(-1, sound[snd], 0);
|
||||||
|
}
|
||||||
|
if (pos.x < 0) {
|
||||||
|
pos.x = 0;
|
||||||
|
vel_x = rand_range(1, 5);
|
||||||
|
col = rand_range(0, 4);
|
||||||
|
snd = rand_range(0, 3);
|
||||||
|
if (sound[snd])
|
||||||
|
Mix_PlayChannel(-1, sound[snd], 0);
|
||||||
|
}
|
||||||
|
if (pos.y + pos.h > SCREEN_H) {
|
||||||
|
pos.y = SCREEN_H - pos.h;
|
||||||
|
vel_y = -rand_range(1, 5);
|
||||||
|
col = rand_range(0, 4);
|
||||||
|
snd = rand_range(0, 3);
|
||||||
|
if (sound[snd])
|
||||||
|
Mix_PlayChannel(-1, sound[snd], 0);
|
||||||
|
}
|
||||||
|
if (pos.y < 0) {
|
||||||
|
pos.y = 0;
|
||||||
|
vel_y = rand_range(1, 5);
|
||||||
|
col = rand_range(0, 4);
|
||||||
|
snd = rand_range(0, 3);
|
||||||
|
if (sound[snd])
|
||||||
|
Mix_PlayChannel(-1, sound[snd], 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!trail) {
|
||||||
|
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0xFF);
|
||||||
|
SDL_RenderClear(renderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// put logos on screen
|
||||||
|
if (sdllogo_tex)
|
||||||
|
SDL_RenderCopy(renderer, sdllogo_tex, NULL, &sdl_pos);
|
||||||
|
if (switchlogo_tex) {
|
||||||
|
SDL_SetTextureColorMod(switchlogo_tex, colors[col].r, colors[col].g, colors[col].b);
|
||||||
|
SDL_RenderCopy(renderer, switchlogo_tex, NULL, &pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
// put text on screen
|
||||||
|
if (helloworld_tex)
|
||||||
|
SDL_RenderCopy(renderer, helloworld_tex, NULL, &helloworld_rect);
|
||||||
|
|
||||||
|
SDL_RenderPresent(renderer);
|
||||||
|
|
||||||
|
SDL_Delay(wait);
|
||||||
|
}
|
||||||
|
|
||||||
|
// clean up your textures when you are done with them
|
||||||
|
if (sdllogo_tex)
|
||||||
|
SDL_DestroyTexture(sdllogo_tex);
|
||||||
|
|
||||||
|
if (switchlogo_tex)
|
||||||
|
SDL_DestroyTexture(switchlogo_tex);
|
||||||
|
|
||||||
|
if (helloworld_tex)
|
||||||
|
SDL_DestroyTexture(helloworld_tex);
|
||||||
|
|
||||||
|
// stop sounds and free loaded data
|
||||||
|
Mix_HaltChannel(-1);
|
||||||
|
Mix_FreeMusic(music);
|
||||||
|
for (snd = 0; snd < 4; snd++)
|
||||||
|
if (sound[snd])
|
||||||
|
Mix_FreeChunk(sound[snd]);
|
||||||
|
|
||||||
|
IMG_Quit();
|
||||||
|
Mix_CloseAudio();
|
||||||
|
TTF_Quit();
|
||||||
|
Mix_Quit();
|
||||||
|
SDL_Quit();
|
||||||
|
romfsExit();
|
||||||
|
return 0;
|
||||||
|
}
|
@ -18,6 +18,8 @@
|
|||||||
|
|
||||||
static u32 framebuf_width=0;
|
static u32 framebuf_width=0;
|
||||||
|
|
||||||
|
static PadState pad;
|
||||||
|
|
||||||
//Note that this doesn't handle any blending.
|
//Note that this doesn't handle any blending.
|
||||||
void draw_glyph(FT_Bitmap* bitmap, u32* framebuf, u32 x, u32 y)
|
void draw_glyph(FT_Bitmap* bitmap, u32* framebuf, u32 x, u32 y)
|
||||||
{
|
{
|
||||||
@ -102,8 +104,8 @@ static int error_screen(const char* fmt, ...)
|
|||||||
printf("Press PLUS to exit\n");
|
printf("Press PLUS to exit\n");
|
||||||
while (appletMainLoop())
|
while (appletMainLoop())
|
||||||
{
|
{
|
||||||
hidScanInput();
|
padUpdate(&pad);
|
||||||
if (hidKeysDown(CONTROLLER_P1_AUTO) & KEY_PLUS)
|
if (padGetButtonsDown(&pad) & HidNpadButton_Plus)
|
||||||
break;
|
break;
|
||||||
consoleUpdate(NULL);
|
consoleUpdate(NULL);
|
||||||
}
|
}
|
||||||
@ -132,9 +134,9 @@ void userAppInit(void)
|
|||||||
{
|
{
|
||||||
Result rc;
|
Result rc;
|
||||||
|
|
||||||
rc = plInitialize();
|
rc = plInitialize(PlServiceType_User);
|
||||||
if (R_FAILED(rc))
|
if (R_FAILED(rc))
|
||||||
fatalThrow(rc);
|
diagAbortWithResult(rc);
|
||||||
|
|
||||||
LanguageCode = getSystemLanguage();
|
LanguageCode = getSystemLanguage();
|
||||||
}
|
}
|
||||||
@ -149,6 +151,12 @@ int main(int argc, char **argv)
|
|||||||
Result rc=0;
|
Result rc=0;
|
||||||
FT_Error ret=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.
|
//Use this when using multiple shared-fonts.
|
||||||
/*
|
/*
|
||||||
PlFontData fonts[PlSharedFontType_Total];
|
PlFontData fonts[PlSharedFontType_Total];
|
||||||
@ -198,13 +206,13 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
while (appletMainLoop())
|
while (appletMainLoop())
|
||||||
{
|
{
|
||||||
//Scan all the inputs. This should be done once for each frame
|
// Scan the gamepad. This should be done once for each frame
|
||||||
hidScanInput();
|
padUpdate(&pad);
|
||||||
|
|
||||||
//hidKeysDown returns information about which buttons have been just pressed (and they weren't in the previous frame)
|
// padGetButtonsDown returns the set of buttons that have been newly pressed in this frame compared to the previous one
|
||||||
u32 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
u32 kDown = padGetButtonsDown(&pad);
|
||||||
|
|
||||||
if (kDown & KEY_PLUS) break; // break in order to return to hbmenu
|
if (kDown & HidNpadButton_Plus) break; // break in order to return to hbmenu
|
||||||
|
|
||||||
u32 stride;
|
u32 stride;
|
||||||
u32* framebuf = (u32*)framebufferBegin(&fb, &stride);
|
u32* framebuf = (u32*)framebufferBegin(&fb, &stride);
|
||||||
|
@ -33,21 +33,30 @@ int main(int argc, char* argv[])
|
|||||||
|
|
||||||
#ifdef DISPLAY_IMAGE
|
#ifdef DISPLAY_IMAGE
|
||||||
u8* imageptr = (u8*)image_bin;
|
u8* imageptr = (u8*)image_bin;
|
||||||
|
const u32 image_width = 1280;
|
||||||
|
const u32 image_height = 720;
|
||||||
#endif
|
#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;
|
u32 cnt = 0;
|
||||||
|
|
||||||
// Main loop
|
// Main loop
|
||||||
while (appletMainLoop())
|
while (appletMainLoop())
|
||||||
{
|
{
|
||||||
// Scan all the inputs. This should be done once for each frame
|
// Scan the gamepad. This should be done once for each frame
|
||||||
hidScanInput();
|
padUpdate(&pad);
|
||||||
|
|
||||||
// hidKeysDown returns information about which buttons have been
|
// padGetButtonsDown returns the set of buttons that have been
|
||||||
// just pressed in this frame compared to the previous one
|
// newly pressed in this frame compared to the previous one
|
||||||
u64 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
u64 kDown = padGetButtonsDown(&pad);
|
||||||
|
|
||||||
if (kDown & KEY_PLUS)
|
if (kDown & HidNpadButton_Plus)
|
||||||
break; // break in order to return to hbmenu
|
break; // break in order to return to hbmenu
|
||||||
|
|
||||||
// Retrieve the framebuffer
|
// Retrieve the framebuffer
|
||||||
@ -66,7 +75,9 @@ int main(int argc, char* argv[])
|
|||||||
{
|
{
|
||||||
u32 pos = y * stride / sizeof(u32) + x;
|
u32 pos = y * stride / sizeof(u32) + x;
|
||||||
#ifdef DISPLAY_IMAGE
|
#ifdef DISPLAY_IMAGE
|
||||||
framebuf[pos] = RGBA8_MAXALPHA(imageptr[pos*3+0]+(cnt*4), imageptr[pos*3+1], imageptr[pos*3+2]);
|
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]);
|
||||||
#else
|
#else
|
||||||
framebuf[pos] = 0x01010101 * cnt * 4;//Set framebuf to different shades of grey.
|
framebuf[pos] = 0x01010101 * cnt * 4;//Set framebuf to different shades of grey.
|
||||||
#endif
|
#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