diff --git a/paint/shaders/depth_to_ao_pass.kong b/paint/shaders/depth_to_ao_pass.kong index 14254f9e..4139edbd 100644 --- a/paint/shaders/depth_to_ao_pass.kong +++ b/paint/shaders/depth_to_ao_pass.kong @@ -13,9 +13,14 @@ const height_map: tex2d; #[set(everything)] const normal_map: tex2d; -const max_steps: float = 100.0; -const ray_step: float = 0.005; +const num_dirs: float = 64.0; // cosine-weighted hemisphere directions +const num_steps: float = 24.0; // height-field march steps per direction +const max_radius: float = 0.04; // search radius const height_scale: float = 1.0; +const ao_bias: float = 0.0015; // ignore occluders within this height of the surface +const ao_max_diff: float = 0.3; // ignore occluders taller than this +const ao_power: float = 1.6; // contrast of the final occlusion +const PI2: float = 6.28318530718; struct vert_in { pos: float2; @@ -34,44 +39,8 @@ fun depth_to_ao_pass_vert(input: vert_in): vert_out { return output; } -fun get_delta_height(hit_xy: float2, hit_z: float, uv: float2): float { - var coord: float2 = uv + hit_xy; - if (coord.x < 0.0 || coord.x > 1.0 || coord.y < 0.0 || coord.y > 1.0) { - return 1000.0; - } - var sampled: float = sample_lod(height_map, sampler_linear, coord, 0.0).r * height_scale; - return sampled - hit_z; -} - -fun ray_cast(dir: float3, uv: float2, height: float): float { - var n_dir: float3 = normalize(dir); - var step_vec: float3 = n_dir * ray_step; - var hit_xy: float2 = float2(0.0, 0.0); - var hit_z: float = height + 0.001; - var hit_coord: float3 = float3(hit_xy, hit_z); - var prev_delta: float = get_delta_height(hit_xy, hit_z, uv); - var prev_t: float = 0.0; - hit_coord += step_vec; - var t: float = ray_step; - var i: int = 1; - while (i < int(max_steps)) { - var delta: float = get_delta_height(hit_coord.xy, hit_coord.z, uv); - if (delta > 0.0 && delta < 0.2) { - var prev_sdf: float = -prev_delta; - var sdf: float = -delta; - var fraction: float = prev_sdf / (prev_sdf - sdf); - return prev_t + ray_step * fraction; - } - if (t > 0.02) { // max_radius - return 0.02; // max_radius - } - prev_delta = delta; - prev_t = t; - hit_coord += step_vec; - t += ray_step; - i += 1; - } - return 0.02; // max_radius +fun hash(p: float2): float { + return frac(sin(dot(p, float2(12.9898, 78.233))) * 43758.5453); } fun tangent(n: float3): float3 { @@ -83,37 +52,58 @@ fun tangent(n: float3): float3 { return normalize(t2); } +fun ao_ray(dir: float3, uv: float2, h0: float): float { + var step_size: float = max_radius / num_steps; + var t: float = step_size; + var i: int = 0; + while (i < int(num_steps)) { + var coord: float2 = uv + dir.xy * t; + if (coord.x < 0.0 || coord.x > 1.0 || coord.y < 0.0 || coord.y > 1.0) { + return 0.0; // walked off the map + } + var ray_z: float = h0 + dir.z * t; + var surf: float = sample_lod(height_map, sampler_linear, coord, 0.0).r * height_scale; + var delta: float = surf - ray_z; + if (delta > ao_bias && delta < ao_max_diff) { + return 1.0 - t / max_radius; // linear distance falloff + } + t += step_size; + i += 1; + } + return 0.0; +} + fun depth_to_ao_pass_frag(input: vert_out): float4 { var height: float = sample_lod(height_map, sampler_linear, input.tex, 0.0).r * height_scale; var normal: float3 = sample_lod(normal_map, sampler_linear, input.tex, 0.0).rgb * 2.0 - 1.0; var n: float3 = normalize(normal); - var o1: float3 = tangent(n); - var o2: float3 = cross(o1, n); - var c1: float3 = 0.5 * (o1 + o2); - var c2: float3 = 0.5 * (o1 - o2); - var col: float = ray_cast(lerp3(n, o1, 0.5), input.tex, height); - col += ray_cast(lerp3(n, -o1, 0.5), input.tex, height); - col += ray_cast(lerp3(n, o2, 0.5), input.tex, height); - col += ray_cast(lerp3(n, -o2, 0.5), input.tex, height); - col += ray_cast(lerp3(n, c1, 0.5), input.tex, height); - col += ray_cast(lerp3(n, -c1, 0.5), input.tex, height); - col += ray_cast(lerp3(n, c2, 0.5), input.tex, height); - col += ray_cast(lerp3(n, -c2, 0.5), input.tex, height); - var d1: float3 = normalize(o1 + 0.5 * o2); - var d2: float3 = normalize(o1 - 0.5 * o2); - var d3: float3 = normalize(o2 + 0.5 * o1); - var d4: float3 = normalize(o2 - 0.5 * o1); - col += ray_cast(lerp3(n, d1, 0.5), input.tex, height); - col += ray_cast(lerp3(n, -d1, 0.5), input.tex, height); - col += ray_cast(lerp3(n, d2, 0.5), input.tex, height); - col += ray_cast(lerp3(n, -d2, 0.5), input.tex, height); - col += ray_cast(lerp3(n, d3, 0.5), input.tex, height); - col += ray_cast(lerp3(n, -d3, 0.5), input.tex, height); - col += ray_cast(lerp3(n, d4, 0.5), input.tex, height); - col += ray_cast(lerp3(n, -d4, 0.5), input.tex, height); - var f: float = (col / (16.0 * 0.02)); // max_radius - return float4(f, f, f, 1.0); + var t1: float3 = tangent(n); + var t2: float3 = cross(n, t1); + + var jitter: float = hash(input.tex); + var h0: float = height + ao_bias; + + var occ: float = 0.0; + var i: int = 0; + while (i < int(num_dirs)) { + // Cosine-weighted hemisphere sample + var u1: float = (float(i) + 0.5) / num_dirs; + var u2: float = frac(float(i) * 0.61803398875 + jitter); + var r: float = sqrt(u1); + var phi: float = PI2 * u2; + var lx: float = r * cos(phi); + var ly: float = r * sin(phi); + var lz: float = sqrt(max(0.0, 1.0 - u1)); + var dir: float3 = lx * t1 + ly * t2 + lz * n; + + occ += ao_ray(dir, input.tex, h0); + i += 1; + } + + var ao: float = 1.0 - occ / num_dirs; + ao = pow(max(0.0, ao), ao_power); + return float4(ao, ao, ao, 1.0); } #[pipe] diff --git a/paint/shaders/depth_to_normal_pass.kong b/paint/shaders/depth_to_normal_pass.kong index 6a4c1ee2..97d7ffb0 100644 --- a/paint/shaders/depth_to_normal_pass.kong +++ b/paint/shaders/depth_to_normal_pass.kong @@ -1,7 +1,7 @@ #[set(everything)] const constants: { - empty: float4; + texel_size: float2; }; #[set(everything)] @@ -28,31 +28,31 @@ fun depth_to_normal_pass_vert(input: vert_in): vert_out { } fun depth_to_normal_pass_frag(input: vert_out): float4 { - var ts: float = 1.0 / 768.0; - var h00: float = sample_lod(height_map, sampler_linear, input.tex + float2(-2.0 * ts, -2.0 * ts), 0.0).r; - var h01: float = sample_lod(height_map, sampler_linear, input.tex + float2(-ts, -2.0 * ts), 0.0).r; - var h02: float = sample_lod(height_map, sampler_linear, input.tex + float2(0.0, -2.0 * ts), 0.0).r; - var h03: float = sample_lod(height_map, sampler_linear, input.tex + float2( ts, -2.0 * ts), 0.0).r; - var h04: float = sample_lod(height_map, sampler_linear, input.tex + float2( 2.0 * ts, -2.0 * ts), 0.0).r; - var h10: float = sample_lod(height_map, sampler_linear, input.tex + float2(-2.0 * ts, -ts), 0.0).r; - var h11: float = sample_lod(height_map, sampler_linear, input.tex + float2(-ts, -ts), 0.0).r; - var h12: float = sample_lod(height_map, sampler_linear, input.tex + float2(0.0, -ts), 0.0).r; - var h13: float = sample_lod(height_map, sampler_linear, input.tex + float2( ts, -ts), 0.0).r; - var h14: float = sample_lod(height_map, sampler_linear, input.tex + float2( 2.0 * ts, -ts), 0.0).r; - var h20: float = sample_lod(height_map, sampler_linear, input.tex + float2(-2.0 * ts, 0.0), 0.0).r; - var h21: float = sample_lod(height_map, sampler_linear, input.tex + float2(-ts, 0.0), 0.0).r; - var h23: float = sample_lod(height_map, sampler_linear, input.tex + float2( ts, 0.0), 0.0).r; - var h24: float = sample_lod(height_map, sampler_linear, input.tex + float2( 2.0 * ts, 0.0), 0.0).r; - var h30: float = sample_lod(height_map, sampler_linear, input.tex + float2(-2.0 * ts, ts), 0.0).r; - var h31: float = sample_lod(height_map, sampler_linear, input.tex + float2(-ts, ts), 0.0).r; - var h32: float = sample_lod(height_map, sampler_linear, input.tex + float2(0.0, ts), 0.0).r; - var h33: float = sample_lod(height_map, sampler_linear, input.tex + float2( ts, ts), 0.0).r; - var h34: float = sample_lod(height_map, sampler_linear, input.tex + float2( 2.0 * ts, ts), 0.0).r; - var h40: float = sample_lod(height_map, sampler_linear, input.tex + float2(-2.0 * ts, 2.0 * ts), 0.0).r; - var h41: float = sample_lod(height_map, sampler_linear, input.tex + float2(-ts, 2.0 * ts), 0.0).r; - var h42: float = sample_lod(height_map, sampler_linear, input.tex + float2(0.0, 2.0 * ts), 0.0).r; - var h43: float = sample_lod(height_map, sampler_linear, input.tex + float2( ts, 2.0 * ts), 0.0).r; - var h44: float = sample_lod(height_map, sampler_linear, input.tex + float2( 2.0 * ts, 2.0 * ts), 0.0).r; + var ts: float2 = constants.texel_size; + var h00: float = sample_lod(height_map, sampler_linear, input.tex + float2(-2.0, -2.0) * ts, 0.0).r; + var h01: float = sample_lod(height_map, sampler_linear, input.tex + float2(-1.0, -2.0) * ts, 0.0).r; + var h02: float = sample_lod(height_map, sampler_linear, input.tex + float2( 0.0, -2.0) * ts, 0.0).r; + var h03: float = sample_lod(height_map, sampler_linear, input.tex + float2( 1.0, -2.0) * ts, 0.0).r; + var h04: float = sample_lod(height_map, sampler_linear, input.tex + float2( 2.0, -2.0) * ts, 0.0).r; + var h10: float = sample_lod(height_map, sampler_linear, input.tex + float2(-2.0, -1.0) * ts, 0.0).r; + var h11: float = sample_lod(height_map, sampler_linear, input.tex + float2(-1.0, -1.0) * ts, 0.0).r; + var h12: float = sample_lod(height_map, sampler_linear, input.tex + float2( 0.0, -1.0) * ts, 0.0).r; + var h13: float = sample_lod(height_map, sampler_linear, input.tex + float2( 1.0, -1.0) * ts, 0.0).r; + var h14: float = sample_lod(height_map, sampler_linear, input.tex + float2( 2.0, -1.0) * ts, 0.0).r; + var h20: float = sample_lod(height_map, sampler_linear, input.tex + float2(-2.0, 0.0) * ts, 0.0).r; + var h21: float = sample_lod(height_map, sampler_linear, input.tex + float2(-1.0, 0.0) * ts, 0.0).r; + var h23: float = sample_lod(height_map, sampler_linear, input.tex + float2( 1.0, 0.0) * ts, 0.0).r; + var h24: float = sample_lod(height_map, sampler_linear, input.tex + float2( 2.0, 0.0) * ts, 0.0).r; + var h30: float = sample_lod(height_map, sampler_linear, input.tex + float2(-2.0, 1.0) * ts, 0.0).r; + var h31: float = sample_lod(height_map, sampler_linear, input.tex + float2(-1.0, 1.0) * ts, 0.0).r; + var h32: float = sample_lod(height_map, sampler_linear, input.tex + float2( 0.0, 1.0) * ts, 0.0).r; + var h33: float = sample_lod(height_map, sampler_linear, input.tex + float2( 1.0, 1.0) * ts, 0.0).r; + var h34: float = sample_lod(height_map, sampler_linear, input.tex + float2( 2.0, 1.0) * ts, 0.0).r; + var h40: float = sample_lod(height_map, sampler_linear, input.tex + float2(-2.0, 2.0) * ts, 0.0).r; + var h41: float = sample_lod(height_map, sampler_linear, input.tex + float2(-1.0, 2.0) * ts, 0.0).r; + var h42: float = sample_lod(height_map, sampler_linear, input.tex + float2( 0.0, 2.0) * ts, 0.0).r; + var h43: float = sample_lod(height_map, sampler_linear, input.tex + float2( 1.0, 2.0) * ts, 0.0).r; + var h44: float = sample_lod(height_map, sampler_linear, input.tex + float2( 2.0, 2.0) * ts, 0.0).r; var dx: float = 2.0 * (h00 - h04) + 1.0 * (h01 - h03) + 3.0 * (h10 - h14) + 2.0 * (h11 - h13) + 4.0 * (h20 - h24) + 3.0 * (h21 - h23) diff --git a/paint/sources/config.c b/paint/sources/config.c index 7198f05f..0161652d 100644 --- a/paint/sources/config.c +++ b/paint/sources/config.c @@ -128,7 +128,6 @@ void config_save() { json_encode_i32("scene_atlas_res", g_config->scene_atlas_res); json_encode_bool("grid_snap", g_config->grid_snap); json_encode_bool("experimental", g_config->experimental); - json_encode_i32("neural_backend", g_config->neural_backend); json_encode_i32("neural_res", g_config->neural_res); json_encode_i32("render_mode", g_config->render_mode); json_encode_i32("workspace", g_config->workspace); @@ -260,7 +259,6 @@ void config_init() { g_config->view2d_grid_cell = 64; g_config->view2d_grid_snap = false; g_config->experimental = false; - g_config->neural_backend = NEURAL_BACKEND_VULKAN; g_config->neural_res = 512; #if defined(IRON_ANDROID) || defined(IRON_IOS) g_config->render_mode = RENDER_MODE_FORWARD; diff --git a/paint/sources/enums.h b/paint/sources/enums.h index c81e9850..61317c2f 100644 --- a/paint/sources/enums.h +++ b/paint/sources/enums.h @@ -274,12 +274,6 @@ typedef enum { LAYOUT_SIZE_HEADER = 6, // 0 - hidden, 1 - visible } layout_size_t; -typedef enum { - NEURAL_BACKEND_CPU = 0, - NEURAL_BACKEND_VULKAN = 1, - NEURAL_BACKEND_CUDA = 2, -} neural_backend_t; - typedef enum { PREFERENCES_TAB_INTERFACE = 0, PREFERENCES_TAB_THEME = 1, diff --git a/paint/sources/functions.h b/paint/sources/functions.h index 1b941499..92e7cd13 100644 --- a/paint/sources/functions.h +++ b/paint/sources/functions.h @@ -789,7 +789,7 @@ char *neural_node_value(ui_node_t *node, ui_node_socket_t *socket); ui_node_t *neural_from_node(ui_node_socket_t *inp, i32 socket); bool neural_node_button(ui_node_t *node, char *model); void neural_node_check_result(ui_node_t *node); -char *neural_node_sd_bin(); +char *neural_node_iris_bin(); char *neural_node_dir(); void neural_node_download_models(string_array_t *models); void neural_node_models_init(); diff --git a/paint/sources/nodes_neural/edit_image_node.c b/paint/sources/nodes_neural/edit_image_node.c index cc4d6099..17b1f9b5 100644 --- a/paint/sources/nodes_neural/edit_image_node.c +++ b/paint/sources/nodes_neural/edit_image_node.c @@ -4,37 +4,11 @@ static string_array_t *edit_image_node_flux_klein_args(char *dir) { string_array_t *argv = any_array_create_from_raw( (void *[]){ - string("%s/%s", dir, neural_node_sd_bin()), - "--diffusion-model", - string("%s/flux-2-klein-4b-Q8_0.gguf", dir), - "--taesd", - string("%s/taef2.safetensors", dir), - "--llm", - string("%s/Qwen3-4B-Q8_0.gguf", dir), - "--steps", - "4", + string("%s/%s", dir, neural_node_iris_bin()), + "-d", + string("%s", dir), }, - 9); - return argv; -} - -static string_array_t *edit_image_node_qwen_args(char *dir) { - string_array_t *argv = any_array_create_from_raw( - (void *[]){ - string("%s/%s", dir, neural_node_sd_bin()), - "--diffusion-model", - string("%s/qwen-image-edit-2511-Q4_K_S.gguf", dir), - "--vae", - string("%s/Qwen_Image-VAE.safetensors", dir), - "--llm", - string("%s/Qwen2.5-VL-7B-Instruct-Q4_K_S.gguf", dir), - "--llm_vision", - string("%s/mmproj-F16.gguf", dir), - "--qwen-image-zero-cond-t", - "--steps", - "30", - }, - 12); + 3); return argv; } @@ -44,7 +18,7 @@ void edit_image_node_button(i32 node_id) { char *node_name = parser_material_node_name(node, NULL); ui_handle_t *h = ui_handle(node_name); - string_array_t *models = any_array_create_from_raw((void *[]){"FLUX 2 klein", "Qwen Image Edit"}, 2); + string_array_t *models = any_array_create_from_raw((void *[]){"FLUX 2 klein"}, 1); i32 model = ui_combo(ui_nest(h, 0), models, tr("Model"), false, UI_ALIGN_LEFT, true); char *prompt = ui_text_area(ui_nest(h, 1), UI_ALIGN_LEFT, true, tr("prompt"), true); node->buttons->buffer[0]->height = string_split(prompt, "\n")->length + 4; @@ -74,17 +48,9 @@ void edit_image_node_button(i32 node_id) { if (model == 0) { argv = edit_image_node_flux_klein_args(dir); } - else { - argv = edit_image_node_qwen_args(dir); - } - string_array_push(argv, "--cfg-scale"); - string_array_push(argv, "1.0"); - string_array_push(argv, "--diffusion-fa"); - string_array_push(argv, "--offload-to-cpu"); - string_array_push(argv, "--strength"); - string_array_push(argv, string("%f", strength)); - string_array_push(argv, "-s"); + // string_array_push(argv, string("%f", strength)); + string_array_push(argv, "--seed"); string_array_push(argv, "-1"); string_array_push(argv, "-W"); string_array_push(argv, string("%d", g_config->neural_res)); @@ -92,12 +58,15 @@ void edit_image_node_button(i32 node_id) { string_array_push(argv, string("%d", g_config->neural_res)); string_array_push(argv, "-p"); string_array_push(argv, prompt); - string_array_push(argv, "-r"); + string_array_push(argv, "-i"); string_array_push(argv, string("%s/input.png", dir)); string_array_push(argv, "-o"); string_array_push(argv, string("%s/output.png", dir)); if (tiled) { - string_array_push(argv, "--circular"); + string_array_push(argv, "--tileable"); + } + if (g_config->neural_res >= 2048) { + string_array_push(argv, "--vae-tiling"); } string_array_push(argv, NULL); diff --git a/paint/sources/nodes_neural/image_to_pbr_node.c b/paint/sources/nodes_neural/image_to_pbr_node.c index 692b86d5..2afe73e3 100644 --- a/paint/sources/nodes_neural/image_to_pbr_node.c +++ b/paint/sources/nodes_neural/image_to_pbr_node.c @@ -6,6 +6,7 @@ gpu_texture_t *image_to_pbr_node_result_normal = NULL; gpu_texture_t *image_to_pbr_node_result_occlusion = NULL; gpu_texture_t *image_to_pbr_node_result_height = NULL; gpu_texture_t *image_to_pbr_node_result_roughness = NULL; +i32 image_to_pbr_node_node_id = -1; char *image_to_pbr_node_vector(ui_node_t *node, ui_node_socket_t *socket) { gpu_texture_t *result = NULL; @@ -62,71 +63,52 @@ void image_to_pbr_node_check_result(void (*done)(gpu_texture_t *)) { } } -void image_to_pbr_node_run_sd(char *model, char *prompt, void (*done)(gpu_texture_t *)) { +void image_to_pbr_node_run_da3(bool tileable, int width, int height, void (*done)(gpu_texture_t *)) { char *dir = neural_node_dir(); string_array_t *argv = any_array_create_from_raw( (void *[]){ - string("%s/%s", dir, neural_node_sd_bin()), - "-m", - string("%s/%s", dir, model), - "--sampling-method", - "ddim_trailing", - "--steps", - "10", - "-s", - "-1", + string("%s/%s", dir, neural_node_iris_bin()), + "-d", + string("%s", dir), + "--depth", "-W", - "768", + string("%d", width), "-H", - "768", - "-p", - prompt, + string("%d", height), "-i", string("%s/input.png", dir), "-o", string("%s/output.png", dir), - NULL, }, - 20); + 12); + if (tileable) { + string_array_push(argv, "--tileable"); + } + string_array_push(argv, NULL); iron_exec_async(argv->buffer[0], argv->buffer); sys_notify_on_update(image_to_pbr_node_check_result, done); } void image_to_pbr_node_all_done(void *_) { + int res_w = image_to_pbr_node_result_height->width; + int res_h = image_to_pbr_node_result_height->height; + render_target_t *occmap; { render_target_t *t = render_target_create(); t->name = "occmap"; - t->width = 768; - t->height = 768; + t->width = res_w; + t->height = res_h; t->format = "RGBA32"; render_path_create_render_target(t); - } - // Ping-pong targets for the blur - { - render_target_t *t = render_target_create(); - t->name = "occmap_blur"; - t->width = 768; - t->height = 768; - t->format = "R8"; - render_path_create_render_target(t); - } - render_target_t *occmap_blurred; - { - render_target_t *t = render_target_create(); - t->name = "occmap_blurred"; - t->width = 768; - t->height = 768; - t->format = "R8"; - render_path_create_render_target(t); - occmap_blurred = t; + occmap = t; } render_target_t *normmap; { render_target_t *t = render_target_create(); t->name = "normmap"; - t->width = 768; - t->height = 768; + t->width = res_w; + t->height = res_h; t->format = "RGBA32"; render_path_create_render_target(t); normmap = t; @@ -134,8 +116,8 @@ void image_to_pbr_node_all_done(void *_) { { render_target_t *t = render_target_create(); t->name = "_height_map"; - t->width = 768; - t->height = 768; + t->width = res_w; + t->height = res_h; t->format = "RGBA32"; t->_image = image_to_pbr_node_result_height; any_map_set(render_path_render_targets, t->name, t); @@ -144,8 +126,8 @@ void image_to_pbr_node_all_done(void *_) { { render_target_t *t = render_target_create(); t->name = "_normal_map"; - t->width = 768; - t->height = 768; + t->width = res_w; + t->height = res_h; t->format = "RGBA32"; t->_image = image_to_pbr_node_result_normal; any_map_set(render_path_render_targets, t->name, t); @@ -157,6 +139,7 @@ void image_to_pbr_node_all_done(void *_) { render_path_load_shader("Scene/ssao_blur_pass/ssao_blur_pass_x"); render_path_load_shader("Scene/ssao_blur_pass/ssao_blur_pass_y"); + // Normal map render_path_set_target("normmap", NULL, NULL, GPU_CLEAR_NONE, 0, 0.0); render_path_bind_target("_height_map", "height_map"); render_path_draw_shader("Scene/depth_to_normal_pass/depth_to_normal_pass"); @@ -166,88 +149,117 @@ void image_to_pbr_node_all_done(void *_) { gc_root(image_to_pbr_node_result_normal); normal_map_rt->_image = normmap->_image; + // Occlusion render_path_set_target("occmap", NULL, NULL, GPU_CLEAR_NONE, 0, 0.0); render_path_bind_target("_height_map", "height_map"); render_path_bind_target("_normal_map", "normal_map"); render_path_draw_shader("Scene/depth_to_ao_pass/depth_to_ao_pass"); - // Blur - render_path_set_target("occmap_blur", NULL, NULL, GPU_CLEAR_NONE, 0, 0.0); - render_path_bind_target("occmap", "tex"); - render_path_bind_target("_normal_map", "gbuffer0"); - render_path_draw_shader("Scene/ssao_blur_pass/ssao_blur_pass_x"); - - render_path_set_target("occmap_blurred", NULL, NULL, GPU_CLEAR_NONE, 0, 0.0); - render_path_bind_target("occmap_blur", "tex"); - render_path_bind_target("_normal_map", "gbuffer0"); - render_path_draw_shader("Scene/ssao_blur_pass/ssao_blur_pass_y"); - - // Second blur - render_path_set_target("occmap_blur", NULL, NULL, GPU_CLEAR_NONE, 0, 0.0); - render_path_bind_target("occmap_blurred", "tex"); - render_path_bind_target("_normal_map", "gbuffer0"); - render_path_draw_shader("Scene/ssao_blur_pass/ssao_blur_pass_x"); - - render_path_set_target("occmap_blurred", NULL, NULL, GPU_CLEAR_NONE, 0, 0.0); - render_path_bind_target("occmap_blur", "tex"); - render_path_bind_target("_normal_map", "gbuffer0"); - render_path_draw_shader("Scene/ssao_blur_pass/ssao_blur_pass_y"); - gc_unroot(image_to_pbr_node_result_occlusion); - image_to_pbr_node_result_occlusion = occmap_blurred->_image; + image_to_pbr_node_result_occlusion = occmap->_image; gc_root(image_to_pbr_node_result_occlusion); -} -void image_to_pbr_node_roughness_done(gpu_texture_t *tex) { - gc_unroot(image_to_pbr_node_result_roughness); - image_to_pbr_node_result_roughness = tex; - gc_root(image_to_pbr_node_result_roughness); - // image_to_pbr_node_run_sd("marigold-iid-lighting-v1-1.q8_0.gguf", "_diffuse_shading", function(tex: gpu_texture_t) { - sys_notify_on_next_frame(&image_to_pbr_node_all_done, NULL); -} + // Base color + char *dir = neural_node_dir(); + gpu_texture_t *input_tex = iron_load_texture(string("%s%sinput.png", dir, PATH_SEP)); + buffer_t *in_px = gpu_get_texture_pixels(input_tex); + buffer_t *occ_px = gpu_get_texture_pixels(occmap->_image); -void image_to_pbr_node_base_done(gpu_texture_t *tex) { - // - buffer_t *pixels = gpu_get_texture_pixels(tex); - for (uint32_t i = 0; i < pixels->length; i += 4) { - for (uint32_t c = 0; c < 3; ++c) { - int v = (int)(pixels->buffer[i + c] * 2.5f); - pixels->buffer[i + c] = v > 255 ? 255 : v; + int bw = occmap->_image->width; + int bh = occmap->_image->height; + + const float linear_light_fac = 0.3f; + const float subtract_fac = 0.3f; + const float bright = 0.1f; + const float contr = 0.0f; + const float bc_a = 1.0f + contr; + const float bc_b = bright - contr * 0.5f; + + buffer_t *base_px = buffer_create(bw * bh * 4); + for (int y = 0; y < bh; ++y) { + for (int x = 0; x < bw; ++x) { + int bi = (y * bw + x) * 4; + + float occ = occ_px->buffer[bi] / 255.0f; + float inv_occ = 1.0f - occ; + for (int c = 0; c < 3; ++c) { + float col = in_px->buffer[bi + c] / 255.0f; + col += linear_light_fac * (2.0f * (inv_occ - 0.5f)); + col -= subtract_fac * inv_occ; + col = bc_a * col + bc_b; + if (col < 0.0f) + col = 0.0f; + if (col > 1.0f) + col = 1.0f; + base_px->buffer[bi + c] = (int)(col * 255.0f); + } + base_px->buffer[bi + 3] = 255; } } - gpu_texture_t *bright = gpu_create_texture_from_bytes(pixels, tex->width, tex->height, GPU_TEXTURE_FORMAT_RGBA32); - // + gpu_texture_t *base = gpu_create_texture_from_bytes(base_px, bw, bh, GPU_TEXTURE_FORMAT_RGBA32); gc_unroot(image_to_pbr_node_result_base); - image_to_pbr_node_result_base = bright; + image_to_pbr_node_result_base = base; gc_root(image_to_pbr_node_result_base); - image_to_pbr_node_run_sd("marigold-iid-appearance-v1-1.q8_0.gguf", "_roughness", &image_to_pbr_node_roughness_done); + + // Roughness + buffer_t *rough_px = buffer_create(bw * bh * 4); + for (int y = 0; y < bh; ++y) { + for (int x = 0; x < bw; ++x) { + int bi = (y * bw + x) * 4; + float lum = (base_px->buffer[bi] * 0.299f + base_px->buffer[bi + 1] * 0.587f + base_px->buffer[bi + 2] * 0.114f) / 255.0f; + + float detail = 0.0f; + int count = 0; + for (int oy = -1; oy <= 1; ++oy) { + for (int ox = -1; ox <= 1; ++ox) { + int sx = x + ox; + int sy = y + oy; + if (sx < 0 || sx >= bw || sy < 0 || sy >= bh) { + continue; + } + int si = (sy * bw + sx) * 4; + float slum = (base_px->buffer[si] * 0.299f + base_px->buffer[si + 1] * 0.587f + base_px->buffer[si + 2] * 0.114f) / 255.0f; + float d = slum - lum; + detail += d < 0.0f ? -d : d; + count += 1; + } + } + detail = count > 0 ? detail / count : 0.0f; + + float rough = 0.5f + detail * 4.0f; + if (rough > 1.0f) { + rough = 1.0f; + } + int v = (int)(rough * 255.0f); + rough_px->buffer[bi] = v; + rough_px->buffer[bi + 1] = v; + rough_px->buffer[bi + 2] = v; + rough_px->buffer[bi + 3] = 255; + } + } + gpu_texture_t *rough = gpu_create_texture_from_bytes(rough_px, bw, bh, GPU_TEXTURE_FORMAT_RGBA32); + + gc_unroot(image_to_pbr_node_result_roughness); + image_to_pbr_node_result_roughness = rough; + gc_root(image_to_pbr_node_result_roughness); + + ui_node_canvas_t *canvas = ui_nodes_get_canvas(true); + ui_node_t *node = ui_get_node(canvas->nodes, image_to_pbr_node_node_id); + if (node != NULL) { + any_imap_set(neural_node_results, node->outputs->buffer[0]->id, image_to_pbr_node_result_base); + any_imap_set(neural_node_results, node->outputs->buffer[1]->id, image_to_pbr_node_result_occlusion); + any_imap_set(neural_node_results, node->outputs->buffer[2]->id, image_to_pbr_node_result_roughness); + any_imap_set(neural_node_results, node->outputs->buffer[3]->id, image_to_pbr_node_result_normal); + any_imap_set(neural_node_results, node->outputs->buffer[4]->id, image_to_pbr_node_result_height); + } } -void image_to_pbr_node_height_done(gpu_texture_t *tex) { - gc_unroot(image_to_pbr_node_result_height); - image_to_pbr_node_result_height = tex; - gc_root(image_to_pbr_node_result_height); - image_to_pbr_node_run_sd("marigold-iid-lighting-v1-1.q8_0.gguf", "_base", &image_to_pbr_node_base_done); -} - -void image_to_pbr_node_normals_done(gpu_texture_t *tex) { - gc_unroot(image_to_pbr_node_result_normal); - image_to_pbr_node_result_normal = tex; - gc_root(image_to_pbr_node_result_normal); - image_to_pbr_node_run_sd("marigold-depth-v1-1.q8_0.gguf", "_height", &image_to_pbr_node_height_done); -} - -void image_to_pbr_node_normals_only_done(gpu_texture_t *tex) { - gc_unroot(image_to_pbr_node_result_normal); - image_to_pbr_node_result_normal = tex; - gc_root(image_to_pbr_node_result_normal); -} - -void image_to_pbr_node_height_only_done(gpu_texture_t *tex) { +void image_to_pbr_node_depth_done(gpu_texture_t *tex) { gc_unroot(image_to_pbr_node_result_height); image_to_pbr_node_result_height = tex; gc_root(image_to_pbr_node_result_height); + sys_notify_on_next_frame(&image_to_pbr_node_all_done, NULL); } void image_to_pbr_node_button(i32 node_id) { @@ -258,20 +270,11 @@ void image_to_pbr_node_button(i32 node_id) { string_array_t *models = any_array_create_from_raw( (void *[]){ - "Marigold", + "DA3MONO", }, 1); i32 model = ui_combo(ui_nest(h, 0), models, tr("Model"), false, UI_ALIGN_LEFT, true); - string_array_t *channels = any_array_create_from_raw( - (void *[]){ - "All", - "Normal Map", - "Height", - }, - 3); - i32 channel = ui_combo(ui_nest(h, 1), channels, tr("Channels"), false, UI_ALIGN_LEFT, true); - if (neural_node_button(node, models->buffer[model])) { ui_node_t *from_node = neural_from_node(node->inputs->buffer[0], 0); gpu_texture_t *input = ui_nodes_get_node_preview_image(from_node); @@ -285,15 +288,9 @@ void image_to_pbr_node_button(i32 node_id) { #endif iron_write_png(string("%s%sinput.png", dir, PATH_SEP), input_buf, input->width, input->height, 0); - if (channel == 1) { // Normal Map only - image_to_pbr_node_run_sd("marigold-normals-v1-1.q8_0.gguf", "_normals", &image_to_pbr_node_normals_only_done); - } - else if (channel == 2) { // Height only - image_to_pbr_node_run_sd("marigold-depth-v1-1.q8_0.gguf", "_height", &image_to_pbr_node_height_only_done); - } - else { // All - image_to_pbr_node_run_sd("marigold-normals-v1-1.q8_0.gguf", "_normals", &image_to_pbr_node_normals_done); - } + bool tileable = node->buttons->buffer[1]->default_value->buffer[0] > 0.0; + image_to_pbr_node_node_id = node_id; + image_to_pbr_node_run_da3(tileable, input->width, input->height, &image_to_pbr_node_depth_done); } } } @@ -385,9 +382,18 @@ void image_to_pbr_node_init() { .min = 0.0, .max = 1.0, .precision = 100, - .height = 3}), + .height = 2}), + GC_ALLOC_INIT(ui_node_button_t, {.name = _tr("Tiled"), + .type = "BOOL", + .output = 0, + .default_value = f32_array_create_x(0), + .data = NULL, + .min = 0.0, + .max = 1.0, + .precision = 100, + .height = 0}), }, - 1), + 2), .width = 0, .flags = 0}); diff --git a/paint/sources/nodes_neural/inpaint_image_node.c b/paint/sources/nodes_neural/inpaint_image_node.c index 1f4d457a..c87244a6 100644 --- a/paint/sources/nodes_neural/inpaint_image_node.c +++ b/paint/sources/nodes_neural/inpaint_image_node.c @@ -7,37 +7,11 @@ bool inpaint_image_node_tiled; static string_array_t *inpaint_image_node_flux_klein_args(char *dir) { string_array_t *argv = any_array_create_from_raw( (void *[]){ - string("%s/%s", dir, neural_node_sd_bin()), - "--diffusion-model", - string("%s/flux-2-klein-4b-Q8_0.gguf", dir), - "--taesd", - string("%s/taef2.safetensors", dir), - "--llm", - string("%s/Qwen3-4B-Q8_0.gguf", dir), - "--steps", - "4", + string("%s/%s", dir, neural_node_iris_bin()), + "-d", + string("%s", dir), }, - 9); - return argv; -} - -static string_array_t *inpaint_image_node_qwen_args(char *dir) { - string_array_t *argv = any_array_create_from_raw( - (void *[]){ - string("%s/%s", dir, neural_node_sd_bin()), - "--diffusion-model", - string("%s/qwen-image-edit-2511-Q4_K_S.gguf", dir), - "--vae", - string("%s/Qwen_Image-VAE.safetensors", dir), - "--llm", - string("%s/Qwen2.5-VL-7B-Instruct-Q4_K_S.gguf", dir), - "--llm_vision", - string("%s/mmproj-F16.gguf", dir), - "--qwen-image-zero-cond-t", - "--steps", - "30", - }, - 12); + 3); return argv; } @@ -77,17 +51,6 @@ void inpaint_image_node_button_on_next_frame(ui_node_t *node) { } iron_write_png(string("%s%smask.png", dir, PATH_SEP), mask_buf, mask->width, mask->height, 0); } - else { - gpu_texture_t *masked = gpu_create_render_target(g_config->neural_res, g_config->neural_res, GPU_TEXTURE_FORMAT_RGBA32); - draw_begin(masked, false, 0); - draw_scaled_image(input, 0, 0, g_config->neural_res, g_config->neural_res); - draw_set_pipeline(ui_view2d_pipe); - gpu_set_int(ui_view2d_channel_loc, 6); - draw_scaled_image(mask, 0, 0, g_config->neural_res, g_config->neural_res); - draw_set_pipeline(NULL); - draw_end(); - iron_write_png(string("%s%sinput.png", dir, PATH_SEP), gpu_get_texture_pixels(masked), masked->width, masked->height, 0); - } string_array_t *argv; if (model == 0) { @@ -99,21 +62,12 @@ void inpaint_image_node_button_on_next_frame(ui_node_t *node) { string_array_push(argv, "--mask"); string_array_push(argv, string("%s/mask.png", dir)); } - else { - argv = edit_image_node_qwen_args(dir); - string_array_push(argv, "-p"); - string_array_push(argv, "remove red area"); - string_array_push(argv, "-r"); - string_array_push(argv, string("%s/input.png", dir)); - } - string_array_push(argv, "--cfg-scale"); - string_array_push(argv, "1.0"); - string_array_push(argv, "--diffusion-fa"); - string_array_push(argv, "--offload-to-cpu"); - string_array_push(argv, "--strength"); - string_array_push(argv, string("%f", inpaint_image_node_strength)); - string_array_push(argv, "-s"); + if (g_config->neural_res >= 2048) { + string_array_push(argv, "--vae-tiling"); + } + // string_array_push(argv, string("%f", inpaint_image_node_strength)); + string_array_push(argv, "--seed"); string_array_push(argv, "-1"); string_array_push(argv, "-W"); string_array_push(argv, string("%d", g_config->neural_res)); @@ -122,7 +76,7 @@ void inpaint_image_node_button_on_next_frame(ui_node_t *node) { string_array_push(argv, "-o"); string_array_push(argv, string("%s/output.png", dir)); if (inpaint_image_node_tiled) { - string_array_push(argv, "--circular"); + string_array_push(argv, "--tileable"); } string_array_push(argv, NULL); @@ -140,9 +94,8 @@ void inpaint_image_node_button(i32 node_id) { string_array_t *models = any_array_create_from_raw( (void *[]){ "FLUX 2 klein", - "Qwen Image Edit", }, - 2); + 1); i32 model = ui_combo(ui_nest(h, 0), models, tr("Model"), false, UI_ALIGN_LEFT, true); ui_handle_t *hs = ui_nest(h, 1); diff --git a/paint/sources/nodes_neural/neural_node.c b/paint/sources/nodes_neural/neural_node.c index 9b655086..77c1ea5c 100644 --- a/paint/sources/nodes_neural/neural_node.c +++ b/paint/sources/nodes_neural/neural_node.c @@ -2,7 +2,7 @@ #include "../global.h" char *neural_node_vector(ui_node_t *node, ui_node_socket_t *socket) { - gpu_texture_t *result = any_imap_get(neural_node_results, node->id); + gpu_texture_t *result = any_imap_get(neural_node_results, socket->id); if (result == NULL) { return "float3(0.0, 0.0, 0.0)"; } @@ -14,7 +14,7 @@ char *neural_node_vector(ui_node_t *node, ui_node_socket_t *socket) { } char *neural_node_value(ui_node_t *node, ui_node_socket_t *socket) { - gpu_texture_t *result = any_imap_get(neural_node_results, node->id); + gpu_texture_t *result = any_imap_get(neural_node_results, socket->id); if (result == NULL) { return "0.0"; } @@ -69,7 +69,7 @@ void neural_node_load_result(ui_node_t *node) { char *file = string("%s%soutput.png", neural_node_dir(), PATH_SEP); if (iron_file_exists(file)) { gpu_texture_t *result = iron_load_texture(file); - any_imap_set(neural_node_results, node->id, result); + any_imap_set(neural_node_results, node->outputs->buffer[0]->id, result); ui_nodes_hwnd->redraws = 2; ui_view2d_hwnd->redraws = 2; ui_node_canvas_t *canvas = ui_nodes_get_canvas(true); @@ -105,27 +105,15 @@ char *neural_node_bin_ext() { #endif } -char *neural_node_sd_bin() { - if (g_config->neural_backend == NEURAL_BACKEND_VULKAN) { - return string("sd_vulkan%s", neural_node_bin_ext()); - } - if (g_config->neural_backend == NEURAL_BACKEND_CUDA) { - return string("sd_cuda%s", neural_node_bin_ext()); - } - return string("sd_cpu%s", neural_node_bin_ext()); +char *neural_node_iris_bin() { + return string("iris%s", neural_node_bin_ext()); } char *neural_node_llama_bin() { #ifdef IRON_MACOS return "llama_metal"; #endif - if (g_config->neural_backend == NEURAL_BACKEND_VULKAN) { - return string("llama_vulkan%s", neural_node_bin_ext()); - } - if (g_config->neural_backend == NEURAL_BACKEND_CUDA) { - return string("llama_cuda%s", neural_node_bin_ext()); - } - return string("llama_cpu%s", neural_node_bin_ext()); + return string("llama_vulkan%s", neural_node_bin_ext()); } char *neural_node_dir() { @@ -162,10 +150,8 @@ void neural_node_download_done(char *url) { console_log(string("%s %s", tr("Downloaded file from"), url)); #ifdef IRON_LINUX - neural_node_chmod_x(url, "sd_vulkan"); - neural_node_chmod_x(url, "sd_cpu"); + neural_node_chmod_x(url, "iris"); neural_node_chmod_x(url, "llama_vulkan"); - neural_node_chmod_x(url, "llama_cpu"); #endif #ifdef WITH_COMPRESS @@ -194,20 +180,13 @@ void neural_node_download_models(string_array_t *models) { } #ifdef IRON_WINDOWS - neural_node_download("https://huggingface.co/armory3d/sd_bin/resolve/main/windows_x64/sd_cpu.exe"); - neural_node_download("https://huggingface.co/armory3d/sd_bin/resolve/main/windows_x64/sd_vulkan.exe"); - neural_node_download("https://huggingface.co/armory3d/sd_bin/resolve/main/windows_x64/sd_cuda.exe"); - neural_node_download("https://huggingface.co/armory3d/llamacpp_bin/resolve/main/windows_x64/llama_cpu.exe"); + neural_node_download("https://huggingface.co/armory3d/iris_bin/resolve/main/windows_x64/iris.exe"); neural_node_download("https://huggingface.co/armory3d/llamacpp_bin/resolve/main/windows_x64/llama_vulkan.exe"); - neural_node_download("https://huggingface.co/armory3d/llamacpp_bin/resolve/main/windows_x64/llama_cuda.exe"); #elif defined(IRON_LINUX) - neural_node_download("https://huggingface.co/armory3d/sd_bin/resolve/main/linux_x64/sd_cpu"); - neural_node_download("https://huggingface.co/armory3d/sd_bin/resolve/main/linux_x64/sd_vulkan"); - neural_node_download("https://huggingface.co/armory3d/llamacpp_bin/resolve/main/linux_x64/llama_cpu"); + neural_node_download("https://huggingface.co/armory3d/iris_bin/resolve/main/linux_x64/iris"); neural_node_download("https://huggingface.co/armory3d/llamacpp_bin/resolve/main/linux_x64/llama_vulkan"); #else - neural_node_download("https://huggingface.co/armory3d/sd_bin/resolve/main/macos/sd_cpu"); - neural_node_download("https://huggingface.co/armory3d/sd_bin/resolve/main/macos/sd_vulkan"); + neural_node_download("https://huggingface.co/armory3d/iris_bin/resolve/main/macos/iris"); neural_node_download("https://huggingface.co/armory3d/llamacpp_bin/resolve/main/macos/llama_metal"); #endif diff --git a/paint/sources/nodes_neural/neural_node_models.c b/paint/sources/nodes_neural/neural_node_models.c index a3c6e26d..482cdd99 100644 --- a/paint/sources/nodes_neural/neural_node_models.c +++ b/paint/sources/nodes_neural/neural_node_models.c @@ -7,86 +7,38 @@ void neural_node_models_init() { (void *[]){ GC_ALLOC_INIT(neural_node_model_t, {.name = "FLUX 2 klein", .memory = "4GB", - .size = "8.2GB", - .nodes = "Text to Image, Edit Image, Inpaint Image, Tile Image", + .size = "8.7GB", + .nodes = "Text to Image, Edit Image, Inpaint Image", .urls = any_array_create_from_raw( (void *[]){ - "https://huggingface.co/leejet/FLUX.2-klein-4B-GGUF/resolve/main/flux-2-klein-4b-Q8_0.gguf", - "https://huggingface.co/madebyollin/taef2/resolve/main/taef2.safetensors", - "https://huggingface.co/unsloth/Qwen3-4B-GGUF/resolve/main/Qwen3-4B-Q8_0.gguf", + "https://huggingface.co/armory3d/FLUX.2-klein-4B-GGUF/resolve/main/flux-2-klein-4b-Q8_0.gguf", + "https://huggingface.co/armory3d/FLUX.2-klein-4B-GGUF/resolve/main/Qwen3-4B-Q8_0.gguf", + "https://huggingface.co/armory3d/FLUX.2-klein-4B-GGUF/resolve/main/vae.safetensors", + "https://huggingface.co/armory3d/FLUX.2-klein-4B-GGUF/resolve/main/tokenizer.json", }, - 3), - .web = "https://huggingface.co/leejet/FLUX.2-klein-4B-GGUF", + 4), + .web = "https://huggingface.co/armory3d/FLUX.2-klein-4B-GGUF", .license = "apache-2.0"}), - GC_ALLOC_INIT(neural_node_model_t, {.name = "Z-Image-Turbo", - .memory = "4GB", - .size = "6.7GB", - .nodes = "Text to Image", + GC_ALLOC_INIT(neural_node_model_t, {.name = "DA3MONO", + .memory = "6GB", + .size = "1.4GB", + .nodes = "Image to PBR", .urls = any_array_create_from_raw( (void *[]){ - "https://huggingface.co/armory3d/z_image_turbo/resolve/main/Qwen3-4B-Instruct-2507-Q4_K_S.gguf", - "https://huggingface.co/armory3d/z_image_turbo/resolve/main/ae.safetensors", - "https://huggingface.co/armory3d/z_image_turbo/resolve/main/z_image_turbo-Q4_K.gguf", + "https://huggingface.co/armory3d/DA3MONO-LARGE/resolve/main/da3-mono-large.safetensors", }, - 3), - .web = "https://huggingface.co/armory3d/z_image_turbo", + 1), + .web = "https://huggingface.co/armory3d/DA3MONO-LARGE", .license = "apache-2.0"}), - GC_ALLOC_INIT(neural_node_model_t, - {.name = "Qwen Image", - .memory = "13GB", - .size = "16.9GB", - .nodes = "Text to Image", - .urls = any_array_create_from_raw( - (void *[]){ - "https://huggingface.co/unsloth/Qwen-Image-2512-GGUF/resolve/main/qwen-image-2512-Q4_K_S.gguf", - "https://huggingface.co/QuantStack/Qwen-Image-GGUF/resolve/main/VAE/Qwen_Image-VAE.safetensors", - "https://huggingface.co/unsloth/Qwen2.5-VL-7B-Instruct-GGUF/resolve/main/Qwen2.5-VL-7B-Instruct-Q4_K_S.gguf", - }, - 3), - .web = "https://huggingface.co/unsloth/Qwen-Image-2512-GGUF", - .license = "apache-2.0"}), - - GC_ALLOC_INIT(neural_node_model_t, - {.name = "Qwen Image Edit", - .memory = "13GB", - .size = "18.3GB", - .nodes = "Edit Image", - .urls = any_array_create_from_raw( - (void *[]){ - "https://huggingface.co/unsloth/Qwen-Image-Edit-2511-GGUF/resolve/main/qwen-image-edit-2511-Q4_K_S.gguf", - "https://huggingface.co/QuantStack/Qwen-Image-GGUF/resolve/main/VAE/Qwen_Image-VAE.safetensors", - "https://huggingface.co/unsloth/Qwen2.5-VL-7B-Instruct-GGUF/resolve/main/Qwen2.5-VL-7B-Instruct-Q4_K_S.gguf", - "https://huggingface.co/unsloth/Qwen2.5-VL-7B-Instruct-GGUF/resolve/main/mmproj-F16.gguf", - }, - 4), - .web = "https://huggingface.co/unsloth/Qwen-Image-Edit-2511-GGUF", - .license = "apache-2.0"}), - - GC_ALLOC_INIT(neural_node_model_t, - {.name = "Marigold", - .memory = "6GB", - .size = "13.7GB", - .nodes = "Image to Depth, Image to Normal Map Node, Image to PBR", - .urls = any_array_create_from_raw( - (void *[]){ - "https://huggingface.co/armory3d/marigold-v1-1-gguf/resolve/main/marigold-depth-v1-1.q8_0.gguf", - "https://huggingface.co/armory3d/marigold-v1-1-gguf/resolve/main/marigold-normals-v1-1.q8_0.gguf", - "https://huggingface.co/armory3d/marigold-v1-1-gguf/resolve/main/marigold-iid-appearance-v1-1.q8_0.gguf", - "https://huggingface.co/armory3d/marigold-v1-1-gguf/resolve/main/marigold-iid-lighting-v1-1.q8_0.gguf", - }, - 4), - .web = "https://huggingface.co/armory3d/marigold-v1-1-gguf", - .license = "openrail"}), - GC_ALLOC_INIT(neural_node_model_t, {.name = "Real-ESRGAN", .memory = "1GB", .size = "0.07GB", .nodes = "Upscale Image", .urls = any_array_create_from_raw( (void *[]){ - "https://huggingface.co/armory3d/Real-ESRGAN/resolve/main/RealESRGAN_x4plus.pth", + "https://huggingface.co/armory3d/Real-ESRGAN/resolve/main/RealESRGAN_x4plus.safetensors", }, 1), .web = "https://huggingface.co/armory3d/Real-ESRGAN", @@ -117,6 +69,6 @@ void neural_node_models_init() { .web = "https://huggingface.co/unsloth/Qwen3.6-27B-GGUF", .license = "apache-2.0"}), }, - 8); + 5); gc_root(neural_node_models); } diff --git a/paint/sources/nodes_neural/text_to_image_node.c b/paint/sources/nodes_neural/text_to_image_node.c index 0fc87a8f..012c246f 100644 --- a/paint/sources/nodes_neural/text_to_image_node.c +++ b/paint/sources/nodes_neural/text_to_image_node.c @@ -4,53 +4,11 @@ string_array_t *text_to_image_node_flux_klein_args(char *dir, char *prompt) { string_array_t *argv = any_array_create_from_raw( (void *[]){ - string("%s/%s", dir, neural_node_sd_bin()), - "--diffusion-model", - string("%s/flux-2-klein-4b-Q8_0.gguf", dir), - "--taesd", - string("%s/taef2.safetensors", dir), - "--llm", - string("%s/Qwen3-4B-Q8_0.gguf", dir), - "--steps", - "4", + string("%s/%s", dir, neural_node_iris_bin()), + "-d", + string("%s", dir), }, - 9); - return argv; -} - -string_array_t *text_to_image_node_zimage_args(char *dir, char *prompt) { - string_array_t *argv = any_array_create_from_raw( - (void *[]){ - string("%s/%s", dir, neural_node_sd_bin()), - "--diffusion-model", - string("%s/z_image_turbo-Q4_K.gguf", dir), - "--vae", - string("%s/ae.safetensors", dir), - "--llm", - string("%s/Qwen3-4B-Instruct-2507-Q4_K_S.gguf", dir), - "--steps", - "40", - }, - 9); - return argv; -} - -string_array_t *text_to_image_node_qwen_args(char *dir, char *prompt) { - string_array_t *argv = any_array_create_from_raw( - (void *[]){ - string("%s/%s", dir, neural_node_sd_bin()), - "--diffusion-model", - string("%s/qwen-image-2512-Q4_K_S.gguf", dir), - "--vae", - string("%s/Qwen_Image-VAE.safetensors", dir), - "--llm", - string("%s/Qwen2.5-VL-7B-Instruct-Q4_K_S.gguf", dir), - "--llm_vision", - string("%s/mmproj-F16.gguf", dir), - "--steps", - "20", - }, - 11); + 3); return argv; } @@ -60,37 +18,27 @@ void text_to_image_node_run(ui_node_t *node, void (*callback)(ui_node_t *)) { i32 model = ui_nest(h, 0)->i; char *prompt = ui_nest(h, 1)->text; char *dir = neural_node_dir(); - if (prompt == NULL || string_equals(prompt, "")) { - prompt = "."; - } string_array_t *argv; if (model == 0) { argv = text_to_image_node_flux_klein_args(dir, prompt); } - else if (model == 1) { - argv = text_to_image_node_zimage_args(dir, prompt); - } - else { - argv = text_to_image_node_qwen_args(dir, prompt); - } - string_array_push(argv, "--cfg-scale"); - string_array_push(argv, "1.0"); - string_array_push(argv, "--diffusion-fa"); - string_array_push(argv, "--offload-to-cpu"); + if (g_config->neural_res >= 2048) { + string_array_push(argv, "--vae-tiling"); + } string_array_push(argv, "-W"); string_array_push(argv, string("%d", g_config->neural_res)); string_array_push(argv, "-H"); string_array_push(argv, string("%d", g_config->neural_res)); - string_array_push(argv, "-s"); + string_array_push(argv, "--seed"); string_array_push(argv, "-1"); string_array_push(argv, "-o"); string_array_push(argv, string("%s/output.png", dir)); string_array_push(argv, "-p"); string_array_push(argv, string("'%s'", prompt)); if (node->buttons->buffer[1]->default_value->buffer[0] > 0.0) { - string_array_push(argv, "--circular"); + string_array_push(argv, "--tileable"); } string_array_push(argv, NULL); @@ -105,10 +53,8 @@ void text_to_image_node_button(i32 node_id) { string_array_t *models = any_array_create_from_raw( (void *[]){ "FLUX 2 klein", - "Z-Image-Turbo", - "Qwen Image", }, - 3); + 1); i32 model = ui_combo(ui_nest(h, 0), models, tr("Model"), false, UI_ALIGN_LEFT, true); char *prompt = ui_text_area(ui_nest(h, 1), UI_ALIGN_LEFT, true, tr("prompt"), true); node->buttons->buffer[0]->height = string_split(prompt, "\n")->length + 2; diff --git a/paint/sources/nodes_neural/texture_mesh_node.c b/paint/sources/nodes_neural/texture_mesh_node.c index 9a2d1dd9..aece6e22 100644 --- a/paint/sources/nodes_neural/texture_mesh_node.c +++ b/paint/sources/nodes_neural/texture_mesh_node.c @@ -110,7 +110,7 @@ static char *texture_mesh_node_grid_prompt(char *prompt) { static string_array_t *texture_mesh_node_flux_klein_args(char *dir, char *prompt) { any_array_t *argv = any_array_create(0); - any_array_push(argv, string("%s/%s", dir, neural_node_sd_bin())); + any_array_push(argv, string("%s/%s", dir, neural_node_iris_bin())); any_array_push(argv, "--diffusion-model"); any_array_push(argv, string("%s/flux-2-klein-4b-Q8_0.gguf", dir)); any_array_push(argv, "--vae"); @@ -143,7 +143,7 @@ static string_array_t *texture_mesh_node_flux_klein_args(char *dir, char *prompt static string_array_t *texture_mesh_node_qwen_args(char *dir, char *prompt) { any_array_t *argv = any_array_create(0); - any_array_push(argv, string("%s/%s", dir, neural_node_sd_bin())); + any_array_push(argv, string("%s/%s", dir, neural_node_iris_bin())); any_array_push(argv, "--diffusion-model"); any_array_push(argv, string("%s/qwen-image-edit-2511-Q4_K_S.gguf", dir)); any_array_push(argv, "--vae"); @@ -181,7 +181,7 @@ static void texture_mesh_node_run_upscale(char *dir) { char *grid = string("%s%soutput_grid.png", dir, PATH_SEP); string_array_t *argv = any_array_create_from_raw( (void *[]){ - string("%s/%s", dir, neural_node_sd_bin()), + string("%s/%s", dir, neural_node_iris_bin()), "-M", "upscale", "--upscale-model", @@ -269,7 +269,7 @@ static void texture_mesh_node_project(ui_node_t *node) { } gpu_texture_t *sd_tex = iron_load_texture(output); - any_imap_set(neural_node_results, node->id, sd_tex); + any_imap_set(neural_node_results, node->outputs->buffer[0]->id, sd_tex); any_map_set(data_cached_images, node_name, sd_tex); texture_mesh_node_set_view(v); diff --git a/paint/sources/nodes_neural/upscale_image_node.c b/paint/sources/nodes_neural/upscale_image_node.c index b32b4c34..6d454bbe 100644 --- a/paint/sources/nodes_neural/upscale_image_node.c +++ b/paint/sources/nodes_neural/upscale_image_node.c @@ -16,25 +16,34 @@ void upscale_image_node_button(i32 node_id) { ui_node_t *from_node = neural_from_node(node->inputs->buffer[0], 0); gpu_texture_t *input = ui_nodes_get_node_preview_image(from_node); if (input != NULL) { - buffer_t *input_buf = gpu_get_texture_pixels(input); - char *dir = neural_node_dir(); + +#ifdef IRON_BGRA + buffer_t *input_buf = buffer_bgra_swap(gpu_get_texture_pixels(input)); // Vulkan non-rt textures need a flip +#else + buffer_t *input_buf = gpu_get_texture_pixels(input); +#endif iron_write_png(string("%s%sinput.png", dir, PATH_SEP), input_buf, input->width, input->height, 0); string_array_t *argv = any_array_create_from_raw( (void *[]){ - string("%s/%s", dir, neural_node_sd_bin()), - "-M", - "upscale", - "--upscale-model", - string("%s/RealESRGAN_x4plus.pth", dir), + string("%s/%s", dir, neural_node_iris_bin()), + "-d", + string("%s", dir), + "--upscale", "-i", string("%s/input.png", dir), "-o", string("%s/output.png", dir), - NULL, }, - 10); + 8); + + bool tileable = node->buttons->buffer[1]->default_value->buffer[0] > 0.0; + if (tileable) { + string_array_push(argv, "--tileable"); + } + string_array_push(argv, NULL); + iron_exec_async(argv->buffer[0], argv->buffer); sys_notify_on_update(neural_node_check_result, node); } @@ -89,8 +98,17 @@ void upscale_image_node_init() { .max = 1.0, .precision = 100, .height = 2}), + GC_ALLOC_INIT(ui_node_button_t, {.name = _tr("Tiled"), + .type = "BOOL", + .output = 0, + .default_value = f32_array_create_x(0), + .data = NULL, + .min = 0.0, + .max = 1.0, + .precision = 100, + .height = 0}), }, - 1), + 2), .width = 0, .flags = 0}); diff --git a/paint/sources/startup.c b/paint/sources/startup.c index 7f1b2a8f..22403b75 100644 --- a/paint/sources/startup.c +++ b/paint/sources/startup.c @@ -4232,7 +4232,14 @@ scene_t *startup_get_scene(void) { ve->data = "float2"; sc->vertex_elements->buffer[0] = ve; } - sc->constants = (shader_const_t_array_t *)any_array_create(0); + sc->constants = (shader_const_t_array_t *)any_array_create(1); + { + shader_const_t *c = (shader_const_t *)gc_alloc(sizeof(shader_const_t)); + c->name = "texel_size"; + c->type = "float2"; + c->link = "_screen_size_inv"; + sc->constants->buffer[0] = c; + } sc->texture_units = (tex_unit_t_array_t *)any_array_create(1); { tex_unit_t *tu = (tex_unit_t *)gc_alloc(sizeof(tex_unit_t)); diff --git a/paint/sources/types.h b/paint/sources/types.h index b105ce6e..e41827a2 100644 --- a/paint/sources/types.h +++ b/paint/sources/types.h @@ -132,7 +132,6 @@ typedef struct config { i32 scene_atlas_res; bool grid_snap; bool experimental; - i32 neural_backend; i32 neural_res; render_mode_t render_mode; workspace_t workspace; diff --git a/paint/sources/ui/box_preferences.c b/paint/sources/ui/box_preferences.c index 3676a614..9800c05e 100644 --- a/paint/sources/ui/box_preferences.c +++ b/paint/sources/ui/box_preferences.c @@ -859,28 +859,6 @@ void box_preferences_model_panel(neural_node_model_t *m) { void box_preferences_neural_tab() { ui_text(tr("All processing is done locally on device"), UI_ALIGN_LEFT, 0x00000000); - ui_handle_t *h_inference = ui_handle(__ID__); - h_inference->i = g_config->neural_backend; -#ifdef IRON_WINDOWS - string_array_t *inference_combo = any_array_create_from_raw( - (void *[]){ - "CPU", - "Vulkan", - "CUDA", - }, - 3); -#else - string_array_t *inference_combo = any_array_create_from_raw( - (void *[]){ - "CPU", - "Vulkan", - }, - 2); -#endif - g_config->neural_backend = ui_combo(h_inference, inference_combo, tr("Inference Backend"), true, UI_ALIGN_LEFT, true); - if (g_ui->is_hovered) { - ui_tooltip(tr("Backend for neural node processing")); - } ui_handle_t *h_neural_res = ui_handle(__ID__); h_neural_res->i = g_config->neural_res == 2048 ? 2 : (g_config->neural_res == 1024 ? 1 : 0); diff --git a/paint/sources/util/util_nodes.c b/paint/sources/util/util_nodes.c index 3f2cff80..8e786efd 100644 --- a/paint/sources/util/util_nodes.c +++ b/paint/sources/util/util_nodes.c @@ -100,8 +100,12 @@ gpu_texture_t *ui_nodes_get_node_preview_image(ui_node_t *n) { img = rt->_image; } } - else if (starts_with(n->type, "NEURAL_") && !string_equals(n->type, "NEURAL_IMAGE_TO_PBR")) { - img = any_imap_get(neural_node_results, n->id); + else if (starts_with(n->type, "NEURAL_")) { + i32 socket = i32_imap_get(g_context->node_preview_socket_map, n->id); + if (socket < 0 || socket >= n->outputs->length) { + socket = 0; + } + img = any_imap_get(neural_node_results, n->outputs->buffer[socket]->id); } else if (ui_nodes_canvas_type == CANVAS_TYPE_MATERIAL) { img = any_imap_get(g_context->node_preview_map, n->id);