darktable core pipeline
작성 기준: darktable-org/darktable 저장소의 master 브랜치 소스와 dev-doc 문서.
주요 구현 파일은 src/develop/pixelpipe_hb.c, src/develop/pixelpipe_hb.h, src/develop/pixelpipe_cache.c, src/develop/imageop.h, src/iop/iop_api.h, src/common/iop_order.*, src/develop/tiling.* 이다.
0. 핵심 요약
darktable의 코어 이미지 처리 엔진은 pixelpipe다. pixelpipe는 RAW 또는 일반 래스터 입력 버퍼를 받아, 정해진 순서의 IOP(Image Operation) 모듈들을 통과시킨 뒤, 화면 표시·프리뷰·썸네일·내보내기용 결과 버퍼를 만든다.
핵심 아이디어는 다음과 같다.
- 파이프는 하나가 아니다. 같은 이미지라도 메인 다크룸 뷰, 네비게이션 프리뷰, 두 번째 프리뷰, 썸네일, export 등 목적별로 별도의
dt_dev_pixelpipe_t인스턴스가 존재한다. - 모듈은 파이프 안에서
piece로 실행된다.dt_iop_module_t는 모듈 인스턴스와 UI/히스토리 상태를 들고, 파이프 안의dt_dev_pixelpipe_iop_t는 해당 파이프 실행 전용 데이터(piece->data, ROI, 해시, 히스토그램, 블렌딩 데이터 등)를 들고 있다. - 최종 출력 영역에서 시작해 입력 필요 영역을 역산한다. 화면에 필요한 최종 ROI가 들어오면, 마지막 모듈부터 첫 모듈까지
modify_roi_in()으로 “이 출력 영역을 만들려면 이전 단계에서 어느 입력 영역이 필요한가?”를 계산한다. - 실제 픽셀 처리는 재귀 호출이 풀리면서 앞에서 뒤로 진행된다.
_dev_pixelpipe_process_rec()는 마지막 모듈에서 시작하지만, 이전 모듈을 재귀 호출해 입력을 확보한 뒤 현재 모듈의process()또는process_cl()을 실행한다. - 캐시는 해시 기반이다. 이미지 ID, 파이프 타입, 디테일 마스크 상태, 컬러 프로파일, ROI, 앞선 모듈들의
piece->hash등을 조합해 캐시 키를 만들고, 같은 결과 버퍼를 재사용한다. - CPU, OpenCL, 타일링이 모두 같은 파이프 안에서 선택된다. 모듈별 OpenCL 지원 여부, 메모리 적합성, 타일링 가능 여부, 블렌딩 조건, 오류 여부에 따라 GPU 직행, GPU 타일링, CPU 타일링, CPU 직접 처리 중 하나로 흐른다.
- OpenCL 오류는 투명하게 CPU 재시작으로 복구한다. GPU 처리 중 오류가 감지되면 파이프 캐시를 비우고 같은 파이프를 CPU 모드로 다시 실행한다.
1. 전체 구조 그림
사용자 조작 / 이미지 로드 / history 변경
│
▼
dt_develop_t
├─ image_storage : 현재 이미지 스냅샷
├─ history : 히스토리 스택
├─ iop : 현재 모듈 인스턴스 목록
├─ iop_order_list : 모듈 실행 순서 목록
├─ forms : 마스크/도형 목록
├─ full.pipe : 메인 다크룸 파이프
├─ preview_pipe : 네비게이션 프리뷰 파이프
└─ preview2.pipe : 두 번째 프리뷰 파이프
│
▼
dt_dev_pixelpipe_t
├─ input : 입력 float 버퍼
├─ nodes : dt_dev_pixelpipe_iop_t 리스트
├─ cache : pixelpipe cache
├─ profiles : input/work/output/export profile snapshot
├─ backbuf : 최종 화면/export 결과
├─ mask/raster/detail : 마스크 표시·왜곡·캐시 상태
└─ OpenCL/tiling/status : 처리 모드와 상태
│
▼
GList nodes
├─ piece(rawprepare)
├─ piece(demosaic)
├─ piece(exposure)
├─ piece(...)
└─ piece(gamma/final output)
│
▼
각 piece가 dt_iop_module_t 콜백 호출
├─ modify_roi_out()
├─ modify_roi_in()
├─ output_format()
├─ input_colorspace()/output_colorspace()
├─ tiling_callback()
├─ process()
├─ process_tiling()
├─ process_cl()
├─ process_tiling_cl()
└─ distort_mask()/distort_transform()/distort_backtransform()
2. 핵심 자료구조
2.1 dt_develop_t: 다크룸 개발 상태의 중심
위치: src/develop/develop.h
dt_develop_t는 현재 다크룸 세션의 큰 상태를 들고 있다. pixelpipe 자체는 아니지만, 파이프가 읽고 동기화해야 할 원본 상태가 여기에 있다.
주요 필드:
| 필드 | 역할 |
|---|---|
image_storage | 현재 이미지의 메타데이터/버퍼 설명 스냅샷. 파이프는 레이스를 피하려고 이를 복사해 사용한다. |
history, history_end | 히스토리 스택과 현재 적용 지점. |
history_mutex | 히스토리와 파이프 동기화 보호. |
iop | 현재 이미지에 로드된 IOP 모듈 인스턴스 목록. |
iop_order_list | 실행 순서를 표현하는 모듈 순서 리스트. |
forms, allforms | drawn mask, raster mask 등 마스크 관련 도형 목록. |
preview_pipe | 네비게이션 프리뷰용 pixelpipe. |
full.pipe | 메인 다크룸 중앙 뷰용 pixelpipe. |
preview2.pipe | 두 번째 프리뷰/듀얼 뷰용 pixelpipe. |
gui_module | 현재 포커스된 모듈. 캐시 중요도, 빠른 블렌드 캐시, mask display 등에 영향을 준다. |
중요한 점은 dt_develop_t가 “현재 편집 상태”이고, dt_dev_pixelpipe_t는 그 상태를 복사·동기화해 실제 픽셀을 처리하는 실행 컨텍스트라는 것이다.
2.2 dt_dev_pixelpipe_t: 하나의 실행 파이프
위치: src/develop/pixelpipe_hb.h
dt_dev_pixelpipe_t는 실제 처리 파이프 하나를 나타낸다. full, preview, preview2, export, thumbnail 각각이 별도 파이프가 될 수 있다.
주요 필드:
| 필드 | 역할 |
|---|---|
cache | 해시 기반 pixelpipe 캐시. |
input | 입력 이미지 버퍼. 일반적으로 float 기반. |
iwidth, iheight, iscale | 입력 버퍼 크기와 원본 대비 스케일. |
processed_width, processed_height | 전체 파이프를 통과했을 때의 결과 크기. |
dsc | 현재/최종 버퍼 설명자. 채널 수, 데이터 타입, 컬러스페이스 등. |
work_profile_info, input_profile_info, output_profile_info, export_profile_info | 파이프 실행 시점의 컬러 프로파일 스냅샷. 캐시 해시에도 들어간다. |
nodes | dt_dev_pixelpipe_iop_t 리스트. 즉 파이프 안의 모듈 실행 노드들. |
changed | DT_DEV_PIPE_* 변경 플래그. |
status | dirty/running/valid/invalid 상태. |
backbuf | 최종 화면 표시 또는 export 결과 버퍼. |
backbuf_mutex, busy_mutex, mutex | 결과 버퍼, 실행 상태, 일반 상태 보호. |
want_detail_mask, scharr | detail mask 처리 상태. |
nocache | 일부 파이프/상황에서 캐시 사용 억제. |
shutdown | atomic stop 플래그. 실행 중인 파이프를 조기 중단하거나 재시작하게 한다. |
opencl_enabled, opencl_error, devid | OpenCL 실행 상태와 디바이스. |
tiling | 현재 타일링 컨텍스트 여부. |
mask_display, bypass_blendif | 마스크 표시/블렌딩 우회 모드. |
type | full, preview, export, thumbnail 등 파이프 종류 bitmask. |
image | 파이프 시작 시점에 복사된 dt_image_t. |
iop, iop_order_list, forms | 파이프 실행용 스냅샷 리스트. |
store_all_raster_masks | export 등에서 raster mask 저장 여부. |
bcache_data, bcache_hash | 포커스 모듈 블렌딩 파라미터만 바뀌는 상황을 빠르게 처리하기 위한 블렌드 캐시. |
mask_distort_buf | 마스크 왜곡 처리 시 재사용하는 ping-pong 버퍼. |
2.3 dt_dev_pixelpipe_iop_t: 파이프 안의 모듈 실행 노드
위치: src/develop/pixelpipe_hb.h
같은 dt_iop_module_t라도 full 파이프, preview 파이프, export 파이프에서 동시에 처리될 수 있다. 그래서 실제 파이프 실행에는 dt_dev_pixelpipe_iop_t가 사용된다. 보통 코드에서 이것을 piece라고 부른다.
주요 필드:
| 필드 | 역할 |
|---|---|
module | 실제 IOP 모듈 인스턴스(dt_iop_module_t). |
pipe | 이 piece가 속한 dt_dev_pixelpipe_t. |
data | commit_params()가 준비한 파이프 전용 처리 데이터. process()는 보통 이것을 읽는다. |
blendop_data | 이 piece에 적용되는 blending 파라미터. |
enabled | 이 파이프 실행에서 활성화 여부. 모듈 자체 enable과 별개로 export 등에서 강제로 비활성화될 수 있다. |
request_histogram, histogram, histogram_stats | 모듈별 히스토그램 요청/결과. |
iscale, iwidth, iheight | 입력 스케일과 크기. |
hash | 모듈 파라미터와 상태를 대표하는 해시. 파이프 캐시 해시에 누적된다. |
bpc, colors | bits per channel, 채널 수. RAW면 1, 일반 RGB 계열은 보통 4. |
buf_in, buf_out | 전체 버퍼 차원 산출 시의 이론적 ROI. |
processed_roi_in, processed_roi_out | 실제 처리에 사용한 ROI. 마스크 왜곡/캐시에도 중요하다. |
process_cl_ready | 이 piece가 OpenCL 처리 준비 완료인지. commit_params()에서 끌 수 있다. |
process_tiling_ready | 이 piece가 타일링 처리 준비 완료인지. |
dsc_in, dsc_out | 입력/출력 버퍼 설명자. |
filters, xtrans | RAW CFA 처리용 필터 패턴 스냅샷. |
raster_masks | 이 piece가 생성한 raster mask 저장소. |
detail_mask_cache, raster_mask_cache | 기하학 모듈 경계에서 왜곡된 마스크를 재사용하기 위한 캐시. |
2.4 dt_iop_module_t: IOP 모듈 인스턴스
위치: src/develop/imageop.h, API 정의는 src/iop/iop_api.h
dt_iop_module_t는 exposure, demosaic, filmic, color calibration 같은 개별 IOP 모듈 인스턴스를 나타낸다. 이 구조체 안에는 모듈 식별자, 히스토리 파라미터, GUI 상태, 콜백 함수들이 들어 있다.
중요한 필드:
| 필드 | 역할 |
|---|---|
op | 내부 operation 문자열. 예: exposure, demosaic. |
instance, multi_priority | 다중 인스턴스 식별. |
iop_order | 파이프 실행 순서. |
position | 현재 파이프 처리 중 위치. |
enabled, default_enabled | 현재/기본 활성화 상태. |
params, default_params | UI/DB/히스토리 기준 파라미터 blob. |
blend_params, default_blendop_params | 블렌딩 파라미터. |
gui_data, widget, expander | GUI 관련 상태. |
raster_mask | raster mask source/sink 관계. |
모듈은 iop_api.h를 통해 다음 콜백들을 제공할 수 있다.
| 콜백 | 역할 |
|---|---|
name() | 표시 이름. |
default_colorspace() | 모듈 기본 처리 컬러스페이스. 필수. |
input_format(), output_format() | 버퍼 포맷 요구/출력 변경. |
input_colorspace(), output_colorspace() | 모듈 입력/출력 컬러스페이스. |
blend_colorspace() | 블렌딩이 수행될 컬러스페이스. |
init_pipe() | 파이프 piece별 데이터 초기화. |
commit_params() | 히스토리/UI 파라미터를 piece->data로 변환. |
cleanup_pipe() | piece별 데이터 해제. |
modify_roi_out() | forward pass에서 입력 ROI가 출력 크기에 주는 영향 계산. |
modify_roi_in() | backward pass에서 출력 ROI에 필요한 입력 ROI 계산. |
tiling_callback() | 메모리/타일링 요구량 보고. |
process() | CPU 처리. 필수. |
process_tiling() | CPU 타일링 처리. |
process_cl() | OpenCL 처리. |
process_tiling_cl() | OpenCL 타일링 처리. |
distort_transform(), distort_backtransform(), distort_mask() | 기하학/마스크 변환. |
2.5 dt_iop_roi_t: 처리 영역
위치: src/develop/pixelpipe.h
typedef struct dt_iop_roi_t
{
int x, y, width, height;
float scale;
} dt_iop_roi_t;
의미:
| 필드 | 의미 |
|---|---|
x, y | 현재 스케일 좌표계에서 ROI 시작점. |
width, height | ROI 크기. |
scale | 원본 전체 이미지 대비 처리 스케일. |
대부분의 점 연산 모듈은 roi_in == roi_out이다. 반면 crop, rotate, lens correction, finalscale, blur, retouch 같은 모듈은 출력 영역을 만들기 위해 더 넓거나 변형된 입력 ROI가 필요하다.
2.6 dt_dev_pixelpipe_cache_t: 파이프 캐시
위치: src/develop/pixelpipe_cache.h, src/develop/pixelpipe_cache.c
캐시는 “특정 파이프 상태 + 특정 모듈 위치 + 특정 ROI”의 결과 버퍼를 저장한다.
주요 필드:
| 필드 | 역할 |
|---|---|
entries | 캐시 라인 개수. 최소 2개는 ping-pong 버퍼처럼 사용된다. |
allmem, memlimit | 현재 사용 메모리와 제한. |
data[] | 캐시된 픽셀 버퍼 포인터. |
size[] | 각 버퍼 크기. |
dsc[] | 각 버퍼의 포맷 설명자. |
hash[] | 캐시 키. |
used[] | 캐시 라인 aging/중요도. 음수는 중요 cacheline에 가깝다. |
ioporder[] | 이 캐시 라인이 어느 모듈 순서에 해당하는지. downstream invalidation에 사용. |
calls, tests, hits | 통계. |
3. 파이프 타입
위치: src/develop/pixelpipe.h
파이프 타입은 bitmask다.
| 타입 | 의미 |
|---|---|
DT_DEV_PIXELPIPE_EXPORT | 내보내기용 파이프. |
DT_DEV_PIXELPIPE_FULL | 다크룸 중앙 메인 뷰. |
DT_DEV_PIXELPIPE_PREVIEW | 네비게이션/미리보기 파이프. |
DT_DEV_PIXELPIPE_THUMBNAIL | 썸네일 생성. |
DT_DEV_PIXELPIPE_PREVIEW2 | 두 번째 프리뷰/듀얼 뷰. |
DT_DEV_PIXELPIPE_SCREEN | 화면 표시용 파이프 묶음: preview, full, preview2. |
DT_DEV_PIXELPIPE_CANVAS | 캔버스 뷰: full, preview2. |
DT_DEV_PIXELPIPE_FAST | 일부 모듈 포커스 상황에서 빠른 파이프 모드. |
DT_DEV_PIXELPIPE_IMAGE | 내부 이미지 추출용 특수 플래그. |
DT_DEV_PIXELPIPE_IMAGE_FINAL | finalscale 사용을 표시하는 특수 플래그. |
DT_DEV_PIXELPIPE_BASIC | full + preview. |
파이프 타입은 캐시 해시에 들어간다. 같은 이미지와 같은 모듈 파라미터라도 full 파이프, preview 파이프, export 파이프 결과는 서로 다른 캐시 공간으로 취급된다.
4. 파이프 생명주기
4.1 초기화
위치: src/develop/pixelpipe_hb.c
초기화 함수는 목적별로 다르다.
| 함수 | 목적 | 캐시 전략 |
|---|---|---|
dt_dev_pixelpipe_init() | full 파이프 | darktable.pipe_cache가 켜져 있으면 최대 64개 캐시 라인. 메모리 제한은 max(64MB, mipmap_memory / 4) 계열. |
dt_dev_pixelpipe_init_preview() | preview 파이프 | 캐시 사용 시 약 12개 라인. |
dt_dev_pixelpipe_init_preview2() | preview2 파이프 | 캐시 사용 시 약 5개 라인. |
dt_dev_pixelpipe_init_export() | export 파이프 | 최종 크기에 맞춘 2개 라인 중심. history cache보다 안정적인 처리 버퍼 목적. |
dt_dev_pixelpipe_init_thumbnail() | thumbnail 파이프 | 썸네일 크기 기준 2개 라인. |
dt_dev_pixelpipe_init_dummy() | 크기/왜곡 계산용 | 실제 픽셀 처리는 하지 않는 더미 성격. |
dt_dev_pixelpipe_init_cached() | 공통 초기화 | mutex, 상태 플래그, 프로파일, 마스크, OpenCL, cache 구조 초기화. |
dt_dev_pixelpipe_init_cached()는 다음을 기본값으로 세팅한다.
devid = DT_DEVICE_CPUchanged = DT_DEV_PIPE_UNCHANGEDstatus = DT_DEV_PIXELPIPE_DIRTYnodes = NULLshutdown = DT_DEV_PIXELPIPE_STOP_NOopencl_error = FALSEmask_display = DT_DEV_PIXELPIPE_DISPLAY_NONEbypass_blendif = FALSE- ICC/profile 관련 포인터 초기화
bcache_data = NULL- mask distortion buffer 초기화
- 마지막에
dt_dev_pixelpipe_cache_init()호출
4.2 입력 설정
함수: dt_dev_pixelpipe_set_input()
역할:
pipe->iwidth,pipe->iheight,pipe->iscale설정pipe->input에 입력 버퍼 저장pipe->image = dev->image_storage로 이미지 스냅샷 복사- 첫 입력 버퍼 설명자
pipe->dsc를 설정
비 RAW 이미지의 경우 processed maximum을 1.0으로 정규화하는 초기 포맷 처리도 여기와 연결된다.
4.3 노드 생성
함수: dt_dev_pixelpipe_create_nodes()
흐름:
busy_mutex lock
shutdown 플래그 초기화
pipe->iop_order_list = dev->iop_order_list deep copy
pipe->iop = dev->iop shallow copy
for each module in pipe->iop:
piece = calloc(dt_dev_pixelpipe_iop_t)
piece->enabled = module->enabled
piece->colors = RAW이면 1, 아니면 4
piece->iscale/iwidth/iheight = pipe 입력 기준
piece->module = module
piece->pipe = pipe
piece->hash = DT_INVALID_HASH
piece->process_cl_ready = FALSE
piece->process_tiling_ready = FALSE
piece->raster_masks = hash table 생성
dt_iop_init_pipe(module, pipe, piece)
pipe->nodes에 append
busy_mutex unlock
즉 dev->iop의 각 모듈에 대해 파이프 실행 전용 piece를 하나씩 만든다.
4.4 파라미터 동기화
파이프는 dt_iop_module_t->params를 직접 계속 읽지 않는다. 대신 히스토리 스택과 모듈 기본값을 piece->data에 커밋한다.
핵심 함수:
| 함수 | 역할 |
|---|---|
_dev_pixelpipe_synch() | 히스토리 항목 하나를 해당 piece에 반영. |
dt_dev_pixelpipe_synch_all() | 모든 piece를 기본값으로 커밋한 뒤, dev->history를 history_end까지 순회하며 반영. |
dt_dev_pixelpipe_synch_top() | 마지막 히스토리 항목만 반영. 파라미터만 바뀐 경우 빠른 경로. |
dt_dev_pixelpipe_change() | 변경 플래그를 보고 top sync, full sync, rebuild 중 선택. |
_dev_pixelpipe_synch()에서 하는 일:
- 히스토리 항목의 모듈과 같은 piece를 찾는다.
piece->enabled = hist->enabled로 설정한다.- RAW 이미지에 필수인
rawprepare,demosaic같은 모듈은 필요 시 강제로 활성화한다. - RAW 전용 모듈이 비 RAW 이미지에 켜져 있으면 비활성화한다.
iop_order == INT_MAX인 활성 모듈은 처리 불가로 보고 비활성화한다.- module filter에 의해 제외되는 모듈을 끈다.
dt_iop_commit_params(hist->module, hist->params, hist->blend_params, pipe, piece)를 호출한다.- blending mask가 detail mask를 요구하면
dt_dev_pixelpipe_usedetails(piece)로 detail mask 필요 상태를 켠다.
중요한 결과는 commit_params() 이후 piece->data, piece->blendop_data, piece->hash, OpenCL/tile 가능 상태가 현재 히스토리에 맞게 갱신된다는 것이다.
4.5 변경 플래그와 rebuild
dt_dev_pixelpipe_change_t:
| 플래그 | 의미 |
|---|---|
DT_DEV_PIPE_UNCHANGED | 변경 없음. |
DT_DEV_PIPE_TOP_CHANGED | 맨 위 히스토리 항목의 파라미터만 바뀜. |
DT_DEV_PIPE_SYNCH | 노드 구조는 그대로지만 모든 모듈 파라미터 재동기화 필요. |
DT_DEV_PIPE_REMOVE | 모듈 추가/삭제/순서 변경 등으로 노드 재생성 필요. |
DT_DEV_PIPE_ZOOMED | 줌 변경. 일부 파이프는 중단/재시작 판단에 사용. |
dt_dev_pixelpipe_change()는 다음 순서로 처리한다.
history_mutex lock
if TOP_CHANGED only:
dt_dev_pixelpipe_synch_top()
if SYNCH:
dt_dev_pixelpipe_synch_all()
if REMOVE:
dt_dev_pixelpipe_cleanup_nodes()
dt_dev_pixelpipe_create_nodes()
dt_dev_pixelpipe_synch_all()
pipe->changed = UNCHANGED
history_mutex unlock
dt_dev_pixelpipe_get_dimensions()로 전체 출력 크기 갱신
dt_dev_pixelpipe_rebuild(dev)는 full, preview, preview2 파이프에 DT_DEV_PIPE_REMOVE를 세우고, cache_obsolete = TRUE로 만든 뒤 다크룸 redraw를 강제한다.
5. IOP order와 히스토리
위치: src/common/iop_order.h, src/common/iop_order.c
darktable에서 모듈 순서는 단순히 UI 리스트 순서가 아니다. iop_order_list가 파이프 실행 순서를 결정한다.
5.1 order 버전
대표 order 종류:
| 값 | 의미 |
|---|---|
DT_IOP_ORDER_CUSTOM | 사용자가 모듈 순서를 변경한 custom order. |
DT_IOP_ORDER_LEGACY | 과거 legacy order. |
DT_IOP_ORDER_V30 | darktable 3.0 계열 order. |
DT_IOP_ORDER_V30_JPG | 비선형 입력용 v3.0 변형. |
DT_IOP_ORDER_V50 | darktable 5.0 계열 RAW 기본 order. |
DT_IOP_ORDER_V50_JPG | darktable 5.0 계열 JPG/비 RAW 기본 order. |
기본값:
#define DT_DEFAULT_IOP_ORDER_RAW DT_IOP_ORDER_V50
#define DT_DEFAULT_IOP_ORDER_JPG DT_IOP_ORDER_V50_JPG
5.2 왜 리스트인가
예전 방식은 double 값을 이용해 모듈 사이에 새 인스턴스를 끼워 넣는 식이었다. 다중 인스턴스 생성·삭제·이동이 반복되면 값 간격이 줄어 충돌 가능성이 있었다.
현재 방식은 단순 연결 리스트가 순서의 원본이다.
operation + instance/multi_priority가 stable key로 사용된다.iop_orderinteger는 메모리상 정렬과 빠른 비교에 쓰인다.- 히스토리 매핑 자체는 리스트 기준으로 이뤄진다.
- 이미지별
module_order테이블에 order 버전과 custom list가 저장된다.
5.3 히스토리와 order의 관계
이미지를 로드할 때:
1. 이미지의 iop_order_list 로드
2. history 로드
3. 각 history item의 operation + multi-instance를 order list에 매핑
4. dev->iop 정렬/동기화
5. pixelpipe create/synch
모듈을 새로 개발할 때는 src/iop/CMakeLists.txt에 등록하는 것만으로 충분하지 않다. src/common/iop_order.c의 legacy/v30/v50 등 관련 order list와 동적 삽입 규칙에도 넣어야 한다. 그렇지 않으면 파이프 순서가 없어서 fatal error가 날 수 있다.
6. 전체 처리 흐름
6.1 최상위 함수
함수: dt_dev_pixelpipe_process()
호출 인자:
gboolean dt_dev_pixelpipe_process(dt_dev_pixelpipe_t *pipe,
dt_develop_t *dev,
const int x,
const int y,
const int width,
const int height,
const float scale,
const int devid)
의미:
| 인자 | 의미 |
|---|---|
x, y, width, height | 최종 출력에서 필요한 영역. 화면 viewport 또는 export 영역. |
scale | 출력 스케일. |
devid | 이미 잠근 OpenCL device가 있으면 그 device. 아니면 파이프가 직접 lock. |
상위 흐름:
dt_dev_pixelpipe_process()
pipe->processing = TRUE
pipe->nocache 설정
pipe->runs++
OpenCL 사용 가능 여부 확인
필요 시 OpenCL device lock
cache memory check
최종 ROI 생성
화면 zoom position을 geometry 기준으로 backtransform
dev->forms를 pipe->forms로 deep copy
modules = pipe->iop 마지막 노드
pieces = pipe->nodes 마지막 노드
restart:
obsolete cache flush
mask_display reset
bypass_blendif reset
_dev_pixelpipe_process_rec_and_backcopy()
OpenCL event error 검사
OpenCL 오류면:
cl buffer release
OpenCL disable
error count 증가
cache flush
dt_dev_pixelpipe_change()
CPU 모드로 goto restart
성공이면:
final buffer를 backbuf에 게시
cache report
pipe->processing = FALSE
6.2 재귀 처리의 핵심
함수: _dev_pixelpipe_process_rec()
이 함수는 이름 그대로 recursive helper다. 중요한 점은 마지막 모듈에서 시작하지만 실제 픽셀 처리는 앞 모듈부터 실행된다는 것이다.
개념적 pseudo-code:
process_rec(pipe, roi_out, modules_last_to_first, pieces_last_to_first, pos):
if shutdown:
return error_or_unfinished
module, piece = 현재 위치의 모듈과 piece
if piece를 건너뛰어야 하면:
return process_rec(previous module, same roi_out, pos-1)
out_format = module->output_format(...)
hash = dt_dev_pixelpipe_cache_hash(roi_out, pipe, pos)
if cache hit:
output = cached buffer
return success
if early exit 조건:
return unfinished
if module이 없음, 즉 base input 단계:
pipe->input에서 roi_out에 맞는 입력 버퍼 생성
return success
module->modify_roi_in(roi_out, &roi_in)
input = process_rec(previous module, roi_in, pos-1)
output cacheline 확보
mask display bypass 가능하면 copy/pass-through
module->tiling_callback(...)
blending tiling 요구량 합산
if OpenCL 가능:
process_cl 또는 process_tiling_cl 시도
실패하면 CPU fallback
else:
CPU process 또는 process_tiling
histogram/color picker/blend/cache 중요도 처리
return success/error
이 구조 때문에 darktable은 최종 화면에 필요한 영역만 계산할 수 있다. 예를 들어 crop, rotate, lens correction이 있으면 마지막 결과 영역을 기준으로 앞단에서 필요한 원본 영역을 역으로 계산한다.
6.3 base input 단계
재귀가 첫 입력까지 도달하면 modules == NULL이 된다. 이때 실제 입력 버퍼를 현재 ROI에 맞게 만든다.
경로는 크게 세 가지다.
1. 전체 원본 그대로 사용
조건:
roi_out->scale == 1.0froi_out->x == 0,roi_out->y == 0- 입력 크기와 ROI 크기가 같음
- 입력 버퍼가 alignment 조건을 만족
이 경우 *output = pipe->input으로 원본 입력 버퍼를 그대로 사용한다.
2. 1:1 부분 복사
scale은 1.0이지만 ROI가 전체와 다르면 cache buffer를 잡고 필요한 부분만 복사한다. ROI가 입력 범위를 벗어나면 나머지 영역은 0으로 채운다.
3. clip & zoom
scale이 1.0이 아니면 dt_iop_clip_and_zoom() 또는 가능한 경우 OpenCL scaling을 사용해 필요한 영역을 리샘플링한다.
비 RAW 이미지에는 gamma correction이 개입될 수 있다. 입력 bpp가 예상과 다르거나 alignment가 맞지 않으면 안전하게 0으로 채우고 fatal/debug 로그를 낸다.
7. ROI 계산
ROI는 두 번의 관점으로 다뤄진다.
7.1 전체 결과 크기 계산: modify_roi_out()
함수: dt_dev_pixelpipe_get_dimensions()
이 함수는 파이프 전체가 입력 이미지를 처리했을 때 최종 크기가 어떻게 바뀌는지 계산한다.
흐름:
roi_in = {0, 0, input_width, input_height, 1.0}
roi_out = roi_in
for module from first to last:
piece->buf_in = roi_in
if piece not skipped:
module->modify_roi_out(module, piece, &roi_out, &roi_in)
else:
roi_out = roi_in
piece->buf_out = roi_out
roi_in = roi_out
final width/height = roi_out.width/height
예:
crop: 출력 크기를 줄일 수 있다.rotate,lens: 결과 좌표계와 크기를 바꿀 수 있다.- 일반 exposure/curve 같은 point operation: ROI 변화 없음.
7.2 실제 처리 입력 영역 계산: modify_roi_in()
함수: _dev_pixelpipe_process_rec() 내부
최종 출력 ROI가 주어졌을 때, 현재 모듈은 다음 질문에 답해야 한다.
“내가 이
roi_out을 만들려면 이전 모듈 결과에서 어떤roi_in이 필요한가?”
예:
| 모듈 유형 | roi_in 특성 |
|---|---|
| point operation | roi_in == roi_out |
| blur/sharpen/denoise | 주변 픽셀이 필요하므로 roi_in이 더 크다. |
| crop | 출력 좌표를 crop 이전 좌표로 되돌린다. |
| rotate/lens/ashift | 역기하학 변환으로 필요한 입력 영역을 계산한다. |
| retouch | 복제/힐링 소스 영역까지 포함할 수 있다. |
| finalscale | 최종 스케일에 맞춰 입력 요구 영역이 바뀐다. |
이 과정은 마지막 모듈에서 첫 모듈 방향으로 진행된다. 그 후 재귀가 풀리면서 실제 처리는 첫 모듈에서 마지막 모듈 방향으로 진행된다.
8. 캐시 구조와 해시
8.1 캐시가 해결하는 문제
다크룸에서 사용자가 exposure 슬라이더를 움직였다고 해도, RAW 디코딩, demosaic, lens correction 같은 앞단 결과는 그대로일 수 있다. pixelpipe cache는 이런 중간 결과를 저장해 재처리를 줄인다.
캐시 hit가 나면 현재 모듈 이전의 재귀 처리를 더 들어가지 않고 바로 버퍼를 반환할 수 있다. 이것이 UI 반응성에 매우 중요하다.
8.2 해시 구성
함수: dt_dev_pixelpipe_cache_hash()
기본 해시는 대략 다음을 포함한다.
image id
pipe type
want_detail_mask
input/work/output/export profile info
position 이전까지의 piece->hash
color picker box/point 상태, 필요한 경우
ROI
scharr detail mask hash
중요한 점:
piece->hash는commit_params()단계에서 모듈 파라미터와 enable/blend 상태를 반영한다.- 컬러 프로파일은 개별
piece->hash가 아니라 파이프 base hash에 포함된다. - ROI가 다르면 같은 모듈 상태라도 다른 cacheline이다.
- mask display 또는
nocache상태에서는 캐시 사용이 제한된다. - GPU
cl_mem자체는 cache하지 않는다. 필요한 경우 host buffer로 복사해 cache/backbuf에 쓴다.
8.3 캐시 라인 선택
캐시는 최소 2개 라인을 특별 취급한다.
DT_PIPECACHE_MIN = 2- 캐시 라인이 2개뿐이거나 mask/nocache 모드이면 앞의 두 라인을 ping-pong 버퍼처럼 번갈아 쓴다.
- history cache가 충분히 있는 경우에는 2번 이후 라인부터 hash cache로 사용한다.
새 버퍼가 필요하면:
- invalid cacheline 우선
- free cacheline 우선
- 가장 오래된 cacheline 선택
- 단, 가장 최근 사용한 라인은 가급적 피함
중요 cacheline은 used 값을 음수로 만들어 오래 유지한다. 예를 들어 현재 포커스된 모듈의 입력 버퍼, 마지막 히스토리 모듈의 입력 버퍼 등은 다시 바뀔 가능성이 높기 때문에 중요하게 표시된다.
8.4 무효화
핵심 함수:
| 함수 | 의미 |
|---|---|
dt_dev_pixelpipe_cache_flush(pipe) | 전체 캐시 무효화. 내부적으로 order 0부터 invalidate. |
dt_dev_pixelpipe_cache_invalidate_later(pipe, order, info) | ioporder >= order인 cacheline 무효화. |
dt_dev_pixelpipe_invalidate_cacheline(pipe, data) | 특정 버퍼 포인터에 해당하는 cacheline 무효화. |
dt_dev_pixelpipe_important_cacheline(pipe, data, size) | 특정 cacheline을 중요하게 표시. |
dt_dev_pixelpipe_cache_checkmem(pipe) | invalid line 제거, 메모리 제한 초과 시 오래된 line 해제. |
무효화가 필요한 대표 상황:
- 모듈 파라미터 변경
- 모듈 enable/disable
- 모듈 순서 변경 또는 인스턴스 추가/삭제
- 컬러 프로파일 변경
- detail mask 필요 상태 변경
- raster mask 새로 생성/삭제
- CPU path에서 input buffer를 in-place 컬러스페이스 변환한 경우
- mask display 모드
- OpenCL 오류 후 CPU 재시작
9. CPU 처리 경로
함수: _pixelpipe_process_on_CPU()
CPU 처리의 흐름:
1. shutdown 확인
2. input/output buffer alignment 확인
3. input colorspace, module input colorspace, module output colorspace 확인
4. RAW/CFA 정보 준비
5. 필요하면 input을 module input colorspace로 변환
6. 변환이 in-place로 cache buffer를 바꾸면 해당 cacheline 무효화
7. histogram 요청 처리
8. 모듈/블렌딩 타일링 요구량과 메모리 적합성 확인
9. 메모리에 안 맞고 타일링 가능하면 process_tiling()
10. 아니면 process()
11. 디버그 dump/benchmark 처리
12. output colorspace 기록
13. color picker 입력/출력 샘플링
14. blending이 필요하면 input/output을 blend colorspace로 변환
15. dt_develop_blend_process()
16. shutdown 여부 반환
9.1 컬러스페이스 변환
모듈마다 기대하는 입력 컬러스페이스가 다를 수 있다.
예:
이전 모듈 출력 cst_from
│
├─ 모듈 input_colorspace() == cst_to
▼
dt_ioppr_transform_image_colorspace(... cst_from -> cst_to ...)
CPU path에서는 입력 버퍼가 in-place로 변환된다. 이 경우 cacheline의 descriptor가 실제 데이터와 맞지 않을 수 있으므로 해당 input cacheline을 무효화한다.
9.2 타일링 판단
모듈의 tiling_callback()은 다음 정보를 제공한다.
| 필드 | 의미 |
|---|---|
factor | CPU에서 입력 이미지 버퍼 크기의 몇 배 메모리가 필요한지. |
factor_cl | GPU에서 몇 배 메모리가 필요한지. |
maxbuf | CPU 임시 버퍼 최대 요구량. |
maxbuf_cl | GPU 임시 버퍼 최대 요구량. |
overhead | 이미지 크기와 별개로 필요한 추가 메모리. |
overlap | 타일 사이에 필요한 겹침 픽셀. |
align | 타일 좌상단 정렬 요구. |
블렌딩이 켜져 있으면 blendop의 타일링 요구량도 계산해 모듈 요구량과 합친다.
9.3 일반 처리 vs 타일 처리
조건:
if host memory에 안 맞고 piece가 타일링 가능:
module->process_tiling(...)
else:
module->process(...)
모듈이 타일링을 허용하지 않는데 메모리 요구량이 큰 경우에는 경고를 내면서 untiled 처리로 진행할 수 있다. 이 경우 시스템 메모리 제한에 더 직접적으로 영향을 받는다.
9.4 블렌딩
모듈 자체 처리 결과와 원본 입력을 blend할 필요가 있으면:
- 입력과 출력 이미지를 blend colorspace로 변환한다.
dt_develop_blend_process()를 호출한다.- parametric mask, drawn mask, raster mask, blend mode 등이 여기에서 결합된다.
포커스된 모듈에서 모듈 본체 처리 결과는 그대로인데 blending 파라미터만 자주 바뀌는 경우가 있다. 이를 위해 bcache_data/bcache_hash로 모듈 처리 결과를 재사용하는 빠른 블렌드 캐시가 있다.
10. OpenCL 처리 경로
OpenCL 경로는 _dev_pixelpipe_process_rec() 내부의 #ifdef HAVE_OPENCL 구간에서 처리된다.
10.1 OpenCL 사용 조건
OpenCL path가 가능하려면 대략 다음 조건을 만족해야 한다.
darktable.opencl->inited == true
opencl not stopped
pipe->opencl_enabled == true
pipe->devid > DT_DEVICE_CPU
module->process_cl exists
piece->process_cl_ready == true
preview에서 IOP_FLAGS_PREVIEW_NON_OPENCL이 아님
추가로:
- 이미지가 해당 GPU 메모리에 맞는지 확인한다.
- 너무 크면 타일링 가능 여부를 본다.
- GPU 타일링이 가능하더라도 CPU 대비 이점이 낮다고 판단되면 CPU path로 보낼 수 있다.
- 일부 blend colorspace/profile 조건은 OpenCL path를 막을 수 있다.
10.2 GPU 직접 처리
메모리에 맞으면:
1. input이 host에만 있으면 cl_mem_input으로 복사
2. 필요하면 GPU에서 colorspace 변환
3. histogram 요청 시 GPU buffer를 읽어 histogram 생성
4. cl_mem_output 할당
5. module->process_cl(...)
6. 필요하면 color picker
7. 필요하면 blend colorspace 변환
8. 가능하면 dt_develop_blend_process_cl()
9. dt_opencl_finish_sync_pipe()
10. 최종 또는 cache 필요 시 host로 copy back
10.3 GPU 타일링 처리
GPU 전체 이미지 처리에는 메모리가 부족하지만 타일링이 가능하면:
- input이 GPU에만 있으면 host로 복사한다.
- 필요하면 CPU에서 colorspace 변환한다.
- histogram은 CPU 경로로 수집할 수 있다.
module->process_tiling_cl()을 호출한다.- blending은 CPU로 처리될 수 있다.
- sync 후 다음 단계로 진행한다.
10.4 CPU fallback
OpenCL 처리 실패 시:
1. cl_mem_output release
2. input이 GPU에만 있으면 host로 되돌림
3. OpenCL event 정리
4. _pixelpipe_process_on_CPU() 호출
더 늦은 시점, 즉 최종 cl_mem_output을 host로 복사하는 과정에서 오류가 감지될 수도 있다. 이 경우 pipe->opencl_error = TRUE로 표시된다.
10.5 전체 파이프 CPU 재시작
dt_dev_pixelpipe_process() 최상위에서 OpenCL event error 또는 pipe->opencl_error가 감지되면:
cl resource release
OpenCL device unlock
pipe->opencl_enabled = FALSE
pipe->devid = DT_DEVICE_CPU
OpenCL error count 증가
필요하면 세션 전체 OpenCL 중지
pipe cache flush
dt_dev_pixelpipe_change(pipe, dev)
goto restart
즉 OpenCL 오류가 발생해도 사용자는 대부분 “느려지지만 처리되는” CPU fallback을 보게 된다.
11. 타일링 시스템
위치: src/develop/tiling.h, src/develop/tiling.c
타일링은 큰 이미지 또는 메모리 사용량이 큰 모듈을 작은 조각으로 나눠 처리하기 위한 시스템이다.
11.1 모듈이 제공하는 정보
각 모듈은 tiling_callback()으로 자신이 필요한 메모리를 보고한다.
예시 의미:
필요 메모리 ≈ factor * image_buffer_size + overhead
GPU는 factor_cl, maxbuf_cl를 별도로 가질 수 있다. 모듈이 GPU 값을 설정하지 않으면 CPU 값을 기본으로 사용한다.
11.2 타일 ROI 계산
타일링은 단순히 이미지를 일정 격자로 자르는 것이 아니다. modify_roi_in()과 modify_roi_out()을 고려해야 한다.
예를 들어 blur는 주변 픽셀 overlap이 필요하고, lens correction은 출력 타일에 대응하는 입력 영역이 비선형일 수 있다. tiling.c에는 ROI를 맞추기 위한 보정과 탐색 로직이 들어 있다. 내부적으로 Nelder-Mead simplex 기반 보정도 사용된다.
11.3 타일링 실패/제한
타일 수가 비정상적으로 많아질 수 있으므로 기본적으로 “합리적인 최대 타일 수” 제한이 있다. resource level이 unrestricted이면 이 제한이 크게 완화된다.
12. 마스크, detail mask, raster mask
12.1 mask display 모드
pipe->mask_display가 켜지면 파이프는 일반 이미지 결과 대신 특정 마스크나 채널을 보여줘야 한다.
특수 경로:
if module is not gamma
and mask display active
and module is not DISTORT
and input/output bpp same
and roi_in == roi_out:
module processing bypass
input을 output으로 copy/pass-through
이렇게 하면 마스크 표시 중에는 픽셀 내용을 바꾸는 일반 모듈을 건너뛰고, 기하학/왜곡 모듈은 유지한다. 최종적으로 gamma 계열 모듈이 마스크/채널 데이터를 표시 가능한 형태로 처리한다.
12.2 detail mask
pipe->want_detail_mask가 켜지면 rawprepare 또는 demosaic 부근에서 detail mask용 Scharr 데이터가 준비된다. 이 상태는 cache hash에 포함된다. 따라서 detail mask가 필요해지는 순간 이전 cacheline을 그대로 쓰지 않고 무효화한다.
12.3 raster mask
일부 모듈은 자신이 만든 mask를 뒤쪽 모듈의 blending mask로 제공할 수 있다.
흐름:
source module이 raster mask 생성
│
├─ piece->raster_masks에 저장
│
▼
target module이 dt_dev_get_raster_mask()로 요청
│
├─ source가 target보다 뒤에서 처리되는 모듈이면 거부
├─ source가 disabled이거나 mask를 쓰지 않으면 삭제/무시
└─ source와 target 사이의 기하학 모듈을 따라 mask를 왜곡/변환
기하학 모듈 경계에서 왜곡된 detail/raster mask는 detail_mask_cache, raster_mask_cache에 저장되어 재사용된다.
13. 히스토그램과 컬러 피커
13.1 히스토그램
각 piece는 request_histogram과 histogram_params를 갖는다. 처리 중 요청이 켜져 있으면 모듈 입력 또는 해당 단계의 buffer에서 histogram을 수집한다.
CPU:
_histogram_collect(piece, input, roi_in, ...)
OpenCL:
_histogram_collect_cl(...)
OpenCL histogram은 GPU buffer를 host로 읽어 계산하므로 큰 이미지에서는 비효율적이며 주로 작은 preview에서 의미가 있다.
13.2 컬러 피커
모듈이 color picker를 요청하면 파이프 처리 중 입력/출력 샘플을 수집한다.
- input picker: 모듈 처리 전 색상
- output picker: 모듈 처리 후, blending 전 색상
- blend colorspace가 필요하면 변환 후 picker를 수행할 수 있다.
- GPU path에서는 필요한 작은 영역을 host로 읽어 picker를 계산한다.
Picker 데이터가 준비되면 DT_SIGNAL_CONTROL_PICKERDATA_READY signal을 올린다.
14. shutdown / 조기 중단 / 재시작
dt_dev_pixelpipe_t에는 atomic shutdown 플래그가 있다.
dt_dev_pixelpipe_stopper_t:
| 값 | 의미 |
|---|---|
DT_DEV_PIXELPIPE_STOP_NO | 정상 실행. |
DT_DEV_PIXELPIPE_STOP_NODES | 노드 구조가 바뀌었으므로 중단 후 rebuild 필요. |
DT_DEV_PIXELPIPE_STOP_HQ | HQ 모드 전환 등으로 재시작 필요. 캐시는 유지 가능. |
DT_DEV_PIXELPIPE_STOP_LAST 이상 | 특정 module iop_order로 해석. 해당 모듈 이후 cacheline 무효화 필요. |
재귀 처리 중 계속 _pipe_has_shutdown(pipe)를 확인한다. shutdown이 발견되면 가능한 빨리 빠져나가고, 필요한 경우 input cacheline이나 downstream cacheline을 무효화한다.
예:
모듈 처리 중 shutdown이 module iop_order로 세팅됨
│
├─ 현재 input cacheline 무효화
├─ iop_order 이후 cacheline 무효화
└─ 파이프는 unfinished/error로 상위에 반환
15. 스레딩과 동시성
pixelpipe는 GUI thread와 분리된 처리 thread에서 실행될 수 있다. 그래서 여러 스냅샷과 mutex가 사용된다.
| 장치 | 역할 |
|---|---|
dev->history_mutex | 히스토리 스택과 파이프 동기화 보호. |
pipe->busy_mutex | 파이프 실행 중 노드 cleanup/rebuild와 충돌 방지. |
pipe->backbuf_mutex | 최종 결과 버퍼 게시/읽기 보호. |
pipe->image | dev->image_storage의 실행 시점 복사본. |
pipe->iop, pipe->iop_order_list, pipe->forms | 실행 시점의 모듈/order/mask 스냅샷. |
piece->data | GUI의 module->params를 직접 읽지 않는 파이프 전용 처리 데이터. |
모듈의 process() 내부 픽셀 루프는 보통 OpenMP macro인 DT_OMP_FOR()를 사용해 CPU 병렬화된다. GUI API를 process()에서 직접 호출하면 안 된다.
16. 새 IOP 모듈이 파이프라인에 들어가는 방식
새 모듈을 추가할 때 필요한 핵심 지점:
src/iop/<module>.c작성src/iop/CMakeLists.txt에 등록src/common/iop_order.c의 관련 order list에 추가- 필요하면
_insert_before()등 동적 order 보정 규칙 추가 - 필수 콜백 구현
name()default_colorspace()process()
- 필요 시 선택 콜백 구현
init_pipe()/cleanup_pipe()commit_params()input_format()/output_format()input_colorspace()/output_colorspace()modify_roi_in()/modify_roi_out()tiling_callback()process_cl()/process_tiling_cl()distort_mask()등
16.1 params_t와 data_t
모듈 파라미터에는 두 층이 있다.
| 구분 | 위치 | 목적 |
|---|---|---|
params_t | module->params | DB/히스토리/UI에 저장되는 사용자 의도. 포인터를 넣으면 안 된다. 버전 관리 필요. |
data_t | piece->data | 처리에 최적화된 런타임 데이터. LUT, matrix, spline, 정규화 값, profile 포인터 등을 둘 수 있다. |
기본 commit_params()는 params_t를 piece->data에 복사한다. 복잡한 모듈은 자체 data_t를 만들고 commit_params()에서 전처리 값을 계산한다.
process()는 module->params가 아니라 piece->data를 읽어야 한다. 그래야 GUI 변경과 처리 thread가 안전하게 분리된다.
16.2 ROI 콜백 구현 기준
| 모듈 유형 | 필요한 ROI 콜백 |
|---|---|
| 단순 point operation | 기본 ROI pass-through로 충분할 수 있다. |
| 주변 픽셀 필요 | modify_roi_in()에서 padding 확장. |
| 출력 크기 변경 | modify_roi_out() 구현. |
| crop/rotate/lens/finalscale | modify_roi_out()와 modify_roi_in() 모두 중요. |
| mask 왜곡 필요 | distort_mask() 구현 고려. |
16.3 타일링 플래그
타일링 가능한 모듈은 IOP_FLAGS_ALLOW_TILING을 선언하고 tiling_callback()을 정확히 구현해야 한다. ROI 확장, overlap, align 요구를 잘못 보고하면 타일 경계 artifact나 buffer overrun이 발생할 수 있다.
17. 디버깅 포인트
| 목적 | 방법 |
|---|---|
| 파이프 실행 흐름 보기 | darktable -d pipe |
| 캐시 메모리/라인 보기 | darktable -d memory |
| OpenCL 경로/오류 보기 | darktable -d opencl |
| 타일링 결정 보기 | darktable -d tiling |
| NaN/Inf 탐지 | -d nan 계열 debug 사용 |
| 모듈 성능 벤치 | darktable.bench_module 관련 설정/디버그 경로 |
| 중간 PFM dump | dump_pfm_pipe, dump_diff_pipe 관련 디버그 옵션 |
파이프 로그에서 자주 보게 되는 메시지 의미:
| 로그 | 의미 |
|---|---|
pipe starting | 파이프 실행 시작, device/ROI/메모리 정보 출력. |
pipe data: from cache | 현재 위치 결과를 cache에서 얻어 이전 재귀 처리를 생략. |
modified roi IN | 모듈이 출력 ROI에 필요한 입력 ROI를 바꿈. |
modified roi OUT | 전체 dimension 계산 중 모듈이 출력 크기를 바꿈. |
process | CPU 또는 GPU 직접 처리. |
process tiles | CPU 타일링 처리. |
process_tiling | OpenCL 타일링 또는 관련 경로. |
pipe bypass | mask display 등으로 일반 모듈 처리를 우회. |
importance hints | 특정 input cacheline을 오래 유지하도록 중요 표시. |
pipe restarting on CPU | OpenCL 오류로 캐시를 비우고 CPU로 재시작. |
pipe finished | 최종 결과 backbuf 게시 완료. |
18. 핵심 호출 흐름 상세
18.1 이미지/히스토리 변경 후
사용자 슬라이더 변경 / 모듈 enable / history 변경
│
├─ dt_dev_add_history_item() 계열
├─ pipe changed flag 설정
├─ dt_dev_invalidate_all()
└─ render job에서 dt_dev_pixelpipe_change()
│
├─ TOP_CHANGED → synch_top
├─ SYNCH → synch_all
└─ REMOVE → cleanup_nodes + create_nodes + synch_all
18.2 파이프 처리 시작
dt_dev_pixelpipe_process()
│
├─ OpenCL device lock/check
├─ cache_checkmem
├─ 최종 ROI 구성
├─ forms snapshot
└─ _dev_pixelpipe_process_rec_and_backcopy()
18.3 재귀 처리
_process_rec(last module, final roi)
│
├─ cache hit? yes → return cached buffer
│
├─ module->modify_roi_in(final roi → needed previous roi)
│
└─ _process_rec(previous module, needed previous roi)
│
└─ ...
│
└─ base input import/clip/zoom
│
▼
previous output 확보
│
├─ current output cacheline 확보
├─ CPU/OpenCL/tile 처리
├─ blend/mask/histogram/picker
└─ return current output
18.4 최종 게시
_process_rec_and_backcopy()
├─ final cl_mem_output이 있으면 host로 copy back
└─ busy_mutex unlock
dt_dev_pixelpipe_process()
├─ OpenCL late error면 CPU restart
├─ screen pipe면 backbuf에 8-bit/RGBA 결과 copy
├─ export/image pipe면 backbuf가 처리 buffer를 가리킬 수 있음
├─ backbuf_width/height/scale/hash 저장
└─ processing = FALSE
19. 주요 파일 맵
| 파일 | 역할 |
|---|---|
src/develop/pixelpipe.c | 실제로 pixelpipe_hb.c를 include하는 얇은 entry file. |
src/develop/pixelpipe_hb.h | pixelpipe/piece/cache 관련 핵심 구조체와 public 함수 선언. |
src/develop/pixelpipe_hb.c | pixelpipe 초기화, 노드 생성/동기화, ROI 재귀 처리, CPU/OpenCL 경로, backbuf 게시, 마스크 처리. |
src/develop/pixelpipe_cache.h | pixelpipe cache 구조체와 API 선언. |
src/develop/pixelpipe_cache.c | 해시 구성, cache hit/miss, cacheline 선택, invalidation, memory check. |
src/develop/pixelpipe.h | dt_iop_roi_t, pipe type, mask display enum 등 공통 정의. |
src/develop/develop.h | dt_develop_t, history, iop list, viewport, preview/full pipe 관계. |
src/develop/develop.c | history 변경, pipe sync/rebuild/invalidation 호출 흐름. |
src/develop/imageop.h | dt_iop_module_t 구조체, 모듈 flag, GUI/파이프 연결. |
src/develop/imageop.c | 모듈 로드, GUI 이벤트, blend params commit, raster mask 관리 등. |
src/iop/iop_api.h | IOP 모듈이 구현해야 하는 callback API. |
src/common/iop_order.h | IOP order 개념, 버전, API 선언. |
src/common/iop_order.c | 실제 order list와 모듈 순서 관리 구현. |
src/develop/tiling.h | 타일링 구조체와 API. |
src/develop/tiling.c | 타일 크기/ROI 계산, 메모리 추정, CPU/OpenCL 타일링 보조 로직. |
dev-doc/pixelpipe_architecture.md | pixelpipe 구조와 흐름에 대한 개발 문서. |
dev-doc/IOP_Module_API.md | 새 IOP 모듈 작성용 API 문서. |
dev-doc/New_Module_Guide.md | 새 모듈 추가 절차와 order 등록 주의점. |
20. 읽는 순서 추천
소스 코드를 처음 추적한다면 다음 순서가 가장 이해하기 쉽다.
dev-doc/pixelpipe_architecture.mdsrc/develop/pixelpipe.hsrc/develop/pixelpipe_hb.hsrc/iop/iop_api.hsrc/develop/imageop.hsrc/common/iop_order.hsrc/develop/pixelpipe_cache.hsrc/develop/pixelpipe_cache.csrc/develop/pixelpipe_hb.c- 초기화 함수부터 읽기
dt_dev_pixelpipe_create_nodes()dt_dev_pixelpipe_synch_all()dt_dev_pixelpipe_change()dt_dev_pixelpipe_get_dimensions()dt_dev_pixelpipe_process()_dev_pixelpipe_process_rec()_pixelpipe_process_on_CPU()- OpenCL 처리 구간
src/develop/tiling.c- 관심 있는 실제 IOP 모듈, 예:
src/iop/exposure.c,src/iop/demosaic.c,src/iop/filmicrgb.c
21. 결론
darktable의 코어 파이프라인은 단순한 “모듈 리스트를 앞에서 뒤로 실행하는 루프”가 아니다. 실제 구조는 다음 요소가 결합된 시스템이다.
- 목적별 다중 pixelpipe
- history/order 기반 모듈 그래프
- 파이프별 piece 데이터 복사
- ROI backward planning + forward processing
- hash 기반 중간 결과 cache
- CPU/OpenCL/tile adaptive execution
- blending/mask/raster/detail mask 통합
- histogram/color picker 같은 GUI 연동 데이터 수집
- atomic shutdown과 OpenCL 오류 복구
이 설계 덕분에 darktable은 고해상도 RAW 편집에서도 화면에 필요한 영역만 빠르게 계산하고, 반복되는 앞단 결과를 cache하며, GPU가 가능하면 활용하고, 실패하면 CPU로 복구할 수 있다.