Phaser 3 clearing up resources - phaser-framework

I have two scenes in the game and that loads few images on preload(). I need to know the correct way to clear loaded images & other resources when;
Browser tab is reloaded/closed
When loading images in a scene, if the key exists avoid loading that resource.
As of now, when my scene is reloaded(browser refresh) it gives the following error in console:
Texture key already in use: main-lk
So what I tried was:
window.onbeforeunload = function () {
console.log("unloading window... try to unload cached images");
game.cache = new this.Phaser.Cache.CacheManager(game);
game.textures.destroy()
game.destroy(false);
}
assuming that this will clear everything on a browser refresh. This doesn't seem to work cause I still get the same error.
Then I tried below code in preload()
this.textures.remove("main-aq"); <-- force removing the image
this.load.image("main-aq",[my url]); <-- reload the image
When I do that, console shows;
No texture found matching key: main-aq <--this is for removing line
Texture key already in use: main-aq <-- loading line. means this was not cleared with remove
How can I properly release resources onrefresh/reloading scenes ?

Related

How to apply shaders and generate images only once?

I'm trying to apply a pixelation shader to my textures and I need it to be applied only once, after that I can reuse my shader generated images as textures over and over without having to calculate every single time.
so how do I take a few images -> apply a shader and render them only once every time the game loads -> and use them as my textures?
so far I've managed to find the shader to apply:
shader_type canvas_item;
uniform int amount = 40;
void fragment()
{
vec2 grid_uv = round(UV * float(amount)) / float(amount);
vec4 text = texture(TEXTURE, grid_uv);
COLOR = text;
}
but I have no idea how to render out the images using it
Shaders reside in the GPU, and their output goes to the screen. To save the image, the CPU would have to see the GPU output, and that does not happen… Usually. And since it does not go through the CPU, the performance is good. Usually. Well, at least it is better than if the CPU was doing it all the time.
Also, are you sure you don't want to get a pixel art look by other means? Such as removing filter from the texture, changing the stretch mode and working on a small resolution, and perhaps enable pixel snap? No? Watch How to make a silky smooth camera for pixelart games in Godot. Still No? Ok...
Anyway, for what you want, you are going to need a Viewport.
Viewport setup
What you will need is to create a Viewport. Don't forget to set its size. Also may want to set render_target_v_flip to true, this flips the image vertically. If you find the output image is upside down it is because you need to toggle render_target_v_flip.
Then place as child of the Viewport what you want to render.
Rendering
Next, you can read the texture form the Viewport, convert it to an image, and save it to a png. I'm doing this on a tool script attached to the Viewport, so I'll have a workaround to trigger the code from the inspector panel. My code looks like this:
tool
extends Viewport
export var save:bool setget do_save
func do_save(new_value) -> void:
var image := get_texture().get_data()
var error := image.save_png("res://output.png")
if error != OK:
push_error("failed to save output image.")
You can, of course, export a FILE path String to ease changing it in the inspector panel. Here I'm handing common edge cases:
tool
extends Viewport
export(String, FILE) var path:String
export var save:bool setget do_save
func do_save(_new_value) -> void:
var target_path := path.strip_edges()
var folder := target_path.get_base_dir()
var file_name := target_path.get_file()
var extension := target_path.get_extension()
if file_name == "":
push_error("empty file name.")
return
if not (Directory.new()).dir_exists(folder):
push_error("output folder does not exist.")
return
if extension != "png":
target_path += "png" if target_path.ends_with(".") else ".png"
var image := get_texture().get_data()
var error := image.save_png(target_path)
if error != OK:
push_error("failed to save output image.")
return
print("image saved to ", target_path)
Another option is to use ResourceSaver:
tool
extends Viewport
export var save:bool setget do_save
func do_save(new_value) -> void:
var image := get_texture().get_data()
var error := ResourceSaver.save("res://image.res", image)
if error != OK:
push_error("failed to save output image.")
This will only work from the Godot editor, and will only work for Godot, since you get a Godot resource file. Although I find interesting the idea of using Godot to generate images. I'm going to suggest going with ResourceSaver if you want to automate generating them for Godot.
About saving resources from tool scripts
In the examples above, I'm assuming you are saving to a resource path. This is because the intention is to use the output image as a resource in Godot. Using a resource path has a couple implications:
This might not work on an exported game (since the goals is improve the workflow, this is OK).
Godot would need to re-import the resource, but will not notice it changed.
We can deal with the second point from an EditorPlugin, if that is what you are doing, you can do this to tell Godot to scan for changes:
get_editor_interface().get_resource_filesystem().scan()
And if you are not, you can cheat by creating an empty EditorPlugin. The idea is to do this:
var ep = EditorPlugin.new()
ep.get_editor_interface().get_resource_filesystem().scan()
ep.free()
By the way, you will want to cache cache the EditorPlugin instead of making a new one each time. Or better yet, cache the EditorFileSystem you get from get_resource_filesystem.
Automation
Now, I'm aware that it can be cumbersome to have to place things inside the Viewport. It might be Ok for your workflow if you don't need to do it all the time.
But what about automating it? Well, regardless of the approach, you will need a tool script that makes a hidden Viewport, takes a Node, checks if it has a shader, if it does, it moves it temporarily to the Viewport, get the rendered texture (get_texture()) sets it as the texture of the Node, removes the shader, and returns the Node to its original position in the scene. Or instead of looking for a shader in the Node, always apply a shader to whatever Node, perhaps loaded as a resource instead of hard-coded.
Note: I believe you need to let an idle frame pass between adding the Node to the Viewport and getting the texture, so the texture updates. Or was it two idle frames? Well, if one does not work, try adding another one.
About making an EditorPlugin
As you know, you can create an addon from project settings. This will create an EditorPlugin script for you. There you can either add an option to the tools menu (with add_tool_menu_item), or add it to the tool bar of the editor (with add_control_to_container). And have it act on the current selection in the edited scene (you can either use get_selection, or overwrite the edit and handles methods). You may also want to make an undo entry for that, see get_undo_redo.
Or, alternatively you can have it keep track (or look for) the Nodes it has to act upon, and then work on the build virtual method, which runs when the project is about to run. I haven't worked with the build virtual method, so I don't know if it has any quirks to gotchas to be aware of.

nodejs vs. ruby / understanding requests processing order

I have a simple utility that i use to size image on the fly via url params.
Having some troubles with the ruby image libraries (cmyk to rvb is, how to say… "unavailable"), i gave it a shot via nodejs, which solved the issue.
Basically, if the image does not exists, node or ruby transforms it. Otherwise when the image has already been requested/transformed, the ruby or node processes aren't touched, the image is returned statically
The ruby works perfectly, a bit slow if lot of transforms are requested at once, but very stable, it always go through whatever the amount (i see the images arriving one the page one after another)
With node, it works also perfectly, but when a large amount of images are requested, for a single page load, the first images is transformed, then all the others requests returns the very same image (the last transformed one). If I refresh the page, the first images (already transformed) is returned right away, the second one is returned correctly transformed, but then all the other images returned are the same as the one just newly transformed. and it goes on the same for every refresh. not optimal , basically the resquests are "merged" at some point and all return the same image. for reason i don't understand
(When using 'large amount', i mean more than 1)
The ruby version :
get "/:commands/*" do |commands,remote_path|
path = "./public/#{commands}/#{remote_path}"
root_domain = request.host.split(/\./).last(2).join(".")
url = "https://storage.googleapis.com/thebucket/store/#{remote_path}"
img = Dragonfly.app.fetch_url(url)
resized_img = img.thumb(commands).to_response(env)
return resized_img
end
The node js version :
app.get('/:transform/:id', function(req,res,next){
parser.parse(req.params,function(resized_img){
// the transform are done via lovell/sharp
// parser.parse parse the params, write the file,
// return the file path
// then :
fs.readFileSync(resized_img, function(error,data){
res.write(data)
res.end()
})
})
})
Feels like I'm missing here a crucial point in node. I expected the same behaviour with node and ruby, but obviously the same pattern transposed in the node area just does not work as expected. Node is not waiting for a request to process, rather processes those somehow in an order that is not clear to me
I also understand that i'm not putting the right words to describe the issue, hoping that it might speak to some experienced users, let them provide clarifiactions to get a better understanding of what happens behind the node scenes

Use of custom filters in Fabric JS - getting the original back

I have been playing with custom filters in Fabric JS. But I just don't know how to undo anything done. Seems the pixels get overwritten by the process, which is fine, but how do I go back to the orginal? The code project starter is here:
https://github.com/pixolith/fabricjs-async-web-worker-filters
So, in the custom filter, the results are placed into the canvas as follows:
imageDataArray.forEach( function( data ) {
cacheCtx.putImageData( data.data, 0, data.blocks );
} );
That shows the processed image in the render. But I don't understand how to "get back" the original. I have tried this before the processing:
var obj = canvas.getActiveObject();
var originalSource = obj._originalElement.currentSrc; // restore the original since filters trash the canvas
obj.filters[index] = filter;
obj.applyFilters(canvas.renderAll.bind(canvas));
But it does not "get it back". I really don't wish to reload the image each time as they can be large at times. Any help appreciated.
As you noted obj._originalElement is the ORIGINAL element of the image you first loaded. No reason to reload it at all, you have it there. Ready to be smashed on canvas.
So just do obj.element = obj._originalElement and you are back to original after a canvas.renderAll();

jscrollpane colors

I'm trying to modify the scrollbar colors for jscrollpane.
This didn't work for me:
$('a.athlete_popup_content').click(function(){
$('#box_on_top').append($content); //.athlete class is within $content
$('.athlete').jScrollPane({autoReinitialise: true});
$('.jspVerticalBar').css('width', '10px');
$('.jspTrack').css('background','lightgrey');
$('.jspDrag').css('background','black');
$('.athlete').jScrollPane({autoReinitialise: true});
});
i tried placing athlete class both before and after... it doesn't do anything... also, the second time this runs, the scrollbar doesn't appear at all.
any help?
-=update=-
For the issue where it does not appear correctly the second time, I had to destroy the jsp on close and it started working.
var element = $('.athlete').jScrollPane();
var api = element.data('jsp');
api.destroy();
I was unable to get the colors to work.
I am including the .css initially, but want to change the colors on load. I wasn't able to figure out this issue so I just modified the .css
Thanks!
Your code es working perfectly, as you can see here. If you are calling jScrollPane() on a click event then the tags you are trying to reach (.jspVerticalBar, .jspTrack, .jspDrag) are created after css() calls, then you should use .on() to attach those calls to the event.

Zing Chart Memory Leak, causing browser to crash

We are currently rendering 50-100 canvas on browser window. Browsers both IE and chrome crashes.
On further investigation, looks like memory is creeping up steadily. Causing browser to crash.
We are building a solution to print charts To achieve this,
We are displaying all the charts in a simple page (iframe) charts are not visible to user
Using chart id to getting image data.
Since charts are not visible we can ‘destroy’ or remove them from memory once they are rendered.
But ‘destroy’ does not reduce charts memory footprint
Tried setting object to null. this did not work either
Attached snippet for your reference,
var runner = 0
zingchart.complete = function (dataObj) {
for (i = 0; i < ZingChartCollection.length; i++) {
if (dataObj["id"] == ZingChartCollection[i].ChartId) {
var data = zingchart.exec(dataObj["id"], "getimagedata", '{"filetype": "png"}');
zingchart.exec(dataObj["id"], 'destroy');
zingchart.exec();
if (runner < 200) {
document.getElementById("displayCount").value = runner;
render();
}
else {
//zingchart = null;
}
runner++;
}
}
}
Any suggestions would be great.
Here's a note regarding the issue from the ZinChart dev team:
The issue here is that the render() -> complete event -> image generation -> destroy() is a closed loop.
The way the ZingChart lib works, in order to fire the complete as soon as possible, the code was binding the context menu events AFTER the complete was fired.
So, since destroy was being called immediately, the context menu events were left out in the open, and with 50-100 charts it starts to add, leading to memory leaks.
This will be changed & fixed in the next versions in order to fire the complete event after the context menu setup, however, with the current setup, there are two options:
use mode:static on render() since the idea is to get the static image out of the chart. This skips event binding so memory leak will no longer be an issue.
Also, since less canvas objects will be used, this will dramatically decrease the memory needed per chart.
if you need the complete functionality of the charts (although not needed in this particular case), call the destroy() in a delayed function via setTimeout. This will allow
for the context menu events to be set, so destroy() will also unbind them.

Resources