Skip to content

Commit

Permalink
Support GXM
Browse files Browse the repository at this point in the history
  • Loading branch information
xfangfang committed Nov 10, 2024
1 parent f93799c commit 0040663
Show file tree
Hide file tree
Showing 12 changed files with 2,849 additions and 71 deletions.
73 changes: 73 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
cmake_minimum_required(VERSION 2.8)

if (NOT DEFINED CMAKE_TOOLCHAIN_FILE)
if (DEFINED ENV{VITASDK})
set(CMAKE_TOOLCHAIN_FILE "$ENV{VITASDK}/share/vita.toolchain.cmake" CACHE PATH "toolchain file")
else ()
message(FATAL_ERROR "Please define VITASDK to point to your SDK path!")
endif ()
endif ()

option(USE_VITA_SHARK "Using runtime shader compiler." OFF)
option(ENABLE_EXAMPLE "Enable example." OFF)

project(nanovg-gxm)
include("${VITASDK}/share/vita.cmake" REQUIRED)

set(VITA_APP_NAME ${PROJECT_NAME})
set(VITA_TITLEID "NVG000000")
set(VITA_VERSION "01.00")

# Change this to your PSVita's IP
set(PSVITAIP "192.168.1.140")

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 -ffast-math -mtune=cortex-a9 -mfpu=neon -Wall")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -ffast-math -mtune=cortex-a9 -mfpu=neon -Wall -std=gnu++17")
set(VITA_MKSFOEX_FLAGS "${VITA_MKSFOEX_FLAGS} -d ATTRIBUTE2=12") # max heap size mode

add_definitions(-D__psp2__ -D__PSV__ -DSTBI_NEON)
if (USE_VITA_SHARK)
add_definitions(-DUSE_VITA_SHARK)
endif ()

add_subdirectory(src)

if (ENABLE_EXAMPLE)
add_executable(${PROJECT_NAME}
${CMAKE_SOURCE_DIR}/example/perf.c
${CMAKE_SOURCE_DIR}/example/demo.c
${CMAKE_SOURCE_DIR}/example/example_gxm.c
)
target_link_libraries(${PROJECT_NAME}
nanovg
SceCtrl_stub
)

set(PSV_ASSETS_FILES
example/sce_sys sce_sys
example/images example/images
example/entypo.ttf example/entypo.ttf
example/Roboto-Regular.ttf example/Roboto-Regular.ttf
example/Roboto-Bold.ttf example/Roboto-Bold.ttf
example/NotoEmoji-Regular.ttf example/NotoEmoji-Regular.ttf)
if (USE_VITA_SHARK)
list(APPEND PSV_ASSETS_FILES "${CMAKE_BINARY_DIR}/vendor/SceShaccCg" module)
endif ()

vita_create_self(${PROJECT_NAME}.self ${PROJECT_NAME} UNSAFE)
vita_create_vpk(${PROJECT_NAME}.vpk ${VITA_TITLEID} ${PROJECT_NAME}.self
VERSION ${VITA_VERSION}
NAME ${VITA_APP_NAME}
FILE ${PSV_ASSETS_FILES}
)

add_custom_target(vpksend
COMMAND curl -T ${PROJECT_NAME}.vpk ftp://${PSVITAIP}:1337/ux0:/
DEPENDS ${PROJECT_NAME}.vpk-vpk
)

add_custom_target(send
COMMAND curl -T ${PROJECT_NAME}.self ftp://${PSVITAIP}:1337/ux0:/app/${VITA_TITLEID}/eboot.bin
DEPENDS ${PROJECT_NAME}.self-self
)
endif ()
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,33 @@
NanoVG-GXM
==========

Port of NanoVG to Sony's GXM API.

Compared to running through OpenGL ES2, significant performance improvement has been achieved by using GXM API.

## Build

```shell
cmake -B build -DENABLE_EXAMPLE=ON
cmake --build build
```

## TODO

#### 1. blend function
Currently, these functions in nanovg.h are not supported:

```c
nvgGlobalCompositeOperation, nvgGlobalCompositeBlendFunc, nvgGlobalCompositeBlendFuncSeparate
```

The default blend behavior is equivalent to: nvgGlobalCompositeOperation(NVG_SOURCE_OVER)

#### 2. Image flag
The `NVG_IMAGE_GENERATE_MIPMAPS` flag is not supported yet.

---

*This project is not actively maintained.*

NanoVG
Expand Down
37 changes: 30 additions & 7 deletions example/demo.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@
#include <stdio.h>
#include <string.h>
#include <math.h>
#ifdef NANOVG_GLEW
# include <GL/glew.h>
#endif
#include <GLFW/glfw3.h>
#include "nanovg.h"
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"
Expand Down Expand Up @@ -1062,13 +1058,28 @@ void drawScissor(NVGcontext* vg, float x, float y, float t)
nvgRestore(vg);
}

void drawMouse(NVGcontext* vg, float x, float y)
{
nvgBeginPath(vg);
nvgMoveTo(vg, x, y);
nvgLineTo(vg, x, y+26);
nvgLineTo(vg, x+8, y+21);
nvgLineTo(vg, x+18, y+20);
nvgClosePath(vg);

nvgFillColor(vg, nvgRGBA(0,0,0,196));
nvgFill(vg);
nvgStrokeColor(vg, nvgRGBA(255,255,255,196));
nvgStroke(vg);
}

void renderDemo(NVGcontext* vg, float mx, float my, float width, float height,
float t, int blowup, DemoData* data)
{
float x,y,popy;

drawEyes(vg, width - 250, 50, 150, 100, mx, my, t);
drawParagraph(vg, width - 450, 50, 150, 100, mx, my);
drawParagraph(vg, width - 405, 50, 150, 100, mx, my);
drawGraph(vg, 0, height/2, width, height/2, t);
drawColorwheel(vg, width - 300, height - 300, 250.0f, 250.0f, t);

Expand Down Expand Up @@ -1122,6 +1133,8 @@ void renderDemo(NVGcontext* vg, float mx, float my, float width, float height,
// Thumbnails box
drawThumbnails(vg, 365, popy-30, 160, 300, data->images, 12, t);

drawMouse(vg, mx, my);

nvgRestore(vg);
}

Expand Down Expand Up @@ -1212,17 +1225,27 @@ static void flipHorizontal(unsigned char* image, int w, int h, int stride)
}
}

// Defined in nanovg_gxm_utils.h
void *gxmReadPixels();

void saveScreenShot(int w, int h, int premult, const char* name)
{
unsigned char* image = (unsigned char*)malloc(w*h*4);
if (image == NULL)
return;
glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, image);

void *pixels = gxmReadPixels();
if (pixels == NULL)
{
free(image);
return;
}

memcpy(image, pixels, w*h*4);
if (premult)
unpremultiplyAlpha(image, w, h, w*4);
else
setAlpha(image, w, h, w*4, 255);
flipHorizontal(image, w, h, w*4);
stbi_write_png(name, w, h, 4, image, w*4);
free(image);
}
154 changes: 154 additions & 0 deletions example/example_gxm.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
//
// Copyright (c) 2024 xfangfang xfangfang@126.com
//
// 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.
//

#include <stdio.h>
#include <psp2/ctrl.h>
#include <psp2/kernel/clib.h>
#include <psp2/kernel/processmgr.h>

#include "nanovg.h"

#define NANOVG_GXM_IMPLEMENTATION
#define NANOVG_GXM_UTILS_IMPLEMENTATION

#include "nanovg_gxm.h"

#include "demo.h"
#include "perf.h"

#undef sceClibPrintf

#define MOUSE_MOVE 5
#define CLAMP_NUM(a, min, max) { if(a>max) a=max; if(a<min) a=min; }

int main() {
DemoData data;
PerfGraph fps, cpuGraph;
SceCtrlData pad, old_pad;
int blowup = 0;
int screenshot = 0;
int premult = 0;
unsigned int pressed = 0;
NVGcontext *vg = NULL;
double prevt = 0, cpuTime = 0;
NVGcolor clearColor = nvgRGBAf(0.3f, 0.3f, 0.32f, 1.0f);
double mx = DISPLAY_WIDTH / 2, my = DISPLAY_HEIGHT / 2, t, dt;

#ifdef USE_VITA_SHARK
if (shark_init("app0:module/libshacccg.suprx") < 0) {
sceClibPrintf("vitashark: failed to initialize");
return EXIT_FAILURE;
}
#endif

NVGXMframebuffer *gxm = NULL;
NVGXMinitOptions initOptions = {
.SwapInterval = 1,
.dumpShader = 1, // Save shaders to ux0:data/nvg_*.c
.msaa = SCE_GXM_MULTISAMPLE_4X
};

gxm = nvgxmCreateFramebuffer(&initOptions);
if (gxm == NULL) {
sceClibPrintf("gxm: failed to initialize\n");
return EXIT_FAILURE;
}

vg = nvgCreateGXM(gxm, NVG_STENCIL_STROKES);
if (vg == NULL) {
sceClibPrintf("nanovg: failed to initialize\n");
return EXIT_FAILURE;
}

#ifdef USE_VITA_SHARK
// Clean up vitashark as we don't need it anymore
shark_end();
#endif

if (loadDemoData(vg, &data) == -1)
return EXIT_FAILURE;

initGraph(&fps, GRAPH_RENDER_FPS, "Frame Time");
initGraph(&cpuGraph, GRAPH_RENDER_MS, "CPU Time");

memset(&pad, 0, sizeof(pad));
memset(&old_pad, 0, sizeof(old_pad));
gxmClearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a);

for (;;) {
sceCtrlPeekBufferPositive(0, &pad, 1);
pressed = pad.buttons & ~old_pad.buttons;
old_pad = pad;

if (pressed & SCE_CTRL_START)
break;
if (pressed & SCE_CTRL_TRIANGLE)
blowup = !blowup;
if (pad.buttons & SCE_CTRL_CROSS)
screenshot = 1;
if (pressed & SCE_CTRL_SQUARE) {
premult = !premult;
if (premult)
gxmClearColor(0.0f, 0.0f, 0.0f, 0.0f);
else
gxmClearColor(0.3f, 0.3f, 0.32f, 1.0f);
}

// mouse cursor emulation
mx += pad.buttons & SCE_CTRL_RIGHT ? MOUSE_MOVE : 0;
mx -= pad.buttons & SCE_CTRL_LEFT ? MOUSE_MOVE : 0;
my += pad.buttons & SCE_CTRL_DOWN ? MOUSE_MOVE : 0;
my -= pad.buttons & SCE_CTRL_UP ? MOUSE_MOVE : 0;
CLAMP_NUM(mx, 0, DISPLAY_WIDTH)
CLAMP_NUM(my, 0, DISPLAY_HEIGHT)

t = (float) sceKernelGetProcessTimeLow() / 1000000.0f;
dt = t - prevt;
prevt = t;

gxmBeginFrame();
gxmClear();

nvgBeginFrame(vg, DISPLAY_WIDTH, DISPLAY_HEIGHT, 1.0f);
renderDemo(vg, (float) mx, (float) my, DISPLAY_WIDTH, DISPLAY_HEIGHT, (float) t, blowup, &data);
renderGraph(vg, 5, 5, &fps);
renderGraph(vg, 5 + 200 + 5, 5, &cpuGraph);
nvgEndFrame(vg);

gxmEndFrame();

cpuTime = (float) sceKernelGetProcessTimeLow() / 1000000.0f - t;
updateGraph(&fps, (float) dt);
updateGraph(&cpuGraph, (float) cpuTime);

if (screenshot) {
screenshot = 0;
saveScreenShot(DISPLAY_WIDTH, DISPLAY_HEIGHT, premult, "ux0:data/nanovg-gxm-screenshot.png");
}

}

freeDemoData(vg, &data);
nvgDeleteGXM(vg);
nvgxmDeleteFramebuffer(gxm);

sceClibPrintf("Average Frame Time: %.2f ms\n", getGraphAverage(&fps) * 1000.0f);
sceClibPrintf(" CPU Time: %.2f ms\n", getGraphAverage(&cpuGraph) * 1000.0f);

return EXIT_SUCCESS;
}
Loading

0 comments on commit 0040663

Please sign in to comment.