You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Eurorack이라는 모듈러 신디사이저에 넣을 수 있는 하드웨어 모듈의 한 종류로, 해당 모듈에서 VCV Rack이라는 PC프로그램에서 기존에 사용했던 가상 모듈 패치 여러개를 한번에 사용할 수 있게 해주는 역할을 한다. (이때 모듈이란, Oscillator나 Filter를 노브로 조정해서 사용자가 원하는 사운드를 만드는 역할을 해주는 하나의 구역이라고 보면 된다.)
해당 모듈을 사용할 때 모듈의 스크린에서 세부적인 설정들을 조절할 수 있는데, 이 설정들의 GUI를 표시하는데에 ThorVG가 사용된다.
또한, 이 하드웨어를 PC에서 시뮬레이션 하는데에도 GUI로 ThorVG가 사용되고 있다.
ThorVG를 선택한 이유
해당 프로젝트는 원래 NanoVG를 GUI로 사용했으나, Github issue(링크)에서 NanoVG대신 ThorVG를 선택한 이유를 확인할 수 있었다.
다각형 렌더링을 최적화하는데에 ThorVG를 사용했으며 이를 통해 concave polygon을 사용할 수 있게 되었음을 이슈에서 확인할 수 있었다.
프로젝트 구조
해당 프로젝트에서 ThorVG를 사용하는 부분은, 실제로 하드웨어에서 사용되는 firmware폴더와, 해당 하드웨어를 시뮬레이션하는 simulator부분이다.
그 중에서도 단순히 GUI를 렌더링하는 부분은 LVGL을 사용하고, 여러 종류의 도형을 렌더링하는데에 ThorVG를 사용하고 있었다.
이 글에서는 그 중에서 ThorVG를 직접적으로 사용하고 있는 firmware부분에 대해서 작성하였다.
firmware 내부의 구조
firmware에서 ThorVG를 사용하는 부분은 아래처럼 나타낼 수 있다.
firmware
- lib
- thorvg
- CMakeLists.txt
// 해당 폴더 내부에서 thorvg 라이브러리 import
- coreproc_plugin
- grphic
- waveform_display.cc
// 오디오의 waveform을 그리는 데에 thorvg를 활용
- vcv_plugin
- internal
- nanovg_pixbuf_drawctx.hh
- nanovg_pixbuf.cc
// nanovg api는 유지하되, 내부에서는 polygon등을 그리는 데에 thorvg를 활용
CMakeLists.txt의 코드에서는 저성능의 임베디드 하드웨어에서 사용하기 위해 여러 불필요한 라이브러리들을 생략한 것을 확인할 수 있다.
또한 꼭 필요한 cpp, h파일들만 import해서 해당 하드웨어에서 불필요한 JPG, PNG 이미지로드, 그리고 SVG, Lottie파일 로드 등의 기능을 제외한 것을 확인할 수 있다.
# 필요한 라이브러리들만 importtarget_include_directories(ThorVGPUBLIC./thorvg/incthorvg/src/commonthorvg/src/rendererthorvg/src/renderer/sw_enginethorvg/src/loaders/raw
)
target_include_directories(ThorVGPRIVATEthorvg/src)
target_sources(ThorVGPRIVATEthorvg/src/common/tvgCompressor.cppthorvg/src/common/tvgMath.cppthorvg/src/common/tvgStr.cppthorvg/src/renderer/tvgAccessor.cppthorvg/src/renderer/tvgAnimation.cppthorvg/src/renderer/tvgCanvas.cppthorvg/src/renderer/tvgFill.cppthorvg/src/renderer/sw_engine/tvgSwFill.cppthorvg/src/renderer/sw_engine/tvgSwImage.cppthorvg/src/renderer/sw_engine/tvgSwMath.cpp# 기타 필수 cpp파일들...# thorvg/src/renderer/tvgGlCanvas.cpp# thorvg/src/renderer/tvgWgCanvas.cpp# OpenGL, WebGPU 렌더러는 저수준의 하드웨어에서 불필요하기에 생략# 이미지 로드 파일도 raw파일만 가능하도록 설정함thorvg/src/loaders/raw/tvgRawLoader.cpp
)
저렇게 import된 thorvg모듈들은 nanovg_pixbuf_drawctx.hh에서 사용되고, 기존에 사용되던 nanovg대신 백엔드로 사용되어서 ui를 그리게 된다.
마찬가지로, nanovg_pixbuf.cc 파일에서 도형을 그릴 때 ThorVG를 사용해서 Shape를 생성하고, Fill과 Stroke를 적용하는 것을 확인할 수 있었다.
voidrenderFill(void *uptr,
NVGpaint *paint,
NVGcompositeOperationState compositeOperation,
NVGscissor *scissor,
float fringe,
constfloat *bounds,
const NVGpath *paths,
int npaths) {
// 중략for (auto &path : std::span{paths, (size_t)npaths}) {
dump_draw("Fill path: #fill %d = count:%d\n", path.nfill, path.count);
if (path.count < 3)
continue;
auto poly = tvg::Shape::gen();
poly->moveTo(path.fill[0].x, path.fill[0].y);
for (auto pt : std::span{path.fill + 1, (size_t)(path.count - 1)}) {
poly->lineTo(pt.x, pt.y);
}
poly->close();
auto [r, g, b, a] = to_tvg_color(paint->innerColor);
poly->fill(r, g, b, a);
// Clip/Scissorif (scissor->extent[0] >= 0 && scissor->extent[1] >= 0) {
auto clip_region = tvg::Shape::gen();
auto x = scissor->xform[4] - scissor->extent[0];
auto y = scissor->xform[5] - scissor->extent[1];
auto w = 2 * scissor->extent[0];
auto h = 2 * scissor->extent[1];
clip_region->appendRect(x, y, w, h);
poly->clip(clip_region);
}
// Clipping에 적용할 Scene 생성auto scene = tvg::Scene::gen();
scene->push(poly);
scene->scale(scaling);
context->tvg_canvas->push(scene);
context->tvg_canvas->draw();
context->tvg_canvas->sync();
context->tvg_canvas->remove();
}
}
voidrenderStroke(void *uptr,
NVGpaint *paint,
NVGcompositeOperationState compositeOperation,
NVGscissor *scissor,
float fringe,
float strokeWidth,
const NVGpath *paths,
int npaths) {
// 중략for (auto &path : std::span{paths, (size_t)npaths}) {
dump_draw("Stroke path: #strokes %d = count:%d + closed:%d\n", path.nstroke, path.count, path.closed);
if (path.count < 2)
continue;
// ThorVG Shape 생성auto poly = tvg::Shape::gen();
poly->moveTo(path.stroke[0].x, path.stroke[0].y);
for (auto pt : std::span{path.stroke + 1, (size_t)(path.nstroke - 1)}) {
poly->lineTo(pt.x, pt.y);
}
auto [r, g, b, a] = to_tvg_color(paint->innerColor);
poly->strokeFill(r, g, b, a);
float stroke_width = strokeWidth;
if (scaling < 1) {
constexprfloat MinStroke = 1.3f;
// Divide by /scaling so that when the rendering engine scales, it results in MinStroke
stroke_width = std::max(mm_to_px(to_mm(strokeWidth), context->px_per_3U), MinStroke / scaling);
}
poly->strokeWidth(stroke_width);
// 후략
}
}
UI가 그려지는 흐름의 예시
이는 나중에 firmware 내부에서 gui를 사용할 때 여러 단계를 거쳐서 사용되게 된다.
이를 단순한 흐름으로 나타내자면 아래와 같은 흐름으로, 이 모듈이 VCV Rack이라는 프로그램에서 사용하던 모듈패치를 가져와서 자신의 하드웨어에서 플레이 하기 위해서 가상의 Rack을 만들고, 내부에서 NanoVG API를 호출하고, 그 내부에서 최종적으로 백엔드로 ThorVG를 사용하면서 그림이 그려지는 과정을 알 수 있다.
ModuleView / PatchView
-> DynamicDisplayDrawer::draw()
-> module->draw_graphic_display() // MetaModule 쪽 가상 함수
-> rack::engine::Module::draw_graphic_display() // 가짜 Rack 구현
-> widget->draw(args) (NanoVG)
-> nvgFill 등
-> nanovg_pixbuf.cc::renderFill
-> tvg::Shape / SwCanvas
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Metamodule이란?
Eurorack이라는 모듈러 신디사이저에 넣을 수 있는 하드웨어 모듈의 한 종류로, 해당 모듈에서 VCV Rack이라는 PC프로그램에서 기존에 사용했던 가상 모듈 패치 여러개를 한번에 사용할 수 있게 해주는 역할을 한다. (이때 모듈이란, Oscillator나 Filter를 노브로 조정해서 사용자가 원하는 사운드를 만드는 역할을 해주는 하나의 구역이라고 보면 된다.)
해당 모듈을 사용할 때 모듈의 스크린에서 세부적인 설정들을 조절할 수 있는데, 이 설정들의 GUI를 표시하는데에 ThorVG가 사용된다.
또한, 이 하드웨어를 PC에서 시뮬레이션 하는데에도 GUI로 ThorVG가 사용되고 있다.
ThorVG를 선택한 이유
해당 프로젝트는 원래 NanoVG를 GUI로 사용했으나, Github issue(링크)에서 NanoVG대신 ThorVG를 선택한 이유를 확인할 수 있었다.
다각형 렌더링을 최적화하는데에 ThorVG를 사용했으며 이를 통해 concave polygon을 사용할 수 있게 되었음을 이슈에서 확인할 수 있었다.
프로젝트 구조
해당 프로젝트에서 ThorVG를 사용하는 부분은, 실제로 하드웨어에서 사용되는
firmware폴더와, 해당 하드웨어를 시뮬레이션하는simulator부분이다.그 중에서도 단순히 GUI를 렌더링하는 부분은 LVGL을 사용하고, 여러 종류의 도형을 렌더링하는데에 ThorVG를 사용하고 있었다.
이 글에서는 그 중에서 ThorVG를 직접적으로 사용하고 있는
firmware부분에 대해서 작성하였다.firmware 내부의 구조
firmware에서 ThorVG를 사용하는 부분은 아래처럼 나타낼 수 있다.
CMakeLists.txt의 코드에서는 저성능의 임베디드 하드웨어에서 사용하기 위해 여러 불필요한 라이브러리들을 생략한 것을 확인할 수 있다.또한 꼭 필요한
cpp, h파일들만 import해서 해당 하드웨어에서 불필요한 JPG, PNG 이미지로드, 그리고 SVG, Lottie파일 로드 등의 기능을 제외한 것을 확인할 수 있다.저렇게 import된 thorvg모듈들은
nanovg_pixbuf_drawctx.hh에서 사용되고, 기존에 사용되던 nanovg대신 백엔드로 사용되어서 ui를 그리게 된다.마찬가지로,
nanovg_pixbuf.cc파일에서 도형을 그릴 때 ThorVG를 사용해서 Shape를 생성하고, Fill과 Stroke를 적용하는 것을 확인할 수 있었다.UI가 그려지는 흐름의 예시
이는 나중에 firmware 내부에서 gui를 사용할 때 여러 단계를 거쳐서 사용되게 된다.
이를 단순한 흐름으로 나타내자면 아래와 같은 흐름으로, 이 모듈이 VCV Rack이라는 프로그램에서 사용하던 모듈패치를 가져와서 자신의 하드웨어에서 플레이 하기 위해서 가상의 Rack을 만들고, 내부에서 NanoVG API를 호출하고, 그 내부에서 최종적으로 백엔드로 ThorVG를 사용하면서 그림이 그려지는 과정을 알 수 있다.
그 예시로, ModuleView에서 매 프레임마다 UI가 그려지는 흐름을 정리해보았다.
src/gui/pages/module_view/draw_module.cc에서 UI가 drawer를 호출한다.nvgCreatePixelBufferContext는 앞서 언급한nanovg_pixbuf.cc에 있는 함수이고, 여기에서 ThorVG를 사용하는renderFill, renderStroke등을 등록한다.nanovg_pixbuf.cc에서 호출된renderFill함수 내부에서 그림을 그린다.All reactions