mirror of
https://github.com/libretro/parallel-ps2.git
synced 2026-11-09 17:28:25 -10:00
The shared header gives its C++ half plain aggregates for GLSL's vector types -- same scalars in the same order, so every struct keeps the layout the precompiled shader bank was built against. They carry no operators: anything arithmetic on a pair goes through pgs_vertex_kernels.h, which now also has two-operand min and max, and everything else is written out a lane at a time at the call site. That leaves nothing in paraLLEl-GS or in the Granite subset this core links reaching for muglm, so Granite/math goes, along with its include path, its cmake target and the two link lines naming it. The bank ships precompiled, so a field moving here would corrupt draws with no compile error anywhere: pgs_layout_check pins the size, alignment and every offset of the six shared structs, and runs first. The other lanes carried muglm as their reference and now state it themselves, a lane at a time, which is a better check than calling the code under test's former library. Generated code is unchanged in size: -0.10% of .text across the three GS translation units under gcc and +0.57% under clang.
159 lines
6.2 KiB
C++
159 lines
6.2 KiB
C++
#include "common.h"
|
|
#include "pgs_vertex_kernels.h"
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <cstdlib>
|
|
#include <vector>
|
|
#include <ctime>
|
|
#include <algorithm>
|
|
|
|
static Regs R; static int OFX, OFY;
|
|
|
|
static inline pgs_kick_regs gather()
|
|
{
|
|
pgs_kick_regs g;
|
|
g.st = R.st.bits; g.rgbaq = R.rgbaq.bits;
|
|
g.uv = R.uv.words[0]; g.fog = R.fog.words[1] >> 24;
|
|
g.ofx = OFX; g.ofy = OFY;
|
|
return g;
|
|
}
|
|
|
|
/* ---- Q0: current, muglm, shift queue ---- */
|
|
struct Q0 {
|
|
VertexPosition pos[3]; VertexAttribute attr[3]; unsigned count = 0;
|
|
inline void shift(){ if(count==3){pos[0]=pos[1];attr[0]=attr[1];pos[1]=pos[2];attr[1]=attr[2];count=2;} }
|
|
inline void kick(uint64_t v, bool f){
|
|
shift();
|
|
auto &p = pos[count]; auto &a = attr[count];
|
|
if (f) { Reg64<XYZFBits> x(v);
|
|
p.pos.x = int(x.desc.X)-OFX; p.pos.y = int(x.desc.Y)-OFY; p.z = x.desc.Z;
|
|
a.st.x=R.st.desc.S; a.st.y=R.st.desc.T; a.q=R.rgbaq.desc.Q; a.rgba=R.rgbaq.words[0];
|
|
a.fog=float(x.desc.F); a.uv.x = (uint16_t)R.uv.desc.U; a.uv.y = (uint16_t)R.uv.desc.V;
|
|
} else { Reg64<XYZBits> x(v);
|
|
p.pos.x = int(x.desc.X)-OFX; p.pos.y = int(x.desc.Y)-OFY; p.z = x.desc.Z;
|
|
a.st.x=R.st.desc.S; a.st.y=R.st.desc.T; a.q=R.rgbaq.desc.Q; a.rgba=R.rgbaq.words[0];
|
|
a.fog=float(R.fog.desc.FOG); a.uv.x = (uint16_t)R.uv.desc.U; a.uv.y = (uint16_t)R.uv.desc.V;
|
|
}
|
|
count++;
|
|
}
|
|
inline unsigned slot(unsigned i) const { return i; }
|
|
inline void m_list(){ count=0; }
|
|
};
|
|
|
|
/* ---- QC: C89 kernels, shift queue (queue untouched) ---- */
|
|
struct QC {
|
|
VertexPosition pos[3]; VertexAttribute attr[3]; unsigned count = 0;
|
|
inline void shift(){ if(count==3){pos[0]=pos[1];attr[0]=attr[1];pos[1]=pos[2];attr[1]=attr[2];count=2;} }
|
|
inline void kick(uint64_t v, bool f){
|
|
pgs_kick_regs g = gather();
|
|
uint32_t lo = (uint32_t)v, hi = (uint32_t)(v >> 32);
|
|
shift();
|
|
pgs_build_position(&g, lo, f ? (hi & 0xffffffu) : hi, &pos[count]);
|
|
pgs_build_attribute(&g, f ? (float)(hi >> 24) : (float)g.fog, &attr[count]);
|
|
count++;
|
|
}
|
|
inline unsigned slot(unsigned i) const { return i; }
|
|
inline void m_list(){ count=0; }
|
|
};
|
|
|
|
/* ---- QCR: C89 kernels + ring queue ---- */
|
|
struct QCR {
|
|
alignas(16) VertexPosition pos[4]; alignas(16) VertexAttribute attr[4];
|
|
unsigned count = 0, head = 0;
|
|
inline unsigned slot(unsigned i) const { return (head+i)&3; }
|
|
inline void kick(uint64_t v, bool f){
|
|
pgs_kick_regs g = gather();
|
|
uint32_t lo = (uint32_t)v, hi = (uint32_t)(v >> 32);
|
|
unsigned s;
|
|
if (count==3){ head=(head+1)&3; count=2; }
|
|
s = slot(count);
|
|
pgs_build_position(&g, lo, f ? (hi & 0xffffffu) : hi, &pos[s]);
|
|
pgs_build_attribute(&g, f ? (float)(hi >> 24) : (float)g.fog, &attr[s]);
|
|
count++;
|
|
}
|
|
inline void m_list(){ count=0; head=0; }
|
|
};
|
|
|
|
/* ---- QCRP: C89 kernels + ring queue + padded position store ---- */
|
|
struct QCRP {
|
|
alignas(16) VertexPosition pos[4]; alignas(16) VertexAttribute attr[4];
|
|
unsigned count = 0, head = 0;
|
|
inline unsigned slot(unsigned i) const { return (head+i)&3; }
|
|
inline void kick(uint64_t v, bool f){
|
|
pgs_kick_regs g = gather();
|
|
uint32_t lo = (uint32_t)v, hi = (uint32_t)(v >> 32);
|
|
unsigned s;
|
|
if (count==3){ head=(head+1)&3; count=2; }
|
|
s = slot(count);
|
|
pgs_build_position_padded(&g, lo, f ? (hi & 0xffffffu) : hi, &pos[s]);
|
|
pgs_build_attribute(&g, f ? (float)(hi >> 24) : (float)g.fog, &attr[s]);
|
|
count++;
|
|
}
|
|
inline void m_list(){ count=0; head=0; }
|
|
};
|
|
|
|
struct Rec { uint64_t st, rgbaq, uv, fog, xyz; };
|
|
static std::vector<Rec> mk(size_t n){
|
|
std::vector<Rec> v(n); uint64_t s=0x9E3779B97F4A7C15ull;
|
|
auto nx=[&]{ s=s*6364136223846793005ull+1442695040888963407ull; return s>>16; };
|
|
for(size_t i=0;i<n;i++){v[i].st=nx();v[i].rgbaq=nx();v[i].uv=nx();v[i].fog=nx();v[i].xyz=nx();}
|
|
return v;
|
|
}
|
|
template <typename Q>
|
|
static inline uint64_t consume(const Q &q){
|
|
uint64_t s=0;
|
|
for(unsigned k=0;k<3;k++){
|
|
const auto &p=q.pos[q.slot(2-k)]; const auto &a=q.attr[q.slot(2-k)];
|
|
s += uint64_t(uint32_t(p.pos.x))+uint64_t(uint32_t(p.pos.y))+p.z;
|
|
uint32_t w[6]; memcpy(w,&a,24); for(int j=0;j<6;j++) s+=w[j];
|
|
}
|
|
return s;
|
|
}
|
|
template <typename Q, bool XYZF, bool LIST>
|
|
static uint64_t run(const std::vector<Rec> &w, int iters){
|
|
Q q; uint64_t sink=0;
|
|
for(int it=0;it<iters;it++){ q.m_list();
|
|
for(size_t i=0;i<w.size();i++){
|
|
R.st.bits=w[i].st; R.rgbaq.bits=w[i].rgbaq; R.uv.bits=w[i].uv; R.fog.bits=w[i].fog;
|
|
q.kick(w[i].xyz, XYZF);
|
|
if(q.count>=3){ sink+=consume(q); if(LIST) q.m_list(); }
|
|
}
|
|
}
|
|
return sink;
|
|
}
|
|
static double now(){ struct timespec t; clock_gettime(CLOCK_MONOTONIC,&t); return t.tv_sec+1e-9*t.tv_nsec; }
|
|
typedef uint64_t (*Fn)(const std::vector<Rec>&,int);
|
|
struct V { const char*n; Fn f; std::vector<double> s; };
|
|
static uint64_t SINK;
|
|
static void grp(const char*title, std::vector<V>&vs, const std::vector<Rec>&w,int it,int rounds){
|
|
for(auto&v:vs){v.s.clear(); SINK+=v.f(w,2);}
|
|
for(int r=0;r<rounds;r++) for(auto&v:vs){ double a=now(); SINK+=v.f(w,it); double b=now();
|
|
v.s.push_back((b-a)*1e9/(double(w.size())*it)); }
|
|
printf("\n%s (%d rounds, median ns/vertex)\n", title, rounds);
|
|
double base=0;
|
|
for(auto&v:vs){ std::sort(v.s.begin(),v.s.end()); double m=v.s[v.s.size()/2];
|
|
if(base==0) base=m;
|
|
printf(" %-26s %7.3f %+6.1f%%\n", v.n, m, 100.0*(m-base)/base); }
|
|
}
|
|
int main(int argc,char**argv){
|
|
int it = argc>1?atoi(argv[1]):2500, rounds = argc>2?atoi(argv[2]):25;
|
|
auto w = mk(4096); OFX=1024<<PGS_SUBPIXEL_BITS; OFY=OFX;
|
|
std::vector<V> a={{"muglm (current)",run<Q0,false,false>,{}},
|
|
{"C89 kernels",run<QC,false,false>,{}},
|
|
{"C89 + ring queue",run<QCR,false,false>,{}},
|
|
{"C89 + ring + pad store",run<QCRP,false,false>,{}}};
|
|
std::vector<V> b={{"muglm (current)",run<Q0,true,false>,{}},
|
|
{"C89 kernels",run<QC,true,false>,{}},
|
|
{"C89 + ring queue",run<QCR,true,false>,{}},
|
|
{"C89 + ring + pad store",run<QCRP,true,false>,{}}};
|
|
std::vector<V> c={{"muglm (current)",run<Q0,false,true>,{}},
|
|
{"C89 kernels",run<QC,false,true>,{}},
|
|
{"C89 + ring queue",run<QCR,false,true>,{}},
|
|
{"C89 + ring + pad store",run<QCRP,false,true>,{}}};
|
|
grp("triangle strip, XYZ2",a,w,it,rounds);
|
|
grp("triangle strip, XYZF2",b,w,it,rounds);
|
|
grp("triangle list / sprite, XYZ2",c,w,it,rounds);
|
|
if(SINK==0x1234567ull) printf("x\n");
|
|
return 0;
|
|
}
|