"""EXPERIMENTAL modifier-aware OFFLINE G1 validator; importable offline, no launch or writer. WHY/CONTRACT/INVARIANT: parse actual bounded publications, never trust a PASS field or extents for regions; rejected metadata gives no native qualification. """ import hashlib import json import struct import os from pathlib import Path WIDTH, HEIGHT = 800, 600 LIMIT = 16633312 META_LIMIT = 1024 * 1024 NAMES = ('main-before-blur','down-0','down-1','down-2','up-2','up-1','up-0','cache-after-copy','output-presubmit') FRESH = ((800,600),(400,300),(200,150),(100,75),(200,150),(400,300),(800,600),(800,600),(800,600)) BXY = ((784,292),(392,142),(192,67),(92,29),(192,67),(392,142),(784,292),(784,292),(784,292)) GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, GL_UNSIGNED_NORMALIZED = 36064, 36161, 35863 # DRM little-endian AB24/XB24; actual import formats must be recorded. ABGR, XBGR, ARGB, XRGB = [int.from_bytes(x,'little') for x in (b'AB24',b'XB24',b'AR24',b'XR24')] class Rejected(ValueError): pass def need(v, msg): if not v: raise Rejected(msg) def sha(data): return hashlib.sha256(data).hexdigest() def plan(epoch, point): need(epoch in (1,2,3) and 0<=point<9,'epoch/point budget') if epoch==2 or point>=7: return (*BXY[point],16,16) if 3<=point<=5: return (0,0,*FRESH[point]) return (0,0,800,600) def read_plan(): total=sum(plan(e,p)[2]*plan(e,p)[3]*4 for e in (1,2,3) for p in range(9)) need(total==LIMIT and total<16*1024*1024,'planned raw budget') return dict(reads=27,bytes=total) def object_identity(o): return tuple(o[k] if k!='dimensions' else tuple(o[k]) for k in ('fx','wlr','fbo','rbo','image','dimensions')) def validate_object(o, role="effects"): need(o['dimensions']==[800,600] and all(o[k] for k in ('fx','wlr','fbo','rbo','image')) and type(o['tex']) is int and o['tex'] >= 0,'existing physical object') need(o['dmabuf_available'] is True and o['dmabuf_size']==[800,600] and len(o['planes']) in range(1,5),'real dmabuf') need(o['drm_format'] in ((XBGR,) if role=='cache' else (ABGR,) if role=='effects' else (ABGR,XBGR,ARGB,XRGB)),'source compatible actual DRM format') return validate_planes(o) AMD_G1_MODIFIER = '020000001046bb04' UINT32_MAX = (1 << 32) - 1 def validate_planes(o): """Bounded metadata sanity, not a proof of allocation extent or DCC layout. drm_fourcc.h SHA992f3380...: packed RGB has one image plane; AMD DCC without RETILE adds one auxiliary plane. Only the observed AMD modifier is allowlisted. No DCC size/alignment formula is inferred. """ modifier = o['modifier'] need(isinstance(modifier, str) and len(modifier) == 16 and all(c in '0123456789abcdef' for c in modifier), 'modifier encoding') need(modifier in ('0000000000000000', AMD_G1_MODIFIER), 'unknown modifier') planes = o['planes'] need(len(planes) == (2 if modifier == AMD_G1_MODIFIER else 1), 'modifier plane count') for p in planes: need(isinstance(p, list) and len(p) == 2 and all(type(n) is int and 0 <= n <= UINT32_MAX for n in p), 'pitch/offset uint32 fields') need(p[0] > 0 and p[1] + p[0] <= UINT32_MAX, 'nonzero pitch/offset overflow') pitch, offset = planes[0] need(pitch >= WIDTH * 4 and pitch % 4 == 0, 'packed image stride') image_end = offset + pitch * HEIGHT need(image_end <= UINT32_MAX, 'image extent overflow') if modifier == AMD_G1_MODIFIER: aux_pitch, aux_offset = planes[1] # This fixture only accepts a single-allocation, image-before-aux layout. # Exported FD identity/allocation lengths are absent: this is an explicit # bounded sanity restriction, not a universal DRM modifier requirement. need(aux_offset >= image_end and aux_offset > offset and planes[0] != planes[1], 'auxiliary offset/duplicate sanity') return dict(layout='AMD_GFX11_DCC_NO_RETILE', image_stride='PASS', auxiliary_fields='PLAUSIBLE_ONLY', auxiliary_extent='NOT_PROVEN', allocation_bounds='NOT_PROVEN', fd_identity='NOT_OBSERVED') return dict(layout='LINEAR_PACKED', image_stride='PASS', allocation_bounds='NOT_PROVEN') def validate_sample(s, raw, epoch, context, offset): p=s['point']; need(s['name']==NAMES[p] and s['roi']==list(plan(epoch,p)) and s['fresh']==list(FRESH[p]),'fixed ROI/fresh domain') x,y,w,h=s['roi'];need(x>=0 and y>=0 and w>0 and h>0 and x+w<=800 and y+h<=600,'ROI texture bounds') n=w*h*4;need(s['offset']==offset and s['bytes']==n and offset+n<=len(raw),'read framing/truncation') data=raw[offset:offset+n];need(sha(data)==s['sha256'],'sample SHA') need(s['before']==s['after'] and len(s['before'])==9,'READ/DRAW/RBO/PBO/pack/readbuffer restoration') need(s['context_before']==s['context_after']==context,'EGL context restoration') need(s['target_read_buffer']==GL_COLOR_ATTACHMENT0,'per-FBO readbuffer unchanged') a=s['attachment'];need(len(a)==8 and a[:3]==[GL_RENDERBUFFER,s['object']['rbo'],GL_UNSIGNED_NORMALIZED] and a[3]==9729 and a[4:7]==[8,8,8] and a[7] in (0,8),'actual attachment channels/type/encoding') need(len(s['renderbuffer'])==3 and s['renderbuffer'][:2]==[800,600] and s['renderbuffer'][2]>0,'actual RBO size/format') validate_object(s['object'],'cache' if p==7 else 'main' if p in (0,8) else 'effects') need(a[7]==(0 if s['object']['drm_format'] in (XBGR,XRGB) else 8),'alpha storage source compatibility') need(s['sentinel_alpha_written'] is True and 205 not in data[3::4],'sentinel incomplete read') need(type(s['before_ns']) is int and type(s['after_ns']) is int and 0=0] need(len(blur)==6 and [r['stage'] for r in blur]==list(range(6)),'six draws strict order') need([r['sequence'] for r in writes]==list(range(1,len(writes)+1)),'all writes ordered') objs=[samples[p]['object'] for p in range(7)] ping,pong=object_identity(objs[1]),object_identity(objs[2]) need(ping!=pong and objs[1]['wlr']!=objs[2]['wlr'] and objs[1]['fbo']!=objs[2]['fbo'],'distinct ping/pong alias') need([object_identity(o) for o in objs[1:]]==[ping,pong,ping,pong,ping,pong],'same physical buffers within pass') for i,r in enumerate(blur): need(r['region']==[[0,0,*FRESH[i+1]]] and r['box']==[0,0,800,600],'exact domain union, no holes') need(object_identity(r['src'])==object_identity(objs[i]) and object_identity(r['dst'])==object_identity(objs[i+1]),'source/destination continuity') need(r['texture']==r['src']['tex'] and r['src']['tex']>0,'actual source texture mapping') for seen,draw_obj in ((objs[i],r['src']),(objs[i+1],r['dst'])): need(seen['tex']==0 or seen['tex']==draw_obj['tex'],'existing texture replaced') need(r['src']['wlr']!=r['dst']['wlr'] and r['src']['fbo']!=r['dst']['fbo'],'feedback alias') need(r['fbo']==r['dst']['fbo'] and r['context']==context and r['viewport']==[0,0,800,600],'draw FBO/context/viewport') need(r['blend']==r['stencil']==r['scissor_enabled']==0 and r['color_mask']==[1,1,1,1],'unmasked raster write') need(r['sampler']==[9729,9729,33071,33071] and r['program']>0 and r['texture']>0,'effective sampler/program') u={x['name']:x for x in r['uniforms']} need(all(k in u for k in ('radius','halfpixel','proj','tex_proj')),'effective blur uniforms/matrices') floats=lambda n: [struct.unpack('f',struct.pack('I',b))[0] for b in u[n]['value']] need(floats('radius')==[5.0] and len(floats('proj'))==len(floats('tex_proj'))==9,'uniform radius/matrix') expected=(1/800,1/600) if i<3 else (1/3200,1/2400) need(floats('halfpixel')==[struct.unpack('f',struct.pack('f',x))[0] for x in expected],'effective halfpixel') fbo_ids={objs[1]['fbo'],objs[2]['fbo']} need(not any(r['stage']<0 and r['fbo'] in fbo_ids for r in writes),'external ping/pong write') need(not any(r['kind']=='clear' and r['mask']&16384 for r in rows),'color clear') need(not any(r['kind'] in ('destroy','replace','resize','migration') for r in rows),'object lifetime/layout change') copies=[r for r in rows if r['kind']=='copy'] cache=[r for r in copies if r['dst']['fbo']==samples[7]['object']['fbo']] need(len(cache)==1 and object_identity(cache[0]['src'])==pong and cache[0]['region']==[[0,0,800,600]],'cache copy exact source/domain') # Every copy includes its corresponding render write, not just a declaration. for c in copies: need(any(w['stage']==-1 and w['fbo']==c['dst']['fbo'] and w['region']==c['region'] for w in writes),'copy actual raster write missing') # Stages and reads alternate; no unseen color draw between output and sample. positions={id(r):i for i,r in enumerate(rows)} for i,r in enumerate(blur): begin=positions[id(samples[i])];end=positions[id(samples[i+1])] between=[w for w in writes if begin 0 and r['program'] > 0 and r['color_mask'] == [1]*4 and r['stencil'] == r['scissor_enabled'] == 0, 'G1 raster state') need(r['sampler'] in ([9729,9729,33071,33071], [9728,9728,33071,33071]), 'G1 effective sampler') uniforms = r['uniforms'] names = [u['name'] for u in uniforms] need(len(names) == len(set(names)) and all(n in names for n in ('tex','proj','tex_proj')), 'G1 unique sampler/matrix uniforms') u = {u['name']:u for u in uniforms} need(u['tex']['type'] == 35678 and u['tex']['value'] == [0], 'G1 sampler texture unit') need(all(u[n]['type'] == 35675 and len(u[n]['value']) == 9 for n in ('proj','tex_proj')), 'G1 matrix type/size') need(all(type(b) is int and 0 <= b <= UINT32_MAX for x in uniforms for b in x['value']), 'G1 uniform bit encoding') if r['stage'] >= 0: need(u['radius']['type'] == 5126 and u['halfpixel']['type'] == 35664 and len(u['halfpixel']['value']) == 2, 'G1 blur uniform types') else: need(u['alpha']['value'] == [1065353216] and u['discard_transparent']['value'] == [0], 'G1 copy/composition uniforms') for c, seq, src in zip(copies, (7,8), (samples[6]['object'], samples[0]['object'])): validate_object(c['src'], 'main') validate_object(c['dst'], 'main') w = writes[seq-1] need(object_identity(c['src']) == object_identity(src) and w['fbo'] == c['dst']['fbo'] and w['texture'] == c['src']['tex'] and w['region'] == c['region'] == [[0,0,WIDTH,HEIGHT]], 'G1 copy continuity') need(rows.index(w) < rows.index(c), 'G1 copy follows raster write') need(rows.index(copies[0]) < rows.index(samples[7]) < rows.index(writes[7]) and rows.index(writes[-1]) < rows.index(samples[8]), 'G1 copy/sample order') need(writes[8]['region'] == [[0,0,480,600]] and writes[9]['region'] == writes[10]['region'] == [[480,0,800,600]] and all(w['fbo'] == samples[8]['object']['fbo'] for w in writes[8:]), 'G1 final composition regions/target') return dict(writes=11, copies=2, status='STRUCTURAL_ONLY')