GML Surfaces are bad?

C

CedSharp

Guest
Unless I'm wrong, it seems (from what I've read in other posts) that surfaces break the batching process and that a lot of "higher level" of programming developers avoid using them.

If that is the case, I'm ready to drop surfaces from the features I use, but I have a couple questions.

First, if I drop surfaces, is there any way to draw clipped? One of the main reason I used surfaces is to allow to draw on a defined area without any "overflow". Is there an other way to do this?

Second, another reason I use surfaces is to draw something that requires lots of computing. I use surfaces so I can draw only once or every so often, and then I know it's kept in memory and I can "just draw it". Obviously it's volatile but that just mean I gotta manage re-drawing in those situations. Is there a way to "draw and save" something if we don't use surfaces?

Finally, GameMaker's target and final destination for all rendering is 'application_surface'. Is it really that bad to use surfaces if in the end everything ends up on a surface?

Maybe this post is completely pointless, but I'm very confused and I am not ready to drop surfaces unless I understand why I have to do so.

Thanks for anyone taking the time to illuminate me.
 

GMWolf

aka fel666
Unless I'm wrong, it seems (from what I've read in other posts) that surfaces break the batching process and that a lot of "higher level" of programming developers avoid using them.
Surfaces are not bad. But they should be avoided when possible. On the GPU, its often preferable to trade CPU time for VRAM.

First, if I drop surfaces, is there any way to draw clipped?
Not with GM, i dont think so. but that i assume you mean using some form of mask.

One of the main reason I used surfaces is to allow to draw on a defined area without any "overflow"
what do you mean by that?
If you mean alpha clipping or something, you can use the gpu_ functions, or write a shader for more controle.

Second, another reason I use surfaces is to draw something that requires lots of computing. I use surfaces so I can draw only once or every so often, and then I know it's kept in memory and I can "just draw it". Obviously it's volatile but that just mean I gotta manage re-drawing in those situations. Is there a way to "draw and save" something if we don't use surfaces?
nope. After all, that is what surfaces are for.
What you can do is convert the surface to a sprite, and then free the surface. Surfaces are optimized for being used both as targets and sources. The way the graphics library handles it internally makes them slower than sprites (in general).

Finally, GameMaker's target and final destination for all rendering is 'application_surface'. Is it really that bad to use surfaces if in the end everything ends up on a surface?
If you need a surface, use a surface.
The only problem comes when they are used excessively.
As a general rule: Only use screen space surfaces (Don't make a surface the size of your room).
and Don't use surfaces to store data for more than a couple frames.
If you effect is a per-pixel effect, use a shader.


The issue with surfaces mostly come with VRAM usage, and with binding (though, I'm not 100% clear on binding costs).
They do break the batch, but that isn't a huge issue anymore.
 
Last edited:

jo-thijs

Member
Unless I'm wrong, it seems (from what I've read in other posts) that surfaces break the batching process and that a lot of "higher level" of programming developers avoid using them.

If that is the case, I'm ready to drop surfaces from the features I use, but I have a couple questions.
It's true that drawing to a surface will break the batching process (as far as I know)
and it is true that breaking the batching process will cause a performance hit.
However, that does not yet mean that you have to avoid using them.
The performance hit might be relatively small so that it is unnoticeable
and some functionality requires using surfaces.

First, if I drop surfaces, is there any way to draw clipped? One of the main reason I used surfaces is to allow to draw on a defined area without any "overflow". Is there an other way to do this?
Yes, but not very conveniently.
If you're not using a shader and you can use shaders, you can create a shader where the pixel shader skips every pixel that is out of bounds (this might break batches too though).
Otherwise, if you're just drawing sprites and basic shapes (but no text and such), you can manually cut the parts out of bounds away.
For example, you could use primitives and a bunch of comparisons to only draw what is inside the rectangle.

Second, another reason I use surfaces is to draw something that requires lots of computing. I use surfaces so I can draw only once or every so often, and then I know it's kept in memory and I can "just draw it". Obviously it's volatile but that just mean I gotta manage re-drawing in those situations. Is there a way to "draw and save" something if we don't use surfaces?
Not really no.
I thought of some hacks that might work, but all of them would still break batches.

Finally, GameMaker's target and final destination for all rendering is 'application_surface'. Is it really that bad to use surfaces if in the end everything ends up on a surface?

Maybe this post is completely pointless, but I'm very confused and I am not ready to drop surfaces unless I understand why I have to do so.

Thanks for anyone taking the time to illuminate me.
It's not that drawing to a surface breaks batches, but that changing to which surface you draw breaks batches
and that loading a surface in memory might unload a texture page, breaking batches if that texure page was being use by the current batches
(as far as I know, not entirely sure about it though).

By now GMWolf has replied, but I still wanted to post this.
 
F

Fishman1175

Guest
Computers have limited resources, but if you don’t use them then you lose them. A “higher level” developer in my opinion simply uses the right tools they are strong with for the right task, and they are strong with many tools. Surfaces make perfect sense sometimes.
 
M

Matt Hawkins

Guest
I found that using surfaces on android devices causes performance issues so I disable them.
 

GMWolf

aka fel666
Hmm funny, the profiler tells me surface_reset_target() is more taxing than surface_set_target()
Huh. Presumably that's because of the extra matrix calculations.
When setting a surface, GM will (annoyingly so) reset the view matrix.
I assume that it is recalculating the MVP when reseting the surface. But im surprised there woul be significant difference.
 
I vote yes! for surfaces. Too many people on these forums (including me) are scared of surfaces because we are being told to be afraid over and over again.
But honestly: They are more scared of us but all they really want is add juice to our games and they're really good at that. So befriend them and allow them to make your games look great instead of just mehh.
 

jo-thijs

Member
They are more scared of us
Wouldn't you be if you knew you can be removed any moment from existence
whenever the (operating) system sees fit or your creator finds no use for you anymore?
All supposedly because you're too fat and there's no place for you anymore
or because you (memory) leaked once and others found it dirty?
Stop surface abuse! Give them a home in sprites through sprite_create_from_surface services!

... ok, I'm done now.

It seems like only 1 person so far adviced against using surfaces on android,
1 person adviced to avoid using surfaces to some extend
and 5 people adviced not to particularly avoid surfaces.
So, your vote would be the majority vote, I guess?

I've never encountered people telling to be afraid of surfaces though.

PS: Don't use sprite_create_from_service as advertised above, that'd be terrible.
 
C

CedSharp

Guest
Thank you all for your input. The asset I'm currently building is a text dialog asset. I have to loop structures, set colors, call scripts, etc, in order to draw the text and it's box. The frame might be using slice9 (or patch9 whatever you call that) which requires more computing.

For that purpose, I am automatically thinking about a surface. In addition, I don't want my asset to directly draw on the screen since I have no idea if user wants to draw in room, on gui, or on another surface, so my application draws on a surface and returns that surface to the user (I could return a sprite but I don't think computing a new sprite everytime something changes on the textbox is a good idea).

So surfaces aren't bad but rather potentially slow is missused? That's what I seem to understand from all of you. Thanks!
 

GMWolf

aka fel666
The frame might be using slice9 (or patch9 whatever you call that) which requires more computing.
9 patch is dirt cheap. No need for surfaces there.
You can use draw_sprite_part and make use of batching too! (I should fix my asset to do that)
. In addition, I don't want my asset to directly draw on the screen since I have no idea if user wants to draw in room, on gui, or on another surface, so my application draws on a surface and returns that surface to the user (I could return a sprite but I don't think computing a new sprite everytime something changes on the textbox is a good idea).
What? Nooo!
Just draw normaly.
If the user wants it on a surface, they will set the surface target.
If they want to draw to GUI, they will call it in the GUI event.
Make your draw_* functions work just like any other draw_* functions.
 
C

CedSharp

Guest
9 patch is dirt cheap. No need for surfaces there.
You can use draw_sprite_part and make use of batching too! (I should fix my asset to do that)

What? Nooo!
Just draw normaly.
If the user wants it on a surface, they will set the surface target.
If they want to draw to GUI, they will call it in the GUI event.
The 9path I use is not exactly cheap. It does a lot of testing/calculating because you can stretch the border, tile them, tile-stretch them, etc.
It might run without issues on decent computers, but if used say on 20-100 elements on the screen, that's where surfaces usually help.
(yes, 20-100 elements can happen, for example using a GUI system)


As for the asset returning a surface, I obviously have scripts that automatically draw the result for you, asset_draw(x, y).
But some people like to have more control over what they do, so instead of doing
Code:
var surf = surface_create(asset_get_width(), asset_get_height());
surface_set_target(surf);
asset_draw();
...;
surface_reset_target()
draw_surface(surf, x, y);
they can just do
Code:
var surf = asset_get_surface();
surface_set_target(surf);
...;
surface_reset_target();
draw_surface(surf);
I dunno, personally, I like the idea of getting the surface if I want or not bother and just draw the asset.
My asset's goal is to be customizable to the point there is nothing you can't change, yet as easy to use as no setting to change at all.
 

GMWolf

aka fel666
The 9path I use is not exactly cheap. It does a lot of testing/calculating because you can stretch the border, tile them, tile-stretch them, etc.
Can you not precalculate them? If you can use surface, you can precalculate those...
It might run without issues on decent computers, but if used say on 20-100 elements on the screen, that's where surfaces usually help.
(yes, 20-100 elements can happen, for example using a GUI system)
I guarantee you 100 surfaces will perform worse.
Remember surfaces use a lot of vram, and introduce a lot of overhead!
Your 9patch code must be pretty dreadful to be that expensive!
Remember, tiling a sprite does not require multiple draw calls, you can do it with a single draw call.
If you need to tile part of a sprite, use a VB or something.
As for the asset returning a surface, I obviously have scripts that automatically draw the result for you, asset_draw(x, y).
But some people like to have more control over what they do, so instead of doing
Code:
var surf = surface_create(asset_get_width(), asset_get_height());
surface_set_target(surf);
asset_draw();
...;
surface_reset_target()
draw_surface(surf, x, y);
they can just do
Code:
var surf = asset_get_surface();
surface_set_target(surf);
...;
surface_reset_target();
draw_surface(surf);
I dunno, personally, I like the idea of getting the surface if I want or not bother and just draw the asset.
My asset's goal is to be customizable to the point there is nothing you can't change, yet as easy to use as no setting to change at all.
All you did here was create the surface inside the function. If anything you made things less customizable.


I must warn you with creating individual surfaces for each element like that, surfaces will get rounded up to powers of two on many platforms. That makes them use up far more memory than you realize.

One thing to keep in mind is bandwidth.
A 9patch is designed to have a relatively small texture stretch nicely over a large area.
This makes them use very little bandwidth.
If you are drawing multiple 9patches with the same texture, even better!
However, surfaces use a lot of bandwidth. And they each are their own texture. This alone will probably tank your performance, as the required bandwidth will be much higher.

Memory bandwidth is probably the most valuable resource you have to deal with, along with VRAM usage.
 
Last edited:

jo-thijs

Member
I guarantee you 100 surfaces will perform worse.
I think their idea wasn't to use 100 surfaces, but to encourage drawing the independent parts only once to a surface and then reuse that surface.

If you need to tile part of a sprite, use a VB or something.
I was just about to suggest that, I refreshed, saw you edited something and read that part.
In case someone doesn't know what VB is, it's a vertex buffer.

All you did here was create the surface inside the function. If anything you made things less customizable.
He said he would provide functions to do both and you would need to figure out the surface size yourself otherwise, which might sometimes be a bit of a bother.
I don't see how in thi way, it would be less costumizable.

I must warn you with creating individual surfaces for each element like that, surfaces will get rounded up to powers of two on many platforms. That makes them use up far more memory than you realize.
That is definitely a concern that some people would not concider.
 

GMWolf

aka fel666
I think their idea wasn't to use 100 surfaces, but to encourage drawing the independent parts only once to a surface and then reuse that surface.


I was just about to suggest that, I refreshed, saw you edited something and read that part.
In case someone doesn't know what VB is, it's a vertex buffer.


He said he would provide functions to do both and you would need to figure out the surface size yourself otherwise, which might sometimes be a bit of a bother.
I don't see how in thi way, it would be less costumizable.


That is definitely a concern that some people would not concider.
I added more concerns above about bandwidth, which is probably the biggest factor at play.

In the end, most optimization techniques revolve around bandwidth:
Tesselation reduces the amount of vertex data you need to submit.
But it goes the other way too, you can throw in more vertex data is it drastically reduces your texture sizes.
 

Tsa05

Member
I'm creating, as my signature implies, a full-service dialogue box and visual novel engine in GameMaker and I initially did everything with surfaces. I've removed them in every place that I could.

My engine runs at about 1400fps on my (very modern) computer. It runs at 200fps with surfaces.
Mind you, my engine does quite a lot, and the issue is that the size of surfaces makes everything much worse. When I tested my engine out with little placeholder graphics, nothing seemed wrong. When I started swapping in hi-def 1080p graphics, it died in a hurry.

Some things to consider, from significant experience:
If your textbox engine plans to pre-composite layered character graphics (with changeable expressions) onto a surface, forget it. Generating surfaces for multiple high-resolution graphics every draw event is too slow by a long shot. Pre-composing the layers into a sprite seems appealing, but if each layer is an animated sprite, you'd have to pre-composite the least common multiple number of frames to get the whole animation loop. The RAM involved in that is more than my mere 32Gb could handle.

If your textbox allows markup that can dynamically change values based on game variables, maybe forget about putting it on a surface. You'll end up having to generate textboxes that are possibly full-screen-width every game step, so that's going to be a performance issue too.

Otherwise, it's fine. Surfaces are great for providing things like a persistent effects layer (footprints, tire tracks, splatters, etc). They aren't, in my 2-years of working on this dialog box engine, good for generating large, high-resolution graphics on-the-fly.
 
C

CedSharp

Guest
The 9patch system I use creates "pieces" of the patch and store them in a structure (I call it a skin, but whatever).
Using an "apply" script, I then create a surface on which I draw those pieces. I have another script which calls that script and converts the surface to a sprite.
My point is that instead of re-drawing the whole patch, I use a surface to "immortalize" the final drawing. I do not keep that surface the whole time.
Surfaces, to me, are an off-screen render buffer where I can do stuff temporarly. It is not a permanent resource to me.

All you did here was create the surface inside the function. If anything you made things less customizable.
Sadly you have no idea how my asset works so I can't understand how you came to that conclusion.

My asset always ends with a surface. Always. the "asset_get_surface()" is actually the one who isn't wrapping anything. the "asset_draw" calls draw_surface with "asset_get_surface()". What I said is that I rather offer many different scripts to play with the asset rather than force one way to the user.

As I mentioned before, my asset never draws directly, since I don't know if the user wants to draw in the room or on the gui. For that reason, all draw calls are done on a surface, which is re-created when required. To get the textbox to show up on the screen, you require to either call the script that draws it as a specific position (I even have 'asset_draw_self()' if you call that in the dialog object itself) or the script that will return the actual final surface, so that you can apply whatever effect you want, stretch it, break it, it's up to you.

If using a single surface for a complex drawing task like a dialog box is considered too much overhead and dreadful, then can you please give me an example of how you would draw:

- a 9patch background
- text, stored as tokens with many different properties, like color, font, position offset, padding, etc
- animated cursors (without redrawing the whole text, most of the time only the cursor needs redrawing)
- potential animations on the 9path
- transitions of the whole thing (fade in/out or stretch effect, whatever)

Between one character appearing and another (typerwriter effect), there can be a couple steps before anything visually changes on the textbox. Having a surface, I have no need to re-draw if nothing changed. Same-wise, when I wait for user to select a choice, nothing is animated (well there are hover and active effects, but again, they don't change every single step).

If you can draw all of that, every frame/step, and tell me it's better than using a surface, then I'll be all ears and restart my asset from scratch.

I agree that today that might not be a concern. But then I have to take into account that my asset is just a piece of the actual game in which it will be implemented. I certainly don't want to make my asset the one to use most of the step/draw event for no reason.

Again, maybe I'm wrong and my usage of surfaces is dumb. That's the whole reason why I started this post. If you can give me tips/examples of how you would do some of the points I listed above that would help me a lot.

Also thank you for taking the time to answer.

(to GMWorlf) I understand you and I often debate recently. I'm in a learning phase, let's call it like this. I by no mean try to offend you. I'm probably an idiot coder who thinks he knows his stuff and is probably completely wrong. Which again brings me to this post. Please excuse me if I actually did offend you recently :x
 
Last edited:

Tsa05

Member
@CedSharp Can I give you an example of an entire engine? Ehhh, hold my beer? I mean, won't that be kind of a long post?

Have a look-see at a little piece of what I'm making:
https://i.imgur.com/DH359nM.gifv

There's animated text with tagged color effects and stretching, animating, fading character with layers and expressions (and a visual editor piled on top just for fun).
There's no surfaces, though. Not for what you saw,

Trust me, I want them. This way I've done isn't better. Surfaces are modular--I have specific reasons for wanting them (screen transitions are so much easier... applying 3rd party effects, so much easier...). I want them so badly that they are in my engine. The whole thing is done with surfaces, and all of the scripts involved are sitting in a folder labelled "OldVersion."

I don't want you to--how did you put it--"restart your asset from scratch". I already did that with mine :) Wasn't fun, set me back a month. Definitely don't let me tell you how to do your asset; do it your way. You wanted to know about surface performance, though, so I'm just telling you about surface performance. What you see in the clip above, with graphics that size, cannot run with enough fps to spare to allow a game to run smoothly when I did it with surfaces. And because of that, yes, I solved the other things--but there is definitely one problem I can't solve. I can't make it so that all computed graphical elements become one final nice single graphic that can later be stretched, color-blended, and all other cool after-market add-on effects. I want that really badly. That's why I wrote the whole thing with surfaces. But, in order to composite images of people at that size, with all those layers...that was the nail in the coffin. Just the text area wasn't too bad, but that's because I wasn't using a full-screen textbox. Big surfaces and lots of surfaces both get slow.

As for the question of flexibility in drawing decisions, let me show you the Draw event of the dialog playback object:


These scripts hand off the data, and use lerp amounts or text progress amounts to handle everything. The scripts take in data structures containing "what to draw" and the lerp amounts...and they just perform on standard drawing calls. Obviously, there's a lot inside these scripts, but it's certainly doable every frame... Only thing I don't have is "squash and stretch" animation on the textbox--not because that can only be done with a surface, but rather because I haven't added separate scale sliders yet so it uses the same value for both axes...

There's ONE place I'm using surfaces:
When a character fades through opacity. During that fade, I deactivate animation, to avoid the giant least-common-multiple problem I mentioned earlier. The character is composited to a surface (slow), stored as a sprite (slower), and then the sprite is draw with fade applied (super fast). Then the sprite is removed. In this way, I avoid the problem of "seeing parts through parts" while fading in, and the slowest step is done once rather than during every animating step. But that also means that the character doesn't play internal sprite subimage animation during fades. In all other cases, just using math and GameMaker's transform functions is fine.

Now, there's probably some other way that someone could do all of this that would make my methods look slow and clumsy. But running miles of maths runs 8x faster than surfaces for the kinds of things I just showed.

As to the question of the text animating per-frame with formatting--see https://forum.yoyogames.com/index.p...timization-the-mother-of-all-textboxes.35901/
I posted mine. And there's some great optimization suggestions in that thread to handle the parsing even more efficiently by separating each formatted part in advance with a helper function.
As for 9-part, ok, you got me. I haven't posted that on here... Mine's not the fanciest thing, but looks like this:

Kinda 9-partish, right? It uses 8+1, actually. One sprite has 8 subimages for the border slices and then a separate one is the fill sprite.
Here's the call:

And the script:
Code:
/*
*   draw_9part(x,y,w,h,sprite_base,sprite_edges,fromCenter)
*
*   Edges Sprite:
*       Upper-Left, Upper-Mid, Upper-Right,
*       Middle-Left, Middle-Right,
*       Lower-Left, Lower-Mid, Lower-Right
*/
var _x,_y,_w,_h,sb,se,c;
_x=argument0;
_y=argument1;
_w=argument2;
_h=argument3;
sb=argument4;
se=argument5;
c=argument6;

var sx,sy; // Start x/y for drawing
var dw, sew, dh, seh, sbw, sbh;
sew=sprite_get_width(se);
seh=sprite_get_height(se);
sbw=sprite_get_width(sb);
sbh=sprite_get_height(sb);

/*  sprite_edges has the following sub-images
*   Since sub-images in Game Maker must be uniform in size
*   a separate image will be used for the box fill.
*
*   011111111111112
*   3             4
*   3      sb     4
*   3             4
*   566666666666667
*
*/

if(c){
    sx=_x-_w/2-sew; sy=_y-_h/2-seh; // If centered
}else{
    sx=_x;sy=_y;
}

/* Draw the background fill */
dh=0;
while(dh+sbh<_h){
    dw=0;
    while(dw+sbw<_w){
        draw_sprite(sb,-1,sx+dw+sew,sy+dh+seh);  // Draw across, full size
        dw+=sbw; // Keep track of width
    }
    if(dw!=_w){  // The last image doesn't quite reach the desired width
        draw_sprite_part(sb,-1,0,0,_w-dw,sprite_get_height(sb),sx+dw+sew,sy+dh+seh);
    }
    dh+=sbh; // Next line
}
if(dh!=_h){  // Uneven heights
    dw=0;
    while(dw+sbw<_w){
        draw_sprite_part(sb,-1,0,0,sprite_get_width(sb),_h-dh,sx+dw+sew,sy+dh+seh);
        dw+=sbw; // Keep track of width
    }
    if(dw!=_w){  // The last image doesn't quite reach the desired width
        draw_sprite_part(sb,-1,0,0,_w-dw,_h-dh,sx+dw+sew,sy+dh+seh);
    }
}
    

draw_sprite(se,0,sx,sy); // Draw the first corner.

/* Draw top edge */
dw=0;
while(dw+sew<_w){
    draw_sprite(se,1,sx+dw+sew,sy);  // Draw the top edge
    dw+=sew; // Keep track of width
}
if(dw!=_w){  // The last image doesn't quite reach the desired width
    draw_sprite_part(se,1,0,0,_w-dw,sprite_get_height(se),sx+dw+sew,sy);
}
draw_sprite(se,2,sx+_w+sew,sy); // Draw far top corner

/* Draw left side */
dh=0;
while(dh+seh<_h){
    draw_sprite(se,3,sx,sy+dh+seh);  // Draw left edge
    dh+=seh; // Keep track of height
}
if(dh!=_h){  // The last image doesn't quite reach the desired height
    draw_sprite_part(se,3,0,0,sprite_get_width(se),_h-dh,sx,sy+dh+seh);
}
draw_sprite(se,5,sx,sy+seh+_h); // Draw bottom left corner

/* Draw right side */
dh=0;
while(dh+seh<_h){
    draw_sprite(se,4,sx+_w+sew,sy+dh+seh);  // Draw left edge
    dh+=seh; // Keep track of height
}
if(dh!=_h){  // The last image doesn't quite reach the desired height
    draw_sprite_part(se,4,0,0,sprite_get_width(se),_h-dh,sx+_w+sew,sy+dh+seh);
}
draw_sprite(se,7,sx+_w+sew,sy+seh+_h); // Draw bottom right corner

/* Draw bottom edge */
dw=0;
while(dw+sew<_w){
    draw_sprite(se,6,sx+dw+sew,sy+seh+_h);  // Draw the top edge
    dw+=sew; // Keep track of width
}
if(dw!=_w){  // The last image doesn't quite reach the desired width
    draw_sprite_part(se,6,0,0,_w-dw,sprite_get_height(se),sx+dw+sew,sy+seh+_h);
}
Can be called whenever something's got to draw--in my case, deep within the draw_conversation_boxes() script, when the 9-part element is defined as a box background.
Again, not saying that surfaces aren't the most modular, customizeable, future-compatible, best-looking, easiest-to-work-with solution.
But I would say that I couldn't do my engine with them, and that I can do many things without?
 
C

CedSharp

Guest
@Tsa05 Thanks for giving such a complete set of examples. I wasnt asking for complete code but rather tips, like which combination of scripts and/or what "general idea" to follow, but posting actual code works too ^^

The way I create slice9 is with 2 scripts.
The first script will split the sprite into smaller sprites. The slice sizes and the 9 sprites are then stored in a ds_grid which is what the script returns. This structure is what I usually reference as a skin.
The second script is the one that will create a surface and draw all the sprites pieces on it in order to generate the final sized box, using stretch, tiled or a combination for the borders. I never had the need for a N-slice script, but I suppose it wouldn't change much to how I deal with things. The second script returns the generated surface. It's up to the user of the script to create a sprite out of it if he doesn't need it to be dynamic, or to do the checks and call my script again if the surface doesn't exists anymore.

I could rewrite my code so that it slices/calculate/draw each piece on every call, but that feels kinda bad. Is it really better to slice/calculate at every frame rather than save everything to surface/sprite ?

Are surfaces so slow that saving all that takes actually more time ?

As for the text animating per frame, I know how to draw it (which is the purpose of that other post) my question is does drawing the text by going through the whole dataset every single time faster than drawing once on a surface every couple frames? Because in GameMaker, if I add a condition statement the text will flicker (the screen is erased, which is the expected behavior anyways).
 
Last edited:

GMWolf

aka fel666
As I mentioned before, my asset never draws directly, since I don't know if the user wants to draw in the room or on the gui.
There is no such thing as drawing directly. You will always be rendering to a buffer.
- a 9patch background
That is rather cheap to do using by draw_sprite_part 9times.
If your 9patch doesn't change shape in often, then you can build a vertex buffer and use a single draw call. But do be aware that (I believe) vertex buffers will break your batch, so it may be worth just using draw_sprite_part.
- text, stored as tokens with many different properties, like color, font, position offset, padding, etc
Text is actually very expensive to draw.
It's not the offset, colours or fonts that is expensive, but the draw_text itself.
Probably your best iptioo for text is to build a vertex buffer that holds each character in a quad.
This means you need all your fonts on the same texture.
I have never tried it, but there are functions available to get the UVs of a character.
In order to animate it tyoewritty style, you could rebuild your VB, or use a shader with an extra vertex attribute.

It's unclrun to me if it's worth building a VB or nusg calling draw_text directly at this time.
- animated cursors (without redrawing the whole text, most of the time only the cursor needs redrawing)
Presumably you have a way of knowing the position of each of the characters.
You can just use draw_line or something.
- potential animations on the 9path
If those are sprite animations, then using draw_sprite_part is perfect.
If it's resizing the 9patch, then draw_sprite_part is also perfect.
- transitions of the whole thing (fade in/out or stretch effect, whatever)
That is where things actually get a little trickier.
If you draw each element layer by layer, then fading will not be super consistent:
Where comoinenco (9patch and text) overlap, the alpha will be greater.
Most of the time this is ok, and most VN engines I have seen don't care too much.

As far as I know the only solution to this is indeed to use a surface. Here you have to decide if this small detail is worth the extra cost.
 
L

Lonewolff

Guest
Probably your best iptioo for text is to build a vertex buffer that holds each character in a quad.
That's what does happen already though. Each character is rendered to a quad.

You can verify this by enabling wire frame mode (either via shader or a DLL).


And yes, surfaces are great in moderation. Sometimes there are effects that just can't be done without surfaces.


Consider shaders:
A single shader is not always the be all and end all. Something like a bloom effect can require up to four or five shader passes, in which you need surfaces to store each pass.
 
Last edited by a moderator:
L

Lonewolff

Guest
Yeah, i meant, precalculate that vertex buffer, and reuse it.
Ah, got ya :)

A lot of work for not a huge gain.

Rendering the text to a surface would probably perform better, assuming the text isn't dynamic.
 
C

CedSharp

Guest
Alright, so correct me if I'm wrong, but:

- draw_sprite_part called every frame is better than keeping a surface,
- draw_text is slow so surfaces won't help
- surfaces are good for transitions to avoid Alpha artifacts

I've never used vertex buffers nor shaders (i mean write one) I'll have to dive into that

Thanks you all for taking the time to help me.
 

GMWolf

aka fel666
By that i meant i never draw on the screen. always on a surface.
you never draw to screen. You will always draw to some form of frame buffer or back buffer.

Remember draw_sprite does not draw to screen either. It just draws to whatever surface is currently set as the target.
 
C

CedSharp

Guest
you never draw to screen. You will always draw to some form of frame buffer or back buffer.

Remember draw_sprite does not draw to screen either. It just draws to whatever surface is currently set as the target.
Ok, let me formulate it differently then because you don't seem to get what I mean.

I know I am not drawing directly on the screen. I know I am drawing on a buffer may it be a surface, the room or the gui.
I know all of that, the point I meant and I'm sure you understood it perfectly is that I don't want my asset to assume anything.
It doesn't know if it's used in a gui, in a normal draw event, or in a keypress on a surface. For that purpose I draw 'off-screen' (so on a surface) and have options to allow the user to do whatever he wants before it becomes 'visible'. Drawing in the room or on a surface is the same in the end, but at least there is a where the user can handle things with the way I do it now.

Like I mentioned before, I don't like the following idea:
Code:
var surf = surface_create(asset_get_width(), asset_get_height());
surface_set_target(surf);
    asset_draw(0, 0);
    // stuff to modify the final asset render
surface_reset_target();
surface_draw(surf, x, y);
I'm even thinking of merging my event system with my dialog engine in order to allow the following:
Code:
// In rendering code of asset
surface_set_target(asset_surface);
    ev_trigger('BeforeAssetRender', asset_surface);
    // rendering code
    ev_trigger('AfterAssetRender', asset_surface);
surface_reset_target();

// In create event of dialog object
ev_on('BeforeAssetRender', scr_on_before);
ev_on('AfterAssetRender', scr_on_after);

// scr_on_before
shader_set(...);

// scr_on_after
shader_reset();
In a situation like this, no need to set surfaces anywhere. All callbacks will allow drawing directly on the surface during the rendering process, avoiding the need for all of my current scripts. The scripts get the surfaces as an argument which allows the user to use his own surfaces if needed.
 
Last edited:

GMWolf

aka fel666
Like I mentioned before, I don't like the following idea:
Code:
var surf = surface_create(asset_get_width(), asset_get_height());
surface_set_target(surf);
asset_draw(0, 0);
// stuff to modify the final asset render
surface_reset_target();
surface_draw(surf, x, y);
I dont like that idea either: why create and draw to a surface if you are going to draw it right after?

- draw_sprite_part called every frame is better than keeping a surface,
yes
draw_text is slow so surfaces won't help
no. Surfaces will probably help if your vram usage is low. But its probably not worth it.
In the long run, as VRam usage climbs, you will have to cut down on surafaces.
surfaces are good for transitions to avoid Alpha artifacts
Yep.
 
C

CedSharp

Guest
I dont like that idea either: why create and draw to a surface if you are going to draw it right after?
I think you missed the '// stuff to modify the final asset render' comment in the code. I'm not 'just' drawing the asset. It could get scaled, blended, gunge effect applied to it, I have no idea what could be there but just because something might be there people will have to use that syntax. Which is why I proposed the alternative.
 

GMWolf

aka fel666
I think you missed the '// stuff to modify the final asset render' comment in the code. I'm not 'just' drawing the asset. It could get scaled, blended, gunge effect applied to it, I have no idea what could be there but just because something might be there people will have to use that syntax. Which is why I proposed the alternative.
Yeah... I see what you are trying to achieve.
I take it there will be some assets being rendered by the system, not always through draw_* functions then.
 
Top