Plan for per-vertex transformation

Jump to: navigation, search
This article or section is not complete. If you wish, you may finish it, and remove this tag when you are done.

This plan is a transition from per-pixel image transformation to per-vertex, which could allow graphics backends to accelerate the rasterization in hardware. Per-vertex transformation implies that a slice can be transformed in any way (scaled, rotated, translated, sheared, etc.) in 2d relative to its parent. All slices are assumed to be quads. All transforms are performed in vector-matrix algebra, and only the transformed vertices are sent to the backend for rendering.

The fields that are affected by this plan include:

  • Slices
  • Frames
  • Plotscripting

The benefits of this plan include efficiency gains, continued back-compatibility, many visual effects that were difficult to perform otherwise, and greater flexibility for the future.


This section analyzes and discusses changes to slices that would allow per-vertex transformation to be implemented. It is possible that nothing needs to be changed.


This section analyzes and discusses changes to Frames. Frames are currently defined as:

type Frame
	w as integer
	h as integer
	pitch as integer     'pixel (x,y) is at .image[.x + .pitch * .y]; mask and image pitch are the same!
	image as ubyte ptr
	mask as ubyte ptr
	refcount as integer  'see frame_unload in particular for documentation
	arraylen as integer  'how many frames were contiguously allocated in this frame array
	base as Frame ptr    'the Frame which actually owns this memory
	cacheentry as SpriteCacheEntryFwd ptr
	cached:1 as integer  '(not set for views onto cached sprites)
	arrayelem:1 as integer  'not the first frame in a frame array
	isview:1 as integer

	'used only by frames in a SpriteSet, for now
	offset as XYPair
	sprset as SpriteSetFwd ptr  'if not NULL, this Frame array is part of a SpriteSet which
                                    'will need to be freed at the same time
end type

The intent of a Frame is determined by whether it's a "view" or not. This confusion, however, was discussed and the new "Surface" and "Frame" structures are replacing the single entity, something like:

type Surface
end type

type Frame
end type

From this point, the word "Frame" will refer to the view, and "Surface" will refer to the surface that is being referenced.

Everything rendered is a paletted surface. The switch to backend rasterization of the pixels has created a problem with 16-color palettes for hardware-accelerated graphics libraries. The issue is the 16-color palette is a lookup-table into another 256-color palette. A 256-color palette into another 256-color palette is another variation of this same problem.

A simple way to deal with it is to render all "palettes-into-palettes" surfaces onto a "palette-into-master" surface which will then be rendered onto a final full-color backbuffer. The software rasterizer being developed will account for that. Backends that want to accelerate in hardware can mimic the software rasterizer (though backend hardware acceleration is no longer a goal of this plan).


This section analyzes possible plotscripts that can expose new functionality due to the changes.

No longer applicable[edit]

The following is no longer applicable, but is kept for reference until other plans are created.

This section details the new backend interfaces required by the plan for per-vertex transformation. The backend will now extend more window control, window notifications, and surface drawing and manipulation. Since input is inherently tied to the graphics backends, it is logical to conclude the input interfaces will also be revisited. However, that is outside of the scope of this plan, and will thus be unchanged.

Window and Notifications[edit]

Windows, as the engine is concerned, can be described as:

  • Title
  • System buttons (close, restore, minimize...)
  • Client area and surface
  • Message procedure

Though it is possible to develop multiple windows, that is not the goal of this proposal, and it will not be implemented here.

The user may attempt to resize the window, close the window, etc. The engine could choose to process those messages, and either accept or deny changes. The notifications the engine would be concerned with are:

  • close
  • minimize
  • switching focus
  • resize

Other user input could also be received and processed, including characters, key presses, key releases, button presses, button releases, and mouse movement and position. But that is beyond the scope of this plan, so it will not be considered in this proposal.

Here are a few proposed interfaces specific to windows:

int gfx_windowSetTitle( hWindow, szTitle )
int gfx_windowSetClientArea( hWindow, width, height )
int gfx_windowSetNotifyCallback( hWindow, notifyCallback, flags )

void gfx_windowGetClientArea( hWindow, pWidth, pHeight )
void gfx_windowGetSwapChain( hWindow, pSurfaceOut ) /*swap chains discussed later*/

Notification callbacks would be of the form:

long cbFunction( hWindow, msg, param )

Surfaces and Drawing[edit]

Surfaces, as the engine is concerned, can be described as:

  • width
  • height
  • format
  • usage

The engine has created most surfaces of the same format, being 256-color paletted surfaces. With the move to graphics backends, it will be possible to expose other formats, such as 32-bit argb. The proposed formats include:

  • 32bit argb (a8r8g8b8)
  • 16bit argb (a1r5g5b5)
  • 8bit paletted

Surfaces can be accessed for writing, updated, copied to system memory, drawn onto with rectangle blits, and rendered onto with transformed quads. There are mainly 3 usages of surfaces:

  • Regular
  • Render targets
  • Swap chains

Regular surfaces can be written to, updated, copied into system memory, and drawn onto with rectangle blits. Render targets can be drawn onto with rectangle blits and rendered onto with transformed quads. Swap chains are render targets, except they cannot be used as input to other render targets because they represent the backbuffer of the client window surface area.

In the engine, Frames are rectangles identifying parts of the surface. They are important to the backend in identifying "texture coordinates" for mapping a surface onto a quad during rasterization. A Frame is defined as having a left, top, right, and bottom.

Paletted surfaces are handled with a few more parameters. A palette can be described as being 256 entries long, each entry being a dword argb color. In the engine, there is a master palette that is the entire 256 entries, and there are 16-color palettes, which are lookup tables into a master palette.

Where palettes are concerned, including both the 16-color and 256-color palettes, there will need to be certain interfaces to manipulate them. Proposed:

int gfx_paletteCreate( pInitialEntries, numInitialEntries, pPaletteOut )
int gfx_paletteDestroy( pPaletteIn )
int gfx_paletteUpdate( pSrcEntries, numSrcEntries, pPaletteDest )

Some interfaces that expose Surface creation, accessing, etc., include:

int gfx_surfaceCreate( width, height, usage, format, pInitialData, pSurfaceOut )
int gfx_surfaceDestroy( pSurfaceIn )
int gfx_surfaceUpdate( pRectSrc, pRawDataSrc, pRectDest, pSurfaceDest )
int gfx_surfaceFill( color, pRectDest, pSurfaceDest )
int gfx_surfaceStretch( pRectSrc, pSurfaceSrc, pRectDest, pSurfaceDest )
int gfx_surfaceGetCopy( pRectSrc, pSurfaceSrc, pRectDest, pRawDataDest )

Some graphics libraries rely on obtaining a valid context from the OS by making a call every frame. Also the render targets have to be set, and the transformed vertices to be drawn. These interfaces address this need:

int gfx_renderBegin()
int gfx_renderEnd()
int gfx_renderPresent( pSurface )
int gfx_renderSetTarget( pSurface )
int gfx_renderGetTarget( pSurfaceOut )
int gfx_renderDraw( pRectSrc, pSurfaceSrc, pCornersVec3, argbModifier, pPalette )

gfx_renderPresent() is special in that only swap chains can be used as input.