This page provides an overview of changes that were introduced in the major version change between Duality v2.x and Duality v3.0. Any further changes will be documented in the release notes of the individual packages, or on the changelog page of the next major version change.
- Upgrade Guide
- All Changes
The following is a high level overview on the general nature of changes in this major version step.
- Rewrote the entire rendering pipeline, allowing massive improvements in efficiency, ease of use and feature coverage while at the same time shifting towards more modern, shader-focused rendering techniques.
- RenderSetup resources allow replacing how Duality renders a frame, providing a simple entry point for pre- or post processing steps, as well as completely customized rendering behavior.
- Self-contained scenes allow using
Sceneresources as isolated simulation spaces independently of the active main scene.
- Focus on performance and a more data-oriented design of both rendering and update cycles. A worst-case rendering benchmark comparing v2 and v3 performance went down from 13 ms to 6 ms per frame, as well as 2000+ (gen0 to gen2) GC collections per minute to about 5 (gen0) collections.
- Refactored core API, replacing many “first iteration” designs with more streamlined ones. The most prominent example might be the deprecation of manual context checks in
ICmpInitializablein favor of a simple
OnDeactivatemethod pair, but many similar improvements were done as well.
For more detailed information, check out the “All Changes” section down below.
Unlike the regular minor updates and patches, a major version step is free to introduce breaking changes to evolve the framework - and there are lots of them in v3! While they do improve API, design and behavior, that also makes it more difficult to update existing projects, and raises the question whether an update is worth the effort.
Should You Upgrade?
If you’re starting a new project, then yes, definitely! The new v3 release brings lots of improvements to Duality, it’s been out there in source code form for a while, and already used in projects. There’s not really a reason to use an old v2 version for a new project.
If you have an existing project, it depends on the amount of content you have. Source code is the easiest part to upgrade, and while some Resources can be ported via Find / Replace on their text file, others will require a manual re-import and re-integration. The more Resources your project has, the more time it will take to port them all to v3 - so this should be the core part of your decision. Large, half way done projects may be better off staying with v2.
How to Upgrade
If you do choose to upgrade an existing project, here’s a rough guideline on the required steps:
- If your project uses third party packages and plugins, make sure they have a v3 compatible version available. Otherwise, you will have to find a v3 compatible alternative, or you won’t be able to upgrade.
- Make a full backup of your project.
- Run the editor. If you have a scene open, create a new, empty scene, so we don’t run into problems when restarting after the update.
- Switch to XML serialization in the top right editor menu. Since this is the default serialization method, you only need to apply this step if you consciously switched to binary before.
- Update all packages to the latest v3 version. You can use the Package Manager UI in the editor to do this. Apply the installed updates and let the editor restart itself.
- Fix and compile your game plugin and all other custom plugins with the new v3 Duality dependencies. If you are unsure why something that worked before no longer compiles, take a look at the full changelog and especially the new v3 sample source code to find out how you need to adapt your code to v3.
- Fix your game resources. With the editor
Log Viewopen and clear of errors, select resources in the
Project Viewto check if they load correctly. If they don’t, errors will be logged.
- The most common error will be unresolved type names, as some Duality types were renamed, or moved to a different namespace. Some of them can be fixed via Find / Replace in the resource file with a text editor, others have no equivalent in v3 and should be read as a note to manually check on the affected resources and objects.
- The second most common error will be structural changes in Duality resources. In those cases, you will have to re-import, re-configure or re-create the resources manually in the editor. Especially
Fontresources are affected, and
ShaderProgramresources no longer exist at all, as they were merged into
- Check Scene and Prefab content for errors, such as missing animations, a wrong camera setup or slightly off depth sorting. Fix those errors manually.
- Re-Serialize all game resources using the top right editor menu to ensure all game data is now up-to-date.
After you completed all of the above steps, you project should now be fully ported to v3.
The following is a low level overview on all changes that were introduced between Duality v2.x and Duality v3.0, with the exception of some trivial changes such as renamed private fields, minor implementation improvements or similar.
- Extracted smooth animation shaders, techniques and code from the core and moved it into the new
- Integrated the previously external
FarseerDualityphysics library into the main project as
DualityPhysicsto ease future optimizations.
- Added a new
Benchmarkssample project that provides a controlled environment for testing the impact and effects of various rendering techniques and optimizations.
- Added a new
CustomRenderingSetupsample project that demonstrates how to use and extend the new
RenderSetupResource type to define how Duality renders a frame.
- Updated from OpenGL 2.1 to OpenGL 3.0 core profile, and removed all legacy OpenGL code.
- Updated the internal
OpenTKdependency and fixed a context creation glitch that would create a legacy OpenGL context by accident and prevent tools like RenderDoc from interacting with the Duality rendering output.
IDrawDevice, allowing renderers to use temporary materials without any per-frame allocations.
- Added new
CanvasStateAPI to promote efficient material renting and re-use with added convenience.
Canvas, which internally re-uses old vertex data to avoid allocations. The former
RequestVertexArrayfunctionality is now available through
Canvasnow requires enclosing all drawing commands to a specific device in a
Endblock, allowing to share and re-use the same
Canvasobject between multiple renderers and across multiple drawcalls.
- Optimized push / pop of
Canvasstates to re-use the same instances internally, avoiding allocations.
RenderOptionsclass now allows to specify whether depth writing and depth testing are enabled, as well as a global shader parameter collection.
- Added a property to explicitly specify whether a
RenderTargethas a depth buffer.
- The editor graphics context can now be created with a specific antialiasing quality that is independent from game settings.
- Moved all visual picking functionality from
PickingRenderSetup, leaving only a thin API wrapper in place for convenience reasons.
- Added new
Materialconstructors to ease specifying color-only materials without a texture.
- Introduced the new
RenderSetupResource that defines how Duality renders a frame and provides a convenient way to take control of this, or replace the existing render pipeline with a custom one. Superseded
RenderStepitems in a
RenderSetupand greatly simplified
Cameraimplementation and API.
- Conceptually separated viewport and rendered image size, making it trivial to render the same view in lower or higher resolution, or adjust the target area it will be rendered to.
- Added a
ForcedRenderSizeoption to appdata settings, allowing games to render with the same view regardless of window or screen size. Differences in aspect ratio can be accounted for either by letterboxing or by displaying additional content. User input is automatically re-mapped from actual window coordinates to forced view coordinates.
ICmpRendererto no longer expose logic-related methods for determining visibility, but instead provide a single
GetCullingInfomethod. The retrieved plain-old-data struct contains all culling relevant information, which is now cached and batch-processed by the scenes visibility strategy implementation.
- Separated culling updates from culling queries, improving performance by avoiding redundant work for rendering multiple viewports or passes.
- All vertex transformation now takes place on the GPU, and all renderers now specify vertices in world space, rather than pre-transformed intermediate view space. The
IDrawDeviceinterface no longer has a
PreprocessCoordsmethod, and there is no need for it anymore either.
DrawDeviceto streamline vertex processing and dynamic batching. Vertex storage and rendering are now treated separately and using specialized data structures, allowing more efficient batching, without the overhead of any per-frame allocations. Introduced the new
VertexBatchStorehelper classes for both internal usage and custom dynamic batching implementations.
DrawBatchclass has been rewritten, is now public API, and can be managed and submitted manually when needed.
IDrawBatchin favor of direct
DrawBatchusage, avoiding virtualization and easing compiler optimization.
- Introduced the new
VertexBufferclass that represents a GPU side vertex / index data combo and allows to manually setup, load and update them in regular core and game code as needed. For vertex-heavy and potentially static objects such as tilemaps, this is more efficient alternative to re-submitting vertex data every frame.
RenderMatrixenums, as they were superseded by explicit view / projection matrix handling. Introduced
ProjectionModeenum to specify what kind of projection matrix is used by a drawing device.
- Updated default GLSL version from 1.2 to 1.3, and adjusted all builtin and sample shaders accordingly.
- All shader parameters are now specified explicitly in shader code. This includes all uniforms and vertex attributes, as there no longer are any builtin variables.
#pragma duality *support to GLSL shaders, allowing to specify variable metadata such as descriptions, minmax or type information.
- Replaced hardcoded
VertexElementRoleassignments with a name-based
VertexElement-to-attribute lookup. This makes it far easier to specify and use custom vertex elements in both C# and GLSL code.
ShaderParameterCollectionclass for efficiently storing and comparing shader parameters.
Materialdata and API to make efficient use of
ShaderParameterCollection. There are no longer special cases for main color or texture, as they are now regular shader parameters as any other.
DrawTechniqueresources, which now directly specify a vertex and fragment shader.
- There is now an implicit global shader include that provides a base interface for common, Duality-specific functionality, such as
- There are now rendering operation wide / “global” shader variables, which are both available via public API and used for builtin shader variables. Global shader variables are set per-shader, not per-batch.
- Shader compile and link messages are now translated into one Duality log message each, making it easier to debug issues with shader code.
- Added the new
ShaderSourceBuilderutility class to pre-compile and merge multiple shader source files into one.
- The internal physics world is now an explicitly managed part of a scene, and accessible via the
- Moved all physics queries from static
- Removed obsolete
SliderJointdue to unstable behavior.
- Unified most functionality of
PolygonShapeInfoin their new common
GameObjects and Components
ICmpInitializableto only require
OnDeactivatemethods, without any init context checks. The remaining init context equivalents have been extracted into
ICmpAttachmentListenerinterfaces that provide their own, specialized API.
ShutdownContextno longer exist.
GameObjectAPI and optimized its implementation, especially with regard to component retrieval.
- Renamed the
Scene, and introduced a shortcut
- Introduced the
ICmpSpriteRendererinterface that defines shared API for all renderers displaying a(n indexed) sprite.
SpriteAnimator, which contains no rendering functionality, but can animate any
- Added sprite / atlas indexing support to
- Unified all renderer depth offset properties to use a Z axis float offset, rather than some abstract integer value.
Transformcomponent no longer tracks or serializes velocity values, and it no longer implements
ICmpUpdatable, boosting performance and reducing memory usage significantly. Add a
VelocityTrackercomponent to re-enable tracking on specific objects.
TransformAPI to be more consistent and intuitive.
DeriveAngle(false) support from
- Removed obsolete API from
- Automatic slicing of images into animation frames has been moved completely out of
Pixmapand into asset import and the new Pixmap Slicer UI.
- Reworked and partially re-implemented
Fontresources to fully embrace bitmap font features, including kerning pairs and extended glyph geometry information. Removed all dependencies to TTF rendering, which was moved into asset import. A lot of specialized code was moved into separate (public) classes, such as
- Scenes can now be de/activated, updated and rendered explicitly, independent from which scene is
Current. Reduced overall reliance on the static
Scene.Currentproperty and moved all rendering, physics and simulation code towards requiring an explicit
- Added a new
ContentRef<T>constructor that accepts a path string only.
EnsureLoadedto reflect its purpose more closely.
ContentProviderAPI from unused methods and moved some of them to the
- Added the new
DefaultContenthelper class that now houses all related init code.
Textureresource API for convenience, and to match other changes in the project.
- Changed API of
IGraphicsBackendto support data buffer arguments of type
IntPtr, and added generic extension methods to allow specifying array data of any kind. This decouples the declared data type from the actual data and allows to re-interpret plain-old-data struct or primitive arrays as needed.
INativeGraphicsBufferas a generic graphics data storage interface, allowing the core to explicitly manage vertex and index data in the same way that other graphics resources are managed.
IAssemblyLoaderand adjusted its default implementation accordingly.
WindowOptionsdata and API to match other changes in the project.
- Fixed a bug in the default OpenTK backend that would cause OpenTK to be initialized even when a different backend was chosen.
TransformPropertyEditorin the inspector now defaults to show end edit local space values, with an option to switch to world space values.
CamViewnow allows to explicitly specify the game sandbox rendering resolution and re-maps user input to match.
CamViewnow allows to explicitly specify the
RenderSetupto use when rendering regular editing views.
CamViewgizmo rendering to use
Canvas.ColorTintinstead of different materials to improve batching.
CamViewmouse picking to use half resolution scene renderings.
FileEventManagerclass has been re-implemented, tested and optimized with various previously unknown bugs fixed in the process. Its API was redesigned to emit events in batch processing mode, rather than one event at a time.
ProjectViewnow properly handles error cases where moving a file or folder fails because of missing write access.
SceneViewobject insertion to reduce delay when creating a lot of objects in bulk.
- File change events on
Resourcefiles are now automatically translated into
PropertyChangedevents for their loaded instances.
- Text descriptions for
EditorAction<T>are no longer part of their API, but retrieved from XML documentation.
- Removed buggy JIT debugging capability detection and instead enabling the editor debug button in any case.
- Removed obsolete
- Cleaned up logging API and split functionality into
- All logging API is now thread-safe.
- Added support for custom log channels via generic class definition, i.e.
Logs.Get<YourLog>().Write(...), including editor integration.
- Moved log indentation state from the
Logchannel to the
ILogOutputwriter, so a writer subscribed to multiple different channels will still produce reasonable results when they differ in indentation.
LogEntrystruct to be plain-old-data (with the exception of the message string) and easily serializable when needed.
- Log timestamps are now in UTC.
- Introduced a specialized
EditorLogOutputthat supersedes the previous
- Reworked visual logging API to mirror logging API, separating
VisualLogs, as well as introducing custom log channel support via generic class definitions.
- Serializing a builtin .NET collection type will now omit the
_versionfield, which is irrelevant for persistence, but can cause unnecessary merge conflicts.
- Plain old data structs such as
Vector2now omit their contents during XML serialization when they equal their default value.
CloneBehavior.WeakReference, as it was superseded by explicit cloning and its related API additions. Internal cloning logic and public API related to weak references was simplified. This also fixes a bug where flagging an object reference as weak could affect the behavior of different references to the same objects.
IsCopyByAssignmentto reflect its usage and behavior more closely.
FileEventQueueutility types to the core, making it easier to keep track of a sequence of file system changes.
- Updated input API for convenience, and to match other changes in the project.
TimeAPI to favor
TimeMult, increased convenience, and to match other changes in the project.
- Added list extension methods for zero-allocation stable sorting.
DualityAppDatato match other changes in the project, and removed obsolete properties.
- The static
AudioUnitconversion classes now use static readonly fields instead of constants to allow future changes.
PinnedArrayHandlehelper struct to provide an easy way to communicate memory blocks to backend API.
RawList<T>indexer to be inlineable more easily.
RawList<T>clear and remove operations to not bother discarding internal data if it doesn’t contain any object references.
RawListPool<T>class to reduce per-frame allocations during heavy use of
- Optimized reflection helper API to use static
<T>lookups where possible.
- Removed obsolete