Ruby ImageRep fastest image data update from C

I want to replace existing image data inside SU Ruby ImageRep as fast as possible. Ruby is too slow in my specific case. I want simply to overwrite whole ImageRep raw data buffer.

I have done an investigation but couldn’t find a way to get the pointer to the data.

ImageRep.data returns a copy of data as a string. It is not useful.

I appreciate any feedback on this.

Ruby or CAPI extension?

For simplicity - CAPI extension working with a Live model.

SU2023 applications/ruby_api.h
SU_RESULT SUImageRepFromRuby(RUBY_VALUE ruby_imagerep, SUImageRepRef* imagerep);

In general you shouldn’t change anything from LiveAPI but maybe it’ll work for you if you only change the content of pixels, you’ll need to check.

For sure you can read pixels very fast using SUImageRepRef, maybe you can also update the content using this way.

Thanks.

I tried it, but not with the function. I simply get the data pointer from Ruby and tried SUImageRepSetData() on it but it crashes.
I will try the specific function, but as far as I remember it does exactly same.

SUImageRepFromRuby() helps in this regard that subsequent SUImageRepSetData() does not crash on it. It is not as fast as I would like to, but it certainly a step in right direction.

Thanks and greetings from Lodz!

1 Like

In our case, we need only to fast read so I never tried update the data. During some devcamp I’ve asked if we can have fast methods for updating the data, but, as always this are not the most important things :slight_smile:

Maybe you can open a feature request on github issue tracker?

BTW, maybe the simplest way would be to create ImgRep in C, then convert it to Ruby and change the Ruby object in your Ruby code?

SU_RESULT SUImageRepToRuby(SUImageRepRef imagerep, RUBY_VALUE* ruby_imagerep);

@thomthom Is there any differences between SUImageRepTo/FromRuby and getting the pointer from Ruby object?

I run a profiler and actually SUImageRepSetData() is almost as slow as calling Ruby C functions.

I want to just copy the raw data. The width, height, pixel size and padding do not change.

It looks like SketchUp initializes/allocates everything from scratch.

This is not that surprising, since the SketchUp Ruby API is a thin layer over the internal C++ logic, the same goes for the C API.

When it comes to performance it would really help to have a complete representative example of what you are doing. We can then run it through a profiler and see what is going on. Also without going via a profiler it’d be a lot easier to give a proper response to this. Hard to give generic performance response to a specific issue.

Have you checked what data you are feeding it? Again, it’d help to have a reproducible example.

I have @image_rep in a tool. I use this ImageRep to texture objects I draw. I want to paint the texture in real-time. 120ms in SUImageRepSetData() for 2048x2048 texture is too much. The image is not used anywhere else - so nothing has to be updated until I manually view.load_texture(@image_rep).

In this case memcpy(dest,soure,size) would probably be enough. I just need to know the destination.

FYI: The docs say that this function makes a copy of the pixel array.

Have you tried the SUImageRepCopy() function ?

SUImageRepSetData() does the job alright. It replaces the existing image data with a copy of the supplied buffer. It is fine.

The performance is the only issue. Simple memory copy would work alright and way faster in this case, I believe, because the intention is to modify just the bitmap data. I am convinced Ruby and C use the same buffer internally. This buffer is intended to be used later for new texture creation.

Well, Ruby itself is written in C. The two APIs are wrapping the same C++ core functionalities.

But it is the API consumer (you, I or others) that is creating the array buffer in both cases. The buffer can be subsequently reused or discarded.

Well, the C API documentation indicates in ("headers\SketchUpAPI\model\image_rep.h") that SUImageRepRef is a struct. If we think that the order of it’s members is the same as the order of parameters in the SUImageRepSetData function then could you get a pointer to the last member of the struct ?

The Ruby examples show the packing of the array as "C*" (which is 8-bit unsigned char) before passing to Sketchup::ImageRep#set_data. The Ruby parameter type is String.

Perhaps the “slow” C API set data function is doing something similar that you may need to replicate ?

I went this road myself and indeed I have found the pointer to the data. It is faster now.

I wish we could view.update_texture(texture_id, image_rep) , because currently we have to release_texture() first and then load it again … probably there is a space for speed-up there too.

1 Like

Please log an API feature request in the SketchUp API tracker at GitHub.

Yes, I see. The documentation for view#load_texture is not clear as to whether the texture resource has taken ownership of the image_rep argument object.

In the example code given, image_rep is a local reference that would go out of scope when the activate() callback returns. What if the reference is a persistent instance variable instead ?

I suspect you’ve likely answered this in the negative, ie must “do a release and reload”. Meaning that the load function is another data copy function from the image_rep.

1 Like

By the way, there is already a Live C API request to expose the model view to the C-side.

Add comments and ideas if you will …

Thank you Dan. I will “vote” for it too.

This sounds very similar to some of the feedback we got from the last developer meetup.

If you could log a ticket in the issue tracker describing the high level need you have that’d be great.

1 Like