All public snippets from the HMN GitLab

This commit is contained in:
Ben Visness 2022-01-09 20:47:20 -06:00
commit e29ec6c1ca
14 changed files with 55270 additions and 0 deletions

View File

@ -0,0 +1,63 @@
Useful FFMPEG commands:
Turn video into images, 1 image per 5 seconds:
ffmpeg -i Day_11_part_1.mp4 -vf fps=1/5 -start_number 0 %06d.png
Turn images into a slideshow:
ffmpeg -framerate 30 -i %06d.png -c:v libx264 -r 30 -pix_fmt yuv420p Timelapse.mp4
Turn 1 image into a 3 second video:
ffmpeg -framerate 1/3 -i 6.png -c:v libx264 -r 30 -pix_fmt yuv420p 6.mp4
Extract audio
ffmpeg -i Day07c.mp4 -c:a copy -vn audio.aac
Extract video:
ffmpeg -i Day07c.mp4 -c:v copy -an video.mp4
Mux video and audio back together:
ffmpeg -i Timelapse.aac -i Timelapse.mp4 -c:v copy -c:a copy -bsf:a aac_adtstoasc TimelapseNew.mp4
Mux like before, but also stop encoding when the shortest file runs out
ffmpeg -framerate 60 -i %06d.png -c:v libx264 -r 60 -pix_fmt yuv420p -i audiofile.mp3 -c:a copy -filter_complex " [1:0] apad " -shortest vidya.mp4
Concat simple:
ffmpeg -f concat -i a.txt Timelapse.aac
Concat advanced:
ffmpeg -i input1.mp4 -c copy -bsf:v h264_mp4toannexb -f mpegts intermediate1.ts
ffmpeg -i input2.mp4 -c copy -bsf:v h264_mp4toannexb -f mpegts intermediate2.ts
ffmpeg -i "concat:intermediate1.ts|intermediate2.ts" -c copy -bsf:a aac_adtstoasc output.mp4
Copy a 58m part of a video, starting 2h33m20s in to another video:
ffmpeg -ss 02:33:20 -i Day07c.mp4 -t 00:58:00 -c:a copy -c:v copy -bsf:a aac_adtstoasc 3.mp4
# Quality settings
echo Default quality (-crf 23) - 2041kb/s avg
ffmpeg -framerate 60 -i "frame_%%09d.png" -c:v libx264 -r 60 -pix_fmt yuv420p quality_23_default.mp4
echo Lossless - 5444kb/s avg
ffmpeg -framerate 60 -i "frame_%%09d.png" -c:v libx264 -r 60 -preset veryslow -qp 0 -pix_fmt yuv420p lossless.mp4
# If you care just about the quality and are going to upload it to YT,
# you could use -preset veryfast or -preset ultrafast to still get lossless, but without as long a wait
echo Default quality, animation - 1828kb/s avg
ffmpeg -framerate 60 -i "frame_%%09d.png" -c:v libx264 -r 60 -tune animation -pix_fmt yuv420p quality_23_default_anim_tuned.mp4
echo Lossless, animation - 5447kb/s avg
ffmpeg -framerate 60 -i "frame_%%09d.png" -c:v libx264 -r 60 -tune animation -preset veryslow -qp 0 -pix_fmt yuv420p lossless_anim_tuned.mp4
echo Quality level 22 - 2239kb/s avg
ffmpeg -framerate 60 -i "frame_%%09d.png" -c:v libx264 -r 60 -crf 22 -pix_fmt yuv420p quality_22.mp4
echo Quality level 22, animation - 1998kb/s avg
ffmpeg -framerate 60 -i "frame_%%09d.png" -c:v libx264 -r 60 -tune animation -crf 22 -pix_fmt yuv420p quality_22_anim_tuned.mp4
echo Quality level 18 - 3069kb/s avg
ffmpeg -framerate 60 -i "frame_%%09d.png" -c:v libx264 -r 60 -crf 18 -pix_fmt yuv420p quality_18.mp4
echo Quality level 18, animation - 2740kb/s avg
ffmpeg -framerate 60 -i "frame_%%09d.png" -c:v libx264 -r 60 -tune animation -crf 18 -pix_fmt yuv420p quality_18_anim_tuned.mp4
For more quality settings, see: https://trac.ffmpeg.org/wiki/Encode/H.264

File diff suppressed because it is too large Load Diff

43
Kelimion/leibnitz.py Normal file
View File

@ -0,0 +1,43 @@
#!/usr/bin/env python3
if __name__ == '__main__':
target = '3.14159263'
# target = '3.14159263589'
digits = len(target) - 2
power = pow(10, digits)
target = int(float(target) * power)
result = 0
divisor = -1
step = 0
count = -1
count2 = 0
subtract = False
close = False
r4 = 0
while (not close):
count = count + 1
divisor = divisor + 2
step = 1 / divisor
if subtract == True:
result = result - step
subtract = False
else:
result = result + step
subtract = True
r4 = result * 4
if count2 == 100000:
print("Step: %10d | Fraction: 1/%10d = %1.12f | Result: %1.12f | Result * 4: %1.12f" % (count, divisor, step, result, r4))
count2 = 0
if int(r4 * power) == target:
close = True
count2 = count2 + 1
print("Step: %10d | Fraction: 1/%10d = %1.12f | Result: %1.12f | Result * 4: %1.12f" % (count , divisor, step, result, r4))

23
Kelimion/stubs-64.h Normal file
View File

@ -0,0 +1,23 @@
/* This file is automatically generated.
It defines a symbol `__stub_FUNCTION' for each function
in the C library which is a stub, meaning it will fail
every time called, usually setting errno to ENOSYS. */
#ifdef _LIBC
# error Applications may not define the macro _LIBC
#endif
#define __stub_bdflush
#define __stub_chflags
#define __stub_fattach
#define __stub_fchflags
#define __stub_fdetach
#define __stub_getmsg
#define __stub_gtty
#define __stub_lchmod
#define __stub_putmsg
#define __stub_revoke
#define __stub_setlogin
#define __stub_sigreturn
#define __stub_sstk
#define __stub_stty

118
Kelimion/twitchlog.py Normal file
View File

@ -0,0 +1,118 @@
#!/usr/bin/env python3
from datetime import datetime, date, timedelta
import sys
# this is optional, to autolink mentioned urls if format --html is chosen
# if you don't have it, also comment out line 96
from bleach import linkify
if __name__ == '__main__':
#print(sys.argv)
if len(sys.argv) < 2 or (len(sys.argv) > 1 and sys.argv[1] == '-h'):
print("Usage: %s <logname> [offset-in-hours] [--html]" % sys.argv[0])
exit()
elif len(sys.argv) >= 2:
filename = sys.argv[1]
offset = 0
elif len(sys.argv) >= 3:
filename = sys.argv[1]
offset = int(sys.argv[2])
if len(sys.argv) >= 4 and sys.argv[3] == '--html':
formatting = 'html'
else:
formatting = 'plain'
today = date.today()
delta = timedelta(hours=offset)
nickcolor = {}
log = []
colors = ['red', 'blue', 'green', 'yellow', 'teal', 'purple']
max_nick = 0
with open(filename, 'r') as f:
for line in f:
when = line[:15]
when_parsed = datetime.strptime(when, '%b %d %H:%M:%S').replace(year=today.year)
when_est = when_parsed + delta
what = line[15:]
if what[1] == '<':
nick_start = 2
nick_end = what.find('>')
nick = what[nick_start:nick_end].strip()
if not nick in nickcolor:
nickcolor[nick] = colors[0]
colors = colors[1:] + [colors[0]]
if len(nick) > max_nick:
max_nick = len(nick)
what = what[nick_end + 2:].strip('\n')
log.append(dict(when=when_est, nick=nick, what=what))
elif what[1] == '*':
nick_start = 3
nick_end = what.find('\s')
what = what[nick_start:nick_end].strip()
nick_start = 0
nick_end = what.find(' ')
nick = what[nick_start:nick_end].strip()
if not nick in nickcolor:
nickcolor[nick] = colors[0]
colors = colors[1:] + [colors[0]]
if len(nick) > max_nick:
max_nick = len(nick)
what = what[nick_end + 1:].strip('\n')
log.append(dict(when=when_est, nick=nick, what=what, action=True))
if formatting == 'html':
fmt1 = "<tr><td class=\"dateline\">%s</td><td class=\"nick %s\">%s</td><td class=\"chat %s\">%s</td>"
fmt2 = "<tr><td class=\"dateline\">%s</td><td colspan=\"2\">* <span class=\"nick %s\">%s</span> <span class=\"chat %s\">%s</span></td>"
print("""<html>
<head>
<title>%s</title>
<meta charset="utf-8">
<style>
.nick { font-weight: bold; text-align: right; }
table { width: 100%%; }
tr > td { width: 10%%; }
tr > td+td { width: 10%%; }
tr > td+td+td { width: 80%%; }""" % filename)
for nick, color in nickcolor.items():
print(".%s { color: %s; }" % (nick, color))
print(".%s a { color: %s; font-weight: bold; }" % (nick, color))
print("""
</style>
</head>
<body>
<table>
""")
thisnick = ''
for line in log:
line['what'] = linkify(line['what'])
if not 'action' in line.keys():
if thisnick == line['nick']:
displaynick = u''
else:
displaynick = line['nick']
print(fmt1 % (line['when'], line['nick'], displaynick, line['nick'], line['what']))
else:
print(fmt2 % (line['when'], line['nick'], line['nick'], line['nick'], line['what']))
thisnick = line['nick']
print("""
</table>
</body>
</html>""")
else:
fmt = "%%s | %%%ds | %%s" % (max_nick)
for line in log:
if not 'action' in line.keys():
print(fmt % (line['when'], line['nick'], line['what']))
else:
print("%s | * %s %s" % (line['when'], line['nick'], line['what']))

31
Miblo/day317.txt Normal file
View File

@ -0,0 +1,31 @@
;FFMETADATA1
major_brand=isom
minor_version=512
compatible_brands=isomiso2avc1mp41
encoder=Lavf57.41.100
title=Handmade Hero Day 317 - Alpha Blending Multiple Render Targets
[CHAPTER]
TIMEBASE=1/1
START=0
END=66
title=Start
[CHAPTER]
TIMEBASE=1/1
START=66
END=176
title=Run the game and show our current situation with render targets
[CHAPTER]
TIMEBASE=1/1
START=176
END=368
title=handmade_entity.cpp: Make UpdateAndRenderEntities() create two different ClipRects for the alpha and normal floors
[CHAPTER]
TIMEBASE=1/1
START=368
END=413
title=Run the game and see that the alpha stuff is gone

86
Miblo/hmhdl.sh Normal file
View File

@ -0,0 +1,86 @@
#!/bin/zsh
#Default settings
DLART="0"
DLPRE="0"
DLSRC="0"
DLVID="1"
RATE="800K"
# My environment settings
SRCDIR="${HOME}/Software/src/Handmade Hero" # the directory to save Casey's files
VIDDIR="${HOME}/Media/Videos/YouTube/Handmade Hero" # the direct to save the videos
OWLURL="" # the url in the email from Molly@sendowl.com
# Casey's files
HMHSRC_001_099="file?product_id=168514"
HMHSRC_100_199="file?product_id=79802"
HMHART="file?product_id=93897"
usage()
{
echo "usage: dlhmh [option]"
echo
echo " -art - disable art"
echo " +art - enable art"
echo " -pre - disable pre-stream video"
echo " +pre - enable pre-stream video"
echo " -src - disable source"
echo " +src - enable source"
echo " -vid - disable main stream"
echo " +vid - enable main stream"
echo
echo " -fs - download at full-speed"
echo " -h|--help - outputs this message"
exit
}
# Argument parsing
while [[ $1 ]]; do
case "$1" in
'-art') DLART='0';;
'+art') DLART='1';;
'-pre') DLPRE='0';;
'+pre') DLPRE='1' ; echo "Downloading the pre-stream video is not yet implemented\nExiting..." ; exit;;
'-src') DLSRC='0';;
'+src') DLSRC='1';;
'-vid') DLVID='0';;
'+vid') DLVID='1';;
'-fs') RATE="9G";;
'-h'|'--help') usage ;;
*) echo "${0##*/}: No such option, mate!\n" ; usage ; exit 127 ;;
esac
shift
done
# Downloading the art assets
if [[ "$DLART" == "1" ]]; then
wget -O "${SRCDIR}/handmade_hero_assets.zip" "${OWLURL}/${HMHART}"
fi
# Downloading the pre-stream video
if [[ "$DLPRE" == "1" ]]; then
#NOTE(matt): This is where the pre-stream downloading code will go
fi
# Downloading the source code
if [[ "$DLSRC" == "1" ]]; then
if [[ ! -e "${SRCDIR}/handmade_hero_source_001-099.zip" ]]; then
wget -O "${SRCDIR}/handmade_hero_source_001-099.zip" "${OWLURL}/${HMHSRC_001_099}"
fi
wget -O "${SRCDIR}/handmade_hero_source_100-199.zip" "${OWLURL}/${HMHSRC_100_199}"
fi
# YouTube playlists:
# https://www.youtube.com/user/handmadeheroarchive/playlists
#youtube-dl -o "${VIDDIR}/04. Game Architecture/%(title)s-%(id)s.%(ext)s" "https://www.youtube.com/playlist?list=PLEMXAbCVnmY6v0eFYcyiH7twEh1UF2Lxw"
#youtube-dl -o "${VIDDIR}/05. Asset Pipeline/%(title)s-%(id)s.%(ext)s" "https://www.youtube.com/playlist?list=PLEMXAbCVnmY6JVkAI8elXUJ-83HY706AQ"
#youtube-dl -o "${VIDDIR}/06. Rendering/%(title)s-%(id)s.%(ext)s" "https://www.youtube.com/playlist?list=PLEMXAbCVnmY40lfaaowTqIs_dKNgOXR5Q"
# Download all of the videos from the last 4 days ago into the HMH base directory
# NOTE(matt): Maybe figure out a way to download them to playlists while still
# keeping the '4 days ago' thing
if [[ "$DLVID" == "1" ]]; then
youtube-dl -i -r $RATE -f 137+140 --download-archive "${VIDDIR}/.dlarchive" -o "${VIDDIR}/%(title)s-%(id)s.%(ext)s" --dateafter "$(date +%Y%m%d -d'8 days ago')" "https://www.youtube.com/user/handmadeheroarchive"
fi

View File

@ -0,0 +1,39 @@
! ----------------------------------------------------------------------------
! file: ~/.Xresources-softlo
! author: Miblo - http://www.miblodelcarpio.co.uk/
! modified: 2013-11-10
! vim:fenc=utf-8:nu:ai:si:et:ts=4:sw=4:ft=xdefaults:
! ----------------------------------------------------------------------------
*background: #000000
*foreground: #FFFFFF
*fading: 40
*fadeColor: #000000
*cursorColor: #33FF33
*pointerColorBackground:#99FF33
*pointerColorForeground:#000000
!! blacks: Black / Dark grey
*color0: #000000
*color8: #3D3D3D
!! reds: Super Meat Boy red / Light brilliant red
*color1: #BA0001
*color9: #FF3333
!! greens: Light brilliant green / Light brilliant chartreuse green
*color2: #33FF33
*color10: #99FF33
!! yellows: Light brilliant orange / L—event yellow
*color3: #FF9933
*color11: #FEF697
!! blues: Light brilliant blue / Light brilliant azure
*color4: #3333FF
*color12: #3399FF
!! magentas: Light brilliant violet / Light brilliant magenta
*color5: #9933FF
*color13: #FF33FF
!! cyans: Light brilliant cyan / Very light cyan
*color6: #33FFFF
*color14: #99FFFF
!! whites: Cosmic latte / White
*color7: #FFF8E7
*color15: #FFFFFF

41
Miblo/xresources.txt Normal file
View File

@ -0,0 +1,41 @@
! ----------------------------------------------------------------------------
! file: ~/.Xresources
! author: Miblo - http://www.miblodelcarpio.co.uk/
! modified: 2012-06-15
! vim:fenc=utf-8:nu:ai:si:et:ts=4:sw=4:ft=xdefaults:
! ----------------------------------------------------------------------------
#include ".Xresources-softlo"
Xcursor.theme: oxy-neon
XTerm*vt100.faceName: inconsolata
XTerm*vt100.faceSize: 9
URxvt*termName: rxvt-256color
!URxvt.letterSpace: -1
URxvt.font: xft:inconsolata:size=9,\
xft:Liberation Mono,\
xft:IPAPMincho,\
xft:symbola
URxvt.boldFont: xft:inconsolata:bold:size=9,\
xft:Liberation Mono,\
xft:IPAPMincho,\
xft:symbola
URxvt*externalBorder: 0
URxvt*internalBorder: 0
URxvt*saveLines: 4096
URxvt.scrollBar: false
!URxvt.tabbed.tabbar-fg: 15
!URxvt.tabbed.tabbar-bg: 0
!URxvt.tabbed.tab-fg: 15
!URxvt.tabbed.tab-bg: 4
!URxvt.perl-ext: default,clipboard,tabbed,url-select,keyboard-select
URxvt.perl-ext: default,clipboard,url-select,keyboard-select
URxvt.keysym.M-u: perl:url-select:select_next
URxvt.keysym.M-Escape: perl:keyboard-select:activate
URxvt.keysym.M-s: perl:keyboard-select:search
URxvt.url-select.launcher: firefox
URxvt.url-select.underline: true
URxvt*boldMode: false

View File

@ -0,0 +1,477 @@
a 0 512
a 1 128
r 0 640
a 2 128
f 1
r 0 768
a 3 128
f 2
r 0 896
a 4 128
f 3
r 0 1024
a 5 128
f 4
r 0 1152
a 6 128
f 5
r 0 1280
a 7 128
f 6
r 0 1408
a 8 128
f 7
r 0 1536
a 9 128
f 8
r 0 1664
a 10 128
f 9
r 0 1792
a 11 128
f 10
r 0 1920
a 12 128
f 11
r 0 2048
a 13 128
f 12
r 0 2176
a 14 128
f 13
r 0 2304
a 15 128
f 14
r 0 2432
a 16 128
f 15
r 0 2560
a 17 128
f 16
r 0 2688
a 18 128
f 17
r 0 2816
a 19 128
f 18
r 0 2944
a 20 128
f 19
r 0 3072
a 21 128
f 20
r 0 3200
a 22 128
f 21
r 0 3328
a 23 128
f 22
r 0 3456
a 24 128
f 23
r 0 3584
a 25 128
f 24
r 0 3712
a 26 128
f 25
r 0 3840
a 27 128
f 26
r 0 3968
a 28 128
f 27
r 0 4096
a 29 128
f 28
r 0 4224
a 30 128
f 29
r 0 4352
a 31 128
f 30
r 0 4480
a 32 128
f 31
r 0 4608
a 33 128
f 32
r 0 4736
a 34 128
f 33
r 0 4864
a 35 128
f 34
r 0 4992
a 36 128
f 35
r 0 5120
a 37 128
f 36
r 0 5248
a 38 128
f 37
r 0 5376
a 39 128
f 38
r 0 5504
a 40 128
f 39
r 0 5632
a 41 128
f 40
r 0 5760
a 42 128
f 41
r 0 5888
a 43 128
f 42
r 0 6016
a 44 128
f 43
r 0 6144
a 45 128
f 44
r 0 6272
a 46 128
f 45
r 0 6400
a 47 128
f 46
r 0 6528
a 48 128
f 47
r 0 6656
a 49 128
f 48
r 0 6784
a 50 128
f 49
r 0 6912
a 51 128
f 50
r 0 7040
a 52 128
f 51
r 0 7168
a 53 128
f 52
r 0 7296
a 54 128
f 53
r 0 7424
a 55 128
f 54
r 0 7552
a 56 128
f 55
r 0 7680
a 57 128
f 56
r 0 7808
a 58 128
f 57
r 0 7936
a 59 128
f 58
r 0 8064
a 60 128
f 59
r 0 8192
a 61 128
f 60
r 0 8320
a 62 128
f 61
r 0 8448
a 63 128
f 62
r 0 8576
a 64 128
f 63
r 0 8704
a 65 128
f 64
r 0 8832
a 66 128
f 65
r 0 8960
a 67 128
f 66
r 0 9088
a 68 128
f 67
r 0 9216
a 69 128
f 68
r 0 9344
a 70 128
f 69
r 0 9472
a 71 128
f 70
r 0 9600
a 72 128
f 71
r 0 9728
a 73 128
f 72
r 0 9856
a 74 128
f 73
r 0 9984
a 75 128
f 74
r 0 10112
a 76 128
f 75
r 0 10240
a 77 128
f 76
r 0 10368
a 78 128
f 77
r 0 10496
a 79 128
f 78
r 0 10624
a 80 128
f 79
r 0 10752
a 81 128
f 80
r 0 10880
a 82 128
f 81
r 0 11008
a 83 128
f 82
r 0 11136
a 84 128
f 83
r 0 11264
a 85 128
f 84
r 0 11392
a 86 128
f 85
r 0 11520
a 87 128
f 86
r 0 11648
a 88 128
f 87
r 0 11776
a 89 128
f 88
r 0 11904
a 90 128
f 89
r 0 12032
a 91 128
f 90
r 0 12160
a 92 128
f 91
r 0 12288
a 93 128
f 92
r 0 12416
a 94 128
f 93
r 0 12544
a 95 128
f 94
r 0 12672
a 96 128
f 95
r 0 12800
a 97 128
f 96
r 0 12928
a 98 128
f 97
r 0 13056
a 99 128
f 98
r 0 13184
a 100 128
f 99
r 0 13312
a 101 128
f 100
r 0 13440
a 102 128
f 101
r 0 13568
a 103 128
f 102
r 0 13696
a 104 128
f 103
r 0 13824
a 105 128
f 104
r 0 13952
a 106 128
f 105
r 0 14080
a 107 128
f 106
r 0 14208
a 108 128
f 107
r 0 14336
a 109 128
f 108
r 0 14464
a 110 128
f 109
r 0 14592
a 111 128
f 110
r 0 14720
a 112 128
f 111
r 0 14848
a 113 128
f 112
r 0 14976
a 114 128
f 113
r 0 15104
a 115 128
f 114
r 0 15232
a 116 128
f 115
r 0 15360
a 117 128
f 116
r 0 15488
a 118 128
f 117
r 0 15616
a 119 128
f 118
r 0 15744
a 120 128
f 119
r 0 15872
a 121 128
f 120
r 0 16000
a 122 128
f 121
r 0 16128
a 123 128
f 122
r 0 16256
a 124 128
f 123
r 0 16384
a 125 128
f 124
r 0 16512
a 126 128
f 125
r 0 16640
a 127 128
f 126
r 0 16768
a 128 128
f 127
r 0 16896
a 129 128
f 128
r 0 17024
a 130 128
f 129
r 0 17152
a 131 128
f 130
r 0 17280
a 132 128
f 131
r 0 17408
a 133 128
f 132
r 0 17536
a 134 128
f 133
r 0 17664
a 135 128
f 134
r 0 17792
a 136 128
f 135
r 0 17920
a 137 128
f 136
r 0 18048
a 138 128
f 137
r 0 18176
a 139 128
f 138
r 0 18304
a 140 128
f 139
r 0 18432
a 141 128
f 140
r 0 18560
a 142 128
f 141
r 0 18688
a 143 128
f 142
r 0 18816
a 144 128
f 143
r 0 18944
a 145 128
f 144
r 0 19072
a 146 128
f 145
r 0 19200
a 147 128
f 146
r 0 19328
a 148 128
f 147
r 0 19456
a 149 128
f 148
r 0 19584
a 150 128
f 149
r 0 19712
a 151 128
f 150
r 0 19840
a 152 128
f 151
r 0 19968
a 153 128
f 152
r 0 20096
a 154 128
f 153
r 0 20224
a 155 128
f 154
r 0 20352
a 156 128
f 155
r 0 20480
a 157 128
f 156
r 0 20608
a 158 128
f 157
r 0 20736
a 159 128
f 158
r 0 20864

35
mmozeiko/images2ico.py Normal file
View File

@ -0,0 +1,35 @@
#!/usr/bin/env python3
# packs multiple images (bmp/png/...) into ico file
# width and height of images must be <= 256
# pixel format of images must be 32-bit RGBA
import argparse
import struct
import os
from PIL import Image # https://python-pillow.org/
def pack(output, inp):
count = len(inp)
with open(output, "wb") as f:
f.write(struct.pack("HHH", 0, 1, count))
offset = struct.calcsize("HHH") + struct.calcsize("BBBBHHII")*count
for i in inp:
size = os.stat(i).st_size
img = Image.open(i)
w = 0 if img.width == 256 else img.width
h = 0 if img.height == 256 else img.height
f.write(struct.pack("BBBBHHII", w, h, 0, 0, 1, 32, size, offset))
offset += size
for i in inp:
f.write(open(i, "rb").read())
if __name__ == "__main__":
ap = argparse.ArgumentParser(description="pack multiple images into ico file")
ap.add_argument("-o", "--out", help="output file")
ap.add_argument("input", type=str, nargs='+', help="input images")
args = ap.parse_args()
pack(args.out, args.input)

773
mmozeiko/win32_d3d11.c Normal file
View File

@ -0,0 +1,773 @@
// example how to set up D3D11 rendering
// set to 0 to create resizable window
#define WINDOW_WIDTH 1280
#define WINDOW_HEIGHT 720
// do you need depth buffer?
#define WINDOW_DEPTH 1
// do you need stencil buffer?
#define WINDOW_STENCIL 0
// use sRGB or MSAA for color buffer
// set 0 to disable, or 1 to enable sRGB
// typical values for MSAA are 2, 4, 8, 16, ...
// when enabled, D3D11 cannot use more modern lower-latency flip swap effect on Windows 8.1/10
// instead you can use sRGB/MSAA render target and copy it to non-sRGB window
#define WINDOW_SRGB 0
#define WINDOW_MSAA 0
// do you need vsync?
#define WINDOW_VSYNC 1
// keep this enabled when debugging
#define USE_DEBUG_MODE 1
#define COBJMACROS
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <d3d11_1.h>
#include <dxgi1_4.h>
#include <d3dcompiler.h>
#include <stddef.h>
#pragma comment (lib, "user32.lib")
#pragma comment (lib, "dxgi.lib")
#pragma comment (lib, "d3d11.lib")
#pragma comment (lib, "dxguid.lib")
#define SAFE_RELEASE(release, obj) if (obj) release##_Release(obj)
#define LOG_AND_RETURN_ERROR(hr, msg) do \
{ \
if (FAILED(hr)) \
{ \
LogWin32Error(hr, msg); \
return hr; \
} \
} while (0)
struct Vertex
{
float x, y;
float r, g, b;
};
static const struct Vertex vertices[] =
{
{ 0.0f, 0.5f, 1.f, 0.f, 0.f },
{ 0.5f, -0.5f, 0.f, 1.f, 0.f },
{ -0.5f, -0.5f, 0.f, 0.f, 1.f },
};
// You can compile shader to bytecode at build time, this will increase startup
// performance and also will avoid dependency on d3dcompiler dll file.
// To do so, put the shader source in shader.hlsl file and run these two commands:
// fxc.exe /nologo /T vs_4_0_level_9_0 /E vs /O3 /WX /Zpc /Ges /Fh d3d11_vshader.h /Vn d3d11_vshader /Qstrip_reflect /Qstrip_debug /Qstrip_priv shader.hlsl
// fxc.exe /nologo /T ps_4_0_level_9_0 /E ps /O3 /WX /Zpc /Ges /Fh d3d11_pshader.h /Vn d3d11_pshader /Qstrip_reflect /Qstrip_debug /Qstrip_priv shader.hlsl
// then set next setting to 1
#define USE_PRECOMPILED_SHADERS 0
#if USE_PRECOMPILED_SHADERS
#include "d3d11_vshader.h"
#include "d3d11_pshader.h"
#else
#pragma comment (lib, "d3dcompiler.lib")
static const char d3d11_shader[] =
"struct VS_INPUT \n"
"{ \n"
" float2 pos : POSITION; \n"
" float3 col : COLOR0; \n"
"}; \n"
" \n"
"struct PS_INPUT \n"
"{ \n"
" float4 pos : SV_POSITION; \n"
" float3 col : COLOR0; \n"
"}; \n"
" \n"
"PS_INPUT vs(VS_INPUT input) \n"
"{ \n"
" PS_INPUT output; \n"
" output.pos = float4(input.pos.xy, 0.f, 1.f); \n"
" output.col = input.col; \n"
" return output; \n"
"} \n"
" \n"
"float4 ps(PS_INPUT input) : SV_Target \n"
"{ \n"
" return float4(input.col, 1.f); \n"
"} \n";
#endif
// is window visible?
// if this is 0 you can skip rendering part in your code to save time
static int render_occluded;
static IDXGISwapChain* render_swapchain;
static ID3D11Device* render_device;
static ID3D11DeviceContext* render_context;
static ID3D11DeviceContext1* render_context1;
static ID3D11RenderTargetView* render_window_rtview;
#if WINDOW_DEPTH || WINDOW_STENCIL
static ID3D11DepthStencilView* render_window_dpview;
#endif
static HANDLE render_frame_latency_wait;
static ID3D11RasterizerState* render_raster_state;
static ID3D11DepthStencilState* render_depthstencil_state;
static ID3D11BlendState* render_blend_state;
static ID3D11PixelShader* render_pixel_shader;
static ID3D11VertexShader* render_vertex_shader;
static ID3D11InputLayout* render_input_layout;
static ID3D11Buffer* render_vertex_buffer;
static void LogWin32Error(DWORD err, const char* msg)
{
OutputDebugStringA(msg);
OutputDebugStringA("!\n");
LPWSTR str;
if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL,
err, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), (LPWSTR)&str, 0, NULL))
{
OutputDebugStringW(str);
LocalFree(str);
}
}
static void LogWin32LastError(const char* msg)
{
LogWin32Error(GetLastError(), msg);
}
static HRESULT FatalDeviceLostError()
{
MessageBoxW(NULL, L"Cannot recreate D3D11 device, it is reset or removed!", L"Error", MB_ICONEXCLAMATION);
return E_FAIL;
}
// called when device & all d3d resources needs to be released
// can happen multiple times (e.g. after device is removed/reset)
static void RenderDestroy()
{
if (render_context)
{
ID3D11DeviceContext_ClearState(render_context);
}
SAFE_RELEASE(ID3D11Buffer, render_vertex_buffer);
SAFE_RELEASE(ID3D11InputLayout, render_input_layout);
SAFE_RELEASE(ID3D11VertexShader, render_vertex_shader);
SAFE_RELEASE(ID3D11PixelShader, render_pixel_shader);
SAFE_RELEASE(ID3D11RasterizerState, render_raster_state);
SAFE_RELEASE(ID3D11DepthStencilState, render_depthstencil_state);
SAFE_RELEASE(ID3D11BlendState, render_blend_state);
#if WINDOW_DEPTH || WINDOW_STENCIL
SAFE_RELEASE(ID3D11DepthStencilView, render_window_dpview);
#endif
SAFE_RELEASE(ID3D11RenderTargetView, render_window_rtview);
SAFE_RELEASE(ID3D11DeviceContext, render_context);
SAFE_RELEASE(ID3D11Device, render_device);
SAFE_RELEASE(IDXGISwapChain, render_swapchain);
render_context1 = NULL;
render_frame_latency_wait = NULL;
}
// called any time device needs to be created
// can happen multiple times (e.g. after device is removed/reset)
static HRESULT RenderCreate(HWND wnd)
{
HRESULT hr;
D3D_FEATURE_LEVEL level;
// device, context
{
UINT flags = D3D11_CREATE_DEVICE_SINGLETHREADED;
#if USE_DEBUG_MODE
flags |= D3D11_CREATE_DEVICE_DEBUG;
#endif
if (FAILED(hr = D3D11CreateDevice(
NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, flags, NULL, 0, D3D11_SDK_VERSION,
&render_device, &level, &render_context)))
{
if (FAILED(hr = D3D11CreateDevice(
NULL, D3D_DRIVER_TYPE_WARP, NULL, flags, NULL, 0, D3D11_SDK_VERSION,
&render_device, &level, &render_context)))
{
LOG_AND_RETURN_ERROR(hr, "D3D11CreateDevice failed");
}
}
hr = ID3D11DeviceContext_QueryInterface(render_context, &IID_ID3D11DeviceContext1, &render_context1);
if (SUCCEEDED(hr))
{
// using ID3D11DeviceContext1 to discard render target
ID3D11DeviceContext_Release(render_context);
render_context = (ID3D11DeviceContext*)render_context1;
}
}
// swap chain
{
IDXGIFactory* factory;
if (FAILED(hr = CreateDXGIFactory(&IID_IDXGIFactory, &factory)))
{
LOG_AND_RETURN_ERROR(hr, "CreateDXGIFactory failed");
}
DXGI_SWAP_CHAIN_DESC desc =
{
.BufferDesc =
{
#if WINDOW_SRGB
.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB,
#else
.Format = DXGI_FORMAT_R8G8B8A8_UNORM,
#endif
.RefreshRate =
{
.Numerator = 60,
.Denominator = 1,
},
},
.SampleDesc =
{
#if WINDOW_MSAA
.Count = WINDOW_MSAA,
#else
.Count = 1,
#endif
.Quality = 0,
},
.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT,
.OutputWindow = wnd,
.Windowed = TRUE,
};
#if !WINDOW_SRGB && !WINDOW_MSAA
// Windows 10 and up
desc.BufferCount = 2;
desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
desc.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT;
if (FAILED(IDXGIFactory_CreateSwapChain(factory, (IUnknown*)render_device, &desc, &render_swapchain)))
{
// Windows 8.1
desc.BufferCount = 2;
desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
desc.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT;
hr = IDXGIFactory_CreateSwapChain(factory, (IUnknown*)render_device, &desc, &render_swapchain);
}
#else
hr = E_FAIL;
#endif
if (FAILED(hr))
{
// older Windows
desc.BufferCount = 1;
desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
desc.Flags = 0;
hr = IDXGIFactory_CreateSwapChain(factory, (IUnknown*)render_device, &desc, &render_swapchain);
LOG_AND_RETURN_ERROR(hr, "IDXGIFactory::CreateSwapChain failed");
}
if (desc.Flags & DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT)
{
IDXGISwapChain2* swapchain2;
if (SUCCEEDED(hr = IDXGISwapChain_QueryInterface(render_swapchain, &IID_IDXGISwapChain2, &swapchain2)))
{
// using IDXGISwapChain2 for frame latency control
render_frame_latency_wait = IDXGISwapChain2_GetFrameLatencyWaitableObject(swapchain2);
IDXGISwapChain2_Release(swapchain2);
}
}
hr = IDXGIFactory_MakeWindowAssociation(factory, wnd, DXGI_MWA_NO_WINDOW_CHANGES | DXGI_MWA_NO_ALT_ENTER);
LOG_AND_RETURN_ERROR(hr, "IDXGIFactory::MakeWindowAssociation failed");
IDXGIFactory_Release(factory);
}
// rasterizer state
{
D3D11_RASTERIZER_DESC desc =
{
.FillMode = D3D11_FILL_SOLID,
.CullMode = D3D11_CULL_BACK,
.FrontCounterClockwise = FALSE,
.DepthBias = 0,
.DepthBiasClamp = 0,
.SlopeScaledDepthBias = 0.f,
.DepthClipEnable = TRUE,
.ScissorEnable = FALSE,
.MultisampleEnable = WINDOW_MSAA > 0,
.AntialiasedLineEnable = FALSE,
};
hr = ID3D11Device_CreateRasterizerState(render_device, &desc, &render_raster_state);
LOG_AND_RETURN_ERROR(hr, "ID3D11Device::CreateRasterizerState failed");
}
#if WINDOW_DEPTH || WINDOW_STENCIL
// depth & stencil state
{
D3D11_DEPTH_STENCIL_DESC desc =
{
.DepthEnable = WINDOW_DEPTH ? TRUE : FALSE,
.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL,
.DepthFunc = D3D11_COMPARISON_LESS,
.StencilEnable = FALSE, // if you need stencil, set up it here
.StencilReadMask = 0,
.StencilWriteMask = 0,
};
hr = ID3D11Device_CreateDepthStencilState(render_device, &desc, &render_depthstencil_state);
LOG_AND_RETURN_ERROR(hr, "ID3D11Device::CreateDepthStencilState failed");
}
#endif
// blend state
{
D3D11_BLEND_DESC desc =
{
.AlphaToCoverageEnable = FALSE,
.IndependentBlendEnable = FALSE,
.RenderTarget =
{
{
.BlendEnable = FALSE,
.SrcBlend = D3D11_BLEND_SRC_ALPHA,
.DestBlend = D3D11_BLEND_INV_SRC_ALPHA,
.BlendOp = D3D11_BLEND_OP_ADD,
.SrcBlendAlpha = D3D11_BLEND_SRC_ALPHA,
.DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA,
.BlendOpAlpha = D3D11_BLEND_OP_ADD,
.RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL,
},
},
};
hr = ID3D11Device_CreateBlendState(render_device, &desc, &render_blend_state);
LOG_AND_RETURN_ERROR(hr, "ID3D11Device::CreateBlendState failed");
}
#if !USE_PRECOMPILED_SHADERS
UINT shader_flags = D3DCOMPILE_ENABLE_STRICTNESS | D3DCOMPILE_WARNINGS_ARE_ERRORS
#if USE_DEBUG_MODE
| D3DCOMPILE_OPTIMIZATION_LEVEL0 | D3DCOMPILE_SKIP_OPTIMIZATION | D3DCOMPILE_DEBUG;
#else
| D3DCOMPILE_OPTIMIZATION_LEVEL3;
#endif
#endif
// vertex shader & input layout
{
D3D11_INPUT_ELEMENT_DESC layout[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(struct Vertex, x), D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "COLOR", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, offsetof(struct Vertex, r), D3D11_INPUT_PER_VERTEX_DATA, 0 },
};
ID3DBlob* code = NULL;
const void* vshader;
size_t vshader_size;
#if USE_PRECOMPILED_SHADERS
vshader = d3d11_vshader;
vshader_size = sizeof(d3d11_vshader);
#else
ID3DBlob* error;
hr = D3DCompile(d3d11_shader, sizeof(d3d11_shader)-1, NULL, NULL, NULL,
"vs", "vs_4_0_level_9_0", shader_flags, 0, &code, &error);
if (FAILED(hr))
{
const void* data = ID3D10Blob_GetBufferPointer(error);
size_t size = ID3D10Blob_GetBufferSize(error);
char msg[1024];
lstrcpynA(msg, data, (int)size);
msg[size] = 0;
OutputDebugStringA(msg);
ID3D10Blob_Release(error);
LOG_AND_RETURN_ERROR(hr, "D3DCompile vs failed");
}
vshader = ID3D10Blob_GetBufferPointer(code);
vshader_size = ID3D10Blob_GetBufferSize(code);
#endif
hr = ID3D11Device_CreateVertexShader(render_device, vshader, vshader_size, NULL, &render_vertex_shader);
if (FAILED(hr))
{
SAFE_RELEASE(ID3D10Blob, code);
LOG_AND_RETURN_ERROR(hr, "ID3D11Device::CreateVertexShader failed");
}
hr = ID3D11Device_CreateInputLayout(render_device, layout, _countof(layout),
vshader, vshader_size, &render_input_layout);
SAFE_RELEASE(ID3D10Blob, code);
LOG_AND_RETURN_ERROR(hr, "ID3D11Device::CreateInputLayout failed");
}
// pixel shader
{
ID3DBlob* code = NULL;
const void* pshader;
size_t pshader_size;
#if USE_PRECOMPILED_SHADERS
pshader = d3d11_pshader;
pshader_size = sizeof(d3d11_pshader);
#else
ID3DBlob* error;
hr = D3DCompile(d3d11_shader, sizeof(d3d11_shader)-1, NULL, NULL, NULL,
"ps", "ps_4_0_level_9_0", shader_flags, 0, &code, &error);
if (FAILED(hr))
{
const void* data = ID3D10Blob_GetBufferPointer(error);
size_t size = ID3D10Blob_GetBufferSize(error);
char msg[1024];
lstrcpynA(msg, data, (int)size);
msg[size] = 0;
OutputDebugStringA(msg);
ID3D10Blob_Release(error);
LOG_AND_RETURN_ERROR(hr, "D3DCompile ps failed");
}
pshader = ID3D10Blob_GetBufferPointer(code);
pshader_size = ID3D10Blob_GetBufferSize(code);
#endif
hr = ID3D11Device_CreatePixelShader(render_device, pshader, pshader_size, NULL, &render_pixel_shader);
SAFE_RELEASE(ID3D10Blob, code);
LOG_AND_RETURN_ERROR(hr, "ID3D11Device::CreatePixelShader failed");
}
// vertex buffer
{
D3D11_BUFFER_DESC desc =
{
.ByteWidth = sizeof(vertices),
.Usage = D3D11_USAGE_IMMUTABLE,
.BindFlags = D3D11_BIND_VERTEX_BUFFER,
};
D3D11_SUBRESOURCE_DATA data =
{
.pSysMem = vertices,
};
hr = ID3D11Device_CreateBuffer(render_device, &desc, &data, &render_vertex_buffer);
LOG_AND_RETURN_ERROR(hr, "ID3D11Device::CreateBuffer failed");
}
return S_OK;
}
// called when device is reset or removed, recreate it
static HRESULT RecreateDevice(HWND wnd)
{
RenderDestroy();
HRESULT hr = RenderCreate(wnd);
if (FAILED(hr))
{
RenderDestroy();
}
return hr;
}
// called when window is resized
static HRESULT RenderResize(HWND wnd, int width, int height)
{
if (width == 0 || height == 0)
{
return S_OK;
}
if (render_window_rtview)
{
ID3D11DeviceContext_OMSetRenderTargets(render_context, 0, NULL, NULL);
ID3D11RenderTargetView_Release(render_window_rtview);
render_window_rtview = NULL;
}
#if WINDOW_DEPTH || WINDOW_STENCIL
if (render_window_dpview)
{
ID3D11DepthStencilView_Release(render_window_dpview);
render_window_dpview = NULL;
}
#endif
UINT flags = render_frame_latency_wait ? DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT : 0;
HRESULT hr = IDXGISwapChain_ResizeBuffers(render_swapchain, 0, width, height, DXGI_FORMAT_UNKNOWN, flags);
if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET || hr == DXGI_ERROR_DRIVER_INTERNAL_ERROR)
{
if (FAILED(RecreateDevice(wnd)))
{
return FatalDeviceLostError();
}
}
else
{
LOG_AND_RETURN_ERROR(hr, "IDXGISwapChain::ResizeBuffers failed");
}
ID3D11Texture2D* window_buffer;
hr = IDXGISwapChain_GetBuffer(render_swapchain, 0, &IID_ID3D11Texture2D, &window_buffer);
LOG_AND_RETURN_ERROR(hr, "IDXGISwapChain::GetBuffer failed");
hr = ID3D11Device_CreateRenderTargetView(render_device, (ID3D11Resource*)window_buffer, NULL, &render_window_rtview);
ID3D11Texture2D_Release(window_buffer);
LOG_AND_RETURN_ERROR(hr, "ID3D11Device::CreateRenderTargetView failed");
#if WINDOW_DEPTH || WINDOW_STENCIL
{
D3D11_TEXTURE2D_DESC desc =
{
.Width = width,
.Height = height,
.MipLevels = 1,
.ArraySize = 1,
.Format = (WINDOW_STENCIL || ID3D11Device_GetFeatureLevel(render_device) < D3D_FEATURE_LEVEL_10_0)
? DXGI_FORMAT_D24_UNORM_S8_UINT : DXGI_FORMAT_D32_FLOAT,
.SampleDesc =
{
#if WINDOW_MSAA
.Count = WINDOW_MSAA,
#else
.Count = 1,
#endif
.Quality = 0,
},
.Usage = D3D11_USAGE_DEFAULT,
.BindFlags = D3D11_BIND_DEPTH_STENCIL,
};
ID3D11Texture2D* depth_stencil;
hr = ID3D11Device_CreateTexture2D(render_device, &desc, NULL, &depth_stencil);
LOG_AND_RETURN_ERROR(hr, "ID3D11Device::CreateTexture2D failed");
hr = ID3D11Device_CreateDepthStencilView(render_device, (ID3D11Resource*)depth_stencil, NULL, &render_window_dpview);
ID3D11Texture2D_Release(depth_stencil);
LOG_AND_RETURN_ERROR(hr, "ID3D11Device::CreateDepthStencilView failed");
}
#endif
D3D11_VIEWPORT viewport =
{
.TopLeftX = 0.f,
.TopLeftY = 0.f,
.Width = (float)width,
.Height = (float)height,
.MinDepth = 0.f,
.MaxDepth = 1.f,
};
ID3D11DeviceContext_RSSetViewports(render_context, 1, &viewport);
return S_OK;
}
// called at end of frame
static HRESULT RenderPresent(HWND wnd)
{
HRESULT hr = S_OK;
if (render_occluded)
{
hr = IDXGISwapChain_Present(render_swapchain, 0, DXGI_PRESENT_TEST);
if (SUCCEEDED(hr) && hr != DXGI_STATUS_OCCLUDED)
{
// DXGI window is back to normal, resuming rendering
render_occluded = 0;
}
}
if (!render_occluded)
{
hr = IDXGISwapChain_Present(render_swapchain, WINDOW_VSYNC, 0);
}
if (hr == DXGI_ERROR_DEVICE_RESET || hr == DXGI_ERROR_DEVICE_REMOVED)
{
if (FAILED(RecreateDevice(wnd)))
{
return FatalDeviceLostError();
}
RECT rect;
if (!GetClientRect(wnd, &rect))
{
LogWin32LastError("GetClientRect failed");
}
else
{
RenderResize(wnd, rect.right - rect.left, rect.bottom - rect.top);
}
}
else if (hr == DXGI_STATUS_OCCLUDED)
{
// DXGI window is occluded, skipping rendering
render_occluded = 1;
}
else
{
LOG_AND_RETURN_ERROR(hr, "IDXGISwapChain::Present failed");
}
if (render_occluded)
{
Sleep(10);
}
else
{
if (render_context1)
{
ID3D11DeviceContext1_DiscardView(render_context1, (ID3D11View*)render_window_rtview);
}
}
return S_OK;
}
// this is where rendering happens
static void RenderFrame()
{
if (!render_occluded)
{
if (render_frame_latency_wait)
{
WaitForSingleObjectEx(render_frame_latency_wait, INFINITE, TRUE);
}
#if WINDOW_DEPTH || WINDOW_STENCIL
ID3D11DeviceContext_OMSetRenderTargets(render_context, 1, &render_window_rtview, render_window_dpview);
ID3D11DeviceContext_OMSetDepthStencilState(render_context, render_depthstencil_state, 0);
ID3D11DeviceContext_ClearDepthStencilView(render_context, render_window_dpview, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.f, 0);
#else
ID3D11DeviceContext_OMSetRenderTargets(render_context, 1, &render_window_rtview, NULL);
#endif
// clear background
FLOAT clear_color[] = { 100.f/255.f, 149.f/255.f, 237.f/255.f, 1.f };
ID3D11DeviceContext_ClearRenderTargetView(render_context, render_window_rtview, clear_color);
// draw a triangle
const UINT stride = sizeof(struct Vertex);
const UINT offset = 0;
ID3D11DeviceContext_IASetInputLayout(render_context, render_input_layout);
ID3D11DeviceContext_IASetVertexBuffers(render_context, 0, 1, &render_vertex_buffer, &stride, &offset);
ID3D11DeviceContext_IASetPrimitiveTopology(render_context, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ID3D11DeviceContext_VSSetShader(render_context, render_vertex_shader, NULL, 0);
ID3D11DeviceContext_PSSetShader(render_context, render_pixel_shader, NULL, 0);
ID3D11DeviceContext_RSSetState(render_context, render_raster_state);
ID3D11DeviceContext_OMSetBlendState(render_context, render_blend_state, NULL, ~0U);
ID3D11DeviceContext_Draw(render_context, _countof(vertices), 0);
}
}
static LRESULT CALLBACK WindowProc(HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
switch (msg)
{
case WM_CREATE:
if (FAILED(RenderCreate(wnd)))
{
return -1;
}
return 0;
case WM_DESTROY:
RenderDestroy(wnd);
PostQuitMessage(0);
return 0;
case WM_SIZE:
if (FAILED(RenderResize(wnd, LOWORD(lparam), HIWORD(lparam))))
{
DestroyWindow(wnd);
}
return 0;
}
return DefWindowProcW(wnd, msg, wparam, lparam);
}
int WINAPI WinMain(HINSTANCE instance, HINSTANCE prev_instance, LPSTR cmd_line, int cmd_show)
{
WNDCLASSEXW wc =
{
.cbSize = sizeof(wc),
.lpfnWndProc = WindowProc,
.hInstance = instance,
.hIcon = LoadIconA(NULL, IDI_APPLICATION),
.hCursor = LoadCursorA(NULL, IDC_ARROW),
.lpszClassName = L"d3d11_window_class",
};
if (!RegisterClassExW(&wc))
{
LogWin32LastError("RegisterClassEx failed");
}
else
{
int width = CW_USEDEFAULT;
int height = CW_USEDEFAULT;
DWORD exstyle = WS_EX_APPWINDOW;
DWORD style = WS_OVERLAPPEDWINDOW;
if (WINDOW_WIDTH && WINDOW_HEIGHT)
{
style &= ~WS_THICKFRAME & ~WS_MAXIMIZEBOX;
RECT rect = { 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT };
if (!AdjustWindowRectEx(&rect, style, FALSE, exstyle))
{
LogWin32LastError("AdjustWindowRectEx failed");
style = WS_OVERLAPPEDWINDOW;
}
else
{
width = rect.right - rect.left;
height = rect.bottom - rect.top;
}
}
HWND wnd = CreateWindowExW(exstyle, wc.lpszClassName, L"D3D11 Window",
style | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, width, height,
NULL, NULL, wc.hInstance, NULL);
if (!wnd)
{
LogWin32LastError("CreateWindow failed");
}
else
{
for (;;)
{
MSG msg;
if (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
{
break;
}
TranslateMessage(&msg);
DispatchMessageW(&msg);
continue;
}
RenderFrame();
if (FAILED(RenderPresent(wnd)))
{
break;
}
}
}
UnregisterClassW(wc.lpszClassName, wc.hInstance);
}
return 0;
}

561
mmozeiko/win32_opengl.c Normal file
View File

@ -0,0 +1,561 @@
// example how to set up modern OpenGL context with fallback to legacy context
// set to 0 to create resizable window
#define WINDOW_WIDTH 1280
#define WINDOW_HEIGHT 720
// do you need depth buffer?
#define WINDOW_DEPTH 1
// do you need stencil buffer?
#define WINDOW_STENCIL 0
// use sRGB for color buffer
#define WINDOW_SRGB 1
// do you need multisampling?
// to disable set to 0, to enable set to 2, 4, 8, 16, ...
#define WINDOW_MSAA 4
// do you need vsync?
#define WINDOW_VSYNC 1
// keep this enabled when debugging
#define USE_DEBUG_MODE 1
// replace this with your favorite assert() implementation
#include <assert.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <GL/gl.h>
#include "glext.h" // download from https://www.opengl.org/registry/api/GL/glext.h
#include "wglext.h" // download from https://www.opengl.org/registry/api/GL/wglext.h
// https://www.opengl.org/registry/specs/ARB/wgl_extensions_string.txt
// https://www.opengl.org/registry/specs/ARB/wgl_pixel_format.txt
// https://www.opengl.org/registry/specs/ARB/wgl_create_context.txt
// https://www.opengl.org/registry/specs/EXT/wgl_swap_control.txt
// https://www.opengl.org/registry/specs/EXT/wgl_swap_control_tear.txt
// https://www.opengl.org/registry/specs/ARB/framebuffer_sRGB.txt
// https://www.opengl.org/registry/specs/ARB/multisample.txt
// https://www.opengl.org/registry/specs/ARB/debug_output.txt
#pragma comment (lib, "gdi32.lib")
#pragma comment (lib, "user32.lib")
#pragma comment (lib, "opengl32.lib")
#if USE_DEBUG_MODE
#define GL_CHECK(x) do \
{ \
x; \
GLenum err = glGetError(); \
assert(err == GL_NO_ERROR); \
} while (0)
#else
#define GL_CHECK(x) x
#endif
// called after context is set up (only once)
// for example, load GL extensions here & set up GL state
static void RenderInit(void)
{
GL_CHECK( glClearColor(100.f/255.f, 149.f/255.f, 237.f/255.f, 1.f) );
}
// called before render is destroyed
static void RenderDone(void)
{
}
// called when window is resized
static void RenderResize(unsigned width, unsigned height)
{
GL_CHECK( glViewport(0, 0, width, height) );
}
// called every frame before swapping buffers
static void RenderFrame(void)
{
GL_CHECK( glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT) );
// use legacy GL for drawing to display a triangle
glBegin(GL_TRIANGLES);
glColor3f(1.f, 0.f, 0.f);
glVertex2f(0.f, 0.5f);
glColor3f(0.f, 1.f, 0.f);
glVertex2f(0.5f, -0.5f);
glColor3f(0.f, 0.f, 1.f);
glVertex2f(-0.5f, -0.5f);
GL_CHECK( glEnd() );
}
static void LogWin32Error(const char* msg)
{
OutputDebugStringA(msg);
OutputDebugStringA("!\n");
DWORD err = GetLastError();
LPWSTR str;
if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL,
err, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), (LPWSTR)&str, 0, NULL))
{
OutputDebugStringW(str);
LocalFree(str);
}
}
static HGLRC CreateOldOpenGLContext(HDC dc)
{
PIXELFORMATDESCRIPTOR pfd =
{
.nSize = sizeof(pfd),
.nVersion = 1,
.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL |
PFD_DOUBLEBUFFER | (WINDOW_DEPTH ? 0 : PFD_DEPTH_DONTCARE),
.iPixelType = PFD_TYPE_RGBA,
.cColorBits = 24,
.cDepthBits = (WINDOW_DEPTH ? 24 : 0),
.cStencilBits = (WINDOW_STENCIL ? 8 : 0),
};
int format = ChoosePixelFormat(dc, &pfd);
if (!format)
{
LogWin32Error("ChoosePixelFormat failed");
return NULL;
}
if (!DescribePixelFormat(dc, format, sizeof(pfd), &pfd))
{
LogWin32Error("DescribePixelFormat failed");
return NULL;
}
if (!SetPixelFormat(dc, format, &pfd))
{
LogWin32Error("SetPixelFormat failed");
return NULL;
}
HGLRC rc = wglCreateContext(dc);
if (!rc)
{
LogWin32Error("wglCreateContext failed");
return NULL;
}
if (!wglMakeCurrent(dc, rc))
{
LogWin32Error("wglMakeCurrent failed");
wglDeleteContext(rc);
return NULL;
}
return rc;
}
#if USE_DEBUG_MODE
static void APIENTRY OpenGLDebugCallback(
GLenum source, GLenum type, GLuint id, GLenum severity,
GLsizei length, const GLchar *message, const void* user)
{
OutputDebugStringA(message);
OutputDebugStringA("\n");
if (severity >= GL_DEBUG_SEVERITY_LOW_ARB && severity <= GL_DEBUG_SEVERITY_HIGH_ARB)
{
assert(0);
}
}
#endif
static int StringsAreEqual(const char* src, const char* dst, size_t dstlen)
{
while (*src && dstlen-- && *dst)
{
if (*src++ != *dst++)
{
return 0;
}
}
return (dstlen && *src == *dst) || (!dstlen && *src == 0);
}
static HGLRC CreateOpenGLContext(HDC dc)
{
PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = NULL;
PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = NULL;
PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL;
int wgl_ARB_multisample = 0;
int wgl_ARB_framebuffer_sRGB = 0;
int wgl_EXT_swap_control_tear = 0;
HWND wnd = CreateWindowExW(0, L"STATIC", L"Dummy Window", WS_OVERLAPPED,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, NULL, NULL);
if (wnd)
{
HDC dc = GetDC(wnd);
if (dc)
{
HGLRC rc = CreateOldOpenGLContext(dc);
if (rc)
{
PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB =
(void*)wglGetProcAddress("wglGetExtensionsStringARB");
if (wglGetExtensionsStringARB != (void*)0 &&
wglGetExtensionsStringARB != (void*)1 &&
wglGetExtensionsStringARB != (void*)2 &&
wglGetExtensionsStringARB != (void*)3 &&
wglGetExtensionsStringARB != (void*)-1)
{
const char* ext = wglGetExtensionsStringARB(dc);
if (ext)
{
const char* start = ext;
for (;;)
{
while (*ext != 0 && *ext != ' ')
{
ext++;
}
size_t length = ext - start;
if (StringsAreEqual("WGL_ARB_pixel_format", start, length))
{
wglChoosePixelFormatARB = (void*)wglGetProcAddress("wglChoosePixelFormatARB");
}
else if (StringsAreEqual("WGL_ARB_create_context", start, length))
{
wglCreateContextAttribsARB = (void*)wglGetProcAddress("wglCreateContextAttribsARB");
}
else if (StringsAreEqual("WGL_EXT_swap_control", start, length))
{
wglSwapIntervalEXT = (void*)wglGetProcAddress("wglSwapIntervalEXT");
}
else if (StringsAreEqual("WGL_ARB_framebuffer_sRGB", start, length))
{
wgl_ARB_framebuffer_sRGB = 1;
}
else if (StringsAreEqual("WGL_ARB_multisample", start, length))
{
wgl_ARB_multisample = 1;
}
else if (StringsAreEqual("WGL_ARB_framebuffer_sRGB", start, length))
{
wgl_ARB_framebuffer_sRGB = 1;
}
else if (StringsAreEqual("WGL_EXT_swap_control_tear", start, length))
{
wgl_EXT_swap_control_tear = 1;
}
if (*ext == 0)
{
break;
}
ext++;
start = ext;
}
}
}
wglMakeCurrent(NULL, NULL);
wglDeleteContext(rc);
}
ReleaseDC(wnd, dc);
}
DestroyWindow(wnd);
}
HGLRC rc = NULL;
if (wglCreateContextAttribsARB && wglChoosePixelFormatARB)
{
int attrib[32];
int* p = attrib;
*p++ = WGL_DRAW_TO_WINDOW_ARB; *p++ = GL_TRUE;
*p++ = WGL_ACCELERATION_ARB; *p++ = WGL_FULL_ACCELERATION_ARB;
*p++ = WGL_SUPPORT_OPENGL_ARB; *p++ = GL_TRUE;
*p++ = WGL_DOUBLE_BUFFER_ARB; *p++ = GL_TRUE;
*p++ = WGL_PIXEL_TYPE_ARB; *p++ = WGL_TYPE_RGBA_ARB;
*p++ = WGL_COLOR_BITS_ARB; *p++ = 24;
if (WINDOW_DEPTH)
{
*p++ = WGL_DEPTH_BITS_ARB;
*p++ = 24;
}
if (WINDOW_STENCIL)
{
*p++ = WGL_STENCIL_BITS_ARB;
*p++ = 8;
}
if (WINDOW_SRGB && wgl_ARB_framebuffer_sRGB)
{
*p++ = WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB;
*p++ = GL_TRUE;
}
if (WINDOW_MSAA && wgl_ARB_multisample)
{
*p++ = WGL_SAMPLE_BUFFERS_ARB;
*p++ = 1;
*p++ = WGL_SAMPLES_ARB;
*p++ = WINDOW_MSAA;
}
*p = 0;
int format;
UINT formats;
if (!wglChoosePixelFormatARB(dc, attrib, NULL, 1, &format, &formats) || formats == 0)
{
LogWin32Error("wglChoosePixelFormatARB failed");
}
else
{
PIXELFORMATDESCRIPTOR pfd =
{
.nSize = sizeof(pfd),
};
if (!DescribePixelFormat(dc, format, sizeof(pfd), &pfd))
{
LogWin32Error("DescribePixelFormat failed");
return NULL;
}
else
{
if (!SetPixelFormat(dc, format, &pfd))
{
LogWin32Error("SetPixelFormat failed");
}
else
{
int ctx[] =
{
WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
WGL_CONTEXT_MINOR_VERSION_ARB, 0,
#if USE_DEBUG_MODE
WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_DEBUG_BIT_ARB,
#endif
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
0,
};
rc = wglCreateContextAttribsARB(dc, NULL, ctx);
if (!rc)
{
LogWin32Error("wglCreateContextAttribsARB failed");
}
else
{
if (!wglMakeCurrent(dc, rc))
{
LogWin32Error("wglMakeCurrent failed");
wglDeleteContext(rc);
rc = NULL;
}
else
{
if (WINDOW_MSAA && wgl_ARB_multisample)
{
GL_CHECK(glEnable(GL_MULTISAMPLE_ARB));
}
}
}
}
}
}
}
if (!rc)
{
OutputDebugStringA("Failed to create modern OpenGL context, retrying with legacy context!\n");
rc = CreateOldOpenGLContext(dc);
}
if (rc)
{
if (WINDOW_VSYNC && wglSwapIntervalEXT)
{
if (!wglSwapIntervalEXT(wgl_EXT_swap_control_tear ? -1 : 1))
{
LogWin32Error("wglSwapIntervalEXT failed");
}
}
#if USE_DEBUG_MODE
const GLubyte* ext;
GL_CHECK( ext = glGetString(GL_EXTENSIONS) );
if (ext)
{
const GLubyte* start = ext;
for (;;)
{
while (*ext != 0 && *ext != ' ')
{
ext++;
}
size_t length = ext - start;
if (StringsAreEqual("GL_ARB_debug_output", (const char*)start, length))
{
PFNGLDEBUGMESSAGECALLBACKARBPROC glDebugMessageCallbackARB =
(void*)wglGetProcAddress("glDebugMessageCallbackARB");
GL_CHECK( glDebugMessageCallbackARB(OpenGLDebugCallback, NULL) );
GL_CHECK( glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB) );
break;
}
if (*ext == 0)
{
break;
}
ext++;
start = ext;
}
}
#endif
}
return rc;
}
struct Win32Context
{
HDC dc;
HGLRC rc;
};
static LRESULT CALLBACK WindowProc(HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
struct Win32Context* ctx = (void*)GetWindowLongPtr(wnd, GWLP_USERDATA);
switch (msg)
{
case WM_CREATE:
ctx = ((CREATESTRUCTW*)lparam)->lpCreateParams;
SetWindowLongPtr(wnd, GWLP_USERDATA, (LONG_PTR)ctx);
if (!(ctx->dc = GetDC(wnd)))
{
LogWin32Error("GetDC failed");
return -1;
}
if (!(ctx->rc = CreateOpenGLContext(ctx->dc)))
{
ReleaseDC(wnd, ctx->dc);
return -1;
}
RenderInit();
return 0;
case WM_DESTROY:
if (ctx->rc)
{
RenderDone();
wglMakeCurrent(NULL, NULL);
wglDeleteContext(ctx->rc);
}
if (ctx->dc)
{
ReleaseDC(wnd, ctx->dc);
}
PostQuitMessage(0);
return 0;
case WM_SIZE:
RenderResize(LOWORD(lparam), HIWORD(lparam));
return 0;
}
return DefWindowProcW(wnd, msg, wparam, lparam);
}
int WINAPI WinMain(HINSTANCE instance, HINSTANCE prev_instance, LPSTR cmd_line, int cmd_show)
{
WNDCLASSEXW wc =
{
.cbSize = sizeof(wc),
.lpfnWndProc = WindowProc,
.hInstance = instance,
.hIcon = LoadIconA(NULL, IDI_APPLICATION),
.hCursor = LoadCursorA(NULL, IDC_ARROW),
.lpszClassName = L"opengl_window_class",
};
if (!RegisterClassExW(&wc))
{
LogWin32Error("RegisterClassEx failed");
}
else
{
int width = CW_USEDEFAULT;
int height = CW_USEDEFAULT;
DWORD exstyle = WS_EX_APPWINDOW;
DWORD style = WS_OVERLAPPEDWINDOW;
if (WINDOW_WIDTH && WINDOW_HEIGHT)
{
style &= ~WS_THICKFRAME & ~WS_MAXIMIZEBOX;
RECT rect = { 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT };
if (!AdjustWindowRectEx(&rect, style, FALSE, exstyle))
{
LogWin32Error("AdjustWindowRectEx failed");
style = WS_OVERLAPPEDWINDOW;
}
else
{
width = rect.right - rect.left;
height = rect.bottom - rect.top;
}
}
struct Win32Context ctx;
HWND wnd = CreateWindowExW(exstyle, wc.lpszClassName, L"OpenGL Window",
style | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, width, height,
NULL, NULL, wc.hInstance, &ctx);
if (!wnd)
{
LogWin32Error("CreateWindow failed");
}
else
{
for (;;)
{
MSG msg;
if (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
{
break;
}
TranslateMessage(&msg);
DispatchMessageW(&msg);
continue;
}
RenderFrame();
if (!SwapBuffers(ctx.dc))
{
LogWin32Error("SwapBuffers failed");
}
}
}
UnregisterClassW(wc.lpszClassName, wc.hInstance);
}
return 0;
}

433
mmozeiko/xlib_opengl.c Normal file
View File

@ -0,0 +1,433 @@
// example how to set up modern OpenGL context with fallback to legacy context
// compile: gcc xlib_opengl.c -lX11 -lGL -o xlib_opengl
// set to 0 to create resizable window
#define WINDOW_WIDTH 1280
#define WINDOW_HEIGHT 720
// do you need depth buffer?
#define WINDOW_DEPTH 1
// do you need stencil buffer?
#define WINDOW_STENCIL 0
// use sRGB for color buffer
#define WINDOW_SRGB 1
// do you need multisampling?
// to disable set to 0, to enable set to 2, 4, 8, 16, ...
#define WINDOW_MSAA 4
// do you need vsync?
#define WINDOW_VSYNC 1
// keep this enabled when debugging
#define USE_DEBUG_MODE 1
// replace this with your favorite assert() implementation
#include <assert.h>
#include <stdio.h>
#include <X11/Xlib.h>
#include <GL/gl.h>
#include <GL/glx.h>
#include "glext.h" // download from https://www.opengl.org/registry/api/GL/glext.h
#include "glxext.h" // download from https://www.opengl.org/registry/api/GL/glxext.h
// https://www.opengl.org/registry/specs/ARB/glx_create_context.txt
// https://cgit.freedesktop.org/mesa/mesa/plain/docs/specs/MESA_swap_control.spec
// https://www.opengl.org/registry/specs/SGI/swap_control.txt
// https://www.opengl.org/registry/specs/EXT/swap_control.txt
// https://www.opengl.org/registry/specs/EXT/glx_swap_control_tear.txt
// https://www.opengl.org/registry/specs/ARB/framebuffer_sRGB.txt
// https://www.opengl.org/registry/specs/ARB/multisample.txt
// https://www.opengl.org/registry/specs/ARB/debug_output.txt
#if USE_DEBUG_MODE
#define GL_CHECK(x) do \
{ \
x; \
GLenum err = glGetError(); \
assert(err == GL_NO_ERROR); \
} while (0)
#else
#define GL_CHECK(x) x
#endif
// called after context is set up (only once)
// for example, load GL extensions here & set up GL state
static void RenderInit(void)
{
GL_CHECK( glClearColor(100.f/255.f, 149.f/255.f, 237.f/255.f, 1.f) );
}
// called before render is destroyed
static void RenderDone(void)
{
}
// called when window is resized
static void RenderResize(unsigned width, unsigned height)
{
GL_CHECK( glViewport(0, 0, width, height) );
}
// called every frame before swapping buffers
static void RenderFrame(void)
{
GL_CHECK( glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT) );
// use legacy GL for drawing to display a triangle
glBegin(GL_TRIANGLES);
glColor3f(1.f, 0.f, 0.f);
glVertex2f(0.f, 0.5f);
glColor3f(0.f, 1.f, 0.f);
glVertex2f(0.5f, -0.5f);
glColor3f(0.f, 0.f, 1.f);
glVertex2f(-0.5f, -0.5f);
GL_CHECK( glEnd() );
}
#if USE_DEBUG_MODE
static void APIENTRY OpenGLDebugCallback(
GLenum source, GLenum type, GLuint id, GLenum severity,
GLsizei length, const GLchar *message, const void* user)
{
fprintf(stderr, "%s\n", message);
if (severity >= GL_DEBUG_SEVERITY_LOW_ARB && severity <= GL_DEBUG_SEVERITY_HIGH_ARB)
{
assert(0);
}
}
#endif
static int StringsAreEqual(const char* src, const char* dst, size_t dstlen)
{
while (*src && dstlen-- && *dst)
{
if (*src++ != *dst++)
{
return 0;
}
}
return (dstlen && *src == *dst) || (!dstlen && *src == 0);
}
int main(void)
{
Display* display = XOpenDisplay(NULL);
if (!display)
{
fprintf(stderr, "XOpenDisplay failed!\n");
}
else
{
int glx_ARB_create_context = 0;
int glx_ARB_multisample = 0;
int glx_ARB_framebuffer_sRGB = 0;
int glx_SGI_swap_control = 0;
int glx_MESA_swap_control = 0;
int glx_EXT_swap_control = 0;
int glx_EXT_swap_control_tear = 0;
const char* glx = glXQueryExtensionsString(display, DefaultScreen(display));
if (!glx)
{
fprintf(stderr, "glXQueryExtensionsString failed!\n");
}
else
{
const char* start = glx;
for (;;)
{
while (*glx != 0 && *glx != ' ')
{
glx++;
}
size_t length = glx - start;
if (StringsAreEqual("GLX_ARB_create_context", start, length))
{
glx_ARB_create_context = 1;
}
else if (StringsAreEqual("GLX_ARB_multisample", start, length))
{
glx_ARB_multisample = 1;
}
else if (StringsAreEqual("GLX_ARB_framebuffer_sRGB", start, length))
{
glx_ARB_framebuffer_sRGB = 1;
}
else if (StringsAreEqual("GLX_SGI_swap_control", start, length))
{
glx_SGI_swap_control = 1;
}
else if (StringsAreEqual("GLX_MESA_swap_control", start, length))
{
glx_MESA_swap_control = 1;
}
else if (StringsAreEqual("GLX_EXT_swap_control", start, length))
{
glx_EXT_swap_control = 1;
}
else if (StringsAreEqual("GLX_EXT_swap_control_tear", start, length))
{
glx_EXT_swap_control_tear = 1;
}
if (*glx == 0)
{
break;
}
glx++;
start = glx;
}
}
int attr[32];
int* p = attr;
*p++ = GLX_X_VISUAL_TYPE; *p++ = GLX_TRUE_COLOR;
*p++ = GLX_DOUBLEBUFFER; *p++ = True;
*p++ = GLX_RED_SIZE; *p++ = 8;
*p++ = GLX_GREEN_SIZE; *p++ = 8;
*p++ = GLX_BLUE_SIZE; *p++ = 8;
*p++ = GLX_DEPTH_SIZE; *p++ = WINDOW_DEPTH ? 24 : 0;
*p++ = GLX_STENCIL_SIZE; *p++ = WINDOW_STENCIL ? 8 : 0;
if (WINDOW_SRGB && glx_ARB_framebuffer_sRGB)
{
*p++ = GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB;
*p++ = True;
}
if (WINDOW_MSAA && glx_ARB_multisample)
{
*p++ = GLX_SAMPLE_BUFFERS_ARB;
*p++ = 1;
*p++ = GLX_SAMPLES_ARB;
*p++ = WINDOW_MSAA;
}
*p++ = 0;
int count;
GLXFBConfig* config = glXChooseFBConfig(display, DefaultScreen(display), attr, &count);
if (!config || count == 0)
{
fprintf(stderr, "glXChooseFBConfig failed!\n");
}
else
{
XVisualInfo* info = glXGetVisualFromFBConfig(display, config[0]);
if (!info)
{
fprintf(stderr, "glXGetVisualFromFBConfig failed!\n");
}
else
{
Window root = DefaultRootWindow(display);
Colormap cmap = XCreateColormap(display, root, info->visual, AllocNone);
if (!cmap)
{
fprintf(stderr, "XCreateColormap failed!\n");
}
else
{
XSetWindowAttributes swa;
swa.colormap = cmap;
swa.event_mask = StructureNotifyMask;
Window window = XCreateWindow(
display, root, 0, 0,
WINDOW_WIDTH ? WINDOW_WIDTH : 800,
WINDOW_HEIGHT ? WINDOW_HEIGHT : 450,
0, info->depth, InputOutput, info->visual,
CWColormap | CWEventMask, &swa);
if (!window)
{
fprintf(stderr, "XCreateWindow failed!\n");
}
else
{
if (WINDOW_WIDTH && WINDOW_HEIGHT)
{
XSizeHints* hints = XAllocSizeHints();
if (!hints)
{
fprintf(stderr, "XAllocSizeHints failed!\n");
}
else
{
hints->flags |= PMinSize | PMaxSize;
hints->min_width = hints->max_width = WINDOW_WIDTH;
hints->min_height = hints->max_height = WINDOW_HEIGHT;
XSetWMNormalHints(display, window, hints);
XFree(hints);
}
}
Atom WM_DELETE_WINDOW = XInternAtom(display, "WM_DELETE_WINDOW", False);
XSetWMProtocols(display, window, &WM_DELETE_WINDOW, 1);
XStoreName(display, window, "OpenGL window");
XMapWindow(display, window);
GLXContext ctx = NULL;
if (glx_ARB_create_context)
{
int cattr[] =
{
GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
GLX_CONTEXT_MINOR_VERSION_ARB, 0,
#if USE_DEBUG_MODE
GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_DEBUG_BIT_ARB,
#endif
GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
None,
};
PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB =
(PFNGLXCREATECONTEXTATTRIBSARBPROC)glXGetProcAddressARB(
(const GLubyte*)"glXCreateContextAttribsARB");
ctx = glXCreateContextAttribsARB(display, config[0], 0, True, cattr);
if (!ctx)
{
fprintf(stderr, "Failed to create modern OpenGL context, trying legacy context!\n");
}
}
if (!ctx)
{
ctx = glXCreateContext(display, info, NULL, GL_TRUE);
}
if (!ctx)
{
fprintf(stderr, "glXCreateContext failed!\n");
}
else
{
if (!glXMakeCurrent(display, window, ctx))
{
fprintf(stderr, "glXMakeCurrent failed!\n");
}
else
{
if (WINDOW_VSYNC)
{
if (glx_EXT_swap_control)
{
PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT =
(PFNGLXSWAPINTERVALEXTPROC)glXGetProcAddressARB(
(const GLubyte*)"glXSwapIntervalEXT");
glXSwapIntervalEXT(display, window, glx_EXT_swap_control_tear ? -1 : 1);
}
else if (glx_MESA_swap_control)
{
PFNGLXSWAPINTERVALMESAPROC glXSwapIntervalMESA =
(PFNGLXSWAPINTERVALMESAPROC)glXGetProcAddressARB(
(const GLubyte*)"glXSwapIntervalMESA");
if (glXSwapIntervalMESA(1))
{
fprintf(stderr, "glXSwapIntervalMESA failed!\n");
}
}
else if (glx_SGI_swap_control)
{
PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalSGI =
(PFNGLXSWAPINTERVALSGIPROC)glXGetProcAddressARB(
(const GLubyte*)"glXSwapIntervalSGI");
if (glXSwapIntervalSGI(1))
{
fprintf(stderr, "glXSwapIntervalSGI failed!\n");
}
}
}
#if USE_DEBUG_MODE
const GLubyte* ext;
GL_CHECK( ext = glGetString(GL_EXTENSIONS) );
if (ext)
{
const GLubyte* start = ext;
for (;;)
{
while (*ext != 0 && *ext != ' ')
{
ext++;
}
size_t length = ext - start;
if (StringsAreEqual("GL_ARB_debug_output", (const char*)start, length))
{
PFNGLDEBUGMESSAGECALLBACKARBPROC glDebugMessageCallbackARB =
(PFNGLDEBUGMESSAGECALLBACKARBPROC)glXGetProcAddressARB((const GLubyte*)"glDebugMessageCallbackARB");
GL_CHECK( glDebugMessageCallbackARB(OpenGLDebugCallback, NULL) );
GL_CHECK( glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB) );
break;
}
if (*ext == 0)
{
break;
}
ext++;
start = ext;
}
}
#endif
if (WINDOW_MSAA && glx_ARB_multisample)
{
GL_CHECK( glEnable(GL_MULTISAMPLE_ARB) );
}
RenderInit();
for (;;)
{
if (XPending(display))
{
XEvent event;
XNextEvent(display, &event);
if (event.type == ConfigureNotify)
{
RenderResize(event.xconfigure.width, event.xconfigure.height);
}
else if (event.type == ClientMessage)
{
if ((Atom)event.xclient.data.l[0] == WM_DELETE_WINDOW)
{
break;
}
}
continue;
}
RenderFrame();
glXSwapBuffers(display, window);
}
RenderDone();
glXMakeCurrent(display, None, NULL);
}
glXDestroyContext(display, ctx);
}
XDestroyWindow(display, window);
}
XFreeColormap(display, cmap);
}
XFree(info);
}
XFree(config);
}
XCloseDisplay(display);
}
return 0;
}