Code Monkey home page Code Monkey logo

Comments (11)

Kusefiru avatar Kusefiru commented on June 6, 2024 1

The reason I'm using the regular canvas is that the program I built using SceneCanvas and visuals was judged too slow to load and what I have so far using app.Canvas and gloo shaders is way faster. I have not managed to display a visual in a regular canvas. When I add my visual and then call <TextVisual>.draw() in my on_draw function, I get the following error :

[...] in _prepare_draw
    n_pix = (self._font_size / 72.) * transforms.dpi  # logical pix
TypeError: unsupported operand type(s) for *: 'float' and 'NoneType'

I also tried using scene.visuals.Text but putting the canvas as parent doesn't work as it require a scene (view.scene)

I use visuals a lot in my vispy.app.Canvas.

For them to work, you need to set the transform of the visual, with:

visual.transforms.configure()

Here is a sample program that works using vispy visuals in the vispy.app.Canvas:

from PySide6.QtWidgets import *
from PySide6.QtCore import *
from PySide6.QtGui import *
import vispy.app
import vispy.color
import vispy.visuals

class Canvas(vispy.app.Canvas):
    def __init__(self):
        super().__init__(size = (600,600), parent=None)
        self.context.set_clear_color(vispy.color.Color("#ffffff"))
        self.text_visual = vispy.visuals.TextVisual("1a 1b", pos=(self.size[0]/2, self.size[1]/2))
        # This is the important line here
        self.text_visual.transforms.configure(canvas=self)

    def on_draw(self, event):
        vispy.gloo.clear()
        self.text_visual.draw()

    def on_resize(self, event):
        # This will keep the text visual position and size when resizing the canvas
        self.text_visual.transforms.configure(canvas=self)

if __name__ == "__main__":
    app = QApplication([])
    win = QMainWindow()
    canvas = Canvas()
    win.setCentralWidget(canvas.native)
    win.show()
    QApplication.instance().exec()

from vispy.

djhoese avatar djhoese commented on June 6, 2024

I guess my first question is: why are you doing this? Is there a reason you can't use the TextVisual? That should work with the regular Canvas (I think, at least off the top of my head). But also is there a reason you're using the regular Canvas instead of the SceneCanvas?

from vispy.

SegmentedYannig avatar SegmentedYannig commented on June 6, 2024

The reason I'm using the regular canvas is that the program I built using SceneCanvas and visuals was judged too slow to load and what I have so far using app.Canvas and gloo shaders is way faster.
I have not managed to display a visual in a regular canvas. When I add my visual and then call <TextVisual>.draw() in my on_draw function, I get the following error :

[...] in _prepare_draw
    n_pix = (self._font_size / 72.) * transforms.dpi  # logical pix
TypeError: unsupported operand type(s) for *: 'float' and 'NoneType'

I also tried using scene.visuals.Text but putting the canvas as parent doesn't work as it require a scene (view.scene)

from vispy.

djhoese avatar djhoese commented on June 6, 2024

Ok. Correct me if I'm wrong, but I don't recall an issue from you on performance issues. So if we've had this discussion before let me know.

How many Visuals were you using in your old SceneCanvas implementation? What was slow about it? Have you tested the exact same equivalent code in your new implementation (same number of GL Programs)? What is different between what your shader code is doing and VisPy's Visuals are doing?

As for the TextVisual, yeah there is a chance that Visual requires a SceneCanvas to figure out the dpi of the system. We should really make that optional though.

from vispy.

SegmentedYannig avatar SegmentedYannig commented on June 6, 2024

What is slow about my previous imlementation is the original setup of the scene, when placing between 64 and 256 signals lasting 20 minutes to an hour sampled at 1024Hz. I also had a grid, a title, a time axis and a view with labels acting a s a y axis. Once set however, the navigation (camera movement) was fast.

from vispy.

djhoese avatar djhoese commented on June 6, 2024

Ok and you've tested your current shader/gloo implementation with that many Programs and that much data? If the creation of the Scene was the main problem I'm curious what the main bottleneck was. Are you saying you had 64 to 256 different Visuals for each signal? Usually users complain about the performance from having that many Visuals and that includes the drawing and interaction time. That drawing time is mainly a product of the number of GL programs involved, but if drawing time is not your issue then I'd be curious to see an old version of your code even if you end up going with this gloo version.

I should state explicitly that I don't recommend doing what you're doing unless you are extremely desperate. What typically happens is people have slow drawing so they right stuff with their own custom complex shader code, things work, and then they need more complex features like cameras or clickable objects ("picking" is the term vispy uses) and then they end up having to reimplement what vispy already has. So that's why I'm surprised that your drawing performance was fine and would like to know more.

As for debugging your existing gloo text code, I'm not sure I can help much more than suggesting the obvious of comparing with the TextVisual in vispy. You could copy/paste the TextVisual and then hardcode the dpi (or make a pull request to allow for it as a keyword argument) so you can just reuse what vispy has.

from vispy.

SegmentedYannig avatar SegmentedYannig commented on June 6, 2024

I'm testing my current gloo implementation with an hour long 64 channels signal sampled at 1024Hz with one shader program per channel. I have no performance issues at all.
When I load the same data in my scene implementation, I have a 30 seconds long loading time before I can see and do anything, then having a slow navigation. It features one visual per channel.

If you're curious, here's a chunk of my code handling the channels' shaders :

# Shaders
vertex = """
        uniform mat4 projection;
        attribute vec2 position;
        void main()
        {
            gl_Position = projection * vec4(position, 0.0, 1.0);
        } """
fragment = """
        uniform vec3 color;
        void main()
        {
            gl_FragColor = vec4(color, 1);
        } """

# Signals
# Time selection
startTime_index = bisect_left(self.time_vector, self.horizontal_scope[0])
endTime_index = bisect_left(self.time_vector, self.horizontal_scope[1])
selected_time_indexes = np.arange(startTime_index, endTime_index, dtype=np.uint32)
# Signals
self.signals_shaders_list = []
self.signals_status_list = []
for i in range(self.channels_number):
    shader = gloo.Program(vertex, fragment)
    # Position
    pos = np.stack([self.time_vector[selected_time_indexes], self.data[i, selected_time_indexes] * self.gain_factor - i * self.spacing * self.spacing_factor], axis=1)
    shader['position'] = pos
    self.signals_shaders_list.append(shader)
    # Status and color
    status = False
    shader['color'] = (0, 0, 0)
    self.signals_status_list.append(status)
# Projection
projection = util.transforms.ortho(self.horizontal_scope[0], self.horizontal_scope[1], self.vertical_scope[0], self.vertical_scope[1], 0, 1)
for shader in self.signals_shaders_list:
    shader['projection'] = projection

I want to note that I tried to make the shader as simple/light as possible.

I will now retry to use the visual, following your advice.

from vispy.

djhoese avatar djhoese commented on June 6, 2024

I have a 30 seconds long loading time before I can see and do anything, then having a slow navigation.

"navigation" as in interaction? I thought you said the visualization/interaction was fine?

Correct me if I'm wrong, but if you had one visual per signal/line and in your gloo code you have one GL Program (one Visual also uses on GL Program) where you've concatenated all the lines together into segments, you can do that same thing in the LineVisual using the connect kwarg.

Nope...nevermind, I misread your code. You are making a GL Program for each channel. I would expect the SceneCanvas creation to take a while, but the drawing I would expect to be about the same. Interesting.

from vispy.

SegmentedYannig avatar SegmentedYannig commented on June 6, 2024

Apologies for my late response, I was on an unexpected sick leave (they rarely are)

I made progress ! I manage to display legible text but I still have issues with the sizing.

compare

It seems like the font_size parameter only affects the width (1 on left and 0.1 on right). Also, resizing the canvas resizes the text.
Hmm, maybe I could add a transform, and update it in the on_resize function.

I made a child class of TextVisual with adding the parent canvas as an attribute to be able to get the dpi and px_scale in the _prepare_draw function.

class TextVisual_ductTaped(TextVisual):
    # Init
    def __init__(self, canvas, text=None, color='black', bold=False, italic=False, face='OpenSans', font_size=12, pos=[0, 0, 0], rotation=0., anchor_x='center', anchor_y='center', method='cpu', font_manager=None, depth_test=False):
        super().__init__(text, color, bold, italic, face, font_size, pos, rotation, anchor_x, anchor_y, method, font_manager, depth_test)
        self.unfreeze()
        self.canvas = canvas
        self.freeze()

    # _prepare_draw override
    def _prepare_draw(self, view):
        # attributes / uniforms are not available until program is built
        if len(self.text) == 0:
            return False
        if self._vertices is None:
            text = self.text
            if isinstance(text, str):
                text = [text]
            n_char = sum(len(t) for t in text)
            # we delay creating vertices because it requires a context,
            # which may or may not exist when the object is initialized
            self._vertices = np.concatenate([_text_to_vbo(t, self._font, self._anchors[0], self._anchors[1], self._font._lowres_size) for t in text])
            self._vertices = VertexBuffer(self._vertices)
            idx = (np.array([0, 1, 2, 0, 2, 3], np.uint32) +
                   np.arange(0, 4*n_char, 4, dtype=np.uint32)[:, np.newaxis])
            self._index_buffer = IndexBuffer(idx.ravel())
            self.shared_program.bind(self._vertices)
            # This is necessary to reset the GL drawing state after generating
            # SDF textures. A better way would be to enable the state to be
            # pushed/popped by the context.
            self._configure_gl_state()
        if self._pos_changed:
            # now we promote pos to the proper shape (attribute)
            text = self.text
            if not isinstance(text, str):
                repeats = [4 * len(t) for t in text]
                text = ''.join(text)
            else:
                repeats = [4 * len(text)]
            n_text = len(repeats)
            pos = self.pos
            # Rotation
            _rot = self._rotation
            if isinstance(_rot, (int, float)):
                _rot = np.full((pos.shape[0],), self._rotation)
            _rot = np.asarray(_rot)
            if _rot.shape[0] < n_text:
                _rep = [1] * (len(_rot) - 1) + [n_text - len(_rot) + 1]
                _rot = np.repeat(_rot, _rep, axis=0)
            _rot = np.repeat(_rot[:n_text], repeats, axis=0)
            self.shared_program['a_rotation'] = _rot.astype(np.float32)
            # Position
            if pos.shape[0] < n_text:
                _rep = [1] * (len(pos) - 1) + [n_text - len(pos) + 1]
                pos = np.repeat(pos, _rep, axis=0)
            pos = np.repeat(pos[:n_text], repeats, axis=0)
            assert pos.shape[0] == self._vertices.size == len(_rot)
            self.shared_program['a_pos'] = pos
            self._pos_changed = False
        if self._color_changed:
            # now we promote color to the proper shape (varying)
            text = self.text
            if not isinstance(text, str):
                repeats = [4 * len(t) for t in text]
                text = ''.join(text)
            else:
                repeats = [4 * len(text)]
            n_text = len(repeats)
            color = self.color.rgba
            if color.shape[0] < n_text:
                color = np.repeat(color, [1]*(len(color)-1) + [n_text-len(color)+1], axis=0)
            color = np.repeat(color[:n_text], repeats, axis=0)
            assert color.shape[0] == self._vertices.size
            self._color_vbo = VertexBuffer(color)
            self.shared_program.vert['color'] = self._color_vbo
            self._color_changed = False

        #transforms = self.transforms
        #tr = transforms.get_transform('document', 'render')
        #px_scale = (tr.map((1, 0)) - tr.map((0, 1)))[:2]
        n_pix = (self._font_size / 72.) * self.canvas.dpi  # logical pix
        self._text_scale.scale = self.canvas._px_scale * n_pix
        self.shared_program.vert['text_scale'] = self._text_scale
        self.shared_program['u_npix'] = n_pix
        self.shared_program['u_kernel'] = self._font._kernel
        self.shared_program['u_color'] = self._color.rgba
        self.shared_program['u_font_atlas'] = self._font._atlas
        self.shared_program['u_font_atlas_shape'] = self._font._atlas.shape[:2]

from vispy.

djhoese avatar djhoese commented on June 6, 2024

Nice hack! And yeah I would guess you need to work with these bits of the code:

transforms = self.transforms
n_pix = (self._font_size / 72.) * transforms.dpi # logical pix
tr = transforms.get_transform('document', 'render')
px_scale = (tr.map((1, 0)) - tr.map((0, 1)))[:2]
self._text_scale.scale = px_scale * n_pix
self.shared_program.vert['text_scale'] = self._text_scale
self.shared_program['u_npix'] = n_pix
self.shared_program['u_kernel'] = self._font._kernel
self.shared_program['u_color'] = self._color.rgba
self.shared_program['u_font_atlas'] = self._font._atlas
self.shared_program['u_font_atlas_shape'] = self._font._atlas.shape[:2]
def _prepare_transforms(self, view):
self._pos_changed = True
# Note that we access `view_program` instead of `shared_program`
# because we do not want this function assigned to other views.
tr = view.transforms.get_transform()
view.view_program.vert['transform'] = tr # .simplified()

from vispy.

SegmentedYannig avatar SegmentedYannig commented on June 6, 2024

Wow, it does work wonderfully, thanks a lot !

I just modified the on_resize function to update the position when resizing

def on_resize(self, event):
  # This will keep the text visual position and size when resizing the canvas
  self.text_visual.pos = (self.size[0] / 2, self.size[1] / 2)
  self.text_visual.transforms.configure(canvas=self)

Thank you two for the help and I hope this will help other users in the future !

from vispy.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.