-
-
Notifications
You must be signed in to change notification settings - Fork 172
Expand file tree
/
Copy pathMakefile
More file actions
1239 lines (1083 loc) · 63.8 KB
/
Copy pathMakefile
File metadata and controls
1239 lines (1083 loc) · 63.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# TinyEXR Makefile
#
# make - build the legacy v1 test executable (test_tinyexr)
# make lib - build the pure-C11 v3 library (build/libtinyexr3.a)
# make test-c - build + run the pure-C11 v3 reader unit test (ASan+UBSan)
# make c11-gate - compile every src/*.c as strict C11 -Werror (no C++)
# make fuzz-corpus - replay regression corpus under ASan+UBSan+LSan
# make fuzz-corpus-asan - same replay with LSan disabled for ptrace sandboxes
# make clean
CC ?= gcc
CXX ?= g++
EMCC ?= emcc
EMAR ?= emar
NODE ?= node
CFLAGS ?= -O2
CXXFLAGS ?= -O2 -std=c++11
INCLUDES = -I./deps/miniz
MINIZ_SRC = ./deps/miniz/miniz.c
# ---- legacy v1 single-header test (unchanged) -----------------------------
TARGET = test_tinyexr
.PHONY: all test clean help lib test-c test-c-threads test-c-tsan c11-gate texcomp-web fuzz fuzz-jph fuzz-libdeflate fuzz-corpus fuzz-corpus-asan parse-test wasm freestanding-gate freestanding-zstd-gate examples-c bench bench-compare bench-htj2k arm-smoke host-smoke gpu-test vk-test jph-gpu-test bench-gpu-jph texcomp texcomp-arm texcomp-c11-gate texcomp-test texcomp-bench texcomp-astc-psnr texcomp-astc-arm-smoke texcomp-astc-arm-gate texcomp-astc-hdr-gate texcomp-uni-gate texcomp-bc6h-gate texcomp-wasm texcomp-wasm-simd wasm-texcomp wasm-texcomp-simd ptexatlas-c11-gate
all: $(TARGET)
$(TARGET): test_tinyexr.cc miniz.o
$(CXX) $(CXXFLAGS) $(INCLUDES) -o $@ $< miniz.o $(LDFLAGS)
miniz.o: $(MINIZ_SRC)
$(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $<
test: $(TARGET)
./$(TARGET) asakusa.exr
# ---- aggregate: all self-contained tool gates -----------------------------
# CI entry point for tools/ (texcomp, resize/tir, texpipe, envmap). `tools-test`
# runs the pure-C11 gates only (no C++ / no astcenc); `tools-test-all` also runs
# the astcenc conformance cross-checks (astcenc is vendored in deps/, but the
# gate compiles its C++ so it needs a C++ toolchain).
.PHONY: tools-test tools-test-all
tools-test: texcomp-c11-gate texcomp-test texcomp-uni-gate texcomp-xbc7-gate \
texcomp-bc6h-gate \
resize-c11-gate resize-test \
texpipe-c11-gate texpipe-test \
envmap-c11-gate envmap-test envmap-pbr-test ptexatlas-c11-gate
@echo "tools-test: all self-contained tool gates passed"
tools-test-all: tools-test texcomp-astc-hdr-gate texcomp-astc-arm-gate
@echo "tools-test-all: all tool gates (incl. astcenc cross-checks) passed"
# TinyEXR/TinyUSDZ shared Ptex reader + coarse atlas packer. Keep this gate
# independent of the C++ legacy library so it remains usable in freestanding
# and WASM-oriented builds.
ptexatlas-c11-gate:
$(CC) -std=c11 -Wall -Wextra -Werror -Itools/ptexatlas -Ideps/miniz \
-fsyntax-only tools/ptexatlas/ptex.c tools/ptexatlas/texatlas.c
# ---- pure-C11 v3 library + tests ------------------------------------------
V3_INC = -Iinclude -Isrc -Ideps/zstd
V3_CSTD = -std=c11
V3_WARN = -Wall -Wextra -Werror
# Enable the validated AVX2 four-quad HALF cleanup kernel on x86. The source
# keeps the scalar path and runtime capability check for baseline portability.
V3_DEFS = -DEXR_JPH_ENABLE_AVX2_FOUR_QUAD16=1
V3_OPT ?= -O2
V3_ARCH ?=
# Opt-in Ryzen/AVX2 tuning for the HTJ2K translation unit. The portable
# default remains baseline code with runtime CPU dispatch; this mode produces
# a host-specific object and therefore requires an AVX2-capable x86 CPU.
EXR_JPH_HOST_AVX2 ?= 0
JPH_HOST_OPT =
ifeq ($(EXR_JPH_HOST_AVX2),1)
V3_DEFS += -DEXR_JPH_HOST_AVX2=1
JPH_HOST_OPT = -O3 -mavx2 -mfma -mbmi2 -mtune=znver1
endif
# Opt-in Apple Silicon tuning for the NEON HTJ2K translation unit. Clang's
# generic arm64 scheduler leaves a measurable amount of throughput on the
# table in the entropy cleanup loop; this mode is intended for binaries built
# specifically for an Apple M-series host, not portable distribution builds.
EXR_JPH_HOST_NEON ?= 0
ifeq ($(EXR_JPH_HOST_NEON),1)
JPH_HOST_OPT += -O3 -mcpu=apple-m1
endif
V3_SRC = $(wildcard src/*.c)
V3_OBJ = $(patsubst src/%.c,build/%.o,$(V3_SRC))
# Freestanding core: everything except the optional stdio layer, the spectral
# helpers (hosted-only convenience), and the (freestanding-only) mem/str impls.
V3_CORE_SRC = $(filter-out src/exr_stdio.c src/exr_freestanding.c src/exr_spectral.c src/exr_gpu_cuda.c src/exr_vk_vulkan.c,$(V3_SRC))
ZSTD_SRC = deps/zstd/tinyexr_zstd.c
ZSTD_OBJ = build/tinyexr_zstd.o
V3_TEST_OBJ = $(patsubst src/%.c,build/test-%.o,$(V3_SRC))
SAN = -fsanitize=address,undefined
# ---- zlib (DEFLATE) backend for ZIP/ZIPS/PXR24 ----------------------------
# DEFLATE = auto | libdeflate | intree (default: auto)
# auto / libdeflate : compile the vendored libdeflate (deps/libdeflate, MIT -
# see deps/libdeflate/COPYING) AND make it the runtime
# default - it is faster on natural-image data. Both
# codecs are linked; switch at runtime with the public
# exr_zlib_set_backend(). EXR_ZLIB_DEFAULT_LIBDEFLATE=1
# seeds the static default (see src/exr_codec.c).
# intree : in-tree pure-C codec only, no external dependency.
# The freestanding and wasm targets never define EXR_USE_LIBDEFLATE (their flag
# sets / V3_CORE_SRC omit it), so they always use the in-tree codec regardless.
# Legacy: LIBDEFLATE=1 is kept as an alias for DEFLATE=libdeflate.
# NOTE: run `make clean` when changing DEFLATE (object flags are not tracked).
DEFLATE ?= auto
ifeq ($(LIBDEFLATE),1)
DEFLATE = libdeflate
endif
LD_OBJ =
LD_TEST_OBJ =
ifneq ($(DEFLATE),intree)
V3_DEFS += -DEXR_USE_LIBDEFLATE -DEXR_ZLIB_DEFAULT_LIBDEFLATE=1
V3_INC += -Ideps/libdeflate
# Just the zlib (DEFLATE) path: no crc32/gzip. The x86/arm cpu_features files
# self-guard by arch, so compiling both is safe everywhere.
LD_SRC = deps/libdeflate/lib/adler32.c \
deps/libdeflate/lib/deflate_compress.c \
deps/libdeflate/lib/deflate_decompress.c \
deps/libdeflate/lib/zlib_compress.c \
deps/libdeflate/lib/zlib_decompress.c \
deps/libdeflate/lib/utils.c \
deps/libdeflate/lib/x86/cpu_features.c \
deps/libdeflate/lib/arm/cpu_features.c
LD_OBJ = $(patsubst deps/libdeflate/%.c,build/libdeflate/%.o,$(LD_SRC))
LD_TEST_OBJ = $(patsubst deps/libdeflate/%.c,build/test-libdeflate/%.o,$(LD_SRC))
endif
# ---- optional C11-threads multithreading (default OFF; serial is the default)
# Build any target with THREADS=1 to enable per-block parallel encode/decode
# (src/exr_thread.c, uses <threads.h>). Default and freestanding builds stay
# single-threaded. NOTE: run `make clean` when toggling THREADS.
THREADS ?= 0
THREAD_LIBS =
ifeq ($(THREADS),1)
V3_DEFS += -DEXR_USE_THREADS
THREAD_LIBS = -pthread # C11 threads need pthreads on glibc < 2.34
endif
# ---- optional CUDA GPU backend (default OFF; runtime dlopen via cuew) -------
# Build any target with CUDA=1 to compile the GPU backend (src/exr_gpu_cuda.c +
# third_party/cuew). The CUDA driver and NVRTC are resolved at runtime via cuew
# (dlopen) so NO CUDA SDK is needed at build time and we link only -ldl (never
# -lcuda/-lnvrtc). Without CUDA=1 the backend compiles as inert stubs.
# NOTE: run `make clean` when toggling CUDA (object flags are not tracked).
CUDA ?= 0
CUEW_OBJ =
CUDA_LIBS =
ifeq ($(CUDA),1)
V3_DEFS += -DEXR_USE_CUDA
V3_INC += -Ithird_party/cuew
CUEW_OBJ = build/cuew.o
CUDA_LIBS = -ldl
endif
# ---- optional Vulkan GPU backend (default OFF; runtime dlopen via vkew) -----
# Build any target with VULKAN=1 to compile the Vulkan compute backend
# (src/exr_vk_vulkan.c + third_party/vkew). libvulkan is resolved at runtime via
# vkew (dlopen) so NO Vulkan SDK is needed at build time and we link only -ldl
# (never -lvulkan). Compute shaders are precompiled SPIR-V embedded in
# src/exr_vk_shaders.spv.inc. Without VULKAN=1 the backend compiles as inert
# stubs. NOTE: run `make clean` when toggling VULKAN (object flags not tracked).
VULKAN ?= 0
VKEW_OBJ =
VULKAN_LIBS =
ifeq ($(VULKAN),1)
V3_DEFS += -DEXR_USE_VULKAN
V3_INC += -Ithird_party/vkew
VKEW_OBJ = build/vkew.o
VULKAN_LIBS = -ldl
endif
build:
@mkdir -p build
# cuew (Apache-2.0, third-party: warnings off).
build/cuew.o: third_party/cuew/cuew.c third_party/cuew/cuew.h | build
$(CC) -Ithird_party/cuew -O2 -g -w -c $< -o $@
# vkew (Vulkan loader, third-party style: warnings off).
build/vkew.o: third_party/vkew/vkew.c third_party/vkew/vkew.h | build
$(CC) -Ithird_party/vkew -O2 -g -w -c $< -o $@
# GPU backend TU: extra prereqs (public header, kernels, cuew) so edits rebuild.
build/exr_gpu_cuda.o: src/exr_gpu_cuda.c include/exr_gpu.h include/exr.h \
src/exr_internal.h src/exr_gpu_kernels.cuh.inc \
src/exr_gpu_jph_kernels.cuh.inc | build
$(CC) $(V3_CSTD) $(V3_WARN) $(V3_DEFS) $(V3_INC) $(V3_OPT) $(V3_ARCH) -g -c $< -o $@
# Vulkan backend TU: extra prereqs (public header, embedded SPIR-V, vkew).
build/exr_vk_vulkan.o: src/exr_vk_vulkan.c include/exr_vk.h include/exr.h \
src/exr_internal.h src/exr_vk_shaders.spv.inc | build
$(CC) $(V3_CSTD) $(V3_WARN) $(V3_DEFS) $(V3_INC) -O2 -g -c $< -o $@
# Keep the host-tuned JPH build isolated to this TU. Runtime capability
# checks remain in exr_jph.c; EXR_JPH_HOST_AVX2=1 is intentionally a host-only
# build mode and must not be used for baseline-distributed binaries.
build/exr_jph.o: src/exr_jph.c src/exr_jph_avx2_inline.h include/exr.h \
src/exr_internal.h deps/zstd/tinyexr_zstd.h | build
$(CC) $(V3_CSTD) $(V3_WARN) $(V3_DEFS) $(V3_INC) $(V3_OPT) $(V3_ARCH) $(JPH_HOST_OPT) -g -c $< -o $@
build/%.o: src/%.c include/exr.h src/exr_internal.h deps/zstd/tinyexr_zstd.h | build
$(CC) $(V3_CSTD) $(V3_WARN) $(V3_DEFS) $(V3_INC) $(V3_OPT) $(V3_ARCH) -g -c $< -o $@
build/tinyexr_zstd.o: $(ZSTD_SRC) deps/zstd/tinyexr_zstd.h | build
$(CC) $(V3_CSTD) $(V3_INC) -O2 -g -w -c $< -o $@
# Vendored libdeflate (third-party: warnings off). Release + sanitized variants.
build/libdeflate/%.o: deps/libdeflate/%.c | build
@mkdir -p $(dir $@)
$(CC) -Ideps/libdeflate -O3 -g -w -c $< -o $@
build/test-libdeflate/%.o: deps/libdeflate/%.c | build
@mkdir -p $(dir $@)
$(CC) -Ideps/libdeflate -O1 -g $(SAN) -w -c $< -o $@
lib: $(V3_OBJ) $(ZSTD_OBJ) $(LD_OBJ) $(CUEW_OBJ) $(VKEW_OBJ)
$(AR) rcs build/libtinyexr3.a $(V3_OBJ) $(ZSTD_OBJ) $(LD_OBJ) $(CUEW_OBJ) $(VKEW_OBJ)
# GPU backend test (requires CUDA=1; skips at runtime with exit 77 if no device).
EXR_IMAGES ?= $(HOME)/work/openexr-images
gpu-test:
$(MAKE) clean # CUDA flag is not object-tracked; rebuild from clean
$(MAKE) CUDA=1 lib
$(CC) $(V3_CSTD) -Wall -Wextra -DEXR_USE_CUDA -Iinclude -Ithird_party/cuew \
-O2 -g test/gpu/test_exr_gpu.c build/libtinyexr3.a -ldl -lm \
-o build/test_exr_gpu
./build/test_exr_gpu "$(EXR_IMAGES)"; rc=$$?; \
if [ $$rc -eq 77 ]; then echo "gpu-test: SKIPPED (no CUDA device)"; exit 0; \
else exit $$rc; fi
# HTJ2K GPU block-coder bit-exactness test (CUDA=1; exit 77 = skip, no device).
jph-gpu-test:
$(MAKE) clean
$(MAKE) CUDA=1 lib
$(CC) $(V3_CSTD) -Wall -Wextra -DEXR_USE_CUDA -Iinclude -Isrc -Ideps/zstd \
-Ithird_party/cuew -O2 -g test/gpu/test_exr_jph_gpu.c build/libtinyexr3.a \
-ldl -lm -o build/test_exr_jph_gpu
./build/test_exr_jph_gpu "$(EXR_IMAGES)"; rc=$$?; \
if [ $$rc -eq 77 ]; then echo "jph-gpu-test: SKIPPED (no CUDA device)"; exit 0; \
else exit $$rc; fi
# Vulkan backend test (requires VULKAN=1; skips at runtime with exit 77 if no device).
vk-test:
$(MAKE) clean # VULKAN flag is not object-tracked; rebuild from clean
$(MAKE) VULKAN=1 lib
$(CC) $(V3_CSTD) -Wall -Wextra -DEXR_USE_VULKAN -Iinclude -Ithird_party/vkew \
-O2 -g test/vk/test_exr_vk.c build/libtinyexr3.a -ldl -lm \
-o build/test_exr_vk
./build/test_exr_vk "$(EXR_IMAGES)"; rc=$$?; \
if [ $$rc -eq 77 ]; then echo "vk-test: SKIPPED (no Vulkan device)"; exit 0; \
else exit $$rc; fi
# Strict pure-C11 gate: the rewrite must never require a C++ compiler.
c11-gate: | build
@for f in $(V3_SRC); do \
echo " C11 $$f"; \
$(CC) $(V3_CSTD) $(V3_WARN) $(V3_INC) -O1 -fsyntax-only $$f || exit 1; \
done
@echo "pure-C11 gate: OK"
build/test-%.o: src/%.c include/exr.h src/exr_internal.h deps/zstd/tinyexr_zstd.h | build
$(CC) $(V3_CSTD) -Wall -Wextra $(V3_DEFS) $(V3_INC) -O1 -g $(SAN) -c $< -o $@
build/test-tinyexr_zstd.o: $(ZSTD_SRC) deps/zstd/tinyexr_zstd.h | build
$(CC) $(V3_CSTD) $(V3_INC) -O1 -g $(SAN) -w -c $< -o $@
test-c: $(V3_TEST_OBJ) build/test-tinyexr_zstd.o $(LD_TEST_OBJ) test/unit/test_exr_v3.c | build
$(CC) $(V3_CSTD) -Wall -Wextra $(V3_DEFS) $(V3_INC) -O1 -g $(SAN) \
test/unit/test_exr_v3.c $(V3_TEST_OBJ) build/test-tinyexr_zstd.o $(LD_TEST_OBJ) $(THREAD_LIBS) -lm -o build/test_exr_v3
ASAN_OPTIONS=detect_leaks=0 ./build/test_exr_v3
# ---- tools/texcomp: pure-C11 BC/ETC/ASTC texture compression --------------
# One translation unit per codec, plus texcomp.c for the shared core
# (backend dispatch, options, sizes, DDS/KTX/ASTC containers).
TEXCOMP_INC = -Itools/texcomp/include -Iinclude -Isrc -Iexamples/common
TEXCOMP_SRC = tools/texcomp/src/texcomp.c \
tools/texcomp/src/texcomp_bc1.c tools/texcomp/src/texcomp_bc3.c \
tools/texcomp/src/texcomp_bc5.c tools/texcomp/src/texcomp_bc6h.c \
tools/texcomp/src/texcomp_bc7.c tools/texcomp/src/texcomp_etc2.c \
tools/texcomp/src/texcomp_eac.c tools/texcomp/src/texcomp_astc.c \
tools/texcomp/src/texcomp_astc_hdr.c tools/texcomp/src/texcomp_uni.c \
tools/texcomp/src/texcomp_astc_decode.c \
tools/texcomp/src/texcomp_etc2_decode.c \
tools/texcomp/src/texcomp_bc6h_decode.c
TEXCOMP_HDRS = tools/texcomp/include/texcomp.h tools/texcomp/src/texcomp_internal.h
TEXCOMP_OBJ = $(patsubst tools/texcomp/src/%.c,build/texcomp/%.o,$(TEXCOMP_SRC))
TEXCOMP_TEST_OBJ = $(patsubst tools/texcomp/src/%.c,build/texcomp/test-%.o,$(TEXCOMP_SRC))
TEXCOMP_OPT ?= -O3
TEXCOMP_WASM_OPT ?= -O3
TEXCOMP_WASM_CACHE ?= /tmp/tinyexr-emcc-cache
TEXCOMP_WASM_EMCC = EM_CACHE=$(TEXCOMP_WASM_CACHE) $(EMCC)
TEXCOMP_WASM_COMMON = $(V3_CSTD) -Wall -Wextra $(TEXCOMP_INC) $(TEXCOMP_WASM_OPT) -DTC_NO_THREADS=1
TEXCOMP_WASM_CLI_WARN = -Wno-unused-function -Wno-macro-redefined
TEXCOMP_WASM_SIMD = -msimd128
TEXCOMP_WASM_EXR_SRC = $(V3_CORE_SRC) src/exr_stdio.c $(ZSTD_SRC)
TEXCOMP_WASM_EXPORTS = ['_tc_result_string','_tc_backend_name','_tc_backend_available_mask','_tc_backend_force_mask','_tc_bc7_options_init','_tc_bc1_options_init','_tc_bc3_options_init','_tc_bc5_options_init','_tc_bc6h_options_init','_tc_etc2_options_init','_tc_astc_options_init','_tc_astc_hdr_options_init','_tc_bc7_compressed_size','_tc_bc1_compressed_size','_tc_bc3_compressed_size','_tc_bc5_compressed_size','_tc_bc6h_compressed_size','_tc_etc2_rgb_compressed_size','_tc_etc2_rgba_compressed_size','_tc_eac_r11_compressed_size','_tc_eac_rg11_compressed_size','_tc_astc_compressed_size','_tc_astc_hdr_compressed_size','_tc_astc_ise_sequence_bitcount','_tc_astc_ise_encode_bits','_tc_bc7_compress_rgba8','_tc_bc7_decompress_rgba8','_tc_bc1_compress_rgba8','_tc_bc3_compress_rgba8','_tc_bc5_compress_rg8','_tc_bc5_compress_rgba8','_tc_bc6h_compress_rgb32f','_tc_etc2_compress_rgba8','_tc_eac_compress_rgba8','_tc_astc_compress_rgba8','_tc_astc_hdr_compress_rgbf','_tc_dds_bc7_size','_tc_dds_bc1_size','_tc_dds_bc3_size','_tc_dds_bc5_size','_tc_dds_bc6h_size','_tc_ktx_etc2_size','_tc_astc_file_size','_tc_dds_write_bc7_memory','_tc_dds_write_bc1_memory','_tc_dds_write_bc3_memory','_tc_dds_write_bc5_memory','_tc_dds_write_bc6h_memory','_tc_ktx_write_etc2_memory','_tc_ktx_write_eac_memory','_tc_astc_write_file_memory','_malloc','_free']
TEXCOMP_WASM_RUNTIME = ['HEAPU8','HEAPF32','HEAP32','HEAPU32','UTF8ToString','stringToUTF8','lengthBytesUTF8','ccall','cwrap']
build/texcomp:
@mkdir -p build/texcomp
build/texcomp/wasm build/texcomp/wasm-simd $(TEXCOMP_WASM_CACHE):
@mkdir -p $@
build/texcomp/%.o: tools/texcomp/src/%.c $(TEXCOMP_HDRS) | build/texcomp
$(CC) $(V3_CSTD) $(V3_WARN) $(TEXCOMP_INC) $(TEXCOMP_OPT) -g -c $< -o $@
build/texcomp/test-%.o: tools/texcomp/src/%.c $(TEXCOMP_HDRS) | build/texcomp
$(CC) $(V3_CSTD) -Wall -Wextra $(TEXCOMP_INC) -O1 -g $(SAN) -c $< -o $@
texcomp: lib $(TEXCOMP_OBJ) tools/texcomp/src/texcomp_cli.c | build/texcomp
$(AR) rcs build/libtexcomp.a $(TEXCOMP_OBJ)
$(CC) $(V3_CSTD) -Wall -Wextra $(TEXCOMP_INC) $(V3_DEFS) $(V3_INC) -O2 -g \
tools/texcomp/src/texcomp_cli.c build/libtexcomp.a build/libtinyexr3.a \
-pthread -lm -o build/texcomp/texcomp
TEXCOMP_WASM_OBJ = $(patsubst tools/texcomp/src/%.c,build/texcomp/wasm/%.o,$(TEXCOMP_SRC))
TEXCOMP_WASM_SIMD_OBJ = $(patsubst tools/texcomp/src/%.c,build/texcomp/wasm-simd/%.o,$(TEXCOMP_SRC))
build/texcomp/wasm/%.o: tools/texcomp/src/%.c $(TEXCOMP_HDRS) | build/texcomp/wasm $(TEXCOMP_WASM_CACHE)
$(TEXCOMP_WASM_EMCC) $(TEXCOMP_WASM_COMMON) -c $< -o $@
build/texcomp/wasm-simd/%.o: tools/texcomp/src/%.c $(TEXCOMP_HDRS) | build/texcomp/wasm-simd $(TEXCOMP_WASM_CACHE)
$(TEXCOMP_WASM_EMCC) $(TEXCOMP_WASM_COMMON) $(TEXCOMP_WASM_SIMD) -c $< -o $@
build/texcomp/wasm/libtexcomp.a: $(TEXCOMP_WASM_OBJ)
$(EMAR) rcs $@ $^
build/texcomp/wasm-simd/libtexcomp.a: $(TEXCOMP_WASM_SIMD_OBJ)
$(EMAR) rcs $@ $^
build/texcomp/wasm/texcomp.mjs: $(TEXCOMP_WASM_OBJ) | $(TEXCOMP_WASM_CACHE)
$(TEXCOMP_WASM_EMCC) $(TEXCOMP_WASM_OPT) $(TEXCOMP_WASM_OBJ) \
-s FILESYSTEM=0 -s ALLOW_MEMORY_GROWTH=1 -s MODULARIZE=1 \
-s EXPORT_ES6=1 -s ENVIRONMENT=web,node \
-s "EXPORTED_FUNCTIONS=$(TEXCOMP_WASM_EXPORTS)" \
-s "EXPORTED_RUNTIME_METHODS=$(TEXCOMP_WASM_RUNTIME)" \
-o $@
build/texcomp/wasm-simd/texcomp.mjs: $(TEXCOMP_WASM_SIMD_OBJ) | $(TEXCOMP_WASM_CACHE)
$(TEXCOMP_WASM_EMCC) $(TEXCOMP_WASM_OPT) $(TEXCOMP_WASM_SIMD) $(TEXCOMP_WASM_SIMD_OBJ) \
-s FILESYSTEM=0 -s ALLOW_MEMORY_GROWTH=1 -s MODULARIZE=1 \
-s EXPORT_ES6=1 -s ENVIRONMENT=web,node \
-s "EXPORTED_FUNCTIONS=$(TEXCOMP_WASM_EXPORTS)" \
-s "EXPORTED_RUNTIME_METHODS=$(TEXCOMP_WASM_RUNTIME)" \
-o $@
build/texcomp/wasm/texcomp_cli.js: tools/texcomp/src/texcomp_cli.c $(TEXCOMP_SRC) $(TEXCOMP_HDRS) $(TEXCOMP_WASM_EXR_SRC) | build/texcomp/wasm $(TEXCOMP_WASM_CACHE)
$(TEXCOMP_WASM_EMCC) $(TEXCOMP_WASM_COMMON) $(TEXCOMP_WASM_CLI_WARN) $(V3_INC) \
tools/texcomp/src/texcomp_cli.c $(TEXCOMP_SRC) $(TEXCOMP_WASM_EXR_SRC) \
-s FORCE_FILESYSTEM=1 -s NODERAWFS=1 -s ALLOW_MEMORY_GROWTH=1 \
-s EXIT_RUNTIME=1 -s ENVIRONMENT=node \
-o $@
build/texcomp/wasm-simd/texcomp_cli.js: tools/texcomp/src/texcomp_cli.c $(TEXCOMP_SRC) $(TEXCOMP_HDRS) $(TEXCOMP_WASM_EXR_SRC) | build/texcomp/wasm-simd $(TEXCOMP_WASM_CACHE)
$(TEXCOMP_WASM_EMCC) $(TEXCOMP_WASM_COMMON) $(TEXCOMP_WASM_CLI_WARN) $(TEXCOMP_WASM_SIMD) $(V3_INC) \
tools/texcomp/src/texcomp_cli.c $(TEXCOMP_SRC) $(TEXCOMP_WASM_EXR_SRC) \
-s FORCE_FILESYSTEM=1 -s NODERAWFS=1 -s ALLOW_MEMORY_GROWTH=1 \
-s EXIT_RUNTIME=1 -s ENVIRONMENT=node \
-o $@
texcomp-wasm: build/texcomp/wasm/libtexcomp.a build/texcomp/wasm/texcomp.mjs build/texcomp/wasm/texcomp_cli.js
@echo "built build/texcomp/wasm/libtexcomp.a, texcomp.mjs/.wasm, texcomp_cli.js/.wasm"
texcomp-wasm-simd: build/texcomp/wasm-simd/libtexcomp.a build/texcomp/wasm-simd/texcomp.mjs build/texcomp/wasm-simd/texcomp_cli.js
@echo "built build/texcomp/wasm-simd/libtexcomp.a, texcomp.mjs/.wasm, texcomp_cli.js/.wasm"
# Browser demo: web/texcomp (tir + texcomp + texpipe + envmap + EXR decode).
# Needs emcc on PATH. Artifacts land next to index.html so the page can be
# served straight from a checkout.
.PHONY: texcomp-web
texcomp-web:
cd web/texcomp && EMCC=$(EMCC) ./build.sh
wasm-texcomp: texcomp-wasm
wasm-texcomp-simd: texcomp-wasm-simd
texcomp-c11-gate: | build/texcomp
for f in $(TEXCOMP_SRC); do \
$(CC) $(V3_CSTD) $(V3_WARN) $(TEXCOMP_INC) -O1 -fsyntax-only $$f || exit 1; \
done
$(CC) $(V3_CSTD) $(V3_WARN) $(TEXCOMP_INC) $(V3_INC) -O1 -fsyntax-only tools/texcomp/src/texcomp_cli.c
@echo "texcomp pure-C11 gate: OK"
texcomp-test: $(TEXCOMP_TEST_OBJ) tools/texcomp/test/test_texcomp.c | build/texcomp
$(CC) $(V3_CSTD) -Wall -Wextra $(TEXCOMP_INC) -O1 -g $(SAN) -pthread \
tools/texcomp/test/test_texcomp.c $(TEXCOMP_TEST_OBJ) -lm -o build/test_texcomp
./build/test_texcomp
texcomp-uni-gate: $(TEXCOMP_OBJ) tools/texcomp/test/uni_gate.c | build/texcomp
$(CC) $(V3_CSTD) -Wall -Wextra $(TEXCOMP_INC) -O2 -g \
tools/texcomp/test/uni_gate.c $(TEXCOMP_OBJ) -lm -o build/texcomp/uni_gate
./build/texcomp/uni_gate
# BC6H conformance + quality gate: encode HDR, decode every block with an
# independent reference decoder (bcdec port) and check PSNR vs source.
texcomp-bc6h-gate: $(TEXCOMP_OBJ) tools/texcomp/test/bc6h_gate.c | build/texcomp
$(CC) $(V3_CSTD) -Wall -Wextra $(TEXCOMP_INC) -Itools/texcomp/test -O2 -g \
tools/texcomp/test/bc6h_gate.c $(TEXCOMP_OBJ) -lm -o build/texcomp/bc6h_gate
./build/texcomp/bc6h_gate
# BC6H/BC7 pipeline quality gate: loads real EXR images, encodes/decodes,
# checks PSNR + SSIM. Argument: path to openexr-images directory.
texcomp-pipeline-gate: $(TEXCOMP_OBJ) tools/texcomp/test/bc6h_pipeline_gate.c \
tools/texcomp/test/tc_ssim.h \
tools/texcomp/test/tc_ssim_gauss11.inc | build/texcomp
$(CC) $(V3_CSTD) -Wall -Wextra $(TEXCOMP_INC) -Itools/texcomp/test -O2 -g \
tools/texcomp/test/bc6h_pipeline_gate.c $(TEXCOMP_OBJ) build/libtinyexr3.a \
-lm -o build/texcomp/bc6h_pipeline_gate
./build/texcomp/bc6h_pipeline_gate $(OPENEXR_IMAGES_DIR)
texcomp-bench: $(TEXCOMP_OBJ) tools/texcomp/bench/texcomp_bench.c | build/texcomp
$(CC) $(V3_CSTD) -Wall -Wextra $(TEXCOMP_INC) -O3 \
tools/texcomp/bench/texcomp_bench.c $(TEXCOMP_OBJ) -lm -o build/texcomp_bench
./build/texcomp_bench
texcomp-astc-psnr: $(TEXCOMP_OBJ) tools/texcomp/bench/texcomp_psnr.c tools/texcomp/test/astc_ref_decode.h | build/texcomp
$(CC) $(V3_CSTD) -Wall -Wextra $(TEXCOMP_INC) -Itools/texcomp/test -O2 \
tools/texcomp/bench/texcomp_psnr.c $(TEXCOMP_OBJ) -lm -o build/texcomp_psnr
./build/texcomp_psnr
# ---- vendored Arm astcenc backend (deps/astcenc, Apache-2.0) ---------------
# Full port of the upstream Arm encoder, selectable at runtime with
# `texcomp-arm --encoder arm`. Portable SSE2 baseline on x86, NEON on
# aarch64; the default `make texcomp` stays pure C11 with no C++ parts.
ASTCENC_LIB_SRC = $(wildcard deps/astcenc/*.cpp)
ASTCENC_LIB_OBJ = $(patsubst deps/astcenc/%.cpp,build/astcenc/%.o,$(ASTCENC_LIB_SRC))
ASTCENC_HOST_ARCH ?= $(shell uname -m)
ifneq ($(filter aarch64 arm64,$(ASTCENC_HOST_ARCH)),)
ASTCENC_DEFS = -DASTCENC_SSE=0 -DASTCENC_AVX=0 -DASTCENC_NEON=1 -DASTCENC_SVE=0 -DASTCENC_POPCNT=0 -DASTCENC_F16C=0
else
ASTCENC_DEFS = -DASTCENC_SSE=20 -DASTCENC_AVX=0 -DASTCENC_NEON=0 -DASTCENC_SVE=0 -DASTCENC_POPCNT=0 -DASTCENC_F16C=0
endif
build/astcenc/%.o: deps/astcenc/%.cpp
@mkdir -p build/astcenc
$(CXX) -std=c++14 -O3 -g -w $(ASTCENC_DEFS) -Ideps/astcenc -c $< -o $@
texcomp-arm: lib $(TEXCOMP_OBJ) $(ASTCENC_LIB_OBJ) tools/texcomp/src/texcomp_cli.c | build/texcomp
$(AR) rcs build/libtexcomp_astcenc.a $(ASTCENC_LIB_OBJ)
$(CC) $(V3_CSTD) -Wall -Wextra $(TEXCOMP_INC) $(V3_DEFS) $(V3_INC) -O2 -g \
-DTEXCOMP_HAVE_ASTCENC -Ideps/astcenc \
-c tools/texcomp/src/texcomp_cli.c -o build/texcomp/texcomp_cli_arm.o
$(CXX) build/texcomp/texcomp_cli_arm.o $(TEXCOMP_OBJ) \
build/libtexcomp_astcenc.a build/libtinyexr3.a \
-pthread -lm -o build/texcomp/texcomp-arm
ASTCENC ?= /tmp/astc-encoder/build/Source/astcenc-native
texcomp-astc-arm-smoke: texcomp
@test -x "$(ASTCENC)" || { echo "set ASTCENC=/path/to/astcenc-native"; exit 77; }
./build/texcomp/texcomp -i issue40.exr -o build/texcomp/arm_smoke_6x6.astc --format astc --astc-block 6x6 --quality normal
"$(ASTCENC)" -dl build/texcomp/arm_smoke_6x6.astc build/texcomp/arm_smoke_6x6.png -silent
# Self-contained CI gate for the vendored astcenc backend: builds the C++
# encoder, encodes one image with both the pure-C `tc` path and astcenc, and
# cross-checks their PSNR using astcenc's own decoder. No external binaries or
# image assets, so it runs anywhere the backend compiles.
texcomp-astc-arm-gate: $(TEXCOMP_OBJ) $(ASTCENC_LIB_OBJ) tools/texcomp/test/astc_arm_xcheck.c | build/texcomp
$(AR) rcs build/libtexcomp_astcenc.a $(ASTCENC_LIB_OBJ)
$(CC) $(V3_CSTD) -Wall -Wextra $(TEXCOMP_INC) -Itools/texcomp/test \
-DTEXCOMP_HAVE_ASTCENC -Ideps/astcenc -O2 -g -c \
tools/texcomp/test/astc_arm_xcheck.c -o build/texcomp/astc_arm_xcheck.o
$(CXX) build/texcomp/astc_arm_xcheck.o $(TEXCOMP_OBJ) \
build/libtexcomp_astcenc.a -lm -o build/texcomp/astc_arm_xcheck
./build/texcomp/astc_arm_xcheck
# Self-contained CI gate for the ASTC HDR encoder: encodes deterministic HDR
# images with the pure-C tc encoder and verifies them with astcenc's conformant
# HDR decoder (const-colour round-trip + gradient PSNR floor).
# Basis Universal transcoder validation gate. Vendored from
# https://github.com/BinomialLLC/basis_universal (transcoder/basisu_transcoder.cpp)
# into deps/basisu/. C++ build like astcenc; skip if files not present.
BASISU_DIR ?= deps/basisu
BASISU_HDR = $(BASISU_DIR)/basisu_transcoder.h
BASISU_SRC = $(BASISU_DIR)/basisu_transcoder.cpp
BASISU_DEFS = -DBASISD_SUPPORT_KTX2=1 -DBASISD_SUPPORT_KTX2_ZSTD=0
texcomp-basis-gate: tools/texcomp/test/basis_validate.c | build/texcomp
@test -f "$(BASISU_SRC)" || { echo "basis-validate: vendored transcoder not found (cp from https://github.com/BinomialLLC/basis_universal)"; exit 77; }
$(CXX) -std=c++17 -Wall -Wextra -fno-strict-aliasing $(BASISU_DEFS) -I$(BASISU_DIR) -O2 -g -c \
tools/texcomp/test/basis_validate.c -o build/texcomp/basis_validate.o
$(CXX) -std=c++17 $(BASISU_DEFS) -I$(BASISU_DIR) -O2 -g -c \
$(BASISU_SRC) -o build/texcomp/basisu_transcoder.o
$(CXX) build/texcomp/basis_validate.o build/texcomp/basisu_transcoder.o -lm -o build/texcomp/basis_validate
./build/texcomp/basis_validate
@echo "basis-validate: OK"
texcomp-astc-hdr-gate: $(TEXCOMP_OBJ) $(ASTCENC_LIB_OBJ) tools/texcomp/test/astc_hdr_xcheck.c | build/texcomp
$(AR) rcs build/libtexcomp_astcenc.a $(ASTCENC_LIB_OBJ)
$(CC) $(V3_CSTD) -Wall -Wextra $(TEXCOMP_INC) -Itools/texcomp/test \
-DTEXCOMP_HAVE_ASTCENC -Ideps/astcenc -O2 -g -c \
tools/texcomp/test/astc_hdr_xcheck.c -o build/texcomp/astc_hdr_xcheck.o
$(CXX) build/texcomp/astc_hdr_xcheck.o $(TEXCOMP_OBJ) \
build/libtexcomp_astcenc.a -lm -o build/texcomp/astc_hdr_xcheck
./build/texcomp/astc_hdr_xcheck
# xbc7: BC7 windowed RDO + zstd container. C gate checks the RDO improves
# zstd compressibility (and rdo=0 is a no-op); the CLI step checks the
# encode->transcode round-trip is bit-exact standard BC7.
texcomp-xbc7-gate: lib $(TEXCOMP_OBJ) texcomp tools/texcomp/test/xbc7_gate.c tools/texcomp/test/bc7_ref_decode.h | build/texcomp
$(AR) rcs build/libtexcomp.a $(TEXCOMP_OBJ)
$(CC) $(V3_CSTD) -Wall -Wextra $(TEXCOMP_INC) -Itools/texcomp/test -Ideps/zstd -O2 -g \
tools/texcomp/test/xbc7_gate.c build/libtexcomp.a build/libtinyexr3.a \
-pthread -lm -o build/texcomp/xbc7_gate
./build/texcomp/xbc7_gate
@echo "--- xbc7 CLI encode -> transcode round-trip ---"
./build/texcomp/texcomp -i asakusa.png -o build/texcomp/rt.xbc7 \
--format xbc7 --rdo 16 --raw build/texcomp/rt_enc.bc7
./build/texcomp/texcomp -i build/texcomp/rt.xbc7 -o build/texcomp/rt.dds \
--raw build/texcomp/rt_dec.bc7
@git diff --no-index --quiet build/texcomp/rt_enc.bc7 build/texcomp/rt_dec.bc7 \
&& echo "xbc7 CLI round-trip: OK (transcode is bit-exact BC7)" \
|| { echo "FAIL: xbc7 round-trip differs"; exit 1; }
# ---- tools/texpipe: resize-aware texture compression ----------------------
# Ties tir (resize) + texcomp (block compression) into content-aware mip
# chains serialized to multi-mip DDS / KTX2 containers. Pure C11 library
# (no <stdio.h> in src/); only texpipe_cli.c does file I/O.
TEXPIPE_INC = -Itools/texpipe/include -Itools/resize/include \
-Itools/texcomp/include -Iinclude -Isrc -Iexamples/common
TEXPIPE_LIB_SRC = tools/texpipe/src/texpipe.c tools/texpipe/src/texpipe_mip.c \
tools/texpipe/src/texpipe_alpha.c tools/texpipe/src/texpipe_cube.c \
tools/texpipe/src/texpipe_octa.c tools/texpipe/src/texpipe_disp.c \
tools/texpipe/src/texpipe_normal.c tools/texpipe/src/texpipe_container.c
TEXPIPE_HDRS = tools/texpipe/include/texpipe.h tools/texpipe/src/texpipe_internal.h
TEXPIPE_OBJ = $(patsubst tools/texpipe/src/%.c,build/texpipe/%.o,$(TEXPIPE_LIB_SRC))
.PHONY: texpipe texpipe-c11-gate texpipe-test texpipe-three-ktx2-test
build/texpipe:
@mkdir -p build/texpipe
build/texpipe/%.o: tools/texpipe/src/%.c $(TEXPIPE_HDRS) | build/texpipe
$(CC) $(V3_CSTD) $(V3_WARN) $(TEXPIPE_INC) -O2 -g -c $< -o $@
# Full CLI: base image -> content-aware mip chain -> compressed container.
texpipe: lib resize-lib texcomp $(TEXPIPE_OBJ) tools/texpipe/src/texpipe_cli.c | build/texpipe
$(AR) rcs build/libtexpipe.a $(TEXPIPE_OBJ)
$(CC) $(V3_CSTD) -Wall -Wextra $(TEXPIPE_INC) $(V3_DEFS) $(V3_INC) -O2 -g \
tools/texpipe/src/texpipe_cli.c build/libtexpipe.a build/libtir.a \
build/libtexcomp.a build/libtinyexr3.a -pthread -lm \
-o build/texpipe/texpipe
@echo "built build/texpipe/texpipe"
# Strict pure-C11 gate: syntax-check each lib TU with -Werror and forbid
# <stdio.h> outside the CLI.
texpipe-c11-gate: | build/texpipe
@for f in $(TEXPIPE_LIB_SRC); do \
echo " c11-gate $$f"; \
$(CC) $(V3_CSTD) $(V3_WARN) $(TEXPIPE_INC) -O1 -fsyntax-only $$f || exit 1; \
done
@bad=`grep -rl --exclude=texpipe_cli.c '<stdio.h>' tools/texpipe/src/ || true`; \
if [ -n "$$bad" ]; then echo "FAIL: <stdio.h> in texpipe src: $$bad"; exit 1; fi
$(CC) $(V3_CSTD) $(V3_WARN) $(TEXPIPE_INC) $(V3_INC) -O1 -fsyntax-only \
tools/texpipe/src/texpipe_cli.c
@echo "texpipe pure-C11 gate: OK"
# Unit tests: per-mip round-trip PSNR (BC7 shipped decoder) + alpha coverage.
texpipe-test: resize-lib texcomp tools/texpipe/test/test_texpipe.c $(TEXPIPE_HDRS) | build/texpipe
$(CC) $(V3_CSTD) -Wall -Wextra $(TEXPIPE_INC) -Itools/texcomp/test -O1 -g $(SAN) -pthread \
tools/texpipe/test/test_texpipe.c $(TEXPIPE_LIB_SRC) build/libtir.a \
build/libtexcomp.a -lm -o build/test_texpipe
./build/test_texpipe
# Optional browser interoperability gate. This stays outside tools-test because
# it needs Node, Puppeteer, Three.js and Chrome. Install dependencies in the test
# directory, or point THREE_KTX2_NODE_ROOT at an existing web project.
THREE_KTX2_NODE_ROOT ?= tools/texpipe/test/three_ktx2_loader
build/texpipe/three_ktx2_fixture: texpipe \
tools/texpipe/test/three_ktx2_loader/generate_fixture.c
$(CC) $(V3_CSTD) $(V3_WARN) $(TEXPIPE_INC) -Ideps/zstd -O2 -g \
tools/texpipe/test/three_ktx2_loader/generate_fixture.c \
build/libtexpipe.a build/libtir.a build/libtexcomp.a $(ZSTD_OBJ) \
-pthread -lm -o $@
texpipe-three-ktx2-test: build/texpipe/three_ktx2_fixture
./build/texpipe/three_ktx2_fixture build/texpipe/three-ktx2.ktx2
THREE_KTX2_NODE_ROOT="$(THREE_KTX2_NODE_ROOT)" \
$(NODE) tools/texpipe/test/three_ktx2_loader/test.mjs \
build/texpipe/three-ktx2.ktx2
# ---- tools/envmap: environment-map projections, SH, spherical gaussians ----
# Pure C11. Links tir + texcomp + texpipe + libtinyexr3 (CLI does HDR EXR I/O).
ENVMAP_INC = -Itools/envmap/include -Itools/resize/include \
-Itools/texcomp/include -Itools/texpipe/include -Iinclude -Isrc -Iexamples/common
ENVMAP_LIB_SRC = tools/envmap/src/envmap_proj.c tools/envmap/src/envmap_sample.c \
tools/envmap/src/envmap_sh.c tools/envmap/src/envmap_sg.c \
tools/envmap/src/envmap_ibl.c
ENVMAP_HDRS = tools/envmap/include/envmap.h
ENVMAP_OBJ = $(patsubst tools/envmap/src/%.c,build/envmap/%.o,$(ENVMAP_LIB_SRC))
.PHONY: envmap envmap-c11-gate envmap-test envmap-pbr-test
build/envmap:
@mkdir -p build/envmap
build/envmap/%.o: tools/envmap/src/%.c $(ENVMAP_HDRS) | build/envmap
$(CC) $(V3_CSTD) $(V3_WARN) $(ENVMAP_INC) -O2 -g -c $< -o $@
envmap: lib resize-lib texcomp texpipe $(ENVMAP_OBJ) tools/envmap/src/envmap_cli.c | build/envmap
$(AR) rcs build/libenvmap.a $(ENVMAP_OBJ)
$(CC) $(V3_CSTD) -Wall -Wextra $(ENVMAP_INC) $(V3_DEFS) $(V3_INC) -O2 -g \
tools/envmap/src/envmap_cli.c build/libenvmap.a build/libtexpipe.a \
build/libtir.a build/libtexcomp.a build/libtinyexr3.a -pthread -lm \
-o build/envmap/envmap
@echo "built build/envmap/envmap"
envmap-c11-gate: | build/envmap
@for f in $(ENVMAP_LIB_SRC); do \
echo " c11-gate $$f"; \
$(CC) $(V3_CSTD) $(V3_WARN) $(ENVMAP_INC) -O1 -fsyntax-only $$f || exit 1; \
done
@bad=`grep -rl --exclude=envmap_cli.c '<stdio.h>' tools/envmap/src/ || true`; \
if [ -n "$$bad" ]; then echo "FAIL: <stdio.h> in envmap src: $$bad"; exit 1; fi
$(CC) $(V3_CSTD) $(V3_WARN) $(ENVMAP_INC) $(V3_INC) -O1 -fsyntax-only \
tools/envmap/src/envmap_cli.c
@echo "envmap pure-C11 gate: OK"
envmap-test: resize-lib tools/envmap/test/test_envmap.c $(ENVMAP_HDRS) | build/envmap
$(CC) $(V3_CSTD) -Wall -Wextra $(ENVMAP_INC) -O1 -g $(SAN) -pthread \
tools/envmap/test/test_envmap.c $(ENVMAP_LIB_SRC) build/libtir.a -lm \
-o build/test_envmap
./build/test_envmap
# PBR validation harness: shade under IBL with source vs BC7-decoded material.
envmap-pbr-test: resize-lib texcomp tools/envmap/test/test_pbr.c $(ENVMAP_HDRS) | build/envmap
$(CC) $(V3_CSTD) -Wall -Wextra $(ENVMAP_INC) -O1 -g $(SAN) -pthread \
tools/envmap/test/test_pbr.c $(ENVMAP_LIB_SRC) build/libtir.a \
build/libtexcomp.a -lm -o build/test_pbr
./build/test_pbr
# Build + run the unit tests with multithreading enabled (parity + race checks).
test-c-threads:
$(MAKE) test-c THREADS=1
# Thread sanitizer over the threaded build (no ASan; proves no data races).
# NOTE: requires a ThreadSanitizer that instruments C11 <threads.h>; some
# glibc/TSan combos only intercept pthread_* and crash on thrd_create/mtx_*.
# The threaded build also runs cleanly under ASan+UBSan via `make test-c-threads`.
test-c-tsan: | build
$(CC) $(V3_CSTD) -Wall -Wextra -DEXR_USE_THREADS $(V3_INC) -O1 -g \
-fsanitize=thread test/unit/test_exr_v3.c $(V3_SRC) $(ZSTD_SRC) \
-pthread -lm -o build/test_exr_v3_tsan
./build/test_exr_v3_tsan
bench: $(V3_OBJ) $(ZSTD_OBJ) $(LD_OBJ) benchmark/bench.c | build
$(CC) $(V3_CSTD) -Wall -Wextra $(V3_DEFS) $(V3_INC) -O3 \
benchmark/bench.c $(V3_OBJ) $(ZSTD_OBJ) $(LD_OBJ) $(THREAD_LIBS) -lm -o build/bench
./build/bench
# ---- HTJ2K GPU vs CPU throughput (CUDA backend) ---------------------------
# Builds a CUDA lib and times whole-image HTJ2K decode/encode CPU vs GPU.
# Exit 77 = skipped (no CUDA device). Override the image with EXR_BENCH_IMG=...
EXR_BENCH_IMG ?= asakusa.exr
bench-gpu-jph:
$(MAKE) clean
$(MAKE) CUDA=1 lib
$(CC) $(V3_CSTD) -Wall -Wextra -DEXR_USE_CUDA -Iinclude -Isrc -Ideps/zstd \
-Ithird_party/cuew -O3 benchmark/bench_gpu_jph.c build/libtinyexr3.a \
-ldl -lm -o build/bench_gpu_jph
./build/bench_gpu_jph "$(EXR_BENCH_IMG)"; rc=$$?; \
if [ $$rc -eq 77 ]; then echo "bench-gpu-jph: SKIPPED (no CUDA device)"; exit 0; \
else exit $$rc; fi
# ---- tinyexr-vs-OpenEXR comparison (needs a built OpenEXR) -----------------
# Override OPENEXR_ROOT / OPENEXR_BUILD if your tree lives elsewhere. Extra
# files: make bench-compare ARGS="img1.exr img2.exr"
OPENEXR_ROOT ?= $(HOME)/work/openexr
OPENEXR_BUILD ?= $(OPENEXR_ROOT)/_build
OPENEXR_INC = -I$(OPENEXR_ROOT)/src/lib/OpenEXR -I$(OPENEXR_ROOT)/src/lib/OpenEXRCore \
-I$(OPENEXR_ROOT)/src/lib/Iex -I$(OPENEXR_ROOT)/src/lib/IlmThread \
-I$(OPENEXR_BUILD)/cmake \
-I$(OPENEXR_BUILD)/OpenEXR_ImathIncludeCompat \
-I$(OPENEXR_BUILD)/_deps/imath-src/src \
-I$(OPENEXR_BUILD)/_deps/imath-src/src/Imath \
-I$(OPENEXR_BUILD)/_deps/imath-build/config \
$(shell pkg-config --cflags Imath 2>/dev/null)
OPENEXR_LIBDIR = $(OPENEXR_BUILD)/src/lib
OPENEXR_IMATH_LIBDIR = $(OPENEXR_BUILD)/_deps/imath-build/src/Imath
OPENEXR_LIBS = -L$(OPENEXR_LIBDIR)/OpenEXR -L$(OPENEXR_LIBDIR)/OpenEXRCore \
-L$(OPENEXR_LIBDIR)/Iex -L$(OPENEXR_LIBDIR)/IlmThread \
-L$(OPENEXR_IMATH_LIBDIR) \
-lOpenEXR-4_0 -lOpenEXRCore-4_0 -lIex-4_0 -lIlmThread-4_0 \
-lImath-3_2 -pthread
OPENEXR_LDPATH = $(OPENEXR_LIBDIR)/OpenEXR:$(OPENEXR_LIBDIR)/OpenEXRCore:$(OPENEXR_LIBDIR)/Iex:$(OPENEXR_LIBDIR)/IlmThread
OPENEXR_LDPATH := $(OPENEXR_LDPATH):$(OPENEXR_IMATH_LIBDIR)
OPENEXR_RUNPATH = LD_LIBRARY_PATH=$(OPENEXR_LDPATH)
ifeq ($(shell uname -s),Darwin)
OPENEXR_RUNPATH = DYLD_LIBRARY_PATH=$(OPENEXR_LDPATH)
endif
# The tinyexr side (bench_tx.c) is compiled as C because exr.h and OpenEXR's
# C core declare the same global enum names and cannot share a translation unit.
build/bench_tx.o: benchmark/bench_tx.c benchmark/bench_tx.h include/exr.h | build
$(CC) $(V3_CSTD) -Wall -Wextra $(V3_INC) -O3 -c benchmark/bench_tx.c -o $@
build/bench_compare: $(V3_OBJ) $(ZSTD_OBJ) $(LD_OBJ) build/bench_tx.o benchmark/bench_compare.cpp | build
@test -d $(OPENEXR_LIBDIR)/OpenEXR || { \
echo "OpenEXR build not found at $(OPENEXR_BUILD)"; \
echo "build OpenEXR first, or set OPENEXR_ROOT=/path/to/openexr"; exit 1; }
$(CXX) -std=c++14 -Wall -Ibenchmark $(OPENEXR_INC) -O3 \
benchmark/bench_compare.cpp build/bench_tx.o $(V3_OBJ) $(ZSTD_OBJ) $(LD_OBJ) \
$(OPENEXR_LIBS) $(THREAD_LIBS) -lm -o build/bench_compare
bench-compare: build/bench_compare
$(OPENEXR_RUNPATH) ./build/bench_compare $(ARGS)
# HTJ2K-only comparison; set ARGS and optionally pin the process externally.
bench-htj2k: build/bench_compare
EXR_BENCH_HTJ2K_ONLY=1 $(OPENEXR_RUNPATH) ./build/bench_compare $(ARGS)
# Coverage-guided fuzzer (clang+libFuzzer over the whole library).
# ./build/fuzz_v3 -max_total_time=60 test/unit/regression
fuzz: test/fuzzer/fuzz_v3.c | build
clang $(V3_CSTD) $(V3_INC) -O1 -g -w -fsanitize=fuzzer,address,undefined \
test/fuzzer/fuzz_v3.c $(V3_SRC) $(ZSTD_SRC) -lm -o build/fuzz_v3
@echo "built build/fuzz_v3 - e.g. ./build/fuzz_v3 -max_total_time=60 test/unit/regression"
# Same fuzzer but with libdeflate as the default zlib backend, so the shipped
# DEFLATE=auto decode path (ZIP/ZIPS/PXR24 -> libdeflate) is fuzzed too.
fuzz-libdeflate: test/fuzzer/fuzz_v3.c | build
clang $(V3_CSTD) -DEXR_USE_LIBDEFLATE -DEXR_ZLIB_DEFAULT_LIBDEFLATE=1 \
$(V3_INC) -Ideps/libdeflate -O1 -g -w -fsanitize=fuzzer,address,undefined \
test/fuzzer/fuzz_v3.c $(V3_SRC) $(ZSTD_SRC) \
deps/libdeflate/lib/adler32.c deps/libdeflate/lib/deflate_compress.c \
deps/libdeflate/lib/deflate_decompress.c deps/libdeflate/lib/zlib_compress.c \
deps/libdeflate/lib/zlib_decompress.c deps/libdeflate/lib/utils.c \
deps/libdeflate/lib/x86/cpu_features.c deps/libdeflate/lib/arm/cpu_features.c \
-lm -o build/fuzz_v3_libdeflate
@echo "built build/fuzz_v3_libdeflate (libdeflate default backend)"
# HTJ2K (JPH) encode+decode+round-trip fuzzer.
# ./build/fuzz_jph -max_total_time=600 test/fuzzer/corpus_jph
fuzz-jph: test/fuzzer/fuzz_jph.c | build
clang $(V3_CSTD) $(V3_INC) -O1 -g -w -fsanitize=fuzzer,address,undefined \
test/fuzzer/fuzz_jph.c $(V3_SRC) $(ZSTD_SRC) -lm -o build/fuzz_jph
@echo "built build/fuzz_jph"
# Deterministic corpus replay for fuzz_jph (no libFuzzer needed).
fuzz-jph-corpus: test/fuzzer/fuzz_jph.c | build
clang $(V3_CSTD) -Wall $(V3_INC) -O1 -g $(SAN) -DEXR_JPH_FUZZ_STANDALONE \
test/fuzzer/fuzz_jph.c $(V3_SRC) $(ZSTD_SRC) -lm -o build/fuzz_jph_replay
# Deterministic corpus replay under ASan+UBSan (no libFuzzer needed; CI gate).
fuzz-corpus: $(V3_TEST_OBJ) build/test-tinyexr_zstd.o $(LD_TEST_OBJ) test/fuzzer/fuzz_v3.c | build
$(CC) $(V3_CSTD) -Wall -Wextra $(V3_INC) -O1 -g $(SAN) -DEXR_FUZZ_STANDALONE \
test/fuzzer/fuzz_v3.c $(V3_TEST_OBJ) build/test-tinyexr_zstd.o $(LD_TEST_OBJ) -lm \
-o build/fuzz_replay
./build/fuzz_replay test/unit/regression/* asakusa.exr deepscanline.exr
# Some local sandboxes/debug wrappers use ptrace. LeakSanitizer cannot run
# under ptrace, so this target preserves ASan+UBSan corpus coverage there while
# keeping fuzz-corpus as the strict LSan gate for CI/native hosts.
fuzz-corpus-asan: $(V3_TEST_OBJ) build/test-tinyexr_zstd.o $(LD_TEST_OBJ) test/fuzzer/fuzz_v3.c | build
$(CC) $(V3_CSTD) -Wall -Wextra $(V3_INC) -O1 -g $(SAN) -DEXR_FUZZ_STANDALONE \
test/fuzzer/fuzz_v3.c $(V3_TEST_OBJ) build/test-tinyexr_zstd.o $(LD_TEST_OBJ) -lm \
-o build/fuzz_replay
ASAN_OPTIONS=detect_leaks=0 ./build/fuzz_replay test/unit/regression/* asakusa.exr deepscanline.exr
# Parse/load every *.exr under $(EXR_IMAGES) and classify PASS/XFAIL/FAIL.
# Override the corpus dir with: make parse-test EXR_IMAGES=/path/to/images
EXR_IMAGES ?= $(HOME)/work/openexr-images
PARSE_HARNESS = test/v3/parse_harness
parse-test: lib
$(CC) $(V3_CSTD) -Wall -Wextra -Iinclude -O2 \
test/v3/parse_harness.c build/libtinyexr3.a -lm -o $(PARSE_HARNESS)
python3 test/v3/parse-tester.py --harness $(PARSE_HARNESS) $(EXR_IMAGES)
# ---- native example (uses the optional stdio layer) -----------------------
examples-c: lib
$(CC) $(V3_CSTD) -Wall -Wextra -Iinclude -O2 \
examples/exrinfo/exrinfo.c build/libtinyexr3.a -o build/exrinfo
@echo "built build/exrinfo - e.g. ./build/exrinfo asakusa.exr"
# ---- Emscripten WASM build of the v3 C API --------------------------------
# Pure-C exports, no filesystem. Produces an ES6 module + wasm. Needs emcc on
# PATH (override with EMCC=...). The freestanding core links cleanly with no FS.
WASM_EXPORTS = ['_exrw_decode_rgba','_exrw_encode_rgba','_exrw_free','_malloc','_free']
WASM_RUNTIME = ['HEAPU8','HEAPF32','HEAP32','HEAPU32']
wasm: | build
$(EMCC) -O3 $(V3_INC) -w \
$(V3_CORE_SRC) $(ZSTD_SRC) examples/wasm/exr_wasm.c \
-s FILESYSTEM=0 -s ALLOW_MEMORY_GROWTH=1 -s MODULARIZE=1 \
-s EXPORT_ES6=1 -s ENVIRONMENT=web,node \
-s "EXPORTED_FUNCTIONS=$(WASM_EXPORTS)" \
-s "EXPORTED_RUNTIME_METHODS=$(WASM_RUNTIME)" \
-o build/exr_v3.mjs
@echo "built build/exr_v3.mjs + build/exr_v3.wasm"
# ---- ARM (aarch64 NEON) cross build + emulated run ------------------------
# Cross-compile the SIMD smoke test for aarch64 and run it under an emulator to
# confirm the NEON path builds and is bit-identical to scalar. Static link so
# the emulator needs no guest sysroot. Override the toolchain/emulator as needed:
# make arm-smoke ARM_CC=aarch64-linux-gnu-gcc ARM_QEMU=qemu-aarch64
ARM_CC ?= aarch64-linux-gnu-gcc-13
ARM_QEMU ?= qemu-aarch64-static
ARM_SMOKE_SRC = $(filter-out src/exr_zstd.c,$(V3_SRC))
arm-smoke: test/v3/neon_smoke.c | build
$(ARM_CC) -static -march=armv8-a $(V3_CSTD) -Wall -Wextra -DEXR_NO_ZSTD \
$(V3_INC) -O2 test/v3/neon_smoke.c $(ARM_SMOKE_SRC) -lm \
-o build/neon_smoke_arm
@file build/neon_smoke_arm | sed 's/^/ /'
$(ARM_QEMU) ./build/neon_smoke_arm
# Same smoke, built/run natively (sanity-checks the host SIMD tier).
host-smoke: test/v3/neon_smoke.c | build
$(CC) $(V3_CSTD) -Wall -Wextra -DEXR_NO_ZSTD $(V3_INC) -O2 \
test/v3/neon_smoke.c $(ARM_SMOKE_SRC) -lm -o build/neon_smoke_host
./build/neon_smoke_host
# ---- freestanding gate ----------------------------------------------------
# Compile the core with only stdint/stddef/limits, prove there are no forbidden
# libc dependencies (nm scan), and run a functional memory round-trip.
# Opt-in zstd DECODE in the freestanding build (default off). Adds the vendored
# zstd amalgamation (~hundreds of KB) and routes decode through zstd's no-malloc
# static-DCtx API with malloc/calloc/free stubbed out, so the forbidden-symbol
# scan still passes. Decode-only: zstd ENCODE stays UNSUPPORTED in freestanding.
EXR_FREESTANDING_ZSTD ?= 0
ifeq ($(EXR_FREESTANDING_ZSTD),1)
FS_ZSTD_CFG = -DEXR_ZSTD_DECODE_ONLY
FS_ZSTD_SKIP =
FS_ZSTD_OBJ = build/fs-tinyexr_zstd.o
FS_SMOKE_CFG = -DEXR_FREESTANDING_ZSTD -Itest/v3
else
FS_ZSTD_CFG = -DEXR_NO_ZSTD
FS_ZSTD_SKIP = src/exr_zstd.c
FS_ZSTD_OBJ =
FS_SMOKE_CFG =
endif
FS_FLAGS = -DEXR_FREESTANDING $(FS_ZSTD_CFG) -ffreestanding -fno-builtin \
-fno-stack-protector $(V3_CSTD) $(V3_WARN) $(V3_INC) -O2 -g
# Default: exclude the zstd glue (EXR_NO_ZSTD), dispatch returns UNSUPPORTED, no
# amalgamation pulled in. EXR_FREESTANDING_ZSTD=1 includes both (decode path).
FS_CORE_SRC = $(filter-out $(FS_ZSTD_SKIP),$(V3_CORE_SRC))
FS_OBJ = $(patsubst src/%.c,build/fs-%.o,$(FS_CORE_SRC)) build/fs-exr_freestanding.o \
$(FS_ZSTD_OBJ)
FS_FORBIDDEN = fopen|fread|fwrite|fseek|ftell|fclose|fprintf|printf|snprintf|malloc|calloc|realloc|free|abort|exit|qsort|exp|log|pow
build/fs-%.o: src/%.c include/exr.h src/exr_internal.h | build
$(CC) $(FS_FLAGS) -c $< -o $@
# Freestanding zstd amalgamation: no-malloc (static-DCtx) build with the libc
# allocator entry points stubbed to NULL/no-op (the static decode path never
# calls them), so the object carries no forbidden symbols.
build/fs-tinyexr_zstd.o: deps/zstd/tinyexr_zstd.c deps/zstd/tinyexr_zstd.h | build
$(CC) -DEXR_FREESTANDING -ffreestanding -fno-builtin -fno-stack-protector \
-DNDEBUG -DZSTD_DEPS_MALLOC -D'ZSTD_malloc(s)=((void*)0)' \
-D'ZSTD_calloc(n,s)=((void*)0)' -D'ZSTD_free(p)=((void)0)' \
-Ideps/zstd $(V3_CSTD) -O2 -g -w -c $< -o $@
freestanding-gate: $(FS_OBJ) test/v3/freestanding_smoke.c | build
@echo " scan: only exr_stdio.c may include <stdio.h> (GPU backends are hosted-only)"
@bad=`grep -rl '<stdio.h>' src/ | grep -vE 'src/(exr_stdio|exr_gpu_cuda|exr_vk_vulkan)\.c' || true`; \
if [ -n "$$bad" ]; then echo " FAIL: stdio leaked into: $$bad"; exit 1; fi
@echo " scan: no forbidden libc symbols referenced by the freestanding core"
@for o in $(FS_OBJ); do \
hit=`nm -u $$o 2>/dev/null | awk '{print $$NF}' | grep -wE '$(FS_FORBIDDEN)' || true`; \
if [ -n "$$hit" ]; then echo " FAIL: $$o references:" $$hit; exit 1; fi; \
done
@echo " run: freestanding-compiled core + custom-allocator round-trip"
$(CC) $(V3_CSTD) -Wall -Wextra $(FS_SMOKE_CFG) -Iinclude -Isrc -O2 \
test/v3/freestanding_smoke.c $(FS_OBJ) -o build/fs_smoke
./build/fs_smoke
@echo "freestanding gate: OK"
# Regenerate the freestanding zstd decode fixture (test/v3/fs_zstd_blob.inc).
gen-fs-zstd-blob: lib
$(CC) $(V3_CSTD) -Iinclude -O2 test/v3/gen_fs_zstd_blob.c build/libtinyexr3.a \
-lm -o build/gen_fs_zstd_blob
./build/gen_fs_zstd_blob > test/v3/fs_zstd_blob.inc
@echo "regenerated test/v3/fs_zstd_blob.inc"
# Freestanding gate with the opt-in zstd DECODE path enabled (proves the
# stubbed-malloc amalgamation stays forbidden-symbol-clean and decodes).
# The fs-*.o objects are not flag-tracked, so remove any built with different
# zstd flags (e.g. a prior `make freestanding-gate`) to force a correct rebuild.
freestanding-zstd-gate:
rm -f build/fs-*.o build/fs_smoke
$(MAKE) freestanding-gate EXR_FREESTANDING_ZSTD=1
# ---- tocio (sandbox: tiny OpenColorIO config engine + codegen) ------------
# Pure-C11, freestanding, no external deps. Lives outside src/ so it has its own
# build/gate targets. Mirrors the v3 freestanding discipline.
TOC_INC = -Isandbox/tocio/include -Isandbox/tocio/src
TOC_SRC = $(wildcard sandbox/tocio/src/*.c)
TOC_OBJ = $(patsubst sandbox/tocio/src/%.c,build/toc-%.o,$(TOC_SRC))
TOC_HDRS = sandbox/tocio/include/tocio.h sandbox/tocio/src/toc_internal.h
# Freestanding core: everything except the optional hosted stdio loader and the
# hosted JIT (needs OS executable memory).
TOC_CORE_SRC = $(filter-out sandbox/tocio/src/toc_stdio.c sandbox/tocio/src/toc_jit.c,$(TOC_SRC))
TOC_FS_OBJ = $(patsubst sandbox/tocio/src/%.c,build/toc-fs-%.o,$(TOC_CORE_SRC))
TOC_FS_FORBIDDEN = $(FS_FORBIDDEN)
.PHONY: tocio-lib tocio-c11-gate tocio-freestanding-gate tocio-test
build/toc-%.o: sandbox/tocio/src/%.c $(TOC_HDRS) | build
$(CC) $(V3_CSTD) -Wall -Wextra $(TOC_INC) -O2 -g $(SAN) -c $< -o $@
tocio-lib: $(TOC_OBJ)
$(AR) rcs build/libtocio.a $(TOC_OBJ)
@echo "built build/libtocio.a"
tocio-c11-gate: | build
@for f in $(TOC_SRC); do \
echo " C11 $$f"; \
$(CC) $(V3_CSTD) $(V3_WARN) $(TOC_INC) -ffp-contract=off -O1 -fsyntax-only $$f || exit 1; \
done
@echo "tocio pure-C11 gate: OK"
build/toc-fs-%.o: sandbox/tocio/src/%.c $(TOC_HDRS) | build
$(CC) -DTOC_FREESTANDING -ffreestanding -fno-builtin -fno-stack-protector \
$(V3_CSTD) $(V3_WARN) $(TOC_INC) -ffp-contract=off -O2 -g -c $< -o $@
tocio-freestanding-gate: $(TOC_FS_OBJ) sandbox/tocio/tests/toc_fs_smoke.c | build
@echo " scan: only toc_stdio.c may include <stdio.h>"
@bad=`grep -rl '<stdio.h>' sandbox/tocio/src/ | grep -v 'toc_stdio.c' || true`; \
if [ -n "$$bad" ]; then echo " FAIL: stdio leaked into: $$bad"; exit 1; fi
@echo " scan: no forbidden libc symbols in the freestanding core"
@for o in $(TOC_FS_OBJ); do \
hit=`nm -u $$o 2>/dev/null | awk '{print $$NF}' | grep -wE '$(TOC_FS_FORBIDDEN)' || true`; \
if [ -n "$$hit" ]; then echo " FAIL: $$o references:" $$hit; exit 1; fi; \
done
@echo " run: freestanding-compiled core + custom-allocator round-trip"
$(CC) $(V3_CSTD) -Wall -Wextra $(TOC_INC) -O2 \
sandbox/tocio/tests/toc_fs_smoke.c $(TOC_FS_OBJ) -o build/toc_fs_smoke
./build/toc_fs_smoke
@echo "tocio freestanding gate: OK"
tocio-test: | build
$(CC) $(V3_CSTD) -Wall -Wextra $(TOC_INC) -O1 -g $(SAN) \
sandbox/tocio/tests/toc_test.c $(TOC_SRC) -lm -ldl -o build/toc_test
ASAN_OPTIONS=detect_leaks=0 ./build/toc_test
# Validate tocio against the REAL AcademySoftwareFoundation ACES OCIO configs:
# parse each config, build every transform, and compare results to golden values
# captured from PyOpenColorIO (the C++ reference engine). The configs live under
# sandbox/tocio/ref (fetch them first); the golden TSV is committed.
# make tocio-fetch-ref # download configs + reference repos into ref/
# make tocio-gen-golden # regenerate the golden TSV (needs PyOpenColorIO)
TOC_REFDIR = sandbox/tocio/ref/configs
TOC_GOLDEN = sandbox/tocio/tests/golden/aces_golden.tsv
.PHONY: tocio-validate tocio-fetch-ref tocio-gen-golden
tocio-validate: | build
@if [ ! -d "$(TOC_REFDIR)" ]; then \
echo "ref configs missing - run 'make tocio-fetch-ref' first"; exit 2; fi
$(CC) $(V3_CSTD) -Wall -Wextra $(TOC_INC) -O1 -g $(SAN) \
sandbox/tocio/tests/toc_validate.c $(TOC_SRC) -lm -ldl -o build/toc_validate
ASAN_OPTIONS=detect_leaks=0 ./build/toc_validate $(TOC_GOLDEN) $(TOC_REFDIR)
tocio-fetch-ref:
bash sandbox/tocio/scripts/fetch_ocio_ref.sh
tocio-gen-golden:
@sp=$$(echo sandbox/tocio/ref/.pyoracle/lib/python*/site-packages); \
PYTHONPATH="$$sp" python3 sandbox/tocio/scripts/gen_golden.py
# Interpreter throughput benchmark (scalar vs SIMD per op). -O2, no sanitizers.
.PHONY: tocio-bench
tocio-bench: | build
$(CC) $(V3_CSTD) -Wall -Wextra $(TOC_INC) -O2 \
sandbox/tocio/tests/toc_bench.c $(TOC_SRC) -lm -ldl -o build/toc_bench
./build/toc_bench
# ---- tocio ARM64 (aarch64 NEON) cross build + qemu test ---------------------
# Cross-compile the full tocio test suite and run under qemu-aarch64.
# toc_jit.c has a native AArch64/NEON backend (emits A64 code to executable
# memory); under qemu-user the JIT may report TOC_ERROR_UNSUPPORTED and skip.
.PHONY: tocio-arm-test
tocio-arm-test: | build
$(ARM_CC) -static -march=armv8-a $(V3_CSTD) -Wall -Wextra $(TOC_INC) -O2 \
sandbox/tocio/tests/toc_test.c $(TOC_SRC) -lm -o build/toc_test_arm
@file build/toc_test_arm | sed 's/^/ /'
$(ARM_QEMU) ./build/toc_test_arm
# ---- tocio WASM (Emscripten ES6 module for the web viewer) -----------------
TOCW_EXPORTS = ['_tocw_parse','_tocw_free_config','_tocw_processor','_tocw_processor_view','_tocw_free_ops','_tocw_apply','_tocw_emit_glsl','_tocw_jit_glsl','_tocw_emit_metal','_tocw_emit_c','_tocw_free_str','_tocw_num_colorspaces','_tocw_colorspace_name','_malloc','_free']
TOCW_RUNTIME = ['HEAPU8','HEAPF32','HEAP32','UTF8ToString','stringToUTF8','lengthBytesUTF8']
.PHONY: wasm-tocio wasm-tocio-test
# toc_wasm.c references the JIT (tocw_jit_glsl); include toc_jit.c (inert stub
# under wasm32) so the module links.
wasm-tocio: | build
$(EMCC) -O3 $(TOC_INC) -w \
$(TOC_CORE_SRC) sandbox/tocio/src/toc_jit.c sandbox/tocio/wasm/toc_wasm.c \
-s FILESYSTEM=0 -s ALLOW_MEMORY_GROWTH=1 -s MODULARIZE=1 \
-s EXPORT_ES6=1 -s ENVIRONMENT=web,node \
-s "EXPORTED_FUNCTIONS=$(TOCW_EXPORTS)" \
-s "EXPORTED_RUNTIME_METHODS=$(TOCW_RUNTIME)" \