Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
I
imagej-elphel
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
3
Issues
3
List
Board
Labels
Milestones
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
Elphel
imagej-elphel
Commits
1b9515bc
Commit
1b9515bc
authored
Jul 15, 2026
by
Andrey Filippov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
CODEX: Run LWIR conditioning on resident GPU images
Co-authored-by:
Codex
<
codex@elphel.com
>
parent
65c4e35c
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
356 additions
and
11 deletions
+356
-11
CuasConditioning.java
...main/java/com/elphel/imagej/cuas/rt/CuasConditioning.java
+110
-9
GpuQuad.java
src/main/java/com/elphel/imagej/gpu/GpuQuad.java
+18
-0
ConditioningJna.java
src/main/java/com/elphel/imagej/gpu/jna/ConditioningJna.java
+188
-0
GpuQuadJna.java
src/main/java/com/elphel/imagej/gpu/jna/GpuQuadJna.java
+29
-0
TpJna.java
src/main/java/com/elphel/imagej/gpu/jna/TpJna.java
+11
-2
No files found.
src/main/java/com/elphel/imagej/cuas/rt/CuasConditioning.java
View file @
1b9515bc
...
...
@@ -49,6 +49,31 @@ import com.elphel.imagej.tileprocessor.QuadCLT;
* dust) is the future improvement, hence the 2-image (scale, offset) framing.
*/
public
class
CuasConditioning
{
/** One resident calibration record per native GPU instance. The FPN object is
* shared by all spawned scenes; scales/offsets are cloned per scene, so their
* small value arrays are compared rather than their identities. */
private
static
final
java
.
util
.
WeakHashMap
<
GpuQuad
,
ConditioningCache
>
GPU_CONDITIONING
=
new
java
.
util
.
WeakHashMap
<
GpuQuad
,
ConditioningCache
>();
private
static
class
ConditioningCache
{
final
Object
fpn_key
;
final
double
[]
scales
;
final
double
[]
offsets
;
final
long
dc_bits
;
final
int
num_pixels
;
ConditioningCache
(
Object
fpn_key
,
double
[]
scales
,
double
[]
offsets
,
double
dc
,
int
num_pixels
)
{
this
.
fpn_key
=
fpn_key
;
this
.
scales
=
scales
;
this
.
offsets
=
offsets
;
this
.
dc_bits
=
Double
.
doubleToRawLongBits
(
dc
);
this
.
num_pixels
=
num_pixels
;
}
boolean
matches
(
Object
fpn_key
,
double
[]
scales
,
double
[]
offsets
,
double
dc
,
int
num_pixels
)
{
return
(
this
.
fpn_key
==
fpn_key
)
&&
(
this
.
num_pixels
==
num_pixels
)
&&
(
this
.
dc_bits
==
Double
.
doubleToRawLongBits
(
dc
))
&&
java
.
util
.
Arrays
.
equals
(
this
.
scales
,
scales
)
&&
java
.
util
.
Arrays
.
equals
(
this
.
offsets
,
offsets
);
}
}
public
static
class
Config
{
public
boolean
rowcol_enable
=
true
;
// subtract per-row and per-col noise (runs AFTER photometric+FPN since 07/07/2026 - see condition())
...
...
@@ -91,6 +116,66 @@ public class CuasConditioning {
return
cfg
;
}
/**
* Prepare the native production two-map calibration once. This path is valid
* only for the intended camera contract: no software Row/Col, fixed (or
* disabled) DC, and no quadratic term. The CPU implementation remains the
* fallback/oracle for every other configuration and for pose_corr capture.
* By Codex on 07/15/2026.
*/
private
static
synchronized
boolean
prepareGpuConditioning
(
final
GpuQuad
gpuQuad
,
final
double
[][][]
image_data
,
final
double
[]
scales_in
,
final
double
[]
offsets_in
,
final
double
[]
scales2
,
final
double
[][][]
fpn
,
final
Config
cfg
)
{
if
(!
gpuQuad
.
supportsConditioningMaps
()
||
cfg
.
rowcol_enable
||
com
.
elphel
.
imagej
.
gpu
.
testdata
.
PoseCorrExport
.
isArmed
())
return
false
;
if
(
cfg
.
zero_dc
&&
Double
.
isNaN
(
cfg
.
dc_level
))
return
false
;
// AUTO median stays in the CPU oracle
if
(
cfg
.
photometric
&&
(
scales2
!=
null
))
for
(
double
a
:
scales2
)
if
(
a
!=
0.0
)
return
false
;
final
int
num_sens
=
image_data
.
length
;
if
((
num_sens
==
0
)
||
(
image_data
[
0
]
==
null
)
||
(
image_data
[
0
][
0
]
==
null
))
return
false
;
if
(((
scales_in
!=
null
)
&&
(
scales_in
.
length
<
num_sens
))
||
((
offsets_in
!=
null
)
&&
(
offsets_in
.
length
<
num_sens
))
||
((
scales2
!=
null
)
&&
(
scales2
.
length
<
num_sens
))
||
(
cfg
.
apply_fpn
&&
(
fpn
!=
null
)
&&
(
fpn
.
length
<
num_sens
)))
return
false
;
final
int
num_pixels
=
image_data
[
0
][
0
].
length
;
final
double
dc
=
cfg
.
zero_dc
?
cfg
.
dc_level
:
0.0
;
final
double
[]
scales
=
new
double
[
num_sens
];
final
double
[]
offsets
=
new
double
[
num_sens
];
for
(
int
s
=
0
;
s
<
num_sens
;
s
++)
{
if
((
image_data
[
s
]
==
null
)
||
(
image_data
[
s
].
length
!=
1
)
||
(
image_data
[
s
][
0
]
==
null
)
||
(
image_data
[
s
][
0
].
length
!=
num_pixels
))
return
false
;
scales
[
s
]
=
cfg
.
photometric
&&
(
scales_in
!=
null
)
?
scales_in
[
s
]
:
1.0
;
offsets
[
s
]
=
cfg
.
photometric
&&
(
offsets_in
!=
null
)
?
offsets_in
[
s
]
:
0.0
;
if
(
cfg
.
apply_fpn
&&
(
fpn
!=
null
)
&&
((
fpn
[
s
]
==
null
)
||
(
fpn
[
s
][
0
]
==
null
)
||
(
fpn
[
s
][
0
].
length
!=
num_pixels
)))
return
false
;
}
final
Object
fpn_key
=
cfg
.
apply_fpn
?
fpn
:
null
;
final
ConditioningCache
cached
=
GPU_CONDITIONING
.
get
(
gpuQuad
);
if
((
cached
!=
null
)
&&
cached
.
matches
(
fpn_key
,
scales
,
offsets
,
dc
,
num_pixels
))
return
true
;
final
float
[]
scale_maps
=
new
float
[
num_sens
*
num_pixels
];
final
float
[]
offset_maps
=
new
float
[
scale_maps
.
length
];
for
(
int
s
=
0
;
s
<
num_sens
;
s
++)
{
final
double
scale
=
scales
[
s
];
final
double
base
=
scale
*
offsets
[
s
];
final
double
[]
fp
=
(
cfg
.
apply_fpn
&&
(
fpn
!=
null
))
?
fpn
[
s
][
0
]
:
null
;
final
int
base_index
=
s
*
num_pixels
;
for
(
int
i
=
0
;
i
<
num_pixels
;
i
++)
{
scale_maps
[
base_index
+
i
]
=
(
float
)
scale
;
offset_maps
[
base_index
+
i
]
=
(
float
)
(
base
+
((
fp
!=
null
)
?
fp
[
i
]
:
0.0
)
+
dc
);
}
}
if
(!
gpuQuad
.
setConditioningMaps
(
scale_maps
,
offset_maps
))
return
false
;
GPU_CONDITIONING
.
put
(
gpuQuad
,
new
ConditioningCache
(
fpn_key
,
scales
,
offsets
,
dc
,
num_pixels
));
System
.
out
.
println
(
"CuasConditioning: GPU two-map conditioning active (Row/Col OFF, resident calibration, dc="
+
dc
+
")"
);
return
true
;
}
/**
* Condition all sensors in place.
* @param image_data [sensor][0][pixel] mono LWIR, row-major (idx = y*width + x). Modified in place.
...
...
@@ -269,21 +354,37 @@ public class CuasConditioning {
return
false
;
}
final
int
width
=
scene
.
getGeometryCorrection
().
getSensorWH
()[
0
];
condition
(
data
,
// double [][][] image_data (in place)
width
,
// int width
scene
.
getLwirScales
(),
// double [] scales
scene
.
getLwirOffsets
(),
// double [] offsets
scene
.
getLwirScales2
(),
// double [] scales2
scene
.
getFPN
(),
// double [][][] fpn (null - none)
cfg
);
// Config (null - defaults)
final
Config
use_cfg
=
(
cfg
!=
null
)
?
cfg
:
new
Config
();
final
GpuQuad
gpuQuad
=
scene
.
getGPUQuad
();
boolean
gpu_condition
=
prepareGpuConditioning
(
gpuQuad
,
data
,
scene
.
getLwirScales
(),
scene
.
getLwirOffsets
(),
scene
.
getLwirScales2
(),
scene
.
getFPN
(),
use_cfg
);
if
(!
gpu_condition
)
{
condition
(
data
,
// double [][][] image_data (in place)
width
,
// int width
scene
.
getLwirScales
(),
// double [] scales
scene
.
getLwirOffsets
(),
// double [] offsets
scene
.
getLwirScales2
(),
// double [] scales2
scene
.
getFPN
(),
// double [][][] fpn (null - none)
use_cfg
);
// Config
}
scene
.
saveQuadClt
();
// bind this scene (conditional; resets bayer guard on switch)
scene
.
setHasNewImageData
(
false
);
// the guard's other re-pull trigger - must be clear
gpuQuad
.
setBayerImages
(
data
,
true
);
// force H2D; sets bayer_set -> pipeline skips its pull
gpuQuad
.
setBayerImages
(
data
,
true
);
// CPU path: conditioned; GPU path: raw
if
(
gpu_condition
&&
!
gpuQuad
.
execConditioning
())
{
// Fail safe: rebuild the known CPU result and replace the raw device image.
condition
(
data
,
width
,
scene
.
getLwirScales
(),
scene
.
getLwirOffsets
(),
scene
.
getLwirScales2
(),
scene
.
getFPN
(),
use_cfg
);
gpuQuad
.
setBayerImages
(
data
,
true
);
gpu_condition
=
false
;
}
// pose_corr kernel-test capture: the conditioned data is uploaded directly (never
// stored in the QuadCLT), so the exporter takes its bit-exact copy here. No-op
// unless the curt.kernel_test=pose_corr run armed it. By Claude on 07/13/2026.
// An armed pose_corr capture deliberately forced the CPU path above, so data
// is conditioned here. Normal GPU runs leave data raw and the no-op exporter
// returns before looking at it.
com
.
elphel
.
imagej
.
gpu
.
testdata
.
PoseCorrExport
.
offerImages
(
scene
,
data
);
return
true
;
}
...
...
src/main/java/com/elphel/imagej/gpu/GpuQuad.java
View file @
1b9515bc
...
...
@@ -1291,6 +1291,24 @@ public class GpuQuad{ // quad camera description
copyD2H
.
Height
=
img_height
;
// /4;
cuMemcpy2D
(
copyD2H
);
// run copy
}
/** @return true when this backend can condition all resident source images in place. */
public
boolean
supportsConditioningMaps
()
{
return
false
;
}
/**
* Upload resident per-pixel conditioning maps, contiguous
* {@code [sensor][height][width]}. Base/JCuda does not carry this kernel.
*/
public
boolean
setConditioningMaps
(
float
[]
scale_maps
,
float
[]
offset_maps
)
{
return
false
;
}
/** Run {@code raw*scale_map-offset_map} on every resident source image. */
public
boolean
execConditioning
()
{
return
false
;
}
public
int
[]
getWH
(
boolean
use_ref
)
{
return
use_ref
?
gpu_clt_ref_wh
:
gpu_clt_wh
;
}
...
...
src/main/java/com/elphel/imagej/gpu/jna/ConditioningJna.java
0 → 100644
View file @
1b9515bc
/**
** ConditioningJna.java - Java/JNA correctness and timing gate for LWIR conditioning
**
** Copyright (C) 2026 Elphel, Inc.
**
** -----------------------------------------------------------------------------**
**
** ConditioningJna.java is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** ConditioningJna.java is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with ConditioningJna.java. If not, see <http://www.gnu.org/licenses/>.
** -----------------------------------------------------------------------------**
**
*/
package
com
.
elphel
.
imagej
.
gpu
.
jna
;
import
java.util.Arrays
;
import
com.sun.jna.Native
;
import
com.sun.jna.Pointer
;
/**
* Full Java -> JNA -> NVRTC gate for the LWIR conditioning component. It runs
* the production dimensions (16 x 640 x 512), verifies the double kernel
* bit-for-bit against the current {@code CuasConditioning.condition()} linear
* expression, verifies the float kernel bit-for-bit against {@link Math#fma},
* characterizes float-map error, and measures both synchronized kernel calls
* and the current 16-array JNA upload plus kernel path.
*
* Run after building libtileproc.so:
* <pre>
* java -Xmx2g -Djna.library.path=/home/elphel/git/tile_processor_gpu/jna \
* -cp target/classes:~/.m2/repository/net/java/dev/jna/jna/5.14.0/jna-5.14.0.jar \
* com.elphel.imagej.gpu.jna.ConditioningJna
* </pre>
* By Codex on 07/15/2026.
*/
public
class
ConditioningJna
{
private
static
void
require
(
final
TpJna
lib
,
final
int
rc
,
final
String
what
)
{
if
(
rc
!=
0
)
throw
new
IllegalStateException
(
what
+
" rc="
+
rc
+
": "
+
lib
.
tp_last_error
());
}
private
static
long
floatUlpDistance
(
final
float
a
,
final
float
b
)
{
if
(
Float
.
isNaN
(
a
)
||
Float
.
isNaN
(
b
))
return
(
Float
.
isNaN
(
a
)
&&
Float
.
isNaN
(
b
))
?
0
:
Long
.
MAX_VALUE
;
int
ia
=
Float
.
floatToRawIntBits
(
a
);
int
ib
=
Float
.
floatToRawIntBits
(
b
);
if
(
ia
<
0
)
ia
=
0x80000000
-
ia
;
if
(
ib
<
0
)
ib
=
0x80000000
-
ib
;
return
Math
.
abs
((
long
)
ia
-
(
long
)
ib
);
}
private
static
void
printTiming
(
final
String
name
,
final
long
[]
ns
)
{
final
long
[]
sorted
=
ns
.
clone
();
Arrays
.
sort
(
sorted
);
final
double
p50
=
sorted
[(
int
)
Math
.
floor
(
0.50
*
(
sorted
.
length
-
1
))]
*
1.0
e
-
6
;
final
double
p95
=
sorted
[(
int
)
Math
.
floor
(
0.95
*
(
sorted
.
length
-
1
))]
*
1.0
e
-
6
;
final
double
p99
=
sorted
[(
int
)
Math
.
floor
(
0.99
*
(
sorted
.
length
-
1
))]
*
1.0
e
-
6
;
final
double
max
=
sorted
[
sorted
.
length
-
1
]
*
1.0
e
-
6
;
System
.
out
.
printf
(
"%s: p50=%.3f ms p95=%.3f ms p99=%.3f ms max=%.3f ms (%d frames)%n"
,
name
,
p50
,
p95
,
p99
,
max
,
sorted
.
length
);
}
public
static
void
main
(
final
String
[]
args
)
{
final
String
srcdir
=
(
args
.
length
>
0
)
?
args
[
0
]
:
"/home/elphel/git/tile_processor_gpu/src"
;
final
String
devrt
=
(
args
.
length
>
1
)
?
args
[
1
]
:
"/usr/local/cuda-12.8/targets/x86_64-linux/lib/libcudadevrt.a"
;
final
int
cams
=
16
;
final
int
width
=
640
;
final
int
height
=
512
;
final
int
pixels
=
width
*
height
;
final
int
total
=
cams
*
pixels
;
final
double
dc
=
-
3512.375
;
final
float
[][]
raw
=
new
float
[
cams
][
pixels
];
final
float
[][]
expectedDouble
=
new
float
[
cams
][
pixels
];
final
float
[][]
expectedFloat
=
new
float
[
cams
][
pixels
];
final
double
[]
scales
=
new
double
[
cams
];
final
double
[]
offsets
=
new
double
[
cams
];
final
double
[]
scales2
=
new
double
[
cams
];
final
double
[]
fpn
=
new
double
[
total
];
final
float
[]
scaleMaps
=
new
float
[
total
];
final
float
[]
offsetMaps
=
new
float
[
total
];
for
(
int
c
=
0
;
c
<
cams
;
c
++)
{
scales
[
c
]
=
0.9785
+
0.0029
*
c
;
offsets
[
c
]
=
20117.25
+
2.75
*
c
;
for
(
int
i
=
0
;
i
<
pixels
;
i
++)
{
final
int
k
=
c
*
pixels
+
i
;
raw
[
c
][
i
]
=
(
float
)
(
20750.0
+
0.125
*
(
i
%
97
)
-
3.5
*
c
+
0.03125
*
((
i
/
width
)
%
13
));
fpn
[
k
]
=
0.03125
*
((
i
*
17
+
c
*
29
)
%
257
-
128
)
+
0.00031
*
(
i
%
19
);
final
double
d
=
(
double
)
raw
[
c
][
i
]
-
offsets
[
c
];
expectedDouble
[
c
][
i
]
=
(
float
)
(
scales2
[
c
]
*
d
*
d
+
scales
[
c
]
*
d
-
fpn
[
k
]
-
dc
);
scaleMaps
[
k
]
=
(
float
)
scales
[
c
];
offsetMaps
[
k
]
=
(
float
)
(
scales
[
c
]
*
offsets
[
c
]
+
fpn
[
k
]
+
dc
);
expectedFloat
[
c
][
i
]
=
Math
.
fma
(
raw
[
c
][
i
],
scaleMaps
[
k
],
-
offsetMaps
[
k
]);
}
}
final
TpJna
lib
=
Native
.
load
(
"tileproc"
,
TpJna
.
class
);
final
Pointer
module
=
lib
.
tp_create_module
(
srcdir
,
devrt
);
if
(
module
==
null
)
throw
new
IllegalStateException
(
"tp_create_module: "
+
lib
.
tp_last_error
());
Pointer
proc
=
null
;
int
badDouble
=
0
;
int
badFloat
=
0
;
double
maxModelDiff
=
0.0
;
long
maxModelUlp
=
0
;
try
{
proc
=
lib
.
tp_proc_create
(
module
);
if
(
proc
==
null
)
throw
new
IllegalStateException
(
"tp_proc_create: "
+
lib
.
tp_last_error
());
require
(
lib
,
lib
.
tp_proc_setup
(
proc
,
cams
,
1
,
width
,
height
,
0
,
0
),
"tp_proc_setup"
);
final
long
mapStart
=
System
.
nanoTime
();
require
(
lib
,
lib
.
tp_proc_set_conditioning_maps
(
proc
,
scaleMaps
,
offsetMaps
,
pixels
),
"tp_proc_set_conditioning_maps"
);
final
double
mapMs
=
(
System
.
nanoTime
()
-
mapStart
)
*
1.0
e
-
6
;
require
(
lib
,
lib
.
tp_proc_set_conditioning_oracle
(
proc
,
scales
,
offsets
,
scales2
,
fpn
,
pixels
),
"tp_proc_set_conditioning_oracle"
);
for
(
int
c
=
0
;
c
<
cams
;
c
++)
require
(
lib
,
lib
.
tp_proc_set_image
(
proc
,
c
,
raw
[
c
]),
"set raw "
+
c
);
require
(
lib
,
lib
.
tp_proc_exec_conditioning
(
proc
,
1
,
dc
),
"condition_lwir_d"
);
final
float
[]
got
=
new
float
[
pixels
];
for
(
int
c
=
0
;
c
<
cams
;
c
++)
{
require
(
lib
,
lib
.
tp_proc_get_image
(
proc
,
c
,
got
),
"get double "
+
c
);
for
(
int
i
=
0
;
i
<
pixels
;
i
++)
if
(
Float
.
floatToRawIntBits
(
got
[
i
])
!=
Float
.
floatToRawIntBits
(
expectedDouble
[
c
][
i
]))
badDouble
++;
}
for
(
int
c
=
0
;
c
<
cams
;
c
++)
require
(
lib
,
lib
.
tp_proc_set_image
(
proc
,
c
,
raw
[
c
]),
"set raw "
+
c
);
require
(
lib
,
lib
.
tp_proc_exec_conditioning
(
proc
,
0
,
dc
),
"condition_lwir_f"
);
for
(
int
c
=
0
;
c
<
cams
;
c
++)
{
require
(
lib
,
lib
.
tp_proc_get_image
(
proc
,
c
,
got
),
"get float "
+
c
);
for
(
int
i
=
0
;
i
<
pixels
;
i
++)
{
if
(
Float
.
floatToRawIntBits
(
got
[
i
])
!=
Float
.
floatToRawIntBits
(
expectedFloat
[
c
][
i
]))
badFloat
++;
final
double
d
=
Math
.
abs
((
double
)
got
[
i
]
-
expectedDouble
[
c
][
i
]);
if
(
d
>
maxModelDiff
)
maxModelDiff
=
d
;
final
long
ulp
=
floatUlpDistance
(
got
[
i
],
expectedDouble
[
c
][
i
]);
if
(
ulp
>
maxModelUlp
)
maxModelUlp
=
ulp
;
}
}
System
.
out
.
println
(
"condition_lwir_d Java expression: "
+
total
+
" pixels, bit mismatches="
+
badDouble
+
" -> "
+((
badDouble
==
0
)
?
"PASS"
:
"FAIL"
));
System
.
out
.
println
(
"condition_lwir_f two-map fma: "
+
total
+
" pixels, bit mismatches="
+
badFloat
+
" -> "
+((
badFloat
==
0
)
?
"PASS"
:
"FAIL"
));
System
.
out
.
printf
(
"float two-map vs current double model: max|diff|=%.9g counts, max ULP=%d%n"
,
maxModelDiff
,
maxModelUlp
);
System
.
out
.
printf
(
"resident float-map upload (40 MiB through JNA): %.3f ms%n"
,
mapMs
);
// Identity maps keep repeated kernel-only timing numerically stable while
// preserving the exact memory traffic of the production calculation.
Arrays
.
fill
(
scaleMaps
,
1.0f
);
Arrays
.
fill
(
offsetMaps
,
0.0f
);
require
(
lib
,
lib
.
tp_proc_set_conditioning_maps
(
proc
,
scaleMaps
,
offsetMaps
,
pixels
),
"set identity maps"
);
for
(
int
c
=
0
;
c
<
cams
;
c
++)
require
(
lib
,
lib
.
tp_proc_set_image
(
proc
,
c
,
raw
[
c
]),
"timing raw "
+
c
);
for
(
int
i
=
0
;
i
<
20
;
i
++)
require
(
lib
,
lib
.
tp_proc_exec_conditioning
(
proc
,
0
,
dc
),
"kernel warmup"
);
final
long
[]
kernelNs
=
new
long
[
200
];
for
(
int
i
=
0
;
i
<
kernelNs
.
length
;
i
++)
{
final
long
start
=
System
.
nanoTime
();
require
(
lib
,
lib
.
tp_proc_exec_conditioning
(
proc
,
0
,
dc
),
"kernel timing"
);
kernelNs
[
i
]
=
System
.
nanoTime
()
-
start
;
}
for
(
int
i
=
0
;
i
<
10
;
i
++)
{
for
(
int
c
=
0
;
c
<
cams
;
c
++)
require
(
lib
,
lib
.
tp_proc_set_image
(
proc
,
c
,
raw
[
c
]),
"upload warmup"
);
require
(
lib
,
lib
.
tp_proc_exec_conditioning
(
proc
,
0
,
dc
),
"upload warmup kernel"
);
}
final
long
[]
uploadKernelNs
=
new
long
[
100
];
for
(
int
i
=
0
;
i
<
uploadKernelNs
.
length
;
i
++)
{
final
long
start
=
System
.
nanoTime
();
for
(
int
c
=
0
;
c
<
cams
;
c
++)
require
(
lib
,
lib
.
tp_proc_set_image
(
proc
,
c
,
raw
[
c
]),
"timing upload"
);
require
(
lib
,
lib
.
tp_proc_exec_conditioning
(
proc
,
0
,
dc
),
"timing upload kernel"
);
uploadKernelNs
[
i
]
=
System
.
nanoTime
()
-
start
;
}
printTiming
(
"synchronized conditioning kernel"
,
kernelNs
);
printTiming
(
"16 JNA uploads + conditioning"
,
uploadKernelNs
);
System
.
out
.
println
(
"60 FPS frame budget: 16.667 ms"
);
}
finally
{
if
(
proc
!=
null
)
lib
.
tp_proc_destroy
(
proc
);
lib
.
tp_destroy_module
(
module
);
}
if
((
badDouble
!=
0
)
||
(
badFloat
!=
0
))
System
.
exit
(
1
);
}
}
src/main/java/com/elphel/imagej/gpu/jna/GpuQuadJna.java
View file @
1b9515bc
...
...
@@ -315,6 +315,35 @@ public class GpuQuadJna extends GpuQuad {
@Override
public
void
setBayerImage
(
float
[]
bayer_image
,
int
ncam
)
{
lib
.
tp_proc_set_image
(
proc
,
ncam
,
bayer_image
);
}
@Override
public
void
getBayerImage
(
float
[]
bayer_image
,
int
ncam
)
{
int
rc
=
lib
.
tp_proc_get_image
(
proc
,
ncam
,
bayer_image
);
if
(
rc
!=
0
)
throw
new
IllegalStateException
(
"tp_proc_get_image rc="
+
rc
+
": "
+
lib
.
tp_last_error
());
}
// Resident LWIR two-map conditioning. Calibration upload is deliberately
// separate from execution so it is paid once per calibration, never once
// per scene. By Codex on 07/15/2026.
@Override
public
boolean
supportsConditioningMaps
()
{
return
!
rectilinear
&&
!
"cpu"
.
equalsIgnoreCase
(
System
.
getProperty
(
"tp.conditioning"
,
"gpu"
));
}
@Override
public
boolean
setConditioningMaps
(
float
[]
scale_maps
,
float
[]
offset_maps
)
{
if
((
scale_maps
==
null
)
||
(
offset_maps
==
null
)
||
(
scale_maps
.
length
!=
num_cams
*
img_width
*
img_height
)
||
(
offset_maps
.
length
!=
scale_maps
.
length
))
return
false
;
int
rc
=
lib
.
tp_proc_set_conditioning_maps
(
proc
,
scale_maps
,
offset_maps
,
img_width
*
img_height
);
if
(
rc
!=
0
)
{
System
.
out
.
println
(
"GpuQuadJna.setConditioningMaps(): native rc="
+
rc
+
" ("
+
lib
.
tp_last_error
()+
")"
);
return
false
;
}
return
true
;
}
@Override
public
boolean
execConditioning
()
{
int
rc
=
lib
.
tp_proc_exec_conditioning
(
proc
,
0
,
0.0
);
// production maps already contain fixed DC
if
(
rc
!=
0
)
{
System
.
out
.
println
(
"GpuQuadJna.execConditioning(): native rc="
+
rc
+
" ("
+
lib
.
tp_last_error
()+
")"
);
return
false
;
}
return
true
;
}
// CLT buffers (main + ref) are pre-allocated full-frame in tp_proc_setup, so there is nothing to (re)allocate
// per call. Base allocs JCuda gpu_clt[_ref] here; native is fixed-size -> no-op (nothing newly allocated).
@Override
public
boolean
reAllocateClt
(
int
[]
wh
,
boolean
ref_scene
)
{
...
...
src/main/java/com/elphel/imagej/gpu/jna/TpJna.java
View file @
1b9515bc
...
...
@@ -12,8 +12,8 @@ public interface TpJna extends Library {
/** NVRTC-compile the kernels in srcdir (+ getTpDefines), cuLink(libcudadevrt), load the module.
* Returns an opaque module handle, or null on failure (see tp_last_error()). */
Pointer
tp_create_module
(
String
srcdir
,
String
libcudadevrt
);
/** Number of kernel functions resolved in the module (2
6 expected through rung 3
:
* 19 base + 4 consolidation + 2 peak + 1 LMA-products), or -1 if handle is null. */
/** Number of kernel functions resolved in the module (2
8 expected through LWIR conditioning
:
* 19 base +
2 conditioning +
4 consolidation + 2 peak + 1 LMA-products), or -1 if handle is null. */
int
tp_module_num_functions
(
Pointer
module
);
/** Last error message (NVRTC/CUDA log), empty if none. */
String
tp_last_error
();
...
...
@@ -72,6 +72,15 @@ public interface TpJna extends Library {
int
tp_proc_set_kernels
(
Pointer
proc
,
int
cam
,
float
[]
d
,
int
n
);
int
tp_proc_set_kernel_offsets
(
Pointer
proc
,
int
cam
,
float
[]
d
,
int
n
);
int
tp_proc_set_image
(
Pointer
proc
,
int
cam
,
float
[]
d
);
/** De-pitch one resident source image into out[width*height]. */
int
tp_proc_get_image
(
Pointer
proc
,
int
cam
,
float
[]
out
);
/** Resident production calibration maps, contiguous [camera][height][width]. */
int
tp_proc_set_conditioning_maps
(
Pointer
proc
,
float
[]
scales
,
float
[]
offsets
,
int
numPixels
);
/** Java-expression validation calibration; FPN is contiguous [camera][height][width]. */
int
tp_proc_set_conditioning_oracle
(
Pointer
proc
,
double
[]
scales
,
double
[]
offsets
,
double
[]
scales2
,
double
[]
fpn
,
int
numPixels
);
/** Condition all resident source images in place; useDouble=0 selects production two-map float. */
int
tp_proc_exec_conditioning
(
Pointer
proc
,
int
useDouble
,
double
dcLevel
);
/** use_center_image: broadcast one center image to all sensors (FPN back-prop mode). */
int
tp_proc_set_center_image
(
Pointer
proc
,
float
[]
d
);
int
tp_proc_set_tasks
(
Pointer
proc
,
float
[]
ftasks
,
int
ntiles
,
int
totalFloats
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment