Message-ID: <1416938535.m2f.45966 at forums.libsdl.org>
Content-Type: text/plain; charset=“iso-8859-1”
Ok, ok, everyone fight against me
I have not wish and time to make big discussion like this. I just post my own opinion about this tutorial.
But I feel I must answer to some posts here.
To Jared Maddox:
The game is liable to use pixel-to-pixel rendering, including any number of mixtures of bitwise ANDs, IORs, XORs, and comparisons.
I agree, but you can use SDL_Surface: it contain memory buffer. You can use
this buffer any way you like, including work with each pixel (pixel-to-pixel
rendering) and bitwise operations.
And on the preferability of SDL_Surface I did agree last time… but
on further review and consideration I realized that allocating raw
memory probably DID resemble more strongly the practice employed in
the primary tutorial series.
Regardless, were I writing a renderer myself I certainly would use a
SDL_Surface.
Readeing to and from main memory is highly prefered over reading and writing over the peripheral buses.
And about peripheral buses. Every time you call SDL_UpdateTexture, you use
peripheral buses. Many of other SDL functions use peripheral buses too. And
if you use OpenGL functions you use peripheral buses.
No way to output graphics to screen without using peripheral buses. You can
make some rendering in main RAM, but you always use buses after this. And if
you use hardware acceleration, which is much faster then rendering in RAM,
when you use GPU and video memory to rendering, then you always use buses.
I suppose I should give a better explanation.
The operations that are likely to be performed are basically “anything
that you can do in C”. If you have render-to-texture support then this
can be done in shaders, but doing that is a rather large leap,
particularly since you have to translate between languages. You can
get around that by doing reads and writes across the peripheral bus,
but if you make very many of those then you rapidly approach the point
where you do MORE peripheral bus transactions than if you had simply
kept everything in memory in the first place. This is so much the case
that the standard SDL2 advise is that if you’ll be doing frequent
modifications of a texture then instead of copying it from the GPU
when you want to modify it, you should keep a copy in memory at all
times, and only update it from the GPU copy if the GPU copy might have
been modified, AND you need to modify it yourself.
Some peripheral bus transactions can’t be avoided with graphics, but
by reducing the number that you use you can reduce your chances of
negatively affecting OTHER peripheral bus transactions, such as those
that the GPU driver might be performing in the background to move a
texture from memory/disk back to the GPU (I don’t know that it’s
common right now, but it’s very possible for them to do). At the same
time you reduce the chances of exceeding the peripheral bus’s
bandwidth, and simplify the code required.
Conclusion? If you’re doing this sort of rendering then you should
start by keeping a copy of the texture in memory and just working with
that.
…
#define BYTES_PER_PIXEL 4
No, don’t do this. This is not some retro version of C, if you want it to be constant then use a const qualifier on a variable (even if that can be cast away).
May be I use old-style code sometime (I think, I’m not),
You are using old-style code in this case. I don’t think it’s to the
level of having a third argument in main(), though.
but show me please where in new C standard (ISO C99 for example)
#define macro is prohibited?
It’s not PROHIBITED, otherwise you would have had an error during
compilation at some point or another. Defines are done by a system
that at the least is logically distinct from the actual compiler, and
in some cases is a seperate program. The compiler I commonly use (a
GCC branch) will pass info about defines from the preprocessor on to
the compiler, which is useful for debugging, but it’s still NOT a best
practice: best practice converts THESE sorts of defines into const
variables instead.
I use both #define macro constants and const qualifier, when I think this is better.
I try to restrain my use of define & friends to things that simply
make the most sense in the preprocessor (this class of things is
mostly include guards, code generators, and some feature/platform
detection). I personally consider the preprocessor to be weaker than
it should be (I’d like to see count-down loops, a for-each derivative
with some inspiration from C++11 variadic templates, and a define
syntax modelled on #if/#endif, which would be useful for e.g.
automatically generating switch statements and array contents in
conjunction with each other), but I still wouldn’t use defines to
replace cases where const variables can be successfully used instead
(note that I might use them to INITIALIZE those consts, especially if
I wanted that to be compile-time configurable, but I wouldn’t use it
as a replacement for them).
I can use “const int BytesPerPixel = 4;”, or with C99 types like
"const int_fast8_t BytesPerPixel = 4;", or with SDL types like
"const Uint8 BytesPerPixel = 4;" (in SDL_Surface Uint8 type used for bytes
per pixel and bits per pixel), and I can use “#define BYTES_PER_PIXEL 4”. And
I can use enum’s to make a set of constants.
The only one I definitively advise against is using the define to
REPLACE any of the others. The first two are conceptually the ideal
cases if you just want A value instead of SEVERAL POSSIBLE values (in
which case the enum is the way to go), but for historical reasons the
third case makes sense as well.
I learning programming on a source code of best (professional made) libraries
(including, but not only, SDL ). Look to SDL source code: you can see many
#define’s in this code. This practice is not invented by me, I try to use
best practices, which I learned reading professional source code.
Ironically enough, those very same libraries will often contain the
sort of non-recomended practices that you’re speaking against here!
Honestly, “professional” often isn’t the same as “clean”, and "clean"
seems to be what you’re looking for. What I would actually recommend
you seek out is “professional”: just not the define thing, we have
better practices than that now (it dates from a time when C didn’t
have the const qualifier).
Why would you put it in a struct? To avoid globals?
… (stupid words are removed)
They aren’t ideal, no, but a global stuct IS a global variable.
Yes, I know this. I know we can’t live without global vars. But this is
common practice to make a struct with fields related to some “object”. Global
struct is better than tons of global vars, IMO.
It’s too early into the codebase for tons of global vars to be an
issue. Also, where you REALLY want to use a global struct instead of a
global var is where you either have some logically distinct GROUPS of
variables that you might want to have multiple sets of at a later
point in time, so that you can simply make another instance of those
variables then. The author of the original tutorials basically goes
into this in one of the videos (maybe video 4? I forget).
Regardles, I’m going to give you a piece of advice that the original
Unix developers apparently followed: “If you do it more than once,
consider writing a script for it”. For the C/C++ case, that becomes
"If you do it more than once, consider writing a struct, function, or
class for it". Globals are globals, packaging them into structs when
you’ll neither have several instances or more understandable names is
productively identical to slapping a coat of paint onto a car. You
haven’t actually changed anything, you’ve just told yourself that you
have.
As I said above, I read sources of some professional software (many thanks to
free software), and as I can see in most libraries (may be in all) this is
common practice to use global structures instead of separate vars. As I can
see in this tutorial, author in first chapter, started to use C++ (not a C),
then he can make a class with all fields and methods what he need. I use C
for developing and I use structures instead of classes and functions instead
of methods.
Some of them will be genuinely designed as objects: these are the ones
that you should look to duplicating. Others will be designed as
namespaces: this can be done, but if you can’t concretely point to WHY
you’re doing it that way then I’d recommend AGAINST it, as it’s
usually a symptom of an anti-pattern (specifically, a symptom of an
automatic knee-jerk reaction against globals: I believe this specific
one came from a mixture of poor understanding of object-orientation,
and experience with code bases that mostly or entirely used globals,
sometimes due to the language involved lacking name scoping: even the
goto statement has it’s exceedingly, astonishingly rare, sensible
use-cases).
Look again to SDL code: all objects are structures. There are a lot of
structures. But imagine all fields of this structs as a separate vars, then
SDL goes to be very hard to use (may be impossible to use) library.
It’s not actually THAT hard to use (you replace pointers to objects
with indexes into arrays: annoying, but actually practical), though it
IS ill-advised if the variables really do have sufficient relation to
deserve being an “object” (whatever you have to do in your language to
actually implement it). The PARTICULAR problem in this case is that
the changes that you called for in the code would have taken it
further away from the tutorials. WELL DESIGNED implementation of your
suggestion in a codebase that had already gone through all of the
tutorials (including the many that haven’t been done yet) would be
good (unless you stuck with the advise to stick everything in a global
struct: there’s just no good reason to universally do that), but doing
it in this case would just make the tutorial series more confusing.
You should REALLY be watching the primary tutorials if you want to critique this stuff.
Hm, strange logic. Why should I watch primary tutorial? May be primary
tutorial is very good, may be I watch it later, but I think I can start to
read this tutorial even if I never watch original before. And I can write my
opinion about this text what I reading.
The tutorials that you are critiquing are themselves a commentary on
the ORIGINAL tutorials. Thus, to properly understand what THESE
tutorials are trying to say you have to consider them in conjunction
with the original tutorials, since THESE tutorials were never MEANT to
be considered by themselves. You can certainly write your opinion of
these tutorials in isolation, but the resulting critique will be
mostly invalid due to the inherent disconnect. This is simply the way
that things go within the context of inter-connected systems:
understanding a part is NOT often enough to understand whether the
part is well-designed.
To David Gow:
PS: I have ported to SDL2 my own image file format. My function create a
SDL_Surface with buffer size I need, then my code process image file and load
pixel data immediate to surface pixel buffer (like IMG_Load). Next I can
continue using this surface or use SDL_CreateTextureFromSurface to put this
data to a texture. I can use malloc’ed buffer instead of surface and load
data to texture with SDL_UpdateTexture, but I use surface because I need some
fields from surface struct. Using surface free me from creating my own struct
and creating malloc’ed buffer. For me surface is “all in one” solution, ready
to use. Only one call to SDL_CreateRGBSurface function give me a struct with
all fields I need and allocated pixel buffer. And I can use this surface
later in my code if I need.
This is the only part of this section of your post the I’ll comment
on. If I was designing an image format library I would probably base
the interface on SDL_Surface as well (and if I couldn’t, then I’d
probably try to make a derivative of SDL_Surface that I COULD base it
on), but I wouldn’t incorporate this directly into the “core” API.
Instead I would have the functions in question take pointers to the
appropriate variables, and provide a seperate file that provided
standardized wrapper functions that DID directly use SDL_Surface. The
reason is simple: I have found that often you will want to use some
particular library, but it’s designed for another library that for
some reason you want to avoid (example: you might be writing a plug-in
for a program that uses something else), so you have to go pouring
through the source code of the library (that you know from previous
experience works perfectly fine), just for the sake of making minor
modifications.
It’s basically to make it EASIER to not reinvent the wheel. I mention
this because you seem to want your code to be inherently good, so I
think you’ll find the idea occasionally useful.> Date: Tue, 25 Nov 2014 18:02:15 +0000
From: “Alex”
To: sdl at lists.libsdl.org
Subject: Re: [SDL] Linux port tutorial for “Handmade Hero” using SDL