Best way to update loads of text pretty much every tick

0 favourites
  • 14 posts
From the Asset Store
OpenAI TTS
$10 USD
Text-to-Speech plugin.Allows your Construct 3 project to convert written text into spoken words
  • Hey all,

    (A quick preface: I've been learning 6502 Assembly, but don't know any higher level languages at all, and have been a Scirra customer since the Construct Classic days. So I feel pretty well versed in the various iterations of Construct, but have no appliable programming knowledge I can use elsewhere aside from logic design.)

    So I'm working on a 6502 emulator in C3 as a fun personal challenge, and I've got everything working properly. My biggest hurdle, is how to monitor the RAM values in realtime to make sure the opcodes and all their addressing modes are behaving the way I'm intending them to.

    Curently, I have 2048 text objects all displaying 2 characters each to represent ram in hex values (boy, do I wish C3 allowed me to work in hex without having to use strings everywhere...). There must be a better way to do this to not have it brutally bog everything down.

    I'm currently doing this all through the event sheets as well, as I'm only JUST starting to feel brave enough to attempt to learn some JavaScript (and JavaScript inside C3 no less).

    I've been able to use a bit of JS to successfully update my display canvas with generated noise when my emulator is "off" and to display a pre-set colour as a BG colour when on. Previous attempts at doing this through events proved way too slow, so I'm wondering if the event sheet system is just inherently way slowly do to the amount of abstraction it adds, and if so... how do I achieve my goal with JS (if events are indeed incapable of keeping up)

    Thanks so much in advance

    - Brent

    PS: The emulator works for all the 6502 mini-programs I've written for it, and some test programs that I've found online. If all goes well, I could turn this into a fantasy console capable of running actual "real" programs. Or maybe convert it into an Apple ][, NES, Atari 2600, or Master System emulator. I really want to believe that a C3 app can achieve this... Am I crazy?

  • 2048 text objects all displaying 2 characters each

    Text objects are rendered on the canvas, and updating so many of them on every tick is not a great idea. Can you compose a single string and display them all as one large Text or SpriteFont object? Using a monospaced font of course. It should work a lot faster than 2048 separate texts.

    I would probably use a combination of Javascript and events - a script to generate that string and events to display it.

  • I second the idea to use spritefont instead of the text object. Or you could even try setting the text of an html element.

    If rendering all the text is still what’s making it slow you could make the memory view scrollable and only draw on screen text.

    If the rendering isn’t the bottleneck then you could try doing the update over multiple ticks to spread the load. There is an overhead with events over doing it in straight js but it varies. Generally it doesn’t matter but even a small overhead adds up when you do a lot at once.

  • Unfortunately, after converting to using a single SpriteFont object, and constructing the ram values and formatting through a series of loops (and using some BBCode to highlight specific things) I now have a whopping framerate of 15 ticks per second...

    Surely there must be a way to do this... there are plenty of programs I see that can update as many if not more values at 60tps... is this a case of wrong tool (Construct 3) for the job (6502 Emulator with some basic visual feedback)?

    I can't say I've used an HTML element before, so I'm not quite sure where to start with that, but if it'd be faster than I'll have a look.

    Is there a way to only update a specific part of my text layout? Like say settokenat(string, index, separator) or something similar... I'm starting to feel that there is no solution that will be "fast" enough to be useful

  • Is there any kind of "On Value Changed" trigger that one could use with the array plugin, so that I don't have to update all my values, and can only update the ones that are changed when it happens?

  • So slower?

    Apart from changing between text,spritefont or html element to draw text ( they all work the same with a set text action), you could use js instead of events to update the text. Or maybe use multiple instances vs one big one.

    That’s mostly what you have control over in construct.

    In general JavaScript will be much faster than events if you don’t mind using it. Personally I don’t mind using js but I find it very unpleasant using js within construct.

  • You’d have to implement your own on value changed scheme. Maybe store the old values in a separate array.

  • You could use a tilemap to do text too. That would let you only update the characters you wanted.

  • I wound up going back to using 2048 separate spriteFont instances and manually hooked up a RamViewerUpdate for each opcode that directly affects either a Ram value, or the Stack Pointer, so while not ideal, I'm now running at 50tps alongside my 256x240 pixel display generating a full screen of random grayscale pixels every tick.

    I don't mind learning to use JS inside of Construct... I've just been finding it incredibly difficult to find reference that seems to work properly or integrate. I'm finding the ways C3 and JS are linked in terms of using scripting to be super unintuitive (alongside simply not knowing any JS to begin with)

    so as it sits right now... any opcode that triggers a direct change to a value in RAM, also calls a viewer update for the instance of SpriteFont that has a variable storing the RAM address its displaying. Its still not really fast enough at 50tps, as eventually I want to be drawing a full display every tick at 60tps and use the webAudio API to replicate my own version of a sound chip... but its closer than it was...

    Next up will likely be the code editor that will allow you to input your own 6502 code, and it'll be runnable

    EDIT: When add an fps counter to my GUI rather than run in debug mode, other than a brief bottlenack when first "powering on" the emulator that I need to explore, it runs pretty dang close to 60tps (I'm assuming the "fps" expression still means ticks rather then the rejigged frames per second that shows 0 if nothing is being redrawn on screen)

  • I did some tests and the results are interested. Displaying 2048 random 2-digit numbers on every tick:

    • A single text object - 3 fps
    • A single text object with bbcode (random color for each number) - 15 fps
    • A single spritefont object - 144 fps, 25% cpu
    • A single spritefont object with bbcode - 30 fps, 100% cpu
    • 2048 separate text objects - 5 fps
    • 2048 separate spritefont objects - 75 fps, 70% cpu

    For some reason text is rendered slightly faster when it has lots of bbcode tags. And the winner is a single spritefont, it's massively faster, but it doesn't like bbcode.

    Since you only need to display 16 characters, you can add highlighted versions of them to the spritefont image and update the character set in spritefont properties. This way you can display white or red numbers without using bbcode.

  • Cool tests. I think we are very much cpu bound with that much text.

    I wanted to see if using a tilemap to display text could be viable so here’s the results of my tests.

    4096 characters, all random per frame

    Single spritefont (no bbcode) 
    60fps, 35-45% cpu, 3% gpu
    
    Tilemap
    60fps, 30-40% cpu, 3% gpu

    I probably should smooth out the percentages but in general tilemap used 5% less cpu than spritefont, but spritefont actually seemed to be more stable with the % used.

    I must be using too low of a resolution but even with a small font I was hard pressed to get that many characters to fit on screen. If you made the memory view scrollable you could get a nice speed boost by only setting visible the text of visible memory.

    Do you mind some questions about the emulator?

    2048 is only 8 pages of memory and the cpu can access up to 256 pages. Do most programs work with less pages like that? I’m guessing most systems didn’t have all 64k of memory.

    For your display of 256x240 is that memory mapped at 8bits per pixel or are you going to go for a tile based renderer like the nes or maybe something else?

    Do most programs start execution at a specific point in memory or is that specified per program usually? I see there’s a specific memory location to specify the starting location but I was curious if most programs start the pc on page two or something.

    Do you just implement the documented opcodes, or do you implement the illegal ones too? When making it did you first make all the instructions or did you just make enough to test? What’s the behavior if an illegal opcode is encountered?

    How did you end up implementing the bitwise operators since construct doesn’t have any by default? Manually bit by bit with a loop and the bitset/bitget expressions, or a third party plug-in, or maybe a table lookup?

    Have you tried benchmarking your emulator to see how many instructions per second it can do?

    Do you have it disassemble the instructions as you go? What was the trickiest part of the cpu to emulate? How many events does your emulator have so far? Are you emulating the clock cycle timings of instructions too or just run stuff as fast as possible?

    It sounds like an interesting project to make an emulator like that. To make it fast you’ll likely want to drop down to js or lower since events are rather slow. To learn it’s great and the ideas transfer should you redo it later in something else.

    I wonder how much of an emulator could be possible in the free version without js. Guess more than 25 events would be needed.

  • I'm starting out with an NES as the version of a 6502 I'm emulating. There are onyl small tweaks here and there to make it run as an Atari 2600, or a Sega Master System (at least in terms of the CPU each uses, as they're all variants of the standard 6502)

    The NES uses 2k or ram, so that's what I've implemented here. My goal is to have built in debugging much like the NES emulator Mesen, thus trying to push what is viewable in realtime... that said, I'm most likely going to move to a page by page view instead of all at once, both for efficiency's sake as well as what I have showing on the screen in general with regards to layout, modules, etc.

    Most 6502 setups generally start their code running at BusAddress $8000, the last 6 bytes of the program are typically reserved as Vector addresses which tell the system where to jump to for NMI, IRQ/BRK, and RESET/POWER ON.

    As far as how the CPU interacts with RAM, the only real caveat is that the Stack is always ranging from addresses $0100 to $01FF, with the pointer decrementing when values are pushed to the Stack, and incrementing when pulled. The Stack pointer always wraps with the page its on. So decrementing beyond $0100 will jump it back to $01FF, etc

    I currently have all the opcodes in place. I have verified (to the best of my ability with the emulator in its current state) all of the legal opcodes and their addressing modes. The illegal ones are in and functional (with the exception of illegal opcodes marked unstable... the CPU will read and execute them, but as far as the emulator is concerned, they're the same as a NOP) but not verified yet.

    I've been having ChatGPT make small 6502 programs for testing (sure is fun having to double check its work all the time lol it gets addressing modes confused regularily), as well as some small programs from a tutorial series on NES programming I purchased from Pikuma.com about a year ago.

    I've done everything in events so far, as I've mentioned not knowing any JS yet... that said, someone kindly posted a JS example in a different post of mine regarding drawing pixels to the Canvas object, and I have modified that enough to generate a noise image at 60fps (when RAM isn't updating) when the emulator is "off" and showing a solid pre-programmed BG color when "on" (none of this is can actually be affected by a 6502 program...YET!)

    For the bitwise operators, I actually built my own DECtoHEX, DECtoBIN, BINtoDEC, BINtoHEX, HEXtoBIN, and HEXtoDEC funations BEFORE realizing there were getbit() setbit() togglebit() already built into C3... So I'm in the process of converting those over. Until now I've been using a lot of tokenat( binString, targetBit, "" ) which I assume is slow given that its dealing with strings and text comparisons.

    I haven't benchmarked anything yet, as I'm still in the "does it all work properly" and the "how can I make what I've got the most effective/efficient" stages... though the goal is to be able to load and run 6502 programs at full speed, including NES roms for this particular iteration.

    One of my project files is a 2D array of the actual instruction layout of the 6502 chip as seen at https://www.masswerk.at/6502/6502_instruction_set.html, so using my HEXtoDEC function, I can:

    - read a Hex byte from the program, and directly reference the Opcode, Addressing Mode, and expected byte count

    - collect the value of the data bytes (either 0, 1 or 2) to send to the Opcode Function

    - execute the Opcode function, which then calls the addressing mode function (this sends back Memory and Address16 values back to the Opcode function), which then completes the operation, which then either sets the program counter manually (such as for Branches, Jumps, Interrupts, etc) or increments it normally through the program.

    * I haven't included the cycle values yet, but its on my TODO list, to try and get it accurate enough for playing games and running programs and having them behave the way they would on real hardware, at least as far as the user experience is concerned.

    I do recognize the need to move probably most if not all of this to JS, I've just zero experience aside form the snippet I'm using for my little display element, and will likely need to either find some really good tutorials (ideally relating to JS in C3 specifically) or see if I can find/make a friend who might help guide me. As prior to Construct apps the only actual programming experience I have is some Visual Basic from the late 90s... I'm better with 6502 than I am with any "modern" languages, in that 6502 is all I've ever tried to learn lol

    And yes... TONS of events... This project is definitely NOT for the free version...

    The eventual goal for this project (assuming I can get the emulator fast enough to run at 60fps during full playback with debugging, etc) is to create a 6502 SDK, which includes:

    - code editor (I've already prototyped this to the point where I will likely start working on it as my next step)

    - emulator for in-app testing/playing/debugging.

    - chr editor (akin to Aseprite as far as making tiles and sprite graphics goes)

    - sound editing (I have actually done a little bit of playing with WebAudio in JS, and between that and the WebMIDI plugin, intend on making a very basic sound and music editor... I've spent years using both Modplug Tracker and FL Studio for all my sound and music production, so replicating those features is a task, but not an unfamiliar one)

    This is intended to only support nrom format intitially. As far as NES stuff goes, mappers and whatnot might be considered later... I think I've got more than enough on my plate with this currently lol

  • Try Construct 3

    Develop games in your browser. Powerful, performant & highly capable.

    Try Now Construct 3 users don't see these ads
  • I'm a BIG fan of the SpriteFont plugin btw, and its always my goto for onscreen text, so its nice to see how well it fares compared to other options... That said... its STILL missing 2 super important features imo:

    - the ability to have preloaded or load a different image through events (this would facilitate being able to change fonts on a whim, or even animating them)

    - the ability to change the character set through events (this would help support other lanuages WAY more easily imo)

    I also really wish there was support for HEX values without having to manually convert things to string all the time, as I'm SURE that process slows things down a bunch, as I'm having to do it a TON

    Also... regarding the set/get/toggleBit() functions... if I have a decimal value of 15, which is %1111 in binary, and I try to toggleBit( binNum, 7 ) can I safely expect the new number to be %10001111 (143 in decimal) or will it just do nothing as 15 has no bit7?

    A quick search suggests that JS allows interacting with numbers in Decimal, Binary. Hex AND has a number of bitwise operations it can perform (again please remember that I know next to nothing about JS or other modern languages... so please forgive me if I'm wrong on this) so it seems odd that C3 has left those out of its even system?

    Am I just outgrowing the scope of what the event system was built to facilitate in terms of developers using it? I ask as I really love using the Event Sheet format, but I'm starting to really feel its sluggishness and limitations... (unless I'm just using it poorly, which is also super plausible)

  • Cool. That answered most of my questions. I was going to ask what happens with the rest of the memory if the nes only has 2k of memory but found an nes memory map and that answered that.

    Bitwise operations are pretty low level and really I can count on one hand the number of times I might find them useful in construct. Same with hex values. You really only run into that sort of thing if you’re doing low level bit fiddling. So construct doesn’t have them because they haven’t been needed.

    There probably is a plugin that has them or you can use a bit of js to access it.

    The bitset/get expression treats the number as a 32 bit integer so you’ll be fine with that use.

    You probably should stop saying you don’t know JavaScript. If you run through a quick tutorial you’ll see you already know more of it than you thought. The expressions are very similar and there’s ifs and loops. You don’t have to know a language in its entirety to use it. No need to know about prototypes, classes, or other stuff like that. The scripting api is just a bunch of functions to interact with construct from js. No one knows that. You just get used to looking up the functions you need.

Jump to:
Active Users
There are 1 visitors browsing this topic (0 users and 1 guests)