1K Intro

From SizeCoding
Revision as of 10:12, 19 November 2024 by Superogue (talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Introduction

This category is dedicated to 1K Intros for Windows / win32.

Windows 1K Intros can be setup in C or Assembler, or a combination of both, with only the few windows calls that are used being linked in. In general writing everything in assembler should give you a little more freedom in pushing/popping your function arguments and register usage, but in the past quality intros have been written in both languages.

Graphics are usualy done using OpenGL / GLSL, but there are 1K intros out there that have used D3D/DX11 instead.

Tools

There are a couple of options for tools, but generally these tools are often used

Minimal intro code

Here is a step by step breakdown of what a minimal intro/framework should contain.

As a general rule of thumb: The less functions you need to use/import, the more space you have for your effect and data.

Setup (Windows)

  • ChangeDisplaySettings(&screenSettings, CDS_FULLSCREEN) - Initialise fullscreen view
  • hwnd = CreateWindow((LPCSTR)0xC018, 0, WS_POPUP | WS_VISIBLE | WS_MAXIMIZE, 0, 0, 0, 0, 0, 0, 0, 0) - Setting up a Maximised Window
  • hdc = GetDC(hwnd) - Get DeviceContext handle
  • ChoosePixelFormat(hdc, &pfd) - Get pixelformat described by pfde
  • SetPixelFormat(hDC, pf, &pfd) - Set pixelformat
  • ShowCursor(NULL) - Hide mousecursor (usually required by compo rules)

Setup (OpenGL)

  • wglGetProcAddress(functionName) - Get function address for an (extended) OpenGL function
  • glContext = wglCreateContext(hdc) - Create OpenGL context
  • wglMakeCurrent(hdc, glContext) - Set OpenGL Context
  • shaderProgam = glCreateShaderProgramv(GL_FRAGMENT_SHADER, 1, &shader) - Create and compile GLSL Shader
  • glUseProgram(shaderProgram) - Set active shader

Update Loop

  • t = GetTickCount() - Optional: Get Current ticks
  • glColor3us(t,0,0) / glColor3i(t,0,0) - Parse time via gl_Color (saves importing uniform functions)
  • glRects(-1,-1,1,1) / glRecti(-1,-1,1,1) - Draw a single quad with current Shader
  • SwapBuffers(hDC) - Swap Buffers
  • PeekMessageA(0, 0, 0, 0, PM_REMOVE) - Optional: Unanswered messages might stack up, but in practice you should be fine in fullscreen
  • GetAsyncKeyState(VK_ESCAPE) - Checks for escape key (required by compo rules)

Closedown

  • ExitProcess(0) - Close down application

Graphics / Effect

As far as I am aware of, most if not all 1K Windows intros use a single-stage shader setup to run everything. This means that everything including timing, shading, progression, etc. needs to happen in a single GLSL/HLSL shader, often based on a single effect, with some changes over time.

So when it comes to writing your 1K Shader/Effect, there are a couple to tricks you can utilise to get a small shader footprint.

  • Make use of the benefits of (Legacy) Desktop GLSL by using integers for smaller notation
  • Make use of the fixed resolution to convert gl_FragCoord to UV -1..1 (this will have your centerY slightly off-centered)
  • Pass time via glColor (no uniforms used)
  • Using global variables to save space

Minimal GLSL example code

float t=gl_Color.x*1e7;
void main(){
vec2 p=gl_FragCoord.xy/960-1;
gl_FragColor=vec4(sin(p.x+t),0,p.y,1);
}

Shader minifier

Whilst a shader minifier can be helpful at times, it will not save you from writing sloppy code or unthoughtful approaches. For most 4k's people can get away with sub-par optimised code and let shader-minifier do the work for you, but for 1k this is just not enough.

Sound

There are a few ways to produce sound/music in Windows 1K Intros.

Win32 Single Note MIDI

Using the built-in MIDI, this requires just 2 calls

  • midiOutOpen(midihandle, 0,0,0,0) - To initialise the MIDI
  • midiOutShortMsg(midihandle, 0x209035C0) - 4 byte Midi message that sets instrument + plays a single note on channel 0

For a full list of Midi commands, check out this webpage: https://computermusicresource.com/MIDI.Commands.html

Win32 MIDI Player

Depending on the sound budget, you can think about including a small MIDI tune instead. Similar to the single note MIDI approach, but playing multiple notes from data on 1 or more different channels.

Bytebeat softsynth

An alternative option is to use a very simple bytebeat softsynth (Check out https://bytebeat.demozoo.org if you are not familiar with the concept). This requires an additional setup of the Win32 WaveOut callback, which can then be played and filled realtime with data.

There are 2 ways to accomplish this, the first one is using these 3 calls:

  • waveOutOpen
  • waveOutPrepareHeader
  • waveOutWrite

Which allows for realtime buffer updates and getting position using waveOutGetPosition

Or by using a 44 byte WAV header and filling the soundbuffer in advance, then using the sndPlaySoundA call to play it as a looping sample at startup.

For a taste of what is possible with small (<256 character) bytebeat formulas, check out one of the Bytebeat Music competitions from the Lovebyte parties, for example https://demozoo.org/parties/4760/#competition_18623

Shader softsynth

With this approach you render the sound from the GLSL shader and copy that back to the soundbuffer. It takes a bit more code to setup, but depending on your usecase and complexity of the sound, the GLSL code might compress a bit better.

Compression

Microsoft Cabinet decompression

These functions allow for MSZIP (deflate) and MSLZ content to be loaded from the binary as payload at the cost of a a few more win32 function imports. While this might be a good solution for 512 byte intros, the tipping point for Crinkler compression at the moment lies at the 800-850 bytes mark give or take.

From cabinet library:

  • CreateDecompressor(0x20000002, 0, &HDecompressor) - Create Decompressor object
  • Decompress(HDecompressor, CompressedPayload, PackOutputSize, 0x11000, PackInputSize,0) - Decompress payload

Crinkler

Crinkler is the most commonly used compression at the moment for many 1K and 4K intros. It maximizes compression by taking over the linking process, setting up the windows header for you and if needed re-arrange the different segments of the code. It has a seperate settings for 1K intro, namely /TINYIMPORT and /TINYHEADER which will better match the 1K usecase.

Crinkler reports

When using the /REPORT:intro_report.html feature, crinkler will generate a report and heatmap for your intro binary. This is helpful to see how well your (shader)code compresses and where you can maybe gain a few more bytes.

Word about the resulting executable

Please note that when using crinkler in TINYHEADER mode, the resulting windows header will most likely trigger virus scanners / Microsoft Defender and mark it as suspicious content, which is a side-effect in recent years. Just exclude your work directory from this scan during development. Compo machines will run without these scanners too, but it is good practice to include a (slightly larger) safe version too in the final archive.

Links

Frameworks

High Quality 1K intros for Windows

1K intros for Windows with sourcecode