benchmark-toolbox: add benchmark-toolbox

a unified benchmarking tool
This commit is contained in:
souldbminersmwc
2026-06-07 21:10:53 -04:00
parent 8ebf887956
commit 4a5e889c40
1252 changed files with 356811 additions and 491 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,3 @@
void BHRTSceneInit();
void BHRTRender();
void BHRTExit();

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,3 @@
void CPURBSceneinit();
void CPURBRender();
void CPURBExit();

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,3 @@
void CPURTSceneinit();
void CPURTRender();
void CPURTExit();

View File

@@ -0,0 +1,770 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <switch.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <glad/glad.h>
#define GLM_FORCE_PURE
#include "sates.h"
#include "stb_image.h"
#include <glm/gtc/constants.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/mat4x4.hpp>
#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#ifndef ENABLE_NXLINK
#define TRACE(fmt, ...) ((void)0)
#else
#include <unistd.h>
#define TRACE(fmt, ...) printf("%s: " fmt "\n", __PRETTY_FUNCTION__, ##__VA_ARGS__)
static int s_nxlinkSock = -1;
static void initNxLink() {
if (R_FAILED(socketInitializeDefault()))
return;
s_nxlinkSock = nxlinkStdio();
if (s_nxlinkSock >= 0)
TRACE("printf output now goes to nxlink server");
else
socketExit();
}
static void deinitNxLink() {
if (s_nxlinkSock >= 0) {
close(s_nxlinkSock);
socketExit();
s_nxlinkSock = -1;
}
}
extern "C" void userAppInit() {
initNxLink();
}
extern "C" void userAppExit() {
deinitNxLink();
}
#endif
static EGLDisplay s_display;
static EGLContext s_context;
static EGLSurface s_surface;
static bool initEgl(NWindow *win) {
s_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (!s_display) {
TRACE("Could not connect to display! error: %d", eglGetError());
goto _fail0;
}
eglInitialize(s_display, nullptr, nullptr);
if (eglBindAPI(EGL_OPENGL_API) == EGL_FALSE) {
TRACE("Could not set API! error: %d", eglGetError());
goto _fail1;
}
EGLConfig config;
EGLint numConfigs;
static const EGLint framebufferAttributeList[] = { EGL_RENDERABLE_TYPE,
EGL_OPENGL_BIT,
EGL_RED_SIZE,
8,
EGL_GREEN_SIZE,
8,
EGL_BLUE_SIZE,
8,
EGL_ALPHA_SIZE,
8,
EGL_DEPTH_SIZE,
24,
EGL_STENCIL_SIZE,
8,
EGL_NONE };
eglChooseConfig(s_display, framebufferAttributeList, &config, 1, &numConfigs);
if (numConfigs == 0) {
TRACE("No config found! error: %d", eglGetError());
goto _fail1;
}
s_surface = eglCreateWindowSurface(s_display, config, win, nullptr);
if (!s_surface) {
TRACE("Surface creation failed! error: %d", eglGetError());
goto _fail1;
}
static const EGLint contextAttributeList[] = { EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR,
EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR,
EGL_CONTEXT_MAJOR_VERSION_KHR,
4,
EGL_CONTEXT_MINOR_VERSION_KHR,
3,
EGL_NONE };
s_context = eglCreateContext(s_display, config, EGL_NO_CONTEXT, contextAttributeList);
if (!s_context) {
TRACE("Context creation failed! error: %d", eglGetError());
goto _fail2;
}
eglMakeCurrent(s_display, s_surface, s_surface, s_context);
return true;
_fail2:
eglDestroySurface(s_display, s_surface);
s_surface = nullptr;
_fail1:
eglTerminate(s_display);
s_display = nullptr;
_fail0:
return false;
}
static void deinitEgl() {
if (s_display) {
eglMakeCurrent(s_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
if (s_context) {
eglDestroyContext(s_display, s_context);
s_context = nullptr;
}
if (s_surface) {
eglDestroySurface(s_display, s_surface);
s_surface = nullptr;
}
eglTerminate(s_display);
s_display = nullptr;
}
}
static const char *const text_vs = R"text(
#version 330 core
layout(location=0) in vec2 inPos;
layout(location=1) in vec3 inColor;
out vec3 color;
void main() {
color = inColor;
gl_Position = vec4(inPos, 0.0, 1.0);
}
)text";
static const char *const text_fs = R"text(
#version 330 core
in vec3 color;
out vec4 fragColor;
void main() {
fragColor = vec4(color, 1.0);
}
)text";
static GLuint s_textProgram = 0;
static GLuint s_textVao = 0;
static GLuint s_textVbo = 0;
static const unsigned char font8x8[11][8] = { { 0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00 }, { 0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00 },
{ 0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00 }, { 0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00 },
{ 0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00 }, { 0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00 },
{ 0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00 }, { 0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00 },
{ 0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00 }, { 0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00 },
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00 } };
static GLuint createAndCompileShader(GLenum type, const char *source);
static void initTextRenderer() {
GLuint vsh = createAndCompileShader(GL_VERTEX_SHADER, text_vs);
GLuint fsh = createAndCompileShader(GL_FRAGMENT_SHADER, text_fs);
s_textProgram = glCreateProgram();
glAttachShader(s_textProgram, vsh);
glAttachShader(s_textProgram, fsh);
glLinkProgram(s_textProgram);
glDeleteShader(vsh);
glDeleteShader(fsh);
glGenVertexArrays(1, &s_textVao);
glGenBuffers(1, &s_textVbo);
}
static void drawTextPixel(float x, float y, float size, float r, float g, float b, float *vertexData, int *offset) {
float verts[] = { x, y, r, g, b, x + size, y, r, g, b, x + size, y + size, r, g, b,
x, y, r, g, b, x + size, y + size, r, g, b, x, y + size, r, g, b };
memcpy(&vertexData[*offset], verts, sizeof(verts));
*offset += 30;
}
static void drawChar(char c, float x, float y, float scale, float r, float g, float b, float *vertexData, int *offset) {
int idx = -1;
if (c >= '0' && c <= '9')
idx = c - '0';
else if (c == '.')
idx = 10;
else
return;
const unsigned char *glyph = font8x8[idx];
for (int row = 0; row < 8; row++) {
for (int col = 0; col < 8; col++) {
if (glyph[row] & (1 << col)) {
float px = x + col * scale;
float py = y - row * scale;
drawTextPixel(px, py, scale, r, g, b, vertexData, offset);
}
}
}
}
static void drawText(const char *text, float x, float y, float scale, float r, float g, float b) {
float *vertexData = (float *)malloc(100 * 64 * 6 * 5 * sizeof(float));
int offset = 0;
float cx = x;
while (*text) {
drawChar(*text, cx, y, scale, r, g, b, vertexData, &offset);
cx += 8 * scale;
text++;
}
if (offset > 0) {
glUseProgram(s_textProgram);
glBindVertexArray(s_textVao);
glBindBuffer(GL_ARRAY_BUFFER, s_textVbo);
glBufferData(GL_ARRAY_BUFFER, offset * sizeof(float), vertexData, GL_DYNAMIC_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *)(2 * sizeof(float)));
glEnableVertexAttribArray(1);
glDrawArrays(GL_TRIANGLES, 0, offset / 5);
}
free(vertexData);
}
static void cleanupTextRenderer() {
if (s_textVbo) {
glDeleteBuffers(1, &s_textVbo);
s_textVbo = 0;
}
if (s_textVao) {
glDeleteVertexArrays(1, &s_textVao);
s_textVao = 0;
}
if (s_textProgram) {
glDeleteProgram(s_textProgram);
s_textProgram = 0;
}
}
static void setMesaConfig() {
setenv("EGL_LOG_LEVEL", "debug", 1);
setenv("MESA_VERBOSE", "all", 1);
setenv("NOUVEAU_MESA_DEBUG", "1", 1);
setenv("NV50_PROG_OPTIMIZE", "0", 1);
setenv("NV50_PROG_DEBUG", "1", 1);
setenv("NV50_PROG_CHIPSET", "0x120", 1);
}
static const char *const vertexShaderSource = R"text(
#version 330 core
out vec2 uv;
void main()
{
vec2 pos = vec2(
(gl_VertexID == 2) ? 3.0 : -1.0,
(gl_VertexID == 1) ? 3.0 : -1.0
);
uv = 0.5 * (pos + 1.0);
gl_Position = vec4(pos, 0.0, 1.0);
}
)text";
static const char *const fragmentShaderSource = R"text(
#version 330 core
// Alright so originally this was supposed to be a way more complex shader
// It was supposed to be a proceduraly generated path tracing scene
// Unfortunately, my stupidity knows no bounds, and well over 5000 lines in I decided that I was better off waterboarding myself
// To anyone who even wants to ask, no you don't want to see the original, it was a fucking war crime
// Ask Adam why he thought it was a good idea to copy entire libraries into this shit, and then acted surprised when it didn't work
// so, have a cornel box instead, because I cannot be fucked to make a proper shader file loader, despite needing one soon anyway.
in vec2 uv;
out vec4 fragColor;
uniform vec2 u_resolution;
uniform float u_time;
// Sampler variables
uniform int u_frame;
uniform sampler2D u_prevFrame;
#define WHITE 0
#define RED 1
#define GREEN 2
#define LIGHT 3
#define MIRROR 4
struct Hit {
float dist;
int material;
vec3 normal;
};
float hash(vec2 p){
return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5453);
}
vec3 rand3(vec3 p){
float f = float(u_frame);
return vec3(
hash(p.xy + f),
hash(p.yz + f*1.37),
hash(p.zx + f*2.17)
) * 2.0 - 1.0;
}
float sdSphere(vec3 p, float r){
return length(p) - r;
}
Hit map(vec3 p)
{
Hit h;
h.dist = 1e9;
h.material = WHITE;
// ---------- mirror sphere ----------
float s = sdSphere(p - vec3(0,1.0,-0.5), 1.0);
if(s < h.dist){
h.dist = s;
h.material = MIRROR;
h.normal = normalize(p - vec3(0,1.0,-0.5));
}
// ---------- room walls (inward planes) ----------
// left (red)
float left = p.x + 2.0;
if(left < h.dist){
h.dist = left;
h.material = RED;
h.normal = vec3(1, 0, 0);
}
// right (green)
float right = 2.0 - p.x;
if(right < h.dist){
h.dist = right;
h.material = GREEN;
h.normal = vec3(-1, 0, 0);
}
// floor
float floor = p.y;
if(floor < h.dist){
h.dist = floor;
h.material = WHITE;
h.normal = vec3(0, 1, 0);
}
// ceiling
float ceil = 4.0 - p.y;
if(ceil < h.dist){
h.dist = ceil;
h.material = WHITE;
h.normal = vec3(0, -1, 0);
}
// back wall
float back = 2.0 - p.z;
if(back < h.dist){
h.dist = back;
h.material = WHITE;
h.normal = vec3(0, 0, -1);
}
vec3 lCenter = vec3(0.0, 3.98, -1.2);
vec3 lSize = vec3(0.9, 0.01, 0.9);
vec3 d = abs(p - lCenter) - lSize;
float light = length(max(d,0.0)) + min(max(d.x,max(d.y,d.z)),0.0);
if(light < h.dist){
h.dist = light;
h.material = LIGHT;
}
return h;
}
Hit raymarch(vec3 ro, vec3 rd)
{
float t = 0.0;
// 80 steps, lower values give better performance, while higher values increase load
// increasing load beyond this only decreases power draw, while lowering results in graphical glitches
for(int i=0;i<80;i++){
vec3 p = ro + rd*t;
Hit h = map(p);
if(h.dist < 0.001){
h.dist = t;
return h;
}
t += h.dist;
if(t > 50.0) break;
}
Hit miss;
miss.dist = -1.0;
return miss;
}
vec3 getColor(int m){
if(m==RED) return vec3(1,0.2,0.2);
if(m==GREEN) return vec3(0.2,1,0.2);
return vec3(0.9);
}
bool isLight(int m){ return m==LIGHT; }
bool isMirror(int m){ return m==MIRROR; }
vec3 trace(vec3 ro, vec3 rd)
{
vec3 color = vec3(0);
vec3 throughput = vec3(1);
// Controls the amount of bounces made by rays in the scene
// reduced to 3 for parody with CPU mode
for(int bounce=0; bounce<3; bounce++)
{
Hit h = raymarch(ro, rd);
// sky fallback
if(h.dist < 0.0){
color += throughput * vec3(0.7,0.8,1.0);
break;
}
vec3 pos = ro + rd*h.dist;
vec3 n = h.normal;
// hit light
if(isLight(h.material)){
color += throughput * vec3(12.0);
break;
}
// mirror bounce
if(isMirror(h.material)){
rd = reflect(rd, n);
}
else{
// diffuse bounce (hemisphere)
vec3 r = rand3(pos + float(bounce) + float(u_frame));
rd = normalize(n + r);
// cosine weighting (energy can only be transfered)
// I mean if you want to change this sure, but you'll get flashbanged.
float cosTheta = max(dot(rd, n), 0.0);
throughput *= getColor(h.material) * cosTheta;
}
ro = pos + n*0.001;
}
// Clamp color values so we don't get a concussion simulator
return color;
}
vec3 pathTrace(vec2 uv)
{
// convert uv to screen space values
vec2 p = uv * 2.0 - 1.0;
p.x *= u_resolution.x / u_resolution.y;
// Camera setup
vec3 ro = vec3(0,2,-6);
vec3 target = vec3(0,2,0);
vec3 forward = normalize(target - ro);
vec3 right = normalize(cross(forward, vec3(0,1,0)));
vec3 up = cross(right, forward);
vec3 rd = normalize(forward + p.x*right + p.y*up);
// Add pixel jittering to reduce noise
vec2 jitter = vec2(
hash(gl_FragCoord.xy + float(u_frame)),
hash(gl_FragCoord.yx + float(u_frame))
) / u_resolution;
return trace(ro, rd + vec3(jitter, 00));
}
void main()
{
//setup output
vec2 uv = gl_FragCoord.xy / u_resolution;
vec3 newSample = pathTrace(uv);
// Accumulation
vec3 prev = texture(u_prevFrame, gl_FragCoord.xy / u_resolution).rgb;
vec3 accumulated;
if(u_frame == 0)
accumulated = newSample;
else
accumulated = (prev *float(u_frame) + newSample) / float(u_frame + 1);
// Output final pixels, send to vertex
fragColor = vec4(accumulated, 1.0);
}
)text";
static GLuint s_program;
static GLuint s_vao, s_vbo;
static GLint resolutionLoc;
static GLint loc_mdlvMtx, loc_projMtx;
static GLint loc_time;
static u64 s_startTicks;
static u64 s_lastFrameTime = 0;
static float s_fps = 0.0f;
static int s_frameCount = 0;
static u64 s_fpsUpdateTime = 0;
static GLuint tex[2], fbo[2];
static GLuint frameLoc, prevframeLoc;
static int frame = 0;
static GLuint createAndCompileShader(GLenum type, const char *source) {
GLint success;
GLchar msg[512];
GLuint handle = glCreateShader(type);
if (!handle) {
TRACE("%u: cannot create shader", type);
return 0;
}
glShaderSource(handle, 1, &source, nullptr);
glCompileShader(handle);
glGetShaderiv(handle, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(handle, sizeof(msg), nullptr, msg);
TRACE("%u: %s\n", type, msg);
glDeleteShader(handle);
return 0;
}
return handle;
}
void GPUPTSceneInit() {
GLint vsh = createAndCompileShader(GL_VERTEX_SHADER, vertexShaderSource);
GLint fsh = createAndCompileShader(GL_FRAGMENT_SHADER, fragmentShaderSource);
if (!vsh || !fsh) {
TRACE("Shader compile failed — aborting");
return;
}
s_program = glCreateProgram();
glViewport(0, 0, 1280, 720);
glAttachShader(s_program, vsh);
glAttachShader(s_program, fsh);
glBindFragDataLocation(s_program, 0, "fragColor");
glLinkProgram(s_program);
frameLoc = glGetUniformLocation(s_program, "u_frame");
prevframeLoc = glGetUniformLocation(s_program, "u_prevFrame");
resolutionLoc = glGetUniformLocation(s_program, "u_resolution");
loc_time = glGetUniformLocation(s_program, "u_time");
GLint success;
glGetProgramiv(s_program, GL_LINK_STATUS, &success);
if (!success) {
char buf[512];
glGetProgramInfoLog(s_program, sizeof(buf), nullptr, buf);
TRACE("Link error: %s", buf);
}
glDeleteShader(vsh);
glDeleteShader(fsh);
loc_mdlvMtx = glGetUniformLocation(s_program, "mdlvMtx");
loc_projMtx = glGetUniformLocation(s_program, "projMtx");
loc_time = glGetUniformLocation(s_program, "u_time");
static float vertices[] = {
-1.0f, -1.0f, 3.0f, -1.0f, -1.0f, 3.0f,
};
glGenTextures(2, tex);
glGenFramebuffers(2, fbo);
for (int i = 0; i < 2; i++) {
glBindTexture(GL_TEXTURE_2D, tex[i]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, 1280, 720, 0, GL_RGBA, GL_FLOAT, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glBindFramebuffer(GL_FRAMEBUFFER, fbo[i]);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex[i], 0);
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
}
glGenVertexArrays(1, &s_vao);
glGenBuffers(1, &s_vbo);
glBindVertexArray(s_vao);
glBindBuffer(GL_ARRAY_BUFFER, s_vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void *)0);
glEnableVertexAttribArray(0);
glEnable(GL_FRAMEBUFFER_SRGB);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
glUseProgram(s_program);
auto projMtx = glm::perspective(glm::radians(40.0f), 1280.0f / 720.0f, 0.01f, 1000.0f);
glUniformMatrix4fv(loc_projMtx, 1, GL_FALSE, glm::value_ptr(projMtx));
s_startTicks = armGetSystemTick();
s_lastFrameTime = s_startTicks;
s_fpsUpdateTime = s_startTicks;
s_frameCount = 0;
initTextRenderer();
}
float getTime2() {
u64 elapsed = armGetSystemTick() - s_startTicks;
return (elapsed * 625 / 12) / 2000000000.0;
}
void GPUPTRender() {
static int X = 0;
static int Y = 1;
u64 currentTime = armGetSystemTick();
s_frameCount++;
u64 timeSinceUpdate = currentTime - s_fpsUpdateTime;
float secondsSinceUpdate = (timeSinceUpdate * 625.0f / 12.0f) / 1000000000.0f;
if (secondsSinceUpdate >= 0.01f) {
s_fps = s_frameCount / secondsSinceUpdate;
s_frameCount = 0;
s_fpsUpdateTime = currentTime;
}
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glUseProgram(s_program);
glUniform1f(loc_time, getTime2());
glUniform2f(resolutionLoc, 1280.0f, 720.0f);
glBindVertexArray(s_vao);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex[X]);
glUniform1i(prevframeLoc, 0);
glUniform1i(frameLoc, frame);
glBindFramebuffer(GL_FRAMEBUFFER, fbo[Y]);
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo[Y]);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBlitFramebuffer(0, 0, 1280, 720, 0, 0, 1280, 720, GL_COLOR_BUFFER_BIT, GL_NEAREST);
if (frame == 0) {
glBindFramebuffer(GL_FRAMEBUFFER, fbo[X]);
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
}
std::swap(X, Y);
frame++;
glBindVertexArray(0);
char fpsText[32];
snprintf(fpsText, sizeof(fpsText), "%.3f", s_fps);
drawText(fpsText, -0.95f, 0.90f, 0.02f, 1.0f, 0.0f, 0.0f);
char sampleText[64];
snprintf(sampleText, sizeof(sampleText), "Samples: %d", frame);
drawText(sampleText, -0.95f, 0.90f, 0.02f, 1.0f, 0.0f, 0.0f);
}
void GPUPTExit() {
cleanupTextRenderer();
glDeleteBuffers(1, &s_vbo);
glDeleteVertexArrays(1, &s_vao);
glDeleteProgram(s_program);
frame = 0;
}
int GPUPTMain(int argc, char *argv[]) {
setMesaConfig();
if (!initEgl(nwindowGetDefault()))
return EXIT_FAILURE;
gladLoadGLLoader((GLADloadproc)eglGetProcAddress);
GPUPTSceneInit();
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
PadState pad;
padInitializeDefault(&pad);
while (appletMainLoop()) {
padUpdate(&pad);
u32 kDown = padGetButtonsDown(&pad);
if (kDown & HidNpadButton_B) {
GPUPTExit();
deinitEgl();
state = STATE_MENU;
return 0;
}
GPUPTRender();
eglSwapBuffers(s_display, s_surface);
}
GPUPTExit();
deinitEgl();
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,4 @@
void GPUPTSceneInit();
void GPUPTRender();
void GPUPTExit();
void getTime2();

View File

@@ -0,0 +1,691 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <switch.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <glad/glad.h>
#define GLM_FORCE_PURE
#include "fur_png.h"
#include "noise_png.h"
#include "sates.h"
#include "stb_image.h"
#include "wall_png.h"
#include "wunk_png.h"
#include <glm/gtc/constants.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/mat4x4.hpp>
#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#ifndef ENABLE_NXLINK
#define TRACE(fmt, ...) ((void)0)
#else
#include <unistd.h>
#define TRACE(fmt, ...) printf("%s: " fmt "\n", __PRETTY_FUNCTION__, ##__VA_ARGS__)
static int s_nxlinkSock = -1;
static void initNxLink() {
if (R_FAILED(socketInitializeDefault()))
return;
s_nxlinkSock = nxlinkStdio();
if (s_nxlinkSock >= 0)
TRACE("printf output now goes to nxlink server");
else
socketExit();
}
static void deinitNxLink() {
if (s_nxlinkSock >= 0) {
close(s_nxlinkSock);
socketExit();
s_nxlinkSock = -1;
}
}
extern "C" void userAppInit() {
initNxLink();
}
extern "C" void userAppExit() {
deinitNxLink();
}
#endif
static EGLDisplay s_display;
static EGLContext s_context;
static EGLSurface s_surface;
static bool initEgl(NWindow *win) {
s_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (!s_display) {
TRACE("Could not connect to display! error: %d", eglGetError());
goto _fail0;
}
eglInitialize(s_display, nullptr, nullptr);
if (eglBindAPI(EGL_OPENGL_API) == EGL_FALSE) {
TRACE("Could not set API! error: %d", eglGetError());
goto _fail1;
}
EGLConfig config;
EGLint numConfigs;
static const EGLint framebufferAttributeList[] = { EGL_RENDERABLE_TYPE,
EGL_OPENGL_BIT,
EGL_RED_SIZE,
8,
EGL_GREEN_SIZE,
8,
EGL_BLUE_SIZE,
8,
EGL_ALPHA_SIZE,
8,
EGL_DEPTH_SIZE,
24,
EGL_STENCIL_SIZE,
8,
EGL_NONE };
eglChooseConfig(s_display, framebufferAttributeList, &config, 1, &numConfigs);
if (numConfigs == 0) {
TRACE("No config found! error: %d", eglGetError());
goto _fail1;
}
s_surface = eglCreateWindowSurface(s_display, config, win, nullptr);
if (!s_surface) {
TRACE("Surface creation failed! error: %d", eglGetError());
goto _fail1;
}
static const EGLint contextAttributeList[] = { EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR,
EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR,
EGL_CONTEXT_MAJOR_VERSION_KHR,
4,
EGL_CONTEXT_MINOR_VERSION_KHR,
3,
EGL_NONE };
s_context = eglCreateContext(s_display, config, EGL_NO_CONTEXT, contextAttributeList);
if (!s_context) {
TRACE("Context creation failed! error: %d", eglGetError());
goto _fail2;
}
eglMakeCurrent(s_display, s_surface, s_surface, s_context);
return true;
_fail2:
eglDestroySurface(s_display, s_surface);
s_surface = nullptr;
_fail1:
eglTerminate(s_display);
s_display = nullptr;
_fail0:
return false;
}
static void deinitEgl() {
if (s_display) {
eglMakeCurrent(s_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
if (s_context) {
eglDestroyContext(s_display, s_context);
s_context = nullptr;
}
if (s_surface) {
eglDestroySurface(s_display, s_surface);
s_surface = nullptr;
}
eglTerminate(s_display);
s_display = nullptr;
}
}
static const char *const text_vs = R"text(
#version 330 core
layout(location=0) in vec2 inPos;
layout(location=1) in vec3 inColor;
out vec3 color;
void main() {
color = inColor;
gl_Position = vec4(inPos, 0.0, 1.0);
}
)text";
static const char *const text_fs = R"text(
#version 330 core
in vec3 color;
out vec4 fragColor;
void main() {
fragColor = vec4(color, 1.0);
}
)text";
static GLuint s_textProgram = 0;
static GLuint s_textVao = 0;
static GLuint s_textVbo = 0;
static const unsigned char font8x8[11][8] = { { 0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00 }, { 0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00 },
{ 0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00 }, { 0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00 },
{ 0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00 }, { 0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00 },
{ 0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00 }, { 0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00 },
{ 0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00 }, { 0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00 },
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00 } };
static GLuint createAndCompileShader(GLenum type, const char *source);
static void initTextRenderer() {
GLuint vsh = createAndCompileShader(GL_VERTEX_SHADER, text_vs);
GLuint fsh = createAndCompileShader(GL_FRAGMENT_SHADER, text_fs);
s_textProgram = glCreateProgram();
glAttachShader(s_textProgram, vsh);
glAttachShader(s_textProgram, fsh);
glLinkProgram(s_textProgram);
glDeleteShader(vsh);
glDeleteShader(fsh);
glGenVertexArrays(1, &s_textVao);
glGenBuffers(1, &s_textVbo);
}
static void drawTextPixel(float x, float y, float size, float r, float g, float b, float *vertexData, int *offset) {
float verts[] = { x, y, r, g, b, x + size, y, r, g, b, x + size, y + size, r, g, b,
x, y, r, g, b, x + size, y + size, r, g, b, x, y + size, r, g, b };
memcpy(&vertexData[*offset], verts, sizeof(verts));
*offset += 30;
}
static void drawChar(char c, float x, float y, float scale, float r, float g, float b, float *vertexData, int *offset) {
int idx = -1;
if (c >= '0' && c <= '9')
idx = c - '0';
else if (c == '.')
idx = 10;
else
return;
const unsigned char *glyph = font8x8[idx];
for (int row = 0; row < 8; row++) {
for (int col = 0; col < 8; col++) {
if (glyph[row] & (1 << col)) {
float px = x + col * scale;
float py = y - row * scale;
drawTextPixel(px, py, scale, r, g, b, vertexData, offset);
}
}
}
}
static void drawText(const char *text, float x, float y, float scale, float r, float g, float b) {
float *vertexData = (float *)malloc(100 * 64 * 6 * 5 * sizeof(float));
int offset = 0;
float cx = x;
while (*text) {
drawChar(*text, cx, y, scale, r, g, b, vertexData, &offset);
cx += 8 * scale;
text++;
}
if (offset > 0) {
glUseProgram(s_textProgram);
glBindVertexArray(s_textVao);
glBindBuffer(GL_ARRAY_BUFFER, s_textVbo);
glBufferData(GL_ARRAY_BUFFER, offset * sizeof(float), vertexData, GL_DYNAMIC_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *)(2 * sizeof(float)));
glEnableVertexAttribArray(1);
glDrawArrays(GL_TRIANGLES, 0, offset / 5);
}
free(vertexData);
}
static void cleanupTextRenderer() {
if (s_textVbo) {
glDeleteBuffers(1, &s_textVbo);
s_textVbo = 0;
}
if (s_textVao) {
glDeleteVertexArrays(1, &s_textVao);
s_textVao = 0;
}
if (s_textProgram) {
glDeleteProgram(s_textProgram);
s_textProgram = 0;
}
}
static void setMesaConfig() {
}
static const char *const vertexShaderSource = R"text(
#version 330 core
out vec2 v_uv;
void main() {
vec2 pos = vec2(
(gl_VertexID == 1) ? 3.0 : -1.0,
(gl_VertexID == 2) ? 3.0 : -1.0
);
v_uv = pos * 0.5 + 0.5;
gl_Position = vec4(pos, 0.0, 1.0);
}
)text";
static const char *const fragmentShaderSource = R"text(
#version 330 core
out vec4 fragColor;
uniform vec2 u_resolution;
uniform float u_time;
uniform sampler2D u_texture1;
uniform sampler2D u_texture2;
uniform sampler2D u_texture3;
uniform sampler2D u_texture4;
const float PI = 3.1416;
const float TAU = 2 * PI;
float displace(vec3 p, sampler2D tex) {
float s = 4.5;
float u = s / TAU * atan(p.y / p.x);
float v = sign(p.z) / TAU *
acos((p.z * p.z * sqrt(s * s + 1) + sqrt(1 - p.z * p.z * s * s)) / (p.z * p.z + 1));
vec2 uv = 2.0 * vec2(u, v);
float disp = texture(tex, uv).r;
return disp * 0.06;
}
mat2 rot2D(float a) {
float sa = sin(a);
float ca = cos(a);
return mat2(ca, sa, -sa, ca);
}
void rotate(inout vec3 p) {
p.xy *= rot2D(sin(u_time * 0.8) * 0.25);
p.yz *= rot2D(sin(u_time * 0.7) * 0.2);
}
float map(vec3 p) {
float dist = length(vec2(length(p.xy) - 0.6, p.z)) - 0.22;
return dist * 0.7;
}
vec3 getNormal(vec3 p) {
vec2 e = vec2(0.01, 0.0);
vec3 n = vec3(map(p)) - vec3(map(p - e.xyy), map(p - e.yxy), map(p - e.yyx));
return normalize(n);
}
float rayMarch(vec3 ro, vec3 rd) {
float dist = 0.0;
for (int i = 0; i < 32; i++) {
vec3 p = ro + dist * rd;
rotate(p);
float hit = map(p);
dist += hit;
// displace
dist -= displace(0.5 * p, u_texture2);
vec2 uv = fract(p.xy * 0.317);
float t39 = texture(u_texture1, uv.yx).r;
float texNoise = texture(u_texture2, uv).r;
float t3 = texture(u_texture3, uv * 0.73).r;
float car = texture(u_texture4, uv * 0.37).r;
dist += (texNoise + t39 + t3 + car) * 1e-4;
dist += (texNoise + t39 + t3 + car) * 1e-5;
dist += (texNoise + t39 + t3 + car) * 1e-6;
if (dist > 100.0 || abs(hit) < 0.0001) break;
}
return dist;
}
vec3 triPlanar(sampler2D tex, vec3 p, vec3 normal) {
normal = abs(normal);
normal = pow(normal, vec3(15));
normal /= normal.x + normal.y + normal.z;
p = p * 0.5 + 0.5;
return (texture(tex, p.xy) * normal.z +
texture(tex, p.xz) * normal.y +
texture(tex, p.yz) * normal.x).rgb;
}
vec3 render(vec2 offset) {
vec2 uv = (4.0 * (gl_FragCoord.xy + offset) - u_resolution.xy) / u_resolution.y;
vec3 col = vec3(0);
vec3 ro = vec3(0, 0, -1.0);
vec3 rd = normalize(vec3(uv, 1.0));
// return to normal rendering path
float dist = rayMarch(ro, rd);
if (dist < 100.0) {
vec3 p = ro + dist * rd;
rotate(p);
col += triPlanar(u_texture1, p * 1.0, getNormal(p));
} else {
float phi = atan(uv.y, uv.x);
float rho = length(uv) + 0.2;
phi += sin(0.3 * rho - 0.5 * u_time);
float h = sin(8.0 * phi) * 0.5 + 0.5;
vec2 st;
st.x = 3.0 * phi / PI;
st.y = u_time * 0.5 + PI / (rho + 0.1 * smoothstep(0.45, 0.5, h));
col += texture(u_texture3, st).rgb;
float occ = smoothstep(0.0, 0.45, h) - smoothstep(0.5, 1.0, h);
col *= 1.0 - 0.45 * occ * rho;
col *= rho;
}
return col;
}
vec3 renderAAx4() {
vec4 e = vec4(0.125, -0.125, 0.375, -0.375);
vec3 colAA = render(e.xz) + render(e.yw) + render(e.wx) + render(e.zy);
return colAA /= 4.0;
}
void main() {
vec3 color = renderAAx4();
fragColor = vec4(color, 1.0);
}
)text";
static GLuint s_program;
static GLuint s_vao, s_vbo;
static GLint resolutionLoc;
static GLuint tex1;
static GLuint tex2;
static GLuint tex3;
static GLuint tex4;
static GLint loc_mdlvMtx, loc_projMtx;
static GLint loc_lightPos, loc_ambient, loc_diffuse, loc_specular, loc_tex_diffuse;
static GLint loc_time;
static u64 s_startTicks;
static u64 s_lastFrameTime = 0;
static float s_fps = 0.0f;
static int s_frameCount = 0;
static u64 s_fpsUpdateTime = 0;
static GLuint createAndCompileShader(GLenum type, const char *source) {
GLint success;
GLchar msg[512];
GLuint handle = glCreateShader(type);
if (!handle) {
TRACE("%u: cannot create shader", type);
return 0;
}
glShaderSource(handle, 1, &source, nullptr);
glCompileShader(handle);
glGetShaderiv(handle, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(handle, sizeof(msg), nullptr, msg);
TRACE("%u: %s\n", type, msg);
glDeleteShader(handle);
return 0;
}
return handle;
}
void frRamSceneInit() {
GLint vsh = createAndCompileShader(GL_VERTEX_SHADER, vertexShaderSource);
GLint fsh = createAndCompileShader(GL_FRAGMENT_SHADER, fragmentShaderSource);
s_program = glCreateProgram();
glAttachShader(s_program, vsh);
glAttachShader(s_program, fsh);
glLinkProgram(s_program);
resolutionLoc = glGetUniformLocation(s_program, "u_resolution");
GLuint tex1Loc = glGetUniformLocation(s_program, "u_texture1");
GLuint tex2Loc = glGetUniformLocation(s_program, "u_texture2");
GLuint tex3Loc = glGetUniformLocation(s_program, "u_texture3");
GLuint tex4Loc = glGetUniformLocation(s_program, "u_texture4");
loc_time = glGetUniformLocation(s_program, "u_time");
GLint success;
glGetProgramiv(s_program, GL_LINK_STATUS, &success);
if (!success) {
char buf[512];
glGetProgramInfoLog(s_program, sizeof(buf), nullptr, buf);
TRACE("Link error: %s", buf);
}
glDeleteShader(vsh);
glDeleteShader(fsh);
loc_mdlvMtx = glGetUniformLocation(s_program, "mdlvMtx");
loc_projMtx = glGetUniformLocation(s_program, "projMtx");
loc_lightPos = glGetUniformLocation(s_program, "lightPos");
loc_ambient = glGetUniformLocation(s_program, "ambient");
loc_diffuse = glGetUniformLocation(s_program, "diffuse");
loc_specular = glGetUniformLocation(s_program, "specular");
loc_tex_diffuse = glGetUniformLocation(s_program, "tex_diffuse");
loc_time = glGetUniformLocation(s_program, "u_time");
struct Vertex {
float position[3];
float color[3];
glm::vec2 texcoord;
glm::vec3 normal;
};
static const Vertex vertices[] = {
{ { -0.5f, -0.5f, 0.0f }, { 1.0f, 0.0f, 0.0f } },
{ { 0.5f, -0.5f, 0.0f }, { 0.0f, 1.0f, 0.0f } },
{ { 0.0f, 0.5f, 0.0f }, { 0.0f, 0.0f, 1.0f } },
};
glGenVertexArrays(1, &s_vao);
glGenBuffers(1, &s_vbo);
glBindVertexArray(s_vao);
glBindBuffer(GL_ARRAY_BUFFER, s_vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void *)offsetof(Vertex, position));
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void *)offsetof(Vertex, color));
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void *)offsetof(Vertex, texcoord));
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void *)offsetof(Vertex, normal));
glEnableVertexAttribArray(3);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
glGenerateMipmap(GL_TEXTURE_2D);
int width, height, nchan;
stbi_set_flip_vertically_on_load(true);
glGenTextures(1, &tex1);
glBindTexture(GL_TEXTURE_2D, tex1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
stbi_uc *img = stbi_load_from_memory((const stbi_uc *)fur_png, fur_png_size, &width, &height, &nchan, 4);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img);
stbi_image_free(img);
glGenTextures(1, &tex2);
glBindTexture(GL_TEXTURE_2D, tex2);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
img = stbi_load_from_memory((const stbi_uc *)noise_png, noise_png_size, &width, &height, &nchan, 4);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img);
stbi_image_free(img);
glGenTextures(1, &tex3);
glBindTexture(GL_TEXTURE_2D, tex3);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
img = stbi_load_from_memory((const stbi_uc *)wall_png, wall_png_size, &width, &height, &nchan, 4);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img);
stbi_image_free(img);
glGenTextures(1, &tex4);
glBindTexture(GL_TEXTURE_2D, tex4);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
img = stbi_load_from_memory((const stbi_uc *)wunk_png, wunk_png_size, &width, &height, &nchan, 4);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img);
stbi_image_free(img);
glUseProgram(s_program);
auto projMtx = glm::perspective(glm::radians(40.0f), 1280.0f / 720.0f, 0.01f, 500.0f);
glUniformMatrix4fv(loc_projMtx, 1, GL_FALSE, glm::value_ptr(projMtx));
glUniform4f(loc_lightPos, 0.0f, 0.0f, 0.5f, 1.0f);
glUniform3f(loc_ambient, 0.1f, 0.1f, 0.1f);
glUniform3f(loc_diffuse, 0.4f, 0.4f, 0.4f);
glUniform4f(loc_specular, 0.5f, 0.5f, 0.5f, 20.0f);
s_startTicks = armGetSystemTick();
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex1);
glUniform1i(tex1Loc, 0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, tex2);
glUniform1i(tex2Loc, 1);
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, tex3);
glUniform1i(tex3Loc, 2);
glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_2D, tex4);
glUniform1i(tex4Loc, 3);
s_lastFrameTime = s_startTicks;
s_fpsUpdateTime = s_startTicks;
s_frameCount = 0;
initTextRenderer();
}
float getTime1() {
u64 elapsed = armGetSystemTick() - s_startTicks;
return (elapsed * 625 / 12) / 2000000000.0;
}
void frRamRender() {
u64 currentTime = armGetSystemTick();
s_frameCount++;
u64 timeSinceUpdate = currentTime - s_fpsUpdateTime;
float secondsSinceUpdate = (timeSinceUpdate * 625.0f / 12.0f) / 1000000000.0f;
if (secondsSinceUpdate >= 0.01f) {
s_fps = s_frameCount / secondsSinceUpdate;
s_frameCount = 0;
s_fpsUpdateTime = currentTime;
}
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glUseProgram(s_program);
glViewport(0, 0, 1280, 720);
glUniform1f(loc_time, getTime1());
glUniform2f(resolutionLoc, 640.0f, 360.0f);
glBindVertexArray(s_vao);
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindVertexArray(0);
char fpsText[32];
snprintf(fpsText, sizeof(fpsText), "%.3f", s_fps);
drawText(fpsText, -0.95f, 0.90f, 0.02f, 1.0f, 0.0f, 0.0f);
}
void frRamExit() {
cleanupTextRenderer();
glDeleteBuffers(1, &s_vbo);
glDeleteVertexArrays(1, &s_vao);
glDeleteProgram(s_program);
}
int frRamMain(int argc, char *argv[]) {
setMesaConfig();
if (!initEgl(nwindowGetDefault()))
return EXIT_FAILURE;
gladLoadGLLoader((GLADloadproc)eglGetProcAddress);
frRamSceneInit();
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
PadState pad;
padInitializeDefault(&pad);
while (appletMainLoop()) {
padUpdate(&pad);
u32 kDown = padGetButtonsDown(&pad);
if (kDown & HidNpadButton_B) {
frRamExit();
deinitEgl();
state = STATE_MENU;
return 0;
}
frRamRender();
eglSwapBuffers(s_display, s_surface);
}
frRamExit();
deinitEgl();
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,4 @@
void frRamSceneInit();
void frRamRender();
void frRamExit();
void getTime1();

View File

@@ -0,0 +1,682 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <switch.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <glad/glad.h>
#define GLM_FORCE_PURE
#include "fur_png.h"
#include "noise_png.h"
#include "sates.h"
#include "stb_image.h"
#include "wall_png.h"
#include <glm/gtc/constants.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/mat4x4.hpp>
#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#ifndef ENABLE_NXLINK
#define TRACE(fmt, ...) ((void)0)
#else
#include <unistd.h>
#define TRACE(fmt, ...) printf("%s: " fmt "\n", __PRETTY_FUNCTION__, ##__VA_ARGS__)
static int s_nxlinkSock = -1;
static void initNxLink() {
if (R_FAILED(socketInitializeDefault()))
return;
s_nxlinkSock = nxlinkStdio();
if (s_nxlinkSock >= 0)
TRACE("printf output now goes to nxlink server");
else
socketExit();
}
static void deinitNxLink() {
if (s_nxlinkSock >= 0) {
close(s_nxlinkSock);
socketExit();
s_nxlinkSock = -1;
}
}
extern "C" void userAppInit() {
initNxLink();
}
extern "C" void userAppExit() {
deinitNxLink();
}
#endif
static EGLDisplay s_display;
static EGLContext s_context;
static EGLSurface s_surface;
static bool initEgl(NWindow *win) {
s_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (!s_display) {
TRACE("Could not connect to display! error: %d", eglGetError());
goto _fail0;
}
eglInitialize(s_display, nullptr, nullptr);
if (eglBindAPI(EGL_OPENGL_API) == EGL_FALSE) {
TRACE("Could not set API! error: %d", eglGetError());
goto _fail1;
}
EGLConfig config;
EGLint numConfigs;
static const EGLint framebufferAttributeList[] = { EGL_RENDERABLE_TYPE,
EGL_OPENGL_BIT,
EGL_RED_SIZE,
8,
EGL_GREEN_SIZE,
8,
EGL_BLUE_SIZE,
8,
EGL_ALPHA_SIZE,
8,
EGL_DEPTH_SIZE,
24,
EGL_STENCIL_SIZE,
8,
EGL_NONE };
eglChooseConfig(s_display, framebufferAttributeList, &config, 1, &numConfigs);
if (numConfigs == 0) {
TRACE("No config found! error: %d", eglGetError());
goto _fail1;
}
s_surface = eglCreateWindowSurface(s_display, config, win, nullptr);
if (!s_surface) {
TRACE("Surface creation failed! error: %d", eglGetError());
goto _fail1;
}
static const EGLint contextAttributeList[] = { EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR,
EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR,
EGL_CONTEXT_MAJOR_VERSION_KHR,
4,
EGL_CONTEXT_MINOR_VERSION_KHR,
3,
EGL_NONE };
s_context = eglCreateContext(s_display, config, EGL_NO_CONTEXT, contextAttributeList);
if (!s_context) {
TRACE("Context creation failed! error: %d", eglGetError());
goto _fail2;
}
eglMakeCurrent(s_display, s_surface, s_surface, s_context);
eglSwapInterval(s_display, 0);
return true;
_fail2:
eglDestroySurface(s_display, s_surface);
s_surface = nullptr;
_fail1:
eglTerminate(s_display);
s_display = nullptr;
_fail0:
return false;
}
static void deinitEgl() {
if (s_display) {
eglMakeCurrent(s_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
if (s_context) {
eglDestroyContext(s_display, s_context);
s_context = nullptr;
}
if (s_surface) {
eglDestroySurface(s_display, s_surface);
s_surface = nullptr;
}
eglTerminate(s_display);
s_display = nullptr;
}
}
static const char *const text_vs = R"text(
#version 330 core
layout(location=0) in vec2 inPos;
layout(location=1) in vec3 inColor;
out vec3 color;
void main() {
color = inColor;
gl_Position = vec4(inPos, 0.0, 1.0);
}
)text";
static const char *const text_fs = R"text(
#version 330 core
in vec3 color;
out vec4 fragColor;
void main() {
fragColor = vec4(color, 1.0);
}
)text";
static GLuint s_textProgram = 0;
static GLuint s_textVao = 0;
static GLuint s_textVbo = 0;
static const unsigned char font8x8[11][8] = { { 0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00 }, { 0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00 },
{ 0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00 }, { 0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00 },
{ 0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00 }, { 0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00 },
{ 0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00 }, { 0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00 },
{ 0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00 }, { 0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00 },
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00 } };
GLuint createAndCompileShader(GLenum type, const char *source);
static void initTextRenderer() {
GLuint vsh = createAndCompileShader(GL_VERTEX_SHADER, text_vs);
GLuint fsh = createAndCompileShader(GL_FRAGMENT_SHADER, text_fs);
s_textProgram = glCreateProgram();
glAttachShader(s_textProgram, vsh);
glAttachShader(s_textProgram, fsh);
glLinkProgram(s_textProgram);
glDeleteShader(vsh);
glDeleteShader(fsh);
glGenVertexArrays(1, &s_textVao);
glGenBuffers(1, &s_textVbo);
}
static void drawTextPixel(float x, float y, float size, float r, float g, float b, float *vertexData, int *offset) {
float verts[] = { x, y, r, g, b, x + size, y, r, g, b, x + size, y + size, r, g, b,
x, y, r, g, b, x + size, y + size, r, g, b, x, y + size, r, g, b };
memcpy(&vertexData[*offset], verts, sizeof(verts));
*offset += 30;
}
static void drawChar(char c, float x, float y, float scale, float r, float g, float b, float *vertexData, int *offset) {
int idx = -1;
if (c >= '0' && c <= '9')
idx = c - '0';
else if (c == '.')
idx = 10;
else
return;
const unsigned char *glyph = font8x8[idx];
for (int row = 0; row < 8; row++) {
for (int col = 0; col < 8; col++) {
if (glyph[row] & (1 << col)) {
float px = x + col * scale;
float py = y - row * scale;
drawTextPixel(px, py, scale, r, g, b, vertexData, offset);
}
}
}
}
static void drawText(const char *text, float x, float y, float scale, float r, float g, float b) {
float *vertexData = (float *)malloc(100 * 64 * 6 * 5 * sizeof(float));
int offset = 0;
float cx = x;
while (*text) {
drawChar(*text, cx, y, scale, r, g, b, vertexData, &offset);
cx += 8 * scale;
text++;
}
if (offset > 0) {
glUseProgram(s_textProgram);
glBindVertexArray(s_textVao);
glBindBuffer(GL_ARRAY_BUFFER, s_textVbo);
glBufferData(GL_ARRAY_BUFFER, offset * sizeof(float), vertexData, GL_DYNAMIC_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *)(2 * sizeof(float)));
glEnableVertexAttribArray(1);
glDrawArrays(GL_TRIANGLES, 0, offset / 5);
}
free(vertexData);
}
static void cleanupTextRenderer() {
if (s_textVbo) {
glDeleteBuffers(1, &s_textVbo);
s_textVbo = 0;
}
if (s_textVao) {
glDeleteVertexArrays(1, &s_textVao);
s_textVao = 0;
}
if (s_textProgram) {
glDeleteProgram(s_textProgram);
s_textProgram = 0;
}
}
static void setMesaConfig() {
setenv("MESA_NO_ERROR", "1", 1);
setenv("NV50_PROG_CHIPSET", "0", 1);
}
static const char *const vertexShaderSource = R"text(
#version 330 core
out vec2 v_uv;
void main() {
vec2 pos = vec2(
(gl_VertexID == 1) ? 3.0 : -1.0,
(gl_VertexID == 2) ? 3.0 : -1.0
);
v_uv = pos * 0.5 + 0.5;
gl_Position = vec4(pos, 0.0, 1.0);
}
)text";
static const char *const fragmentShaderSource = R"text(
#version 330 core
out vec4 fragColor;
uniform vec2 u_resolution;
uniform float u_time;
uniform sampler2D u_texture1;
uniform sampler2D u_texture2;
uniform sampler2D u_texture3;
const float PI = 3.1416;
const float TAU = 2 * PI;
float displace(vec3 p, sampler2D tex) {
float s = 4.5;
float u = s / TAU * atan(p.y / p.x);
float v = sign(p.z) / TAU *
acos((p.z * p.z * sqrt(s * s + 1) + sqrt(1 - p.z * p.z * s * s)) / (p.z * p.z + 1));
vec2 uv = 2.0 * vec2(u, v);
float disp = texture(tex, uv).r;
return disp * 0.06;
}
mat2 rot2D(float a) {
float sa = sin(a);
float ca = cos(a);
return mat2(ca, sa, -sa, ca);
}
void rotate(inout vec3 p) {
p.xy *= rot2D(sin(u_time * 0.8) * 0.25);
p.yz *= rot2D(sin(u_time * 0.7) * 0.2);
}
float map(vec3 p) {
float dist = length(vec2(length(p.xy) - 0.6, p.z)) - 0.22;
return dist * 0.7;
}
vec3 getNormal(vec3 p) {
vec2 e = vec2(0.01, 0.0);
vec3 n = vec3(map(p)) - vec3(map(p - e.xyy), map(p - e.yxy), map(p - e.yyx));
return normalize(n);
}
float rayMarch(vec3 ro, vec3 rd) {
float dist = 0.0;
for (int i = 0; i < 48; i++) {
vec3 p = ro + dist * rd;
rotate(p);
float hit = map(p);
dist += hit;
// displace
dist -= displace(0.5 * p, u_texture2);
// Saturate FP32
vec3 q = p;
q = q * 1.37 + 0.13;
q = q * q - 0.17;
q = q * 0.91 + q.yzx * 0.09;
dist += dot(q, q) * 1e-5;
// Register stress
vec4 r0 = vec4(p, dist);
vec4 r1 = sin(r0 * 3.1);
vec4 r2 = cos(r1 * 2.7);
vec4 r3 = r2 * r1;
vec4 r4 = normalize(r3);
dist += dot(r4, vec4(1e-5));
if (dist > 100.0 || abs(hit) < 0.0001) break;
}
return dist;
}
vec3 triPlanar(sampler2D tex, vec3 p, vec3 normal) {
normal = abs(normal);
normal = pow(normal, vec3(15));
normal /= normal.x + normal.y + normal.z;
p = p * 0.5 + 0.5;
return (texture(tex, p.xy) * normal.z +
texture(tex, p.xz) * normal.y +
texture(tex, p.yz) * normal.x).rgb;
}
vec3 render(vec2 offset) {
vec2 uv = (2.0 * (gl_FragCoord.xy + offset) - u_resolution.xy) / u_resolution.y;
vec3 col = vec3(0);
vec3 ro = vec3(0, 0, -1.0);
vec3 rd = normalize(vec3(uv, 1.0));
// return to normal rendering path
float dist = rayMarch(ro, rd);
if (dist < 100.0) {
vec3 p = ro + dist * rd;
rotate(p);
col += triPlanar(u_texture1, p * 1.0, getNormal(p));
} else {
float phi = atan(uv.y, uv.x);
float rho = length(uv) + 0.2;
phi += sin(0.3 * rho - 0.5 * u_time);
float h = sin(8.0 * phi) * 0.5 + 0.5;
vec2 st;
st.x = 3.0 * phi / PI;
st.y = u_time * 0.5 + PI / (rho + 0.1 * smoothstep(0.45, 0.5, h));
col += texture(u_texture3, st).rgb;
float occ = smoothstep(0.0, 0.45, h) - smoothstep(0.5, 1.0, h);
col *= 1.0 - 0.45 * occ * rho;
col *= rho;
}
return col;
}
vec3 renderAAx4() {
vec4 e = vec4(0.125, -0.125, 0.375, -0.375);
vec3 colAA = render(e.xz) + render(e.yw) + render(e.wx) + render(e.zy);
return colAA /= 4.0;
}
void main() {
vec3 color = renderAAx4();
fragColor = vec4(color, 1.0);
}
)text";
static GLuint s_program;
static GLuint s_vao, s_vbo;
static GLint resolutionLoc;
static GLuint tex1;
static GLuint tex2;
static GLuint tex3;
static GLint loc_mdlvMtx, loc_projMtx;
static GLint loc_lightPos, loc_ambient, loc_diffuse, loc_specular, loc_tex_diffuse;
static GLint loc_time;
static u64 s_startTicks;
static u64 s_lastFrameTime = 0;
static float s_fps = 0.0f;
static int s_frameCount = 0;
static u64 s_fpsUpdateTime = 0;
GLuint createAndCompileShader(GLenum type, const char *source) {
GLint success;
GLchar msg[512];
GLuint handle = glCreateShader(type);
if (!handle) {
TRACE("%u: cannot create shader", type);
return 0;
}
glShaderSource(handle, 1, &source, nullptr);
glCompileShader(handle);
glGetShaderiv(handle, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(handle, sizeof(msg), nullptr, msg);
TRACE("%u: %s\n", type, msg);
glDeleteShader(handle);
return 0;
}
return handle;
}
void frSceneInit() {
GLint vsh = createAndCompileShader(GL_VERTEX_SHADER, vertexShaderSource);
GLint fsh = createAndCompileShader(GL_FRAGMENT_SHADER, fragmentShaderSource);
s_program = glCreateProgram();
glAttachShader(s_program, vsh);
glAttachShader(s_program, fsh);
glLinkProgram(s_program);
resolutionLoc = glGetUniformLocation(s_program, "u_resolution");
GLuint tex1Loc = glGetUniformLocation(s_program, "u_texture1");
GLuint tex2Loc = glGetUniformLocation(s_program, "u_texture2");
GLuint tex3Loc = glGetUniformLocation(s_program, "u_texture3");
loc_time = glGetUniformLocation(s_program, "u_time");
GLint success;
glGetProgramiv(s_program, GL_LINK_STATUS, &success);
if (!success) {
char buf[512];
glGetProgramInfoLog(s_program, sizeof(buf), nullptr, buf);
TRACE("Link error: %s", buf);
}
glDeleteShader(vsh);
glDeleteShader(fsh);
loc_mdlvMtx = glGetUniformLocation(s_program, "mdlvMtx");
loc_projMtx = glGetUniformLocation(s_program, "projMtx");
loc_lightPos = glGetUniformLocation(s_program, "lightPos");
loc_ambient = glGetUniformLocation(s_program, "ambient");
loc_diffuse = glGetUniformLocation(s_program, "diffuse");
loc_specular = glGetUniformLocation(s_program, "specular");
loc_tex_diffuse = glGetUniformLocation(s_program, "tex_diffuse");
loc_time = glGetUniformLocation(s_program, "u_time");
struct Vertex {
float position[3];
float color[3];
glm::vec2 texcoord;
glm::vec3 normal;
};
static const Vertex vertices[] = {
{ { -0.5f, -0.5f, 0.0f }, { 1.0f, 0.0f, 0.0f } },
{ { 0.5f, -0.5f, 0.0f }, { 0.0f, 1.0f, 0.0f } },
{ { 0.0f, 0.5f, 0.0f }, { 0.0f, 0.0f, 1.0f } },
};
glGenVertexArrays(1, &s_vao);
glGenBuffers(1, &s_vbo);
glBindVertexArray(s_vao);
glBindBuffer(GL_ARRAY_BUFFER, s_vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void *)offsetof(Vertex, position));
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void *)offsetof(Vertex, color));
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void *)offsetof(Vertex, texcoord));
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void *)offsetof(Vertex, normal));
glEnableVertexAttribArray(3);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
glGenerateMipmap(GL_TEXTURE_2D);
int width, height, nchan;
stbi_set_flip_vertically_on_load(true);
glGenTextures(1, &tex1);
glBindTexture(GL_TEXTURE_2D, tex1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
stbi_uc *img = stbi_load_from_memory((const stbi_uc *)fur_png, fur_png_size, &width, &height, &nchan, 4);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img);
stbi_image_free(img);
glGenTextures(1, &tex2);
glBindTexture(GL_TEXTURE_2D, tex2);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
img = stbi_load_from_memory((const stbi_uc *)noise_png, noise_png_size, &width, &height, &nchan, 4);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img);
stbi_image_free(img);
glGenTextures(1, &tex3);
glBindTexture(GL_TEXTURE_2D, tex3);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
img = stbi_load_from_memory((const stbi_uc *)wall_png, wall_png_size, &width, &height, &nchan, 4);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img);
stbi_image_free(img);
glUseProgram(s_program);
auto projMtx = glm::perspective(glm::radians(40.0f), 1280.0f / 720.0f, 0.01f, 1000.0f);
glUniformMatrix4fv(loc_projMtx, 1, GL_FALSE, glm::value_ptr(projMtx));
glUniform4f(loc_lightPos, 0.0f, 0.0f, 0.5f, 1.0f);
glUniform3f(loc_ambient, 0.1f, 0.1f, 0.1f);
glUniform3f(loc_diffuse, 0.4f, 0.4f, 0.4f);
glUniform4f(loc_specular, 0.5f, 0.5f, 0.5f, 20.0f);
s_startTicks = armGetSystemTick();
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex1);
glUniform1i(tex1Loc, 0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, tex2);
glUniform1i(tex2Loc, 1);
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, tex3);
glUniform1i(tex3Loc, 2);
s_lastFrameTime = s_startTicks;
s_fpsUpdateTime = s_startTicks;
s_frameCount = 0;
initTextRenderer();
}
float getTime() {
u64 elapsed = armGetSystemTick() - s_startTicks;
return (elapsed * 625 / 12) / 2000000000.0;
}
void frRender() {
u64 currentTime = armGetSystemTick();
s_frameCount++;
u64 timeSinceUpdate = currentTime - s_fpsUpdateTime;
float secondsSinceUpdate = (timeSinceUpdate * 625.0f / 12.0f) / 1000000000.0f;
if (secondsSinceUpdate >= 0.01f) {
s_fps = s_frameCount / secondsSinceUpdate;
s_frameCount = 0;
s_fpsUpdateTime = currentTime;
}
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glUseProgram(s_program);
glUniform1f(loc_time, getTime());
glUniform2f(resolutionLoc, 1280.0f, 720.0f);
glBindVertexArray(s_vao);
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindVertexArray(0);
char fpsText[32];
snprintf(fpsText, sizeof(fpsText), "%.3f", s_fps);
drawText(fpsText, -0.95f, 0.90f, 0.02f, 1.0f, 0.0f, 0.0f);
}
void frExit() {
cleanupTextRenderer();
glDeleteBuffers(1, &s_vbo);
glDeleteVertexArrays(1, &s_vao);
glDeleteProgram(s_program);
}
int frMain(int argc, char *argv[]) {
setMesaConfig();
if (!initEgl(nwindowGetDefault()))
return EXIT_FAILURE;
gladLoadGLLoader((GLADloadproc)eglGetProcAddress);
frSceneInit();
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
PadState pad;
padInitializeDefault(&pad);
while (appletMainLoop()) {
padUpdate(&pad);
u32 kDown = padGetButtonsDown(&pad);
if (kDown & HidNpadButton_B) {
frExit();
deinitEgl();
state = STATE_MENU;
return 0;
}
frRender();
eglSwapBuffers(s_display, s_surface);
}
frExit();
deinitEgl();
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,4 @@
void frSceneInit();
void frRender();
void frExit();
void getTime();

View File

@@ -0,0 +1,239 @@
#include <atomic>
#include <switch.h>
#include <thread>
#include "run_furmark.h"
#include "sates.h"
#include <EGL/egl.h>
#include <glad/glad.h>
extern void frSceneInit();
extern void frRender();
extern void frExit();
extern void frRamSceneInit();
extern void frRamRender();
extern void frRamExit();
extern void GPUPTSceneInit();
extern void GPUPTRender();
extern void GPUPTExit();
extern void BHRTSceneInit();
extern void BHRTRender();
extern void BHRTExit();
extern void CPURTSceneinit();
extern void CPURTRender();
extern void CPURTExit();
extern void CPURBSceneinit();
extern void CPURBRender();
extern void CPURBExit();
AppState state = STATE_MENU;
namespace {
std::thread g_thread;
std::atomic<bool> g_stop{ false };
std::atomic<bool> g_running{ false };
EGLDisplay s_dpy = EGL_NO_DISPLAY;
EGLContext s_ctx = EGL_NO_CONTEXT;
EGLSurface s_surf = EGL_NO_SURFACE;
bool eglUp() {
s_dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (s_dpy == EGL_NO_DISPLAY)
return false;
if (!eglInitialize(s_dpy, nullptr, nullptr))
return false;
if (!eglBindAPI(EGL_OPENGL_API))
return false;
const EGLint cfgAttr[] = { EGL_RENDERABLE_TYPE,
EGL_OPENGL_BIT,
EGL_RED_SIZE,
8,
EGL_GREEN_SIZE,
8,
EGL_BLUE_SIZE,
8,
EGL_ALPHA_SIZE,
8,
EGL_DEPTH_SIZE,
24,
EGL_STENCIL_SIZE,
8,
EGL_NONE };
EGLConfig cfg;
EGLint n = 0;
if (!eglChooseConfig(s_dpy, cfgAttr, &cfg, 1, &n) || n == 0)
return false;
const EGLint ctxAttr[] = { EGL_CONTEXT_MAJOR_VERSION, 4, EGL_CONTEXT_MINOR_VERSION, 3, EGL_NONE };
s_ctx = eglCreateContext(s_dpy, cfg, EGL_NO_CONTEXT, ctxAttr);
if (s_ctx == EGL_NO_CONTEXT)
return false;
s_surf = EGL_NO_SURFACE;
if (eglMakeCurrent(s_dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, s_ctx) != EGL_TRUE)
return false;
return true;
}
void eglDown() {
if (s_dpy) {
eglMakeCurrent(s_dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
if (s_ctx)
eglDestroyContext(s_dpy, s_ctx);
if (s_surf != EGL_NO_SURFACE)
eglDestroySurface(s_dpy, s_surf);
eglTerminate(s_dpy);
}
s_ctx = EGL_NO_CONTEXT;
s_surf = EGL_NO_SURFACE;
s_dpy = EGL_NO_DISPLAY;
eglReleaseThread();
}
void sceneInit(int which) {
switch (which) {
case 0:
frSceneInit();
break;
case 1:
frRamSceneInit();
break;
case 2:
GPUPTSceneInit();
break;
case 3:
BHRTSceneInit();
break;
case 4:
CPURTSceneinit();
break;
case 5:
CPURBSceneinit();
break;
}
}
void sceneRender(int which) {
switch (which) {
case 0:
frRender();
break;
case 1:
frRamRender();
break;
case 2:
GPUPTRender();
break;
case 3:
BHRTRender();
break;
case 4:
CPURTRender();
break;
case 5:
CPURBRender();
break;
}
}
void sceneExit(int which) {
switch (which) {
case 0:
frExit();
break;
case 1:
frRamExit();
break;
case 2:
GPUPTExit();
break;
case 3:
BHRTExit();
break;
case 4:
CPURTExit();
break;
case 5:
CPURBExit();
break;
}
}
void worker(int which) {
if (!eglUp()) {
eglDown();
g_running.store(false);
return;
}
gladLoadGLLoader((GLADloadproc)eglGetProcAddress);
GLuint fbo = 0, rbColor = 0, rbDepth = 0;
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glGenRenderbuffers(1, &rbColor);
glBindRenderbuffer(GL_RENDERBUFFER, rbColor);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, 1280, 720);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbColor);
glGenRenderbuffers(1, &rbDepth);
glBindRenderbuffer(GL_RENDERBUFFER, rbDepth);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 1280, 720);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbDepth);
glViewport(0, 0, 1280, 720);
sceneInit(which);
const u64 frameNs = 16666666ULL;
while (!g_stop.load()) {
u64 t0 = armGetSystemTick();
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glViewport(0, 0, 1280, 720);
sceneRender(which);
GLsync fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
glFlush();
while (!g_stop.load()) {
GLenum r = glClientWaitSync(fence, 0, 0);
if (r != GL_TIMEOUT_EXPIRED)
break;
svcSleepThread(500000ULL);
}
glDeleteSync(fence);
u64 dt = armTicksToNs(armGetSystemTick() - t0);
if (dt < frameNs)
svcSleepThread(frameNs - dt);
}
sceneExit(which);
glDeleteFramebuffers(1, &fbo);
glDeleteRenderbuffers(1, &rbColor);
glDeleteRenderbuffers(1, &rbDepth);
eglDown();
g_running.store(false);
}
} // namespace
extern "C" void run_furmark_start(int which) {
if (g_running.load())
return;
if (g_thread.joinable())
g_thread.join();
g_stop.store(false);
g_running.store(true);
appletSetAutoSleepDisabled(true);
g_thread = std::thread(worker, which);
}
extern "C" void run_furmark_stop(void) {
g_stop.store(true);
if (g_thread.joinable())
g_thread.join();
g_running.store(false);
appletSetAutoSleepDisabled(false);
}
extern "C" int run_furmark_running(void) {
return g_running.load() ? 1 : 0;
}

View File

@@ -0,0 +1,13 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
void run_furmark_start(int which);
void run_furmark_stop(void);
int run_furmark_running(void);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,3 @@
enum AppState { STATE_MENU, STATE_FURMARK, STATE_FURMARK_RB, STATE_GPU_PT, STATE_BH_RT, STATE_CPU_RT, STATE_CPU_RB };
extern AppState state;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,314 @@
#pragma once
#include <arm_neon.h>
struct vec2f {
float x, y;
inline vec2f() {
}
inline vec2f(float x_, float y_) : x(x_), y(y_) {
}
};
struct vec3x4 {
float32x4_t x, y, z;
};
inline float32x4_t dot(const vec3x4 &a, const vec3x4 &b) {
float32x4_t acc = vmulq_f32(a.x, b.x);
acc = vfmaq_f32(acc, a.y, b.y);
acc = vfmaq_f32(acc, a.z, b.z);
return acc;
}
inline vec3x4 normalize(vec3x4 v) {
float32x4_t len2 = vfmaq_f32(vfmaq_f32(vmulq_f32(v.x, v.x), v.y, v.y), v.z, v.z);
float32x4_t invLen = vrsqrteq_f32(len2);
invLen = vmulq_f32(vrsqrtsq_f32(vmulq_f32(len2, invLen), invLen), invLen);
return { vmulq_f32(v.x, invLen), vmulq_f32(v.y, invLen), vmulq_f32(v.z, invLen) };
}
inline vec3x4 reflect(const vec3x4 &v, const vec3x4 &n) {
float32x4_t two_d = vmulq_n_f32(dot(v, n), 2.0f);
vec3x4 r;
r.x = vfmsq_f32(v.x, n.x, two_d);
r.y = vfmsq_f32(v.y, n.y, two_d);
r.z = vfmsq_f32(v.z, n.z, two_d);
return r;
}
struct vec3f {
float x, y, z;
inline vec3f() {
}
inline vec3f(float v) : x(v), y(v), z(v) {
}
inline vec3f(float x_, float y_, float z_) : x(x_), y(y_), z(z_) {
}
};
inline vec3f operator+(const vec3f &a, const vec3f &b) {
return vec3f(a.x + b.x, a.y + b.y, a.z + b.z);
}
inline vec3f operator-(const vec3f &a, const vec3f &b) {
return vec3f(a.x - b.x, a.y - b.y, a.z - b.z);
}
inline vec3f operator-(const vec3f &v) {
return vec3f(-v.x, -v.y, -v.z);
}
inline vec3f &operator*=(vec3f &a, const vec3f &b) {
a.x *= b.x;
a.y *= b.y;
a.z *= b.z;
return a;
}
inline vec3f &operator*=(vec3f &a, float b) {
a.x *= b;
a.y *= b;
a.z *= b;
return a;
}
inline vec3f operator*(const vec3f &a, float b) {
return vec3f(a.x * b, a.y * b, a.z * b);
}
inline vec3f operator*(float b, const vec3f &a) {
return a * b;
}
inline vec3f operator*(const vec3f &a, const vec3f &b) {
return vec3f(a.x * b.x, a.y * b.y, a.z * b.z);
}
inline vec3f &operator+=(vec3f &a, const vec3f &b) {
a.x += b.x;
a.y += b.y;
a.z += b.z;
return a;
}
inline vec3f operator/(const vec3f &a, float b) {
float inv = 1.0f / b;
return vec3f(a.x * inv, a.y * inv, a.z * inv);
}
inline vec3f &operator/=(vec3f &a, float b) {
float inv = 1.0f / b;
a.x *= inv;
a.y *= inv;
a.z *= inv;
return a;
}
inline float dot(const vec3f &a, const vec3f &b) {
return a.x * b.x + a.y * b.y + a.z * b.z;
}
inline vec3f normalize(const vec3f &v) {
float len2 = dot(v, v);
float invLen = 1.0f / sqrtf(len2 + 1e-20f);
return v * invLen;
}
inline vec3f cross(const vec3f &a, const vec3f &b) {
return vec3f(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x);
}
static inline float32x4_t fastRecip(float32x4_t x) {
float32x4_t r = vrecpeq_f32(x);
r = vmulq_f32(vrecpsq_f32(x, r), r);
r = vmulq_f32(vrecpsq_f32(x, r), r);
return r;
}
static inline float32x4_t fastRsqrt(float32x4_t x) {
float32x4_t r = vrsqrteq_f32(x);
r = vmulq_f32(r, vrsqrtsq_f32(vmulq_f32(x, r), r));
r = vmulq_f32(r, vrsqrtsq_f32(vmulq_f32(x, r), r));
return r;
}
static inline vec3x4 normalizeFast(const vec3x4 &v) {
float32x4_t len2 = vfmaq_f32(vmulq_f32(v.z, v.z), v.y, v.y);
len2 = vfmaq_f32(len2, v.x, v.x);
float32x4_t invLen = fastRsqrt(len2);
vec3x4 out;
out.x = vmulq_f32(v.x, invLen);
out.y = vmulq_f32(v.y, invLen);
out.z = vmulq_f32(v.z, invLen);
return out;
}
inline uint32x4_t xorshift4(uint32x4_t &s) {
s = veorq_u32(s, vshlq_n_u32(s, 13));
s = veorq_u32(s, vshrq_n_u32(s, 17));
s = veorq_u32(s, vshlq_n_u32(s, 5));
return s;
}
inline float32x4_t toFloat01(uint32x4_t x) {
uint32x4_t m = vandq_u32(x, vdupq_n_u32(0xFFFFFF));
return vmulq_n_f32(vcvtq_f32_u32(m), 1.0f / float(0xFFFFFF));
}
inline float32x4_t sin4(float32x4_t phi) {
const float32x4_t pi = vdupq_n_f32(3.14159265f);
const float32x4_t pi_2 = vdupq_n_f32(1.57079633f);
// const float32x4_t two_pi = vdupq_n_f32(6.28318531f);
uint32x4_t neg = vcgeq_f32(phi, pi);
float32x4_t xr = vbslq_f32(neg, vsubq_f32(phi, pi), phi);
uint32x4_t fold = vcgtq_f32(xr, pi_2);
xr = vbslq_f32(fold, vsubq_f32(pi, xr), xr);
float32x4_t x2 = vmulq_f32(xr, xr);
float32x4_t r = vfmsq_f32(vdupq_n_f32(1.0f / 120.0f), x2, vdupq_n_f32(1.0f / 5040.0f));
r = vfmsq_f32(vdupq_n_f32(1.0f / 6.0f), x2, r);
r = vfmsq_f32(vdupq_n_f32(1.0f), x2, r);
r = vmulq_f32(xr, r);
uint32x4_t signBit = vshlq_n_u32(neg, 31);
return vreinterpretq_f32_u32(veorq_u32(vreinterpretq_u32_f32(r), signBit));
}
inline float32x4_t cos4(float32x4_t phi) {
float32x4_t shifted = vaddq_f32(phi, vdupq_n_f32(1.57079633f));
float32x4_t two_pi = vdupq_n_f32(6.28318531f);
uint32x4_t wrap = vcgeq_f32(shifted, two_pi);
shifted = vbslq_f32(wrap, vsubq_f32(shifted, two_pi), shifted);
return sin4(shifted);
}
inline void buildONB4(const vec3x4 &n, vec3x4 &t, vec3x4 &b) {
const float32x4_t one = vdupq_n_f32(1.0f);
const float32x4_t neg = vdupq_n_f32(-1.0f);
uint32x4_t pos = vcgeq_f32(n.z, vdupq_n_f32(0.0f));
float32x4_t sgn = vbslq_f32(pos, one, neg);
float32x4_t denom = vaddq_f32(sgn, n.z);
float32x4_t r = vrecpeq_f32(denom);
r = vmulq_f32(vrecpsq_f32(denom, r), r);
r = vmulq_f32(vrecpsq_f32(denom, r), r);
float32x4_t a = vnegq_f32(r);
float32x4_t bc = vmulq_f32(vmulq_f32(n.x, n.y), a);
t.x = vaddq_f32(one, vmulq_f32(sgn, vmulq_f32(vmulq_f32(n.x, n.y), a)));
t.y = vmulq_f32(sgn, bc);
t.z = vnegq_f32(vmulq_f32(sgn, n.x));
b.x = bc;
b.y = vaddq_f32(sgn, vmulq_f32(vmulq_f32(n.y, n.y), a));
b.z = vnegq_f32(n.y);
}
inline vec3x4 cosineSampleHemisphere4(const vec3x4 &n, uint32x4_t &rng) {
float32x4_t u1 = toFloat01(xorshift4(rng));
float32x4_t u2 = toFloat01(xorshift4(rng));
float32x4_t r = vsqrtq_f32(u1);
float32x4_t phi = vmulq_n_f32(u2, 6.28318531f);
float32x4_t lx = vmulq_f32(r, cos4(phi));
float32x4_t ly = vmulq_f32(r, sin4(phi));
float32x4_t lz = vsqrtq_f32(vmaxq_f32(vsubq_f32(vdupq_n_f32(1.0f), u1), vdupq_n_f32(0.0)));
vec3x4 t, b;
buildONB4(n, t, b);
vec3x4 d;
d.x = vaddq_f32(vaddq_f32(vmulq_f32(lx, t.x), vmulq_f32(ly, b.x)), vmulq_f32(lz, n.x));
d.y = vaddq_f32(vaddq_f32(vmulq_f32(lx, t.y), vmulq_f32(ly, b.y)), vmulq_f32(lz, n.y));
d.z = vaddq_f32(vaddq_f32(vmulq_f32(lx, t.z), vmulq_f32(ly, b.z)), vmulq_f32(lz, n.z));
return d;
}
inline float neon_dot3(float32x4_t a, float32x4_t b) {
float32x4_t mul = vmulq_f32(a, b);
float32x2_t lo = vget_low_f32(mul);
float32x2_t hi = vget_high_f32(mul);
float32x2_t sum = vpadd_f32(lo, lo);
return vget_lane_f32(vadd_f32(sum, hi), 0);
}
inline float32x4_t neon_cross3(float32x4_t a, float32x4_t b) {
float32x4_t a_yzx = __builtin_shufflevector(a, a, 1, 2, 0, 3);
float32x4_t a_zxy = __builtin_shufflevector(a, a, 2, 0, 1, 3);
float32x4_t b_yzx = __builtin_shufflevector(b, b, 1, 2, 0, 3);
float32x4_t b_zxy = __builtin_shufflevector(b, b, 2, 0, 1, 3);
return vsubq_f32(vmulq_f32(a_yzx, b_zxy), vmulq_f32(a_zxy, b_yzx));
}
inline float32x4_t neon_normalize3(float32x4_t v) {
float32x2_t lenSq = vdup_n_f32(neon_dot3(v, v));
float32x2_t est = vrsqrte_f32(lenSq);
est = vmul_f32(est, vrsqrts_f32(vmul_f32(lenSq, est), est));
return vmulq_f32(v, vcombine_f32(est, est));
}
inline void neon_store3(float *p, float32x4_t v) {
vst1q_lane_f32(p + 0, v, 0);
vst1q_lane_f32(p + 1, v, 1);
vst1q_lane_f32(p + 2, v, 2);
}
inline float32x4_t vpermute(float32x4_t x) {
float32x4_t v34 = vdupq_n_f32(34.0f);
float32x4_t v1 = vdupq_n_f32(1.0f);
float32x4_t v289 = vdupq_n_f32(289.0f);
float32x4_t inv289 = vdupq_n_f32(1.0f / 289.0f);
float32x4_t res = vmlaq_f32(v1, x, v34);
res = vmulq_f32(res, x);
float32x4_t quotient = vmulq_f32(res, inv289);
float32x4_t floored = vrndmq_f32(quotient);
res = vmlsq_f32(res, floored, v289);
return res;
}
inline float32x4_t vtaylorInvSqrt(float32x4_t x) {
float32x4_t c1 = vdupq_n_f32(1.79284291400159f);
float32x4_t c2 = vdupq_n_f32(0.85373472095314f);
return vmlsq_f32(c1, x, c2);
}
static inline void fastSinCos(float angle, float *s, float *c) {
const float INV_TWO_PI = 0.15915494309f;
const float TWO_PI = 6.28318530718f;
// const float PI = 3.14159265359f;
float x = angle - TWO_PI * floorf(angle * INV_TWO_PI + 0.5f);
float x2 = x * x;
float x3 = x2 * x;
float x5 = x3 * x2;
float x7 = x5 * x3;
*s = x + x3 * (-0.16666667163f) + x5 * (0.00833333842f) + x7 * (-0.00019840680f);
float x4 = x2 * x2;
float x6 = x4 * x2;
*c = 1.0f + x2 * (-0.49999997020f) + x4 * (0.04166664556f) + x6 * (-0.00138873165f);
}