[HN Gopher] Peredvizhnikov Engine: Lock-free game engine written... ___________________________________________________________________ Peredvizhnikov Engine: Lock-free game engine written in C++20 Author : gjvc Score : 265 points Date : 2023-09-10 15:03 UTC (7 hours ago) (HTM) web link (github.com) (TXT) w3m dump (github.com) | djmips wrote: | I didn't see mention of how hard it is to debug such an engine. | smallstepforman wrote: | I have an Actor framework which uses a vanilla std::deque for | method pointers, and to add messages to the queue, the locking | technique is a Benaphore (the original Futex, which uses an | atomic and a locking primitive, with the twist that my locking | primitive is a combo of spinlock/mutex based on retry count). | Nothing special. Benchmarks show that very rarely does the | message push function block, and the chance of an OS context | switch is also very low, so even though occasionally we get a | locked thread swapping out, it is so infrequent that it doesn't | justify the lock-free algorithm cost. | | In a nutshell, non lock free queues are much faster than lock | free queues, however you must be prepared to accept the very | infrequent longer delay due to a context switch (where the lock | is not available to anyone). My benchmarks show that the price is | acceptable since so much more performance is available with non | lock free queues. 10 million messages per second per work thread | can be queued on modern hardware. | Y_Y wrote: | With a post like this, however interesting, you really should | link the code. Not only will it prove your claim, but others | like me will find the idea intriguing and immediately want to | see how it's done. | smallstepforman wrote: | https://github.com/smallstepforman/Medo/tree/main/Actor | pramalhe wrote: | [dead] | tialaramex wrote: | > the original Futex | | The "Benaphore" was a pretty old idea, but the Futex isn't just | "Oh it's a Benaphore but Linux" the _essential_ trick is that | you don 't actually need a kernel object - your "locking | primitive" at all, and that idea is where this goes from "Yeah, | everybody knows that" to OK, our OS should add this feature | ASAP. | | Instead of an OS synchronisation object which is used to handle | conflicts, with the futex design the OS carries a list of | address -> thread mappings. If a thread T is asleep on a futex | at address X, the address X goes in the list pointing to thread | T. When the OS is asked to wake the X futex, it walks the list | and wakes T. | | The give away is the limits. For something like Benaphores | you're constrained, these are a costly OS wide resource, I | think BeOS only allowed 65536 per machine or something. But a | Futex is just memory, so there's no reason to have any limit at | all. | foota wrote: | Aren't lock free data structures more about reducing the impact | of contention than throughput under low contention? | klodolph wrote: | Depends what you mean by "the impact of contention". It is | possible, if you build a lock-free system, to end up with | threads that keep churning and never make forward progress. | loeg wrote: | Lock free is kind of an overloaded term that can mean a | variety of things depending on what the user is thinking. | Usually the goal is being able to make forward progress even | if one thread is context switched out by the OS scheduler, | which might be called wait-free or non-blocking. Using | mutexes (unless you can prevent OS scheduling, interrupts, | etc) makes this property impossible to achieve. In general, | MPSC queues are super fast and there's no real reason to | prefer a locked queue. | tialaramex wrote: | Under a lock free algorithm, we promise that across our | entire system, eventually some progress is made. I can't | necessarily point at any specific thread of execution and say | "This thread makes progress" but I can say that forward | progress is made for the system as a whole. | | Under a _wait free_ algorithm, we promise that every | individual thread eventually makes progress. | | Suppose I have one really dumb thread which just goes to | sleep for 10 seconds in a loop, as well as other threads | which do some actual work. If we're a lock free algorithm, it | is OK if the 10-second-sleep thread is always chosen to make | progress, its "progress" consists of sleeping for 10 seconds, | too bad, try again next time. | | With a wait free algorithm, the threads which actually do | useful work will also make at least some progress, eventually | despite the 10 second sleeper. | | Generally we can't say for sure that we e.g. "reduce impact | of contention" only that we definitely make _some_ forward | progress, on _at least one thread_ eventually. | samsquire wrote: | Thank you for sharing this. | | Could you tell me if your 10 million figure includes batching | or are they a loop that tries to enqueue as many items as | possible? | enqk wrote: | how do you deal with msvc's `std::deque`? (or maybe you're not | using literal std::dequeue) | OnionBlender wrote: | What is special about msvc's `std::deque`? | nly wrote: | It's block size is something stupid like 16 bytes, so it | effectively becomes a slower linked list. | josefx wrote: | The memory chunks it allocates to store entries are so | small that it ends up allocating one chunk for every | element for almost every type. The result is a lot of | allocation overhead, pointer indirection and various other | things that are bad for performance. | HelloNurse wrote: | Whatever the issue is, they support Clang on linux, i.e. no | MSVC standard library. | EliRivers wrote: | _My benchmarks show that the price is acceptable_ | | Well, except to people who have a hard requirement that there | never be this unpredictable, infrequent longer delay you | mention. | Groxx wrote: | It's probably fair to claim that zero* game developers have | this hard requirement. At best they have a _strong desire_ | which might be partially backed up by benchmarks and | squinting hard enough. | | *: ignoring deliberately esoteric cases, like Doom on a hard | real-time system. | EarthLaunch wrote: | It's a requirement for any game that needs a reliable frame | rate. Unpredictable delays during frame rendering cause | jank. | | (This is not to say the OP has any such issues in its | context.) | [deleted] | electroly wrote: | This is usually called "soft realtime" -- you want it to | be fast but it's not _wrong_ if it misses deadlines. It | doesn 't violate correctness of the system to miss a | deadline. In a "hard realtime" system, it is a fatal | error if the system misses a deadline because correctness | is violated. I presume this difference is what OP is | talking about. Games are never hard realtime; missing | frame deadlines reduces user experience but it doesn't | break the game. Hard realtime is things like industrial | motion control, automotive and aviation electronics, | pacemakers, etc. | munificent wrote: | _> Games are never hard realtime; missing frame deadlines | reduces user experience but it doesn 't break the game._ | | It depends on whether the game is multi-player and, if | so, how it keeps the different players in sync with each | other. | | Games that rely on deterministic gameplay can desync (two | players don't see the same world state) and abort if one | player's simulation drops a frame while the other | doesn't. | forrestthewoods wrote: | > Games that rely on deterministic gameplay can desync | (two players don't see the same world state) and abort if | one player's simulation drops a frame while the other | doesn't. | | Right. This is why lockstep deterministic (LSD) games are | bound to the SLOWEST player's machine. | | No LSD game in existence crashes the game if one player's | machine falls behind. Instead you either pause the | simulation for all players until they catch up or you | slow down time itself. | | Source: shipped RTS games that were lockstep | deterministic. | dzaima wrote: | If you want to support players with ping over 16ms, | there's no way you're gonna be synchronizing input from | one frame to the output of the same frame for another | player; there'll necessarily be some latency hiding, at | which point any freeze should be coverable via just | treating it as a temporarily-high ping and thus | temporarily more latency hiding. | Groxx wrote: | If you have multiplayer like this, you _absolutely_ do | not have a hard requirement on frame latency. | | You don't control network latency spikes. Latency | _tolerance_ is a hard requirement, or you will have | constant problems and likely be unplayable. Or custom | networking and hardware stacks, which is deep into | esoteric territory. | electroly wrote: | That's still just a degradation of user experience and | not a fatal fault. Indeed, support for running in | desynced mode is written is because they know deadlines | can be missed. In hard realtime, deadlines _can 't_ be | missed. There's no recovery; it's a critical fault and | you have to halt or failover to a backup. | munificent wrote: | _> That 's still just a degradation of user experience | and not a fatal fault._ | | I don't know how you'd describe a game spontaneously | aborting not a "fatal fault". Yes, it's not turning off | someone's pacemaker, but within the scope of what a game | is able to do, kicking the player back to the matchmaking | screen in the middle of a game is about as fatal as it | gets. | fluoridation wrote: | That's only going to happen if the client has such a | massive network latency spike that the server thinks it's | disconnected. At least half a second, possibly more. | You're never going to get that kind of delay from | synchronizing threads, unless the process totally | deadlocks. | | EDIT: Well, there is one situation where you might get | delays like that: if the computer is so woefully | inadequate to run the game that it consistently misses | frame deadlines by several hundred milliseconds. Of | course, in such a situation a different concurrent | algorithm wouldn't have solved anything anyway. | fiddlerwoaroof wrote: | What I don't understand here is how missing a frame | deadline in this situation (<1/60 of a second or 16ms) is | intolerable when typical network latency varies | constantly by much more than this. It seems to me that if | the latency requirements were this strict you could only | support LAN multiplayer. | squeaky-clean wrote: | The only scenario I can imagine is some interactive game | installation at Disney/etc. But there'd still be no | actual benefit over an architecture more lenient to | timing deviations. | caconym_ wrote: | Literally any multiplayer game will have a "fatal fault" | if its connection to the other players and/or server is | interrupted for long enough, but it seems disingenuous to | describe a system tolerant of delays variable across | several orders of magnitude, up to hundreds of | milliseconds or even full seconds, as "hard real time" in | the sense in which the term is generally understood. | ack_complete wrote: | Lockstep simulation can't be hard tied to frame rate. Two | computers will never have precisely the same frame timing | -- even if they are running identical video modes, clocks | are only accurate to within a tolerance and will diverge. | The simulation has to allow for simulation ticks running | independently from rendering frames. | jefftk wrote: | _> Games are never hard realtime_ | | Depends how high your standards are! One of the things | that makes playing old games on the original hardware | really satisfying is how consistent they are. | brokencode wrote: | Those old games could be so consistent because the | software and hardware were both so incredibly simple | compared to today. | | Today, a PC game has to work on a large range of hardware | that has come out over the past 5+ years. And there are | GPU features that are only available on certain cards, | like hardware ray tracing and things like DLSS and FSR | for upscaling. | | And the game engines are incredibly more complex today to | handle modern expectations, with dynamic lighting and | shadows, huge maps, etc. | | It doesn't matter what your standards are. Hard realtime | just isn't realistic or even possible any more, except | maybe in a game that would be considered truly primitive | by today's standards. | EliRivers wrote: | I am sure I have memories of how hard it was to support a | range of hardware when I had to write specifically for | each piece. When I had to write separately for a | Soundblaster card and an Adlib and a Disney SoundSource | and a Roland and a Gravis. | | And that was just the sound cards. Writing for different | hardware became so much easier when OpenGl and DirectX | came into being. Suddenly I just had to write to these | APIs. | | I think I'm disagreeing with you. Supporting multiple | hardware configuration way back when was so much harder | than doing it today. | Yoric wrote: | Oh gosh, the many different variants of SVGA that existed | back in the days... | | /me shivers at the recollection | djmips wrote: | I would say that an exception could be made for VR games | where any frame rate 'jank' causes an uncomfortable | experience and can lead to increased simulator sickness. | forrestthewoods wrote: | VR games almost all use Unity and Unreal. They drop | frames left and right. VR platforms use extremely complex | time warp algorithms to hide the jank. | | So you're not wrong. VR games are _much_ more susceptible | to dropped frames causing problems. But it both happens | and is hidden remarkably well. | djmips wrote: | "Unpredictable delays during frame rendering cause jank" | | This is usually not serious inflicted and the | presentation threads will be higher priority than the | simulation in order to minimize visual 'jank' | | Lockfree game code ain't fixing jank from the OS. | [deleted] | rcme wrote: | Well do we have a benchmark where the price of locking is | unacceptable? I think the linked project is a fun theoretical | exercise, but I personally don't accept the assertion that | locking is unacceptable without seeing evidence. | moonchrome wrote: | These people are probably not game developers because frame | dips are all over the place on console and PC gaming. | Phelinofist wrote: | I agree, lock free is cool and all, but often times it is more | complex and not every case justifies the increased complexity. | rowanG077 wrote: | I gather from your comment that lock-free is actually the best | choice for videogames. Where consistent frame timing should be | paramount. | pramalhe wrote: | [dead] | hot_gril wrote: | I don't have time to read through the impl, but the readme makes | it sound like a classic distributed system between game threads, | where patterns like retry-backoff will be common. | jacoblambda wrote: | The paper goes into more detail without forcing you to go into | the impl but it does seem to be a decent bit more advanced than | that. | | https://github.com/eduard-permyakov/peredvizhnikov-engine/bl... | xwdv wrote: | Any games built with this | minitoar wrote: | Yes, it's called C++20 Game Engine Developer. | jeffreygoesto wrote: | Looks BEAMish to me? | | https://youtu.be/bo5WL5IQAd0?feature=shared | edfletcher_t137 wrote: | > At the moment, the only supported platform is Linux. | | Regardless of your feelings on the status quo, there is one thing | you _must_ do when building a game engine if you want it to | succeed: _support Windows_. | [deleted] | WhereIsTheTruth wrote: | bullshit, consoles/mobile are bigger markets than PC/Windows | | PC is just less than 1/3 of the whole picture | | https://www.data.ai/en/insights/mobile-gaming/2022-gaming-sp... | hot_gril wrote: | Rude way of putting it, but yeah, mobile dwarfs everything. | kevingadd wrote: | Got bad news for you about what the console SDKs run on | | (Also, the consoles don't run Linux) | 5e92cb50239222b wrote: | A bit more than that since Xbox runs Windows too. | WhereIsTheTruth wrote: | July console sales: | | PS5: 1.2m | | Switch: 950k | | Xbox: 370k | | Xbox accounts for just 17% of total console sales in July | | Both Switch and PS5 are FreeBSD based | | If we count the whole period of the current gen of each | vendors, it only accounts for 13%, it's not big | | https://www.vgchartz.com/ | rstat1 wrote: | Actually its only the PS5 that's FreeBSD based. Switch | runs a completely proprietary Nintendo OS that borrows a | lot from Android. | WhereIsTheTruth wrote: | A mix of the two | | "partially Unix-like via certain components which are | based on FreeBSD and Android" | | https://en.wikipedia.org/wiki/Nintendo_Switch_system_soft | war... | GranPC wrote: | It borrows very little from Android - I think it mostly | draws some parts from stagefright. | corethree wrote: | He's probably referring to the Desktop/laptop market. In | which case windows controls like 90%. | WhereIsTheTruth wrote: | Title is: "Peredvizhnikov Engine is a fully lock-free game | engine written in C++20 " | | A Game Engine targets various platforms | | A "video-game" is not something exclusive to desktop/laptop | windows market | StevenXC wrote: | I'd love to see the Steam Deck "console" change this status | quo. | tmccrary55 wrote: | Most of the SteamDeck games are just running windows games | through photon, wine and other compatibility layers. | | While that works amazingly well, I tend to prefer games with | native Linux and SteamOS builds, even though they're rare. | NavinF wrote: | win32 is the stable ABI for SteamOS, same as any other Linux | distro | [deleted] | datameta wrote: | The painting used is Barge Haulers on the Volga - | https://en.m.wikipedia.org/wiki/Barge_Haulers_on_the_Volga | | Can someone please expand on the significance of this achievement | to someone used to shooting their foot off in C++ in a | predominantly single threaded manner? | hathym wrote: | I hope the author is not picturing developers using his engine. | [deleted] | drums8787 wrote: | Barge hauler 4th from the back appears to be checking his | messages. | tetris11 wrote: | "Tut tut, those poor Canadian renters..." | mhd wrote: | Never mind that a lot of Volga boats were _very_ single- | threaded. | | https://www.amusingplanet.com/2021/12/belyana-russias-giant-... | perihelions wrote: | It's a logging framework! :D | 3seashells wrote: | You can even see the steam ships that replaced the slave in | the background. | datameta wrote: | Fun fact - this was painted about 8 to 10 years after the | abolition of serfdom in the Russian Empire. Coincidentally, | it happened two years prior to the US Emancipation | Proclamation. I've wondered from time to time how linked | the two events were, if at all. | beatcracker wrote: | They were not the slaves, but unionized workers: | | https://en.m.wikipedia.org/wiki/Burlak | mananaysiempre wrote: | AFAIU an artel' _artel'_ usually did not engage in | collective bargaining as such, so calling it a union is | not really accurate; you might compare it to a guild but | I think there's no implicaton of a monopoly on a | particular trade either. The most accurate description I | can think of is perhaps a cooperative combined with a | mutual insurance fund. | beatcracker wrote: | Agree, I might've stretched It a bit. Your descriptions | is more accurate. | throwawayvatnik wrote: | [flagged] | devit wrote: | It says that it's actor-based, and sending messages to an actor | is equivalent to running the actor function under a mutex, and | thus reduces parallelism (in other words, you have N threads | sending messages, but only 1 thread running the actor code, so | it's just as serialized as a mutex), so while it may technically | be "fully lock-free", using actors means that there is no | parallelization improvement. | onjectic wrote: | Your right that there is no parallelization improvement, but it | does not reduce parallelism either, its just a different(imo | easier) way to think about concurrency. | | Because it is easier for me to think about it is easier for me | to see where things will contend the same resource and actually | helps me improve potential parallelism. Once you recognize a | particular opportunity where SMP can speed things up, you can | stray a way from the actor-model a bit and have multiple | threads receiving on your message queue, or if that isn't | possible, you can just add more actors and split up the data | better. | lelandbatey wrote: | Not quite; with mutex based actors an interruption of the actor | thread would cause that mutex (and thus that actor code) to | remain locked until the original thread resumes. No additional | parallelism will allow that actor code to be "restarted" or | "resumed" as the mutex owned by the interrupted thread is | locked. | | This implementation relies heavily on restartable functions in | order to allow another parallel thread to pick up and continue | the work of an already in progress but otherwise interrupted | actor. See page three of the (excellent) design document: | https://github.com/eduard-permyakov/peredvizhnikov-engine/bl... | | Thus while it might not strictly be "more parallel" (same | number of actors), it does seem to be able to make better use | of more parallelism for completing the same set of work. | jstimpfle wrote: | > sending messages to an actor is equivalent to running the | actor function under a mutex | | Where does it say that? In my understanding actor model means | message-passing with asynchronous execution. So quite the | contrary, actor model allows N threads executing in parallel | given N actors. | mrkeen wrote: | Whenever I read any press about actors or goroutines, they | say the same thing about preventing races (and the need for | explicit locking) through share-nothing concurrency. | | It's easy to scatter the computations, but they never go on | to explain how to gather them back up. | | You're going to render one frame to the screen. Did multiple | actors have a write-handle into the frame buffer? What about | collisions, does each entity collide with itself, without | sharing position information with other actors? | hot_gril wrote: | One way or another, there has to be a master thread that | gathers things back. So instead of a mutex, you have a | supervisor. | [deleted] | schmichael wrote: | Does anyone have experience debugging/profiling highly contended | critical sections of STM vs a more traditional mutex | implementation? At the end of the day _something_ has to mediate | concurrent access to shared memory, there's no free lunches, and | mutexes are so well optimized, profiled, and understood. I'm | unclear if the same applies to STM where a transaction may need | to be retried an unbounded(?!) number of times. | gabereiser wrote: | Yes. The mediator in this case is the scheduler. The one that | actually calls the asynchronous block. Potentially retrying it | if it fails. In the OP's code, there's atomic blocks, | sequential execution of blocks, stateful blocks, etc for | ensuring singular access at a time. | | The meat here is scheduler.cpp. It uses std::coroutines. | | This is like async/await in other languages. The scheduler has | a queue of work(coroutines) and a pool of threads(N>0) to | execute those on. | | In this case, messages are passed between work that contains | the data. No locks are required at the expense of memory | footprint. | schmichael wrote: | Thanks! | | So the scheduler serializes execution of critical sections? | | > In this case, messages are passed between work that | contains the data. No locks are required at the expense of | memory footprint. | | Are messages copied or moved? If moved is there compile time | checking for ownership or runtime debugging tools? | gabereiser wrote: | https://en.cppreference.com/w/cpp/language/coroutines | gabereiser wrote: | You had me at lock-free, you lost me at SDL2. | rychco wrote: | Forgive me if it's common knowledge, but what's wrong with SDL2 | in this case? Broadly speaking, I feel as though there is | largely positive sentiment around the SDL project, no? | gabereiser wrote: | I have nothing against the SDL project. I have everything | against the DirectMedia style api of 1997. I get that people | are actively developing with it and that roadmap towards 3.0 | is getting close but my experience is it's designed for | legacy games, legacy rendering styles, legacy ABI's. No one | is shipping games for Dreamcast or PS2. | | 2/3rds of the library is dead code when working with Vulkan. | rstat1 wrote: | TIL Unreal 5 based games were "legacy" | OnionBlender wrote: | What is a better alternative? | gabereiser wrote: | For what? Working with Vulkan? Or a windowing library? | Personally I use a mix of glfw and native windowing on | mobile, which is surprisingly simple for a gfx context | window and this gets me to triangle. | | SDL2 is about the same amount of boilerplate code only in | SDL_Thing form. | | Now, if I was writing a game that needed to be shipped on | everything possible and I can't afford Unity or Unreal, | SDL is a viable choice. MonoGame, mine, and others have | used it, it's battle tested. | | I just can't look at SDL code anymore and say "this is | the cleanest api" for anything outside of C99. | nickelpro wrote: | As with most SDL usage, it merely provides a rendering | context and an input abstraction. | | That's almost nothing, its boilerplate you don't want to | write. Which particular lib you grab that boilerplate | from, SDL, GLFW, fucking GLEW, whatever, it doesn't | matter. SDL is a widely accepted library for doing so and | it's _fine_. If you want more complete input handling you | 'll be using the platform APIs directly. | pshirshov wrote: | Actors is one of the worst programming models possible. It's hard | to observe actor-based systems and debug them. The protocol | complexity (and complete protocol informality) usually brings | much more trouble than any kind of locking/synchronization. | lll-o-lll wrote: | I've had good success with actor based models. When you say | "protocol complexity", I don't understand what you mean. | | Observation requires good logging, but this isn't out of line | for any complex system. Debugging (as in actual breakpoints in | an IDE or post-mortem analysis) can be facilitated with stack | tracking (this same problem occurs with async await patterns | and is solved in a similar way). | | The advantages and disadvantages exist, but I think it's an | extremely effective programming model for many use cases. | Formal state-machine programming, that's the worst model, | unless you need it. | hot_gril wrote: | For game dev or in general? I hate using actors for general | backend stuff, but for game dev maybe it makes more sense. | [deleted] | hankman86 wrote: | It's licensed under GPL3, where the developer suggests | individually negotiating a license for commercial projects. I am | not sure this is a good approach given the early stage of this | project. Video game development is already commercially and | technically risky enough. | | I cannot see game developers lining up to even try out this | unproven technology when they have no sense of what the eventual | fees will be. | gjvc wrote: | quoting https://github.com/eduard-permyakov/peredvizhnikov- | engine | | _The source code of Peredvizhnikov Engine is freely available | under the GPLv3 license. However, I may grant permission to use | parts or all of the code under a different license on a case- | by-case basis. Please inquire by e-mail._ | [deleted] | hesdeadjim wrote: | It's quite the stretch to call this a game engine, rather than a | tech demo for clever locking strategies. | DennisP wrote: | Maybe it's not done yet. | forrestthewoods wrote: | This. There's no demonstration of any game made with this "game | engine". The benchmarks are some matrix multiplication and | unimpressive message passing. | | There might be some cool data container ideas or primitives. | Those could have useful applications. But it isn't really a | "game engine". Nor does it seem like an interesting way to | build one. | erwincoumans wrote: | Indeed. The project has no renderer or game physics code it | seems? If so, it is too premature to be a game engine. | rhelz wrote: | Say you are in the middle of building a house. The foundation | has been built and they are framing it up. | | Is it a house? | xwdv wrote: | No, and if you accept it as a house then this is a recipe for | developing a bad habit of leaving a trail of half finished | projects in your wake. | nitwit005 wrote: | No | [deleted] | Salgat wrote: | I wouldn't even call it clever. The Actor model trivializes | avoiding locks, since you're letting everything act on stale | information (if the actors process concurrently) and then just | iterating through their intended actions against a singleton | state and resolving those conflicts with sequential code. | sigg3 wrote: | Thanks for the summary, mate. | [deleted] | mdaniel wrote: | All those people who complain about project names squatting on | existing words have finally been heard :-D | forty wrote: | I tried in it a zxcvbn simulator, and it qualified as a strong | password ;) | whynotmaybe wrote: | It seems unreal so see such commands as cd peredvizhnikov- | engine | | Unless the name is explained somewhere, I'm convinced it's | complicated on purpose. | CamperBob2 wrote: | Otherwise known as "cd p<tab>." | [deleted] | forgotusername6 wrote: | The Russian word can be broken into three sections. The first | part is a prefix associated with a transition. The middle | means movement and the bit at the end means a person that | does that. So even though the group of artists with that name | were translated as "the wanderers" or "the itinerants", the | word literally means people who move around. | whynotmaybe wrote: | That's an awesome wordplay with the intent of the engine | tjrgergw wrote: | > Unless the name is explained somewhere, I'm convinced it's | complicated on purpose. | | Tell me you only speak English without telling me you only | speak English. | whynotmaybe wrote: | I guess that my pun using "unreal" in a post about a game | engine went under the radar and now I look like I can only | speak English which is kinda flattering and worrying. | loeg wrote: | From the grammar, I would not assume GP is a native english | speaker, much less monolingual. | drt5b7j wrote: | I would love to hear what exactly about the grammar | doesn't sound native. | bee_rider wrote: | The only thing that seems strange is "such commands as" | used to describe a single command. I think a more common | way of writing it would be "a command like." But, I don't | think it is at all definitive, it could just be a native | English speaker who went with a slightly odd phrasing. | bigbillheck wrote: | It seemed perfectly cromulent to me. | pekka22 wrote: | Peredvizhniki [0] were a group of Russian realist painters from | the 19th century. Among other things, they organized | "traveling" art exhibitions to promote Russian art in the | provinces. The name roughly translates as "The travelers". | | - [0] https://en.wikipedia.org/wiki/Peredvizhniki | rhelz wrote: | Not all who wander are lost. | charcircuit wrote: | >Lock-free export std::mutex iolock{}; | export std::mutex errlock{}; SDL_PollEvent | | Not yet at least. | [deleted] ___________________________________________________________________ (page generated 2023-09-10 23:00 UTC)