134 lines
9.0 KiB
Python
134 lines
9.0 KiB
Python
#!/usr/bin/env python3
|
|
"""Compare the native raster encoder with Mesa's actual A740 state function."""
|
|
import hashlib
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
import re
|
|
import shlex
|
|
import subprocess
|
|
|
|
ROOT=Path(__file__).resolve().parents[1]
|
|
OUT=ROOT/'build/adreno-raster';OUT.mkdir(parents=True,exist_ok=True)
|
|
KIT=ROOT/'kits/AdrenoPM4';MESA=ROOT/'.cache/mesa-work'
|
|
HEAD='85fda3b36405bcaea8bffc64aa4f2651dc90770d'
|
|
def run(cmd,**kwargs):
|
|
return subprocess.check_output([str(x) for x in cmd],timeout=60,**kwargs)
|
|
def sha(p):return hashlib.sha256(Path(p).read_bytes()).hexdigest()
|
|
def require(v,why):
|
|
if not v:raise SystemExit(why)
|
|
require(run(['git','-C',MESA,'rev-parse','HEAD'],text=True).strip()==HEAD,'Pinned Mesa source')
|
|
pins=json.loads((ROOT/'scripts/data/adreno-raster-inputs.json').read_text())
|
|
sources={}
|
|
for name,digest in pins['files'].items():
|
|
content=run(['git','-C',MESA,'show',HEAD+':'+name])
|
|
require(hashlib.sha256(content).hexdigest()==digest,'Pinned primary input '+name)
|
|
if (MESA/name).exists():require(sha(MESA/name)==digest,'Working reference unchanged '+name)
|
|
sources[name]=content.decode()
|
|
for name in ('adreno_common','adreno_pm4','a6xx_enums','a7xx_enums','a8xx_enums','a6xx'):
|
|
generated=run([ROOT/'.cache/mesa-venv/bin/python',MESA/'src/freedreno/registers/gen_header.py','--rnn',MESA/'src/freedreno/registers','--xml',MESA/'src/freedreno/registers/adreno'/(name+'.xml'),'c-defines'],env=dict(os.environ,PYTHONHASHSEED='0'))
|
|
(OUT/(name+'.xml.h')).write_bytes(generated)
|
|
(OUT/'a6xx-pack.xml.h').write_bytes(run([ROOT/'.cache/mesa-venv/bin/python',MESA/'src/freedreno/registers/gen_header.py','--rnn',MESA/'src/freedreno/registers','--xml',MESA/'src/freedreno/registers/adreno/a6xx.xml','c-pack-structs'],env=dict(os.environ,PYTHONHASHSEED='0')))
|
|
def function(text,name):
|
|
match=re.search(r'static inline (?:float|bool)\n'+re.escape(name)+r'\(',text)
|
|
require(match,'Exact primary helper '+name)
|
|
start=text.index('{',match.start());end=start+1;depth=1
|
|
while depth:
|
|
require(end<len(text),'Bounded primary helper')
|
|
depth+=(text[end]=='{')-(text[end]=='}');end+=1
|
|
return text[match.start():end]+'\n'
|
|
raster=sources['src/gallium/drivers/freedreno/a6xx/fd6_rasterizer.cc']
|
|
start=raster.index('template <chip CHIP>');end=raster.index('\nFD_GENX(__fd6_setup_rasterizer_stateobj);',start)
|
|
actual_function=raster[start:end]+'\n'
|
|
reference=OUT/'reference.cpp'
|
|
reference.write_text('''#include <cassert>
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
#include "pipe/p_state.h"
|
|
#include "util/half_float.h"
|
|
#include "util/u_math.h"
|
|
#include "AdrenoRaster.h"
|
|
#include "adreno_common.xml.h"
|
|
#include "adreno_pm4.xml.h"
|
|
#include "a6xx_enums.xml.h"
|
|
#include "a7xx_enums.xml.h"
|
|
#include "a8xx_enums.xml.h"
|
|
#include "a6xx.xml.h"
|
|
struct fd_reg_pair {uint32_t reg;uint64_t value;void *bo;bool is_address;uint32_t bo_offset,bo_shift,bo_low;};
|
|
#define __bo_type void *
|
|
#include "a6xx-pack.xml.h"
|
|
// Only the transport is replaced: collect actual register/value pairs.
|
|
struct fd_ringbuffer {unsigned count;uint32_t reg[64],value[64];};
|
|
struct ref_props {bool is_a702,has_legacy_pipeline_shading_rate;};
|
|
struct ref_info {ref_props props;};struct ref_screen {ref_info *info;};
|
|
struct fd_context {fd_ringbuffer *pipe;ref_screen *screen;};
|
|
struct fd_crb {fd_ringbuffer *ring;
|
|
fd_crb(fd_ringbuffer *r,unsigned) : ring(r) {ring->count=0;}
|
|
fd_crb &add(fd_reg_pair pair) {assert(ring->count<64 && !pair.bo && !pair.is_address && pair.value<=UINT32_MAX);unsigned n=ring->count++;ring->reg[n]=pair.reg;ring->value[n]=(uint32_t)pair.value;return *this;}
|
|
operator fd_ringbuffer *() {return ring;}
|
|
};
|
|
#define assert_dt
|
|
'''+function(sources['src/gallium/auxiliary/util/u_inlines.h'],'util_get_min_point_size')+
|
|
function(sources['src/gallium/drivers/freedreno/freedreno_state.h'],'fd_rast_depth_clamp_enabled')+actual_function+'''
|
|
static float raw_float(uint32_t bits) {float value;memcpy(&value,&bits,4);return value;}
|
|
extern "C" size_t AdrenoRasterReference(const AdrenoRasterState *,uint32_t *,uint32_t *,size_t);
|
|
extern "C" size_t AdrenoRasterReference(const AdrenoRasterState *s,uint32_t *regs,uint32_t *values,size_t capacity) {
|
|
pipe_rasterizer_state cso={};uint32_t f=s->flags;
|
|
cso.depth_clip_near=f&AdrenoRasterClipNear;cso.depth_clip_far=(f&AdrenoRasterClipFar)!=0;
|
|
cso.clip_halfz=(f&AdrenoRasterHalfZ)!=0;cso.cull_face=((f&AdrenoRasterCullFront)?PIPE_FACE_FRONT:0)|((f&AdrenoRasterCullBack)?PIPE_FACE_BACK:0);
|
|
cso.front_ccw=(f&AdrenoRasterFrontCCW)!=0;cso.offset_tri=(f&AdrenoRasterOffsetTriangles)!=0;
|
|
cso.multisample=(f&AdrenoRasterMultisample)!=0;cso.flatshade_first=(f&AdrenoRasterFlatFirst)!=0;
|
|
cso.point_size_per_vertex=(f&AdrenoRasterPointSizePerVertex)!=0;
|
|
cso.point_quad_rasterization=(f&AdrenoRasterPointQuad)!=0;cso.point_smooth=(f&AdrenoRasterPointSmooth)!=0;
|
|
cso.fill_front=s->polygon_mode==1?PIPE_POLYGON_MODE_POINT:s->polygon_mode==2?PIPE_POLYGON_MODE_LINE:PIPE_POLYGON_MODE_FILL;
|
|
cso.point_size=s->point_size_q4/16.0f;cso.line_width=s->line_width_q1/2.0f;
|
|
cso.offset_scale=raw_float(s->offset_scale_bits);cso.offset_units=raw_float(s->offset_units_bits);cso.offset_clamp=raw_float(s->offset_clamp_bits);
|
|
fd_ringbuffer ring={};fd_context ctx={&ring,nullptr};
|
|
assert(__fd6_setup_rasterizer_stateobj<A7XX>(&ctx,&cso,(f&AdrenoRasterPrimitiveRestart)!=0)==&ring);
|
|
assert(ring.count<=capacity);memcpy(regs,ring.reg,ring.count*4);memcpy(values,ring.value,ring.count*4);return ring.count;
|
|
}
|
|
''')
|
|
build=ROOT/'build/mesa-ir3'
|
|
commands=run(['ninja','-C',build,'-t','commands','src/freedreno/ir3/air_nir'],text=True).splitlines()
|
|
base=next(shlex.split(x) for x in commands if shlex.split(x)[-1]==str(ROOT/'kits/AirNir/MesaBackend.c'))
|
|
for flag in ('-MQ','-MF'):
|
|
i=base.index(flag);del base[i:i+2]
|
|
base=[x for x in base if x not in ('-MD',)]
|
|
base[1:1]=['-DMESA_REFERENCE','-DFD_NO_DEPRECATED_PACK','-fsanitize=address,undefined','-I'+str(KIT),'-I'+str(OUT),'-I'+str(MESA/'src/gallium/include')]
|
|
objects=[]
|
|
for name,path in (('test',KIT/'test_raster.c'),('raster',KIT/'AdrenoRaster.c'),('pm4',KIT/'AdrenoPM4.c'),('reference',reference)):
|
|
cmd=base.copy();obj=OUT/(name+'.o');cmd[cmd.index('-o')+1]=str(obj);cmd[-1]=str(path)
|
|
if name=='reference':
|
|
cmd[0]=str(Path(cmd[0]).with_name('clang++'));cmd=[x for x in cmd if not x.startswith('-std=')];cmd[1:1]=['-std=c++20','-Wno-c99-designator','-Wno-c++11-narrowing','-Wno-missing-field-initializers']
|
|
run(cmd,cwd=build);objects.append(obj)
|
|
exe=OUT/'test-reference';run(['xcrun','clang++','-fsanitize=address,undefined',*objects,'-o',exe])
|
|
unit=run([exe],text=True).strip();print(unit)
|
|
require(unit=='PASS: 114916 raster profiles, 41 transactional refusals','Exact source-reference suite inventory')
|
|
import sys
|
|
import struct
|
|
sys.path.insert(0,str(ROOT/'kits/XnuPrelink'))
|
|
from native_prelink import macho
|
|
sdk=run(['xcrun','--sdk','iphoneos','--show-sdk-path'],text=True).strip()
|
|
objects=[]
|
|
for name in ('AdrenoPM4','AdrenoConstants','AdrenoShader','AdrenoVPC','AdrenoRaster'):
|
|
obj=OUT/(name+'.ios.o')
|
|
run(['xcrun','clang','-target','arm64-apple-ios17.0','-isysroot',sdk,'-std=c11','-O2','-Wall','-Wextra','-Werror','-ffreestanding','-fstack-protector-strong','-c',KIT/(name+'.c'),'-o',obj])
|
|
raw=obj.read_bytes();parsed=macho(raw)
|
|
require(parsed['kind']==1 and [struct.unpack_from('<II',raw,p+8) for cmd,p,_ in parsed['commands'] if cmd==0x32]==[(2,0x110000)],'Actual ordinary ARM64/iOS17 object')
|
|
objects.append(obj)
|
|
cpp=OUT/'client.cpp';cpp.write_text('#include "AdrenoRaster.h"\nstatic_assert(sizeof(AdrenoRasterState)==40 && sizeof(AdrenoRasterResult)==12);\nextern "C" unsigned client(const AdrenoRasterState *s,unsigned *o){return AdrenoA740Raster(s,o,60).status;}\n')
|
|
client=OUT/'client.ios.o';run(['xcrun','clang++','-target','arm64-apple-ios17.0','-isysroot',sdk,'-std=c++17','-O2','-Wall','-Wextra','-Werror','-I',KIT,'-c',cpp,'-o',client])
|
|
native=OUT/'AdrenoPipeline.ios.dylib';run(['xcrun','clang','-target','arm64-apple-ios17.0','-isysroot',sdk,'-dynamiclib',client,*objects,'-Wl,-undefined,error','-o',native])
|
|
imports=run(['xcrun','nm','-u',native],text=True).split()
|
|
require(set(imports)<=set(['___stack_chk_fail','___stack_chk_guard','dyld_stub_binder']),'Actual complete helper link without missing symbols')
|
|
(OUT/'report.json').write_text(json.dumps(dict(mesa_revision=HEAD,
|
|
kit_revision=run(['git','-C',KIT,'rev-parse','HEAD'],text=True).strip(),
|
|
source_sha256={str(p.relative_to(ROOT)):sha(p) for p in (KIT/'AdrenoRaster.c',KIT/'AdrenoRaster.h',KIT/'test_raster.c')},
|
|
primary_function_sha256=hashlib.sha256(actual_function.encode()).hexdigest(),
|
|
generated_reference_sha256=sha(reference),primary_manifest_sha256=sha(ROOT/'scripts/data/adreno-raster-inputs.json'),
|
|
generated_header_sha256={p.name:sha(p) for p in sorted(OUT.glob('*.xml.h'))},
|
|
native_link_sha256=sha(native),native_link_imports=imports,
|
|
unit=unit,actual_primary_function=True,transport_replaced_by_register_collector=True,
|
|
gpu_execution=False,active_gpu_smmu_qualified=False,complete_pipeline_state=False),indent=2)+'\n')
|
|
print('PASS: every encoded raster register matches the actual pinned A740 rasterizer function')
|