Files

209 lines
11 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Bars 32-48: how it works. Pipeline, frontend, shaders, two backends."""
import math
from anim import *
from kit import *
from devices import *
from engine import LW, LH, hexc
STAGES = [('YOUR APP', 'OpenGL 4.6 calls'), ('FRONTEND', 'state + validation'), ('MGPIPE', 'records'),
('BACKEND', 'Vulkan · OpenGL ES'), ('GPU', 'pixels')]
SX = [220, 590, 960, 1330, 1700]
TOKENS = ['glDrawElements(...)', 'valid · state Δ', 'record #4217', 'vkCmdDrawIndexed', 'pixels!']
def pipeline(E, f, lb):
plate(E, f, 'ink', drift=(30, 0))
E.text('THE PIPELINE', 130, 150, 34, a(GLUE, 0.9), font='mono', wght=800, align='left')
y = 520
for i, (name, sub) in enumerate(STAGES):
bi = 4 + i
if lb < bi:
continue
u = out_back((lb - bi) / 0.35)
hot = 0.0
hop = (lb - 8) / 2.0
if 0 <= hop and int(hop) == i and lb >= 8:
hot = 1 - (hop % 1)
iso = Iso(SX[i], y + 40, 54 * u)
top = mix(FACE_DARK[0], scale_rgb(GLUE, 1.3), hot * 0.8)
iso.box(E, -1.6, -1.6, 0, 3.2, 3.2, 1.4, top, FACE_DARK[1], FACE_DARK[2], OUTL, 5)
E.text(name, SX[i], y + 210, 40, a(BONE, u), wght=800, wdth=105)
E.text(sub, SX[i], y + 258, 22, a(BONE, 0.6 * u), font='mono')
if i < 4 and lb >= bi + 0.3:
arrow(E, SX[i] + 120, y + 40, SX[i + 1] - 120, y + 40, 6, a(GLUE, 0.8), u=(lb - bi - 0.3) / 0.4)
if lb >= 8: # the call rides a glue drop through the stages
hop = min((lb - 8) / 2.0, 4.0)
i = int(hop)
fr = hop - i
j = min(i + 1, 4)
x = lerp(SX[i], SX[j], in_out_cubic(fr / 0.6) if fr < 0.6 else 1.0)
yy = y - 120 - 140 * bump(min(fr / 0.6, 1.0)) - 6 * f.kick
stage = j if fr >= 0.6 else i
goo(E, [(x, yy, 34 + 6 * f.kick)], k=20, outline=5, bevel=20, glow=0.3)
chip(E, x, yy - 80, TOKENS[stage], 26, INK, GLUE if stage < 4 else PALE)
if lb >= 16 - 0.3:
for k in range(14):
ang = k * 2 * math.pi / 14 + f.lt
rr = 90 + 260 * out_expo((lb - 15.7) / 0.6)
E.rect(SX[4] + math.cos(ang) * rr, y + 40 + math.sin(ang) * rr * 0.6, 26, 26,
[GLUE, MINT, AMBER, CORAL, PALE][k % 5], r=4, rot=ang)
STATE = ['DEPTH_TEST on', 'BLEND off', 'CULL_FACE back', 'VAO 3', 'PROGRAM 12', 'TEX0 2D:7', 'VIEWPORT 1920×1080',
'STENCIL off', 'FBO 2', 'SCISSOR off', 'UBO0 5', 'SSBO1 9', 'DEPTH_FUNC less', 'COLOR_MASK rgba',
'BLEND_EQ add', 'FRONT ccw', 'SAMPLER0 4', 'TEX1 cube:2', 'DRAW_BUF 0', 'PRIM_RESTART off', 'POLYGON fill',
'PATCH_VERTS 3', 'SRGB on', 'LINE 1.0']
ALT = ['DEPTH_TEST off', 'BLEND on', 'CULL_FACE front', 'VAO 8', 'PROGRAM 4', 'TEX0 2D:11', 'VIEWPORT 512×512',
'STENCIL on', 'FBO 0', 'SCISSOR on', 'UBO0 2', 'SSBO1 3', 'DEPTH_FUNC lequal', 'COLOR_MASK rgb_',
'BLEND_EQ max', 'FRONT cw', 'SAMPLER0 9', 'TEX1 cube:6', 'DRAW_BUF 1', 'PRIM_RESTART on', 'POLYGON line',
'PATCH_VERTS 4', 'SRGB off', 'LINE 2.0']
def frontend(E, f, lb):
plate(E, f, 'mint', drift=(0, 20))
E.text('FRONTEND', 120, 170, 150, INK, wght=900, align='left', wdth=105)
E.text('MG_State · MG_Impl', 130, 270, 28, a(INK, 0.7), font='mono', wght=700, align='left')
if lb < 24:
slam(E, f, 'TRACKS EVERY BIT OF STATE', 1800, 290, f.t - (lb - 16.5) * S.BEAT, 54, MOSS, align='right')
step = int(lb * 4)
for k in range(24):
r, c = k // 4, k % 4
x = 300 + c * 440
yy = 420 + r * 95
flips = sum(1 for s in range(int(16 * 4), step + 1) if rnd('flip', s) * 24 // 1 == k)
alt = flips % 2 == 1
recent = any(rnd('flip', s) * 24 // 1 == k for s in range(step - 1, step + 1))
col = NEON if recent else (MOSS if alt else INK)
E.rect(x, yy, 410, 78, a(PALE, 0.95) if not recent else a(INK, 0.95), r=12)
E.text((ALT if alt else STATE)[k], x, yy, 26, col if recent else INK, font='mono', wght=600)
else:
slam(E, f, 'VALIDATES EVERY CALL', 1800, 290, f.t - (lb - 24) * S.BEAT, 54, MOSS, align='right')
calls = ['glBindBuffer(GL_ARRAY_BUFFER, 4);', 'glBufferSubData(GL_ARRAY_BUFFER, 0, 256, data);',
'glUseProgram(12);', 'glUniformMatrix4fv(0, 1, GL_FALSE, mvp);', 'glBindVertexArray(3);',
'glBindVertexArray(999);', 'glBindTexture(GL_TEXTURE_2D, 7);',
'glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);']
BAD = 5
E.rect(960, 680, 1640, 600, INK, r=24)
# the checker steps down the list, one call per 8th; it stops dead on the bad one
step = (lb - 24.5) * 2
cur = BAD if step >= BAD else int(max(step, -1))
after = lb - (24.5 + BAD * 0.5) # beats since the bad call was hit
if after >= 2.0: # then carries on past it
cur = min(len(calls) - 1, BAD + 1 + int((after - 2.0) * 2))
for i, c in enumerate(calls):
yy = 420 + i * 66
checked = i < cur or (i == cur and (step - int(step)) > 0.5) or (i < BAD + 1 and after >= 0 and i != BAD and i < cur + 1)
if i == BAD and after >= 0:
shake = 10 * math.exp(-after * 6) * math.sin(after * 60)
E.rect(960 + shake, yy, 1612, 56, a(CORAL, 0.35 + 0.35 * math.exp(-after * 3)), r=8)
code_line(E, c, 210 + shake, yy, 30)
cross(E, 172 + shake, yy, 15, CORAL, 5)
continue
if i == cur:
E.rect(960, yy, 1612, 56, a(GLUE, 0.22), r=8, stroke=a(GLUE, 0.9), sw=3)
code_line(E, c, 210, yy, 30, alpha=1.0 if i <= cur else 0.4)
if i < cur or (i > BAD and i <= cur and i != cur):
check(E, 172, yy, 16, GLUE, 5)
if after >= 0.5:
stamp(E, f, 'GL_INVALID_OPERATION', 1250, 420 + BAD * 66 - 8, f.t - (after - 0.5) * S.BEAT, 54, CORAL,
rot=-0.05)
if after >= 1.0:
E.text('caught in the frontend, before the driver ever sees it', 960, 1020, 30,
a(INK, clamp01((after - 1.0) / 0.5)), font='mono', wght=600)
GLSL = ['#version 460 core', 'layout(location = 0) in vec3 pos;', 'uniform mat4 mvp;', 'void main() {',
' gl_Position = mvp * vec4(pos, 1.0);', '}']
ESSL = ['#version 320 es', 'precision highp float;', 'layout(location = 0) in vec3 pos;', 'uniform mat4 mvp;',
'void main() { ... }']
SPV = ['0x07230203', '0x00010600', '0x0008000b', '0x0000002e', '0x00000000', '0x00020011', '0x00000001',
'0x0006000b', '0x00000001', '0x4c534c47', '0x6474732e', '0x3035342e', '0x00000000', '0x0003000e']
def shaders(E, f, lb):
plate(E, f, 'ink')
E.text('SHADERS', 120, 150, 120, BONE, wght=900, align='left', wdth=105)
E.text('one compiler front, two ways out', 130, 245, 28, a(GLUE, 0.85), font='mono', wght=600, align='left')
# GLSL panel
E.rect(390, 640, 680, 420, INK2, r=20, stroke=a(GLUE, 0.6), sw=3)
E.text('GLSL 4.60', 80, 465, 26, GLUE, font='mono', wght=800, align='left')
total = sum(len(s_) for s_ in GLSL)
n = int(total * clamp01((lb - 32) / 2.0))
for i, s_ in enumerate(GLSL):
before = sum(len(x) for x in GLSL[:i])
code_line(E, s_, 80, 525 + i * 52, 25, chars=n - before)
if lb >= 35:
arrow(E, 745, 640, 850, 640, 7, GLUE, u=(lb - 35) / 0.3)
chip(E, 800, 570, 'glslang', 26, INK, GLUE, sc=out_back((lb - 35) / 0.3))
E.rect(1010, 640, 300, 420, INK2, r=20, stroke=a(GLUE, 0.6), sw=3)
E.text('SPIR-V', 880, 465, 26, GLUE, font='mono', wght=800, align='left')
k = int((lb - 35) * 6)
for i in range(9):
w = SPV[(i + k) % len(SPV)]
E.text(w, 1010, 520 + i * 40 - ((lb * 6) % 1) * 40, 26, a(AMBER, 0.9), font='mono', wght=600)
E.rect(1010, 452, 300, 30, INK) # crop the scrolling words at the panel top
E.text('SPIR-V', 880, 465, 26, GLUE, font='mono', wght=800, align='left')
if lb >= 40:
u1 = (lb - 40) / 0.4
arrow(E, 1170, 560, 1290, 420, 7, GLUE, u=u1)
arrow(E, 1170, 720, 1290, 830, 7, GLUE, u=u1)
uu = out_back((lb - 40.3) / 0.35)
if lb >= 40.3:
E.rect(1580, 380, 520 * uu, 210 * uu, GLUE, r=22)
E.text('VULKAN', 1580, 345, 70 * uu, INK, wght=900)
E.text('SPIR-V, as-is · Magma', 1580, 420, 28 * uu, INK, font='mono', wght=700)
E.rect(1580, 830, 520 * uu, 330 * uu, INK2, r=22, stroke=a(MINT, 0.8), sw=3)
E.text('OPENGL ES', 1580, 710, 54 * uu, MINT, wght=900)
for i, s_ in enumerate(ESSL):
code_line(E, s_, 1345, 770 + i * 38, 22, alpha=uu, chars=int(len(s_) * clamp01((lb - 41) / 1.5)))
chip(E, 1580, 1030, 'SPIRV-Cross → GLSL ES · Espryt', 24, INK, MINT, sc=uu)
def backends(E, f, lb):
"""Split screen: Magma/Vulkan vs Espryt/GLES, same cube, the switch flips each beat."""
side = int(lb) % 2 # 0 = Vulkan active
E.rect(480, 540, 960, 1080, INK)
E.rect(1440, 540, 960, 1080, MINT)
dot_grid(E, f, a(MOSS, 0.5), 60, 2.0)
yaw = f.lt * 0.9
sq = 0.18 * f.kick
cubes(E, f, [(480, 520, 150, yaw, sq, 0.4 * f.kick, 0.0, 1.0)], deep=hexc('#123a18'))
cubes(E, f, [(1440, 520, 150, yaw, sq, 0.4 * f.kick, 0.0, 1.0)], deep=hexc('#2e6e2f'))
E.text('MAGMA', 480, 160, 120, BONE, wght=900, wdth=110)
E.text('→ VULKAN', 480, 250, 40, GLUE, font='mono', wght=800)
E.text('ESPRYT', 1440, 160, 120, INK, wght=900, wdth=110)
E.text('→ OPENGL ES', 1440, 250, 40, MOSS, font='mono', wght=800)
# dim the inactive half
if lb < 60:
E.rect(480 + 960 * (1 - side), 540, 960, 1080, a(INK, 0.45))
# the switch
E.rect(960, 870, 980, 110, INK2, r=55, stroke=a(GLUE, 0.7), sw=3)
E.text('MOBILEGL_BACKEND_TYPE=', 560, 870, 30, BONE, font='mono', wght=700, align='left')
E.text('DirectVulkan' if side == 0 else 'DirectGLES', 1000, 870, 30, NEON, font='mono', wght=800, align='left')
E.text('identical frames, either way', 960, 980, 26, a(BONE, 0.7), font='mono')
if lb >= 60:
words = [('SAME APP.', 60), ('SAME FRAME.', 61), ('YOUR PICK.', 62)]
k = min(int(lb - 60), 2)
w, b = words[k]
E.rect(960, 540, 1920, 330, a(INK, 0.85))
slam(E, f, w, 960, 540, f.t - (lb - b) * S.BEAT, fit_size(E, w, 1500, cap=240), NEON if k == 2 else BONE)
def how(E, f):
lb = f.lbeat
P = {'bloom': 0.5, 'grain': 0.032, 'ca': 0.25 * f.kick}
if lb < 4:
plate(E, f, 'ink')
slam(E, f, 'SO…', 960, 300, f.t - lb * S.BEAT, 200, a(BONE, 0.8))
if lb >= 1:
slam(E, f, 'HOW?', 960, 640, f.t - (lb - 1) * S.BEAT, 440, NEON, punch=0.3)
elif lb < 16:
pipeline(E, f, lb)
elif lb < 32:
frontend(E, f, lb)
elif lb < 48:
shaders(E, f, lb)
else:
backends(E, f, lb)
return P