mirror of
https://github.com/ZDoom/gzdoom-gles.git
synced 2024-12-11 13:01:19 +00:00
040cb17370
# Conflicts: # CMakeLists.txt # libraries/glslang/OGLCompilersDLL/CMakeLists.txt # libraries/glslang/OGLCompilersDLL/InitializeDll.cpp # libraries/glslang/OGLCompilersDLL/InitializeDll.h # libraries/glslang/glslang/CMakeLists.txt # libraries/glslang/glslang/GenericCodeGen/CodeGen.cpp # libraries/glslang/glslang/GenericCodeGen/Link.cpp # libraries/glslang/glslang/Include/BaseTypes.h # libraries/glslang/glslang/Include/Common.h # libraries/glslang/glslang/Include/ConstantUnion.h # libraries/glslang/glslang/Include/InfoSink.h # libraries/glslang/glslang/Include/InitializeGlobals.h # libraries/glslang/glslang/Include/PoolAlloc.h # libraries/glslang/glslang/Include/ResourceLimits.h # libraries/glslang/glslang/Include/ShHandle.h # libraries/glslang/glslang/Include/Types.h # libraries/glslang/glslang/Include/arrays.h # libraries/glslang/glslang/Include/intermediate.h # libraries/glslang/glslang/Include/revision.h # libraries/glslang/glslang/Include/revision.template # libraries/glslang/glslang/MachineIndependent/Constant.cpp # libraries/glslang/glslang/MachineIndependent/InfoSink.cpp # libraries/glslang/glslang/MachineIndependent/Initialize.cpp # libraries/glslang/glslang/MachineIndependent/Initialize.h # libraries/glslang/glslang/MachineIndependent/IntermTraverse.cpp # libraries/glslang/glslang/MachineIndependent/Intermediate.cpp # libraries/glslang/glslang/MachineIndependent/LiveTraverser.h # libraries/glslang/glslang/MachineIndependent/ParseContextBase.cpp # libraries/glslang/glslang/MachineIndependent/ParseHelper.cpp # libraries/glslang/glslang/MachineIndependent/ParseHelper.h # libraries/glslang/glslang/MachineIndependent/PoolAlloc.cpp # libraries/glslang/glslang/MachineIndependent/RemoveTree.cpp # libraries/glslang/glslang/MachineIndependent/RemoveTree.h # libraries/glslang/glslang/MachineIndependent/Scan.cpp # libraries/glslang/glslang/MachineIndependent/Scan.h # libraries/glslang/glslang/MachineIndependent/ScanContext.h # libraries/glslang/glslang/MachineIndependent/ShaderLang.cpp # libraries/glslang/glslang/MachineIndependent/SymbolTable.cpp # libraries/glslang/glslang/MachineIndependent/SymbolTable.h # libraries/glslang/glslang/MachineIndependent/Versions.cpp # libraries/glslang/glslang/MachineIndependent/Versions.h # libraries/glslang/glslang/MachineIndependent/attribute.cpp # libraries/glslang/glslang/MachineIndependent/attribute.h # libraries/glslang/glslang/MachineIndependent/gl_types.h # libraries/glslang/glslang/MachineIndependent/glslang.y # libraries/glslang/glslang/MachineIndependent/glslang_tab.cpp # libraries/glslang/glslang/MachineIndependent/glslang_tab.cpp.h # libraries/glslang/glslang/MachineIndependent/intermOut.cpp # libraries/glslang/glslang/MachineIndependent/iomapper.cpp # libraries/glslang/glslang/MachineIndependent/iomapper.h # libraries/glslang/glslang/MachineIndependent/limits.cpp # libraries/glslang/glslang/MachineIndependent/linkValidate.cpp # libraries/glslang/glslang/MachineIndependent/localintermediate.h # libraries/glslang/glslang/MachineIndependent/parseConst.cpp # libraries/glslang/glslang/MachineIndependent/parseVersions.h # libraries/glslang/glslang/MachineIndependent/pch.cpp # libraries/glslang/glslang/MachineIndependent/pch.h # libraries/glslang/glslang/MachineIndependent/preprocessor/Pp.cpp # libraries/glslang/glslang/MachineIndependent/preprocessor/PpAtom.cpp # libraries/glslang/glslang/MachineIndependent/preprocessor/PpContext.cpp # libraries/glslang/glslang/MachineIndependent/preprocessor/PpContext.h # libraries/glslang/glslang/MachineIndependent/preprocessor/PpScanner.cpp # libraries/glslang/glslang/MachineIndependent/preprocessor/PpTokens.cpp # libraries/glslang/glslang/MachineIndependent/preprocessor/PpTokens.h # libraries/glslang/glslang/MachineIndependent/propagateNoContraction.cpp # libraries/glslang/glslang/MachineIndependent/propagateNoContraction.h # libraries/glslang/glslang/MachineIndependent/reflection.cpp # libraries/glslang/glslang/MachineIndependent/reflection.h # libraries/glslang/glslang/OSDependent/Unix/CMakeLists.txt # libraries/glslang/glslang/OSDependent/Unix/ossource.cpp # libraries/glslang/glslang/OSDependent/Windows/CMakeLists.txt # libraries/glslang/glslang/OSDependent/Windows/main.cpp # libraries/glslang/glslang/OSDependent/Windows/ossource.cpp # libraries/glslang/glslang/OSDependent/osinclude.h # libraries/glslang/glslang/Public/ShaderLang.h # libraries/glslang/glslang/updateGrammar # libraries/glslang/spirv/CMakeLists.txt # libraries/glslang/spirv/GLSL.ext.AMD.h # libraries/glslang/spirv/GLSL.ext.EXT.h # libraries/glslang/spirv/GLSL.ext.KHR.h # libraries/glslang/spirv/GLSL.ext.NV.h # libraries/glslang/spirv/GLSL.std.450.h # libraries/glslang/spirv/GlslangToSpv.cpp # libraries/glslang/spirv/GlslangToSpv.h # libraries/glslang/spirv/InReadableOrder.cpp # libraries/glslang/spirv/Logger.cpp # libraries/glslang/spirv/Logger.h # libraries/glslang/spirv/SPVRemapper.cpp # libraries/glslang/spirv/SPVRemapper.h # libraries/glslang/spirv/SpvBuilder.cpp # libraries/glslang/spirv/SpvBuilder.h # libraries/glslang/spirv/SpvPostProcess.cpp # libraries/glslang/spirv/SpvTools.cpp # libraries/glslang/spirv/SpvTools.h # libraries/glslang/spirv/bitutils.h # libraries/glslang/spirv/disassemble.cpp # libraries/glslang/spirv/disassemble.h # libraries/glslang/spirv/doc.cpp # libraries/glslang/spirv/doc.h # libraries/glslang/spirv/hex_float.h # libraries/glslang/spirv/spirv.hpp # libraries/glslang/spirv/spvIR.h # src/CMakeLists.txt
194 lines
9 KiB
Text
194 lines
9 KiB
Text
Game_Music_Emu 0.6.0 Design
|
|
---------------------------
|
|
This might be slightly out-of-date at times, but will be a big help in
|
|
understanding the library implementation.
|
|
|
|
|
|
Architecture
|
|
------------
|
|
The library is essentially a bunch of independent game music file
|
|
emulators unified with a common interface.
|
|
|
|
Gme_File and Music_Emu provide a common interface to the emulators. The
|
|
virtual functions are protected rather than public to allow pre- and
|
|
post-processing of arguments and data in one place. This allows the
|
|
emulator classes to assume that everything is set up properly when
|
|
starting a track and playing samples.
|
|
|
|
All file input is done with the Data_Reader interface. Many derived
|
|
classes are present, for the usual disk-based file and block of memory,
|
|
to specialized adaptors for things like reading a subset of data or
|
|
combining a block of memory with a Data_Reader to the remaining data.
|
|
This makes the library much more flexible with regard to the source of
|
|
game music file data. I still added a specialized load_mem() function to
|
|
have the emulator keep a pointer to data already read in memory, for
|
|
those formats whose files can be absolutely huge (GYM, some VGMs). This
|
|
is important if for some reason the caller must load the data ahead of
|
|
time, but doesn't want the emulator needlessly making a copy.
|
|
|
|
Since silence checking and fading are relatively complex, they are kept
|
|
separate from basic file loading and track information, which are
|
|
handled in the base class Gme_File. My original intent was to use
|
|
Gme_File as the common base class for full emulators and track
|
|
information-only readers, but implementing the C interface was much
|
|
simpler if both derived from Music_Emu. User C++ code can still benefit
|
|
from static checking by using Gme_File where only track information will
|
|
be accessed.
|
|
|
|
Each emulator generally has three components: main emulator, CPU
|
|
emulator, and sound chip emulator(s). Each component has minimal
|
|
coupling, so use in a full emulator or stand alone is fairly easy. This
|
|
modularity really helps reduce complexity. Blip_Buffer helps a lot with
|
|
simplifying the APU interfaces and implementation.
|
|
|
|
The "classic" emulators derive from Classic_Emu, which handles
|
|
Blip_Buffer filling and multiple channels. It uses Multi_Buffer for
|
|
output, allowing you to derive a custom buffer that could output each
|
|
voice to a separate sound channel and do different processing on each.
|
|
At some point I'm going to implement a better Effects_Buffer that allows
|
|
individual control of every channel.
|
|
|
|
In implementing the C interface, I wanted a way to specify an emulator
|
|
type that didn't require linking in all the emulators. For each emulator
|
|
type there is a global object with pointers to functions to create the
|
|
emulator or a track information reader. The emulator type is thus a
|
|
pointer to this, which conveniently allows for a NULL value. The user
|
|
referencing this emulator type object is what ultimately links the
|
|
emulator in (unless new Foo_Emu is used in C++, of course). This type
|
|
also serves as a useful substitute for RTTI on older C++ compilers.
|
|
|
|
Addendum: I have since added gme_type_list(), which causes all listed
|
|
emulators to be linked in. To avoid this, I make the list itself
|
|
editable in blargg_config.h. Having a built-in list allows
|
|
gme_load_file() to take a path and give back an emulator with the file
|
|
loaded, which is extremely useful for new users.
|
|
|
|
|
|
Interface conventions
|
|
----------------------
|
|
If a function retains a pointer to or replaces the value of an object
|
|
passed, it takes a pointer so that it will be clear in the caller's
|
|
source code that care is required.
|
|
|
|
Multi-word names have an underscore '_' separator between individual
|
|
words.
|
|
|
|
Functions are named with lowercase words. Functions which perform an
|
|
action with side-effects are named with a verb phrase (i.e. load, move,
|
|
run). Functions which return the value of a piece of state are named
|
|
using a noun phrase (i.e. loaded, moved, running).
|
|
|
|
Classes are named with capitalized words. Only the first letter of an
|
|
acronym is capitalized. Class names are nouns, sometimes suggestive of
|
|
what they do (i.e. File_Scanner).
|
|
|
|
Structure, enumeration, and typedefs to these and built-in types are
|
|
named using lowercase words with a _t suffix.
|
|
|
|
Macros are named with all-uppercase words.
|
|
|
|
Internal names which can't be hidden due to technical reasons have an
|
|
underscore '_' suffix.
|
|
|
|
|
|
Managing Complexity
|
|
-------------------
|
|
Complexity has been a factor in most library decisions. Many features
|
|
have been passed by due to the complexity they would add. Once
|
|
complexity goes past a certain level, it mentally grasping the library
|
|
in its entirety, at which point more defects will occur and be hard to
|
|
find.
|
|
|
|
I chose 16-bit signed samples because it seems to be the most common
|
|
format. Supporting multiple formats would add too much complexity to be
|
|
worth it. Other formats can be obtained via conversion.
|
|
|
|
I've kept interfaces fairly lean, leaving many possible features
|
|
untapped but easy to add if necessary. For example the classic emulators
|
|
could have volume and frequency equalization adjusted separately for
|
|
each channel, since they each have an associated Blip_Synth.
|
|
|
|
Source files of 400 lines or less seem to be the best size to limit
|
|
complexity. In a few cases there is no reasonable way to split longer
|
|
files, or there is benefit from having the source together in one file.
|
|
|
|
|
|
Preventing Bugs
|
|
---------------
|
|
I've done many things to reduce the opportunity for defects. A general
|
|
principle is to write code so that defects will be as visible as
|
|
possible. I've used several techniques to achieve this.
|
|
|
|
I put assertions at key points where defects seem likely or where
|
|
corruption due to a defect is likely to be visible. I've also put
|
|
assertions where violations of the interface are likely. In emulators
|
|
where I am unsure of exact hardware operation in a particular case, I
|
|
output a debug-only message noting that this has occurred; many times I
|
|
haven't implemented a hardware feature because nothing uses it. I've
|
|
made code brittle where there is no clear reason flexibility; code
|
|
written to handle every possibility sacrifices quality and reliability
|
|
to handle vaguely defined situations.
|
|
|
|
|
|
Flexibility through indirection
|
|
-------------------------------
|
|
I've tried to allow the most flexibility of modules by using indirection
|
|
to allow extension by the user. This keeps each module simpler and more
|
|
focused on its unique task.
|
|
|
|
The classic emulators use Multi_Buffer, which potentially allows a
|
|
separate Blip_Buffer for each channel. This keeps emulators free of
|
|
typical code to allow output in mono, stereo, panning, etc.
|
|
|
|
All emulators use a reader object to access file data, allowing it to be
|
|
stored in a regular file, compressed archive, memory, or generated
|
|
on-the-fly. Again, the library can be kept free of the particulars of
|
|
file access and changes required to support new formats.
|
|
|
|
|
|
Emulators in general
|
|
--------------------
|
|
When I wrote the first NES sound emulator, I stored most of the state in
|
|
an emulator-specific format, with significant redundancy. In the
|
|
register write function I decoded everything into named variables. I
|
|
became tired of the verbosity and wanted to more closely model the
|
|
hardware, so I moved to a style of storing the last written value to
|
|
each register, along with as little other state as possible, mostly the
|
|
internal hardware registers. While this involves slightly more
|
|
recalculation, in most cases the emulation code is of comparable size.
|
|
It also makes state save/restore (for use in a full emulator) much
|
|
simpler. Finally, it makes debugging easier since the hardware registers
|
|
used in emulation are obvious.
|
|
|
|
|
|
CPU Cores
|
|
---------
|
|
I've spent lots of time coming up with techniques to optimize the CPU
|
|
cores. Some of the most important: execute multiple instructions during
|
|
an emulation call, keep state in local variables to allow register
|
|
assignment, optimize state representation for most common instructions,
|
|
defer status flag calculation until actually needed, read program code
|
|
directly without a call to the memory read function, always pre-fetch
|
|
the operand byte before decoding instruction, and emulate instructions
|
|
using common blocks of code.
|
|
|
|
I've successfully used Nes_Cpu in a fairly complete NES emulator, and
|
|
I'd like to make all the CPU emulators suitable for use in emulators. It
|
|
seems a waste for them to be used only for the small amount of emulation
|
|
necessary for game music files.
|
|
|
|
I debugged the CPU cores by writing a test shell that ran them in
|
|
parallel with other CPU cores and compared all memory accesses and
|
|
processor states at each step. This provided good value at little cost.
|
|
|
|
The CPU mapping page size is adjustable to allow the best tradeoff
|
|
between memory/cache usage and handler granularity. The interface allows
|
|
code to be somewhat independent of the page size.
|
|
|
|
I optimize program memory accesses to direct reads rather than calls to
|
|
the memory read function. My assumption is that it would be difficult to
|
|
get useful code out of hardware I/O addresses, so no software will
|
|
intentionally execute out of I/O space. Since the page size can be
|
|
changed easily, most program memory mapping schemes can be accommodated.
|
|
This greatly reduces memory access function calls.
|
|
|