The host_read*, and host_write* functions currently rely on
casting unaligned memory to translate from byte arrays into
higher-level types (like 16and 32bit ints), however this
approach is implementation specific, can cause undefined
behavior, and forces the compiler to use less efficient
aliasing rules.
Unaligned memory casts have historically been corrected by
expanding multi-byte types into their constituents bytes,
shifting them, and re-packing. They've also been solved using
union objects to access the same underlying memory for each
member (legal under C, but not C++). However, we use memcpy
which is compact, readable, universally compatible, and
compiles down efficient inline single-instructions; therefore
imparting no penalty.
This commit adds host_add* functions (for 16 and 32bit values)
that add a host-formatted value to the implied value at a
memory address.
This commit adds handling for quad-words as well: host_readq,
host_writeq, which we use in the cache, which otherwise suffers
from the same alignment issues.
This way it's more obvious to the reader, what given parameter does
(instead of non-descriptive 'false/true'.
Create alias 'FIXED_SIZE' for 'false' meaning "non-resizable" window. It's a
constexpr bool global variable, so compiler will remind us when it'll
be time to remove it.
This call was useful during development and testing with
output=opengl, but now it's no longer necessary. This call causes
output=texture to go into initialization loop, which falls back to
surface, which we definitely do not want.
'resizable' option turns on resizable window if all the prerequisites
are met (OpenGL and compatible shader). Right now only shader 'sharp' was
tested as compatible, but other built-in shaders might be whitelisted in
the future as well (e.g. 'advmame*' shaders seem like good candidates).
Using incompatible shader does not cause any particular problems for the
users, but it looks really ugly - shader implementations clearly were
implemented only for a fixed window size.
'default' option acts as 'resizable' on Linux and 'original' everywhere
else. It also gives as an option to tweak available windowresolution
settings without causing too much disruption to end users.
It's necessary for usecase when game changes resolution while in game.
We don't want to resize the window in such case, just change minimum
size (SDL might decide to update the window size in the result, if new
resolution is higher than current window size).
This works very well when user is resizing the window and going
in/leaving fullscreen. It does not affect usecase where window size is
being updated by the emulator (e.g. game changing resolution).
Some deployments of GCC won't link ASAN build without explicitly
specifying the library, and report:
"ASan runtime does not come first in initial library list;
you should either link runtime to your application or
manually preload it with LD_PRELOAD"
This commit includes the asan library by default for respective
builds.
Overrides previous, Windows-only hack used for the same purpose.
After initial window position, OS or window manager will take care of
emplacing window - this change takes care only of situation, when there
is NO initial window position.
Additionally, avoid re-setting the window size on every SetSDLWindowMode
call. This prevents a number of issues with the window being set to
incorrect size while switching between fullscreen and window.
Overally this change takes care of number of small issues happening on
macOS, Windows (without need for an ifdef), and Linux with KDE and MATE.
Also makes the behaviour more consistent for Gnome when starting dosbox
via XWayland.
SDL2 introduced a bunch of new events, that old SDL1.2 implementation
did not have. We probably should start using some of them…
It's also extremely helpful for comparing order/number of window events
arriving on different OSes and WMs.
Move the code to a separate function to make it easier to understand and
modify. Remove traces of windowresolution=X% support - this option
causes more harm than benefit.
Add bound checks to prevent user from setting up window size bigger then
configured display allows.
Changing this option dynamically via "config" built-in never worked, it
was always broken. Set it to change'able only on start, otherwise all
broken edge-cases would need to be fixed.
And do small cleanup in surrounding area.
Using normal2x has several undesired side-effects:
- slightly slower performance (I did some perf flamegraphs and turns out
scaler code is a significant bottleneck, even when using normal1x aka
none) - we should limit it as much as possible before addressing the
problem directly.
- it makes default glshader=sharp less precise
- it will negatively affect window resizing code
The 'optinfo' build target asks GCC to print optimizations that
could not be performed to local 'missing.txt' files; these will
appear in each repsective subdirectory having source files.
Both GCC and Clang will now print Verbose vectorization information
during the build process, often describing why vectorization
cannot be performed.
This commit also enables basic instruction and math vectorization
for both the 'release' and 'optinfo' targets. This includes making
use of altivec instructions (available on all powerpc processors),
and at a minimum sse4.2 on all x86_64 processors (circa-2008+ AMD
and Intel CPUs).
Vectorization is also re-enabled for GCC FDO builds, which would
otherwise be disabled when we switch to -O2 optimizations.
These are false-positive findings, but they touch flimsy part of code,
that could very easily break.
In all 4 cases the code looked like this:
uint8_t buf[MEM_PAGE_SIZE]; // old lines 600, 601
…
if (region.bytes > MEM_PAGE_SIZE)
toread = MEM_PAGE_SIZE; // old line 635
else
toread = region.bytes;
// assert toread <= MEM_PAGE_SIZE
if (toread < remain) {
MEM_BlockWrite(…, buf, toread);
} else {
MEM_BlockWrite(…, buf, remain);
MEM_BlockWrite(…, &buf[remain], toread - remain);
// ~~~~~~~~~~~~
// ^
// Coverity flags buffer overrun here
// when: toread == remain == MEM_PAGE_SIZE
// because buf has size MEM_PAGE_SIZE
//
// Sometimes it's MEM_BlockRead, but the problem is the same
}
In such cases, second MEM_BlockWrite is a no-op because
(toread - remain == 0), but Coverity did not reach this point in
analysis (Coverity is right to check the corner case, but didn't know,
that we can assert toread <= MEM_PAGE_SIZE, so the corner case is the
only value triggering this buffer overrun).
Change `if (toread < remain)` to `if (toread <= remain)`, so corner case
will never trigger second (no-op) MEM_BlockRead/Write inside `else`.