Skip to content

Commit

Permalink
actually update texture atlas on change (#3737)
Browse files Browse the repository at this point in the history
* actually update texture atlas on change

* update changelog

* add refimg test

* properly deregister callbacks and cache texture as part of screen

---------

Co-authored-by: ffreyer <frederic481994@hotmail.de>
  • Loading branch information
SimonDanisch and ffreyer committed Mar 27, 2024
1 parent d50dad0 commit 1d50405
Show file tree
Hide file tree
Showing 7 changed files with 156 additions and 78 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -9,6 +9,7 @@
- Added `scale` attribute to `violin` [#3352](https://github.com/MakieOrg/Makie.jl/pull/3352).
- Use label formatter in barplot [#3718](https://github.com/MakieOrg/Makie.jl/pull/3718).
- Fix the incorrect shading with non uniform markerscale in meshscatter [#3722](https://github.com/MakieOrg/Makie.jl/pull/3722)
- Fixed an issue with the texture atlas not updating in WGLMakie after display, causing new symbols to not show up [#3737](https://github.com/MakieOrg/Makie.jl/pull/3737)

## [0.20.8] - 2024-02-22

Expand Down
12 changes: 12 additions & 0 deletions ReferenceTests/src/tests/text.jl
Expand Up @@ -368,3 +368,15 @@ end
ax.zlabel[] = L"\sum_{n=1}^{\infty} 2^{-n} = 1"
fig
end

# test #3232
@reference_test "texture atlas update" begin
scene = Scene(size = (250, 100))
campixel!(scene)
p = text!(scene, "test", fontsize = 85)
st = Stepper(scene)
Makie.step!(st)
p[1][] = "-!ħ█?-" # "!ħ█?" are all new symbols
Makie.step!(st)
st
end
15 changes: 8 additions & 7 deletions WGLMakie/src/Lines.js
Expand Up @@ -178,8 +178,8 @@ function lines_fragment_shader(uniforms, attributes) {
`;
}

function create_line_material(uniforms, attributes) {
const uniforms_des = deserialize_uniforms(uniforms);
function create_line_material(scene, uniforms, attributes) {
const uniforms_des = deserialize_uniforms(scene, uniforms);
const mat = new THREE.RawShaderMaterial({
uniforms: uniforms_des,
glslVersion: THREE.GLSL3,
Expand Down Expand Up @@ -278,7 +278,7 @@ function attach_updates(mesh, buffers, attributes, is_segments) {
}
}

export function _create_line(line_data, is_segments) {
export function _create_line(scene, line_data, is_segments) {
const geometry = create_line_instance_geometry();
const buffers = {};
create_line_buffers(
Expand All @@ -288,6 +288,7 @@ export function _create_line(line_data, is_segments) {
is_segments
);
const material = create_line_material(
scene,
line_data.uniforms,
geometry.attributes
);
Expand All @@ -301,10 +302,10 @@ export function _create_line(line_data, is_segments) {
return mesh;
}

export function create_line(line_data) {
return _create_line(line_data, false)
export function create_line(scene, line_data) {
return _create_line(scene, line_data, false)
}

export function create_linesegments(line_data) {
return _create_line(line_data, true)
export function create_linesegments(scene, line_data) {
return _create_line(scene, line_data, true)
}
84 changes: 55 additions & 29 deletions WGLMakie/src/Serialization.js
Expand Up @@ -63,8 +63,8 @@ export function delete_plots(plot_uuids) {
plots.forEach(delete_plot);
}

function convert_texture(data) {
const tex = create_texture(data);
function convert_texture(scene, data) {
const tex = create_texture(scene, data);
tex.needsUpdate = true;
tex.minFilter = THREE[data.minFilter];
tex.magFilter = THREE[data.magFilter];
Expand All @@ -88,10 +88,10 @@ function is_three_fixed_array(value) {
);
}

function to_uniform(data) {
function to_uniform(scene, data) {
if (data.type !== undefined) {
if (data.type == "Sampler") {
return convert_texture(data);
return convert_texture(scene, data);
}
throw new Error(`Type ${data.type} not known`);
}
Expand Down Expand Up @@ -120,7 +120,7 @@ function to_uniform(data) {
return data;
}

export function deserialize_uniforms(data) {
export function deserialize_uniforms(scene, data) {
const result = {};
// Deno may change constructor names..so...

Expand All @@ -132,28 +132,28 @@ export function deserialize_uniforms(data) {
// nothing needs to be converted
result[name] = value;
} else {
const ser = to_uniform(value);
const ser = to_uniform(scene, value);
result[name] = new THREE.Uniform(ser);
}
}
return result;
}

export function deserialize_plot(data) {
export function deserialize_plot(scene, data) {
let mesh;
const update_visible = (v) => {
mesh.visible = v;
// don't return anything, since that will disable on_update callback
return;
};
if (data.plot_type === "lines") {
mesh = create_line(data);
mesh = create_line(scene, data);
} else if (data.plot_type === "linesegments") {
mesh = create_linesegments(data);
mesh = create_linesegments(scene, data);
} else if ("instance_attributes" in data) {
mesh = create_instanced_mesh(data);
mesh = create_instanced_mesh(scene, data);
} else {
mesh = create_mesh(data);
mesh = create_mesh(scene, data);
}
mesh.name = data.name;
mesh.frustumCulled = false;
Expand Down Expand Up @@ -225,7 +225,7 @@ export function add_plot(scene, plot_data) {
});
}

const p = deserialize_plot(plot_data);
const p = deserialize_plot(scene, plot_data);
plot_cache[p.plot_uuid] = p;
scene.add(p);
// execute all next insert callbacks
Expand Down Expand Up @@ -276,8 +276,8 @@ function convert_RGB_to_RGBA(rgbArray) {
return rgbaArray;
}

function create_texture(data) {
const buffer = data.data;
function create_texture_from_data(data) {
let buffer = data.data;
if (data.size.length == 3) {
const tex = new THREE.Data3DTexture(
buffer,
Expand All @@ -289,20 +289,13 @@ function create_texture(data) {
tex.type = THREE[data.three_type];
return tex;
} else {
// a little optimization to not send the texture atlas over & over again
let tex_data;
if (buffer == "texture_atlas") {
tex_data = TEXTURE_ATLAS[0].value;
} else {
tex_data = buffer;
}
let format = THREE[data.three_format];
if (data.three_format == "RGBFormat") {
tex_data = convert_RGB_to_RGBA(tex_data);
buffer = convert_RGB_to_RGBA(buffer);
format = THREE.RGBAFormat;
}
return new THREE.DataTexture(
tex_data,
buffer,
data.size[0],
data.size[1],
format,
Expand All @@ -311,6 +304,39 @@ function create_texture(data) {
}
}

function create_texture(scene, data) {
const buffer = data.data;
// we allow to have a global texture atlas which gets only uploaded once to the browser
// it's not the nicest way, but by setting buffer to "texture_atlas" on the julia side
// instead of actual data, we just get the texture atlas from the global.
// Special care has to be taken to deregister the callback when the context gets destroyed
// Since TEXTURE_ATLAS uses "Bonito.Retain" and will live for the whole browser session.
if (buffer == "texture_atlas") {
const {texture_atlas} = scene.screen
if (texture_atlas) {
return texture_atlas;
} else {
data.data = TEXTURE_ATLAS[0].value
const texture = create_texture_from_data(data);
scene.screen.texture_atlas = texture;
TEXTURE_ATLAS[0].on((new_data) => {
if (new_data === texture) {
// if the data is our texture, it means the WGL context got destroyed and we want to deregister
// TODO, better Observables.js API for this
return false; // deregisters the callback
} else {
texture.image.data.set(new_data);
texture.needsUpdate = true;
return
}
});
return texture;
}
} else {
return create_texture_from_data(data);
}
}

function re_create_texture(old_texture, buffer, size) {
let tex;
if (size.length == 3) {
Expand Down Expand Up @@ -417,10 +443,10 @@ function recreate_instanced_geometry(mesh) {
mesh.needsUpdate = true;
}

function create_material(program) {
function create_material(scene, program) {
const is_volume = "volumedata" in program.uniforms;
return new THREE.RawShaderMaterial({
uniforms: deserialize_uniforms(program.uniforms),
uniforms: deserialize_uniforms(scene, program.uniforms),
vertexShader: program.vertex_source,
fragmentShader: program.fragment_source,
side: is_volume ? THREE.BackSide : THREE.DoubleSide,
Expand All @@ -431,24 +457,24 @@ function create_material(program) {
});
}

function create_mesh(program) {
function create_mesh(scene, program) {
const buffer_geometry = new THREE.BufferGeometry();
const faces = new THREE.BufferAttribute(program.faces.value, 1);
attach_geometry(buffer_geometry, program.vertexarrays, faces);
const material = create_material(program);
const material = create_material(scene, program);
const mesh = new THREE.Mesh(buffer_geometry, material);
program.faces.on((x) => {
mesh.geometry.setIndex(new THREE.BufferAttribute(x, 1));
});
return mesh;
}

function create_instanced_mesh(program) {
function create_instanced_mesh(scene, program) {
const buffer_geometry = new THREE.InstancedBufferGeometry();
const faces = new THREE.BufferAttribute(program.faces.value, 1);
attach_geometry(buffer_geometry, program.vertexarrays, faces);
attach_instanced_geometry(buffer_geometry, program.instance_attributes);
const material = create_material(program);
const material = create_material(scene, program);
const mesh = new THREE.Mesh(buffer_geometry, material);
program.faces.on((x) => {
mesh.geometry.setIndex(new THREE.BufferAttribute(x, 1));
Expand Down
3 changes: 2 additions & 1 deletion WGLMakie/src/WGLMakie.jl
Expand Up @@ -78,7 +78,8 @@ function __init__()
atlas = wgl_texture_atlas()
TEXTURE_ATLAS[] = convert(Vector{Float32}, vec(atlas.data))
Makie.font_render_callback!(atlas) do sd, uv
TEXTURE_ATLAS[] = convert(Vector{Float32}, vec(wgl_texture_atlas().data))
TEXTURE_ATLAS[] = convert(Vector{Float32}, vec(atlas.data))
return
end
DISABLE_JS_FINALZING[] = false
return
Expand Down

0 comments on commit 1d50405

Please sign in to comment.