Delete src directory

This commit is contained in:
Kravickas authored and GitHub committed 2026-07-18 10:47:16 +02:00
1 parent be1d06c8a6
commit 2a0d92fd73
8 files changed
-12696

No files matched your search

-74
View File
@@ -1,74 +0,0 @@
/* bmp_loader.h — Load BMP texture into GPU memory as RGBA8 */
typedef struct {
void *pixels; /* RGBA8 pixel data (GPU allocated) */
int width, height;
unsigned long size; /* total bytes */
} BmpTexture;
static int bmp_load(const char *path, void *(*alloc_fn)(unsigned long, unsigned long), BmpTexture *out) {
int fd = sceKernelOpen(path, 0, 0);
if (fd < 0) return -1;
/* Read BMP header (14 bytes) + DIB header (40 bytes) */
unsigned char hdr[54];
if (sceKernelRead(fd, hdr, 54) < 54) { sceKernelClose(fd); return -2; }
if (hdr[0] != 'B' || hdr[1] != 'M') { sceKernelClose(fd); return -3; }
unsigned int data_off = *(unsigned int*)(hdr + 10);
int w = *(int*)(hdr + 18);
int h = *(int*)(hdr + 22);
int bpp = *(unsigned short*)(hdr + 28);
int compr = *(unsigned int*)(hdr + 30);
int flip = 1; /* BMP is bottom-up by default */
if (h < 0) { h = -h; flip = 0; } /* top-down BMP */
if (compr != 0 || (bpp != 24 && bpp != 32)) { sceKernelClose(fd); return -4; }
/* Allocate RGBA8 output */
unsigned long out_size = (unsigned long)w * h * 4;
void *pixels = alloc_fn(out_size, 0x1000);
if (!pixels) { sceKernelClose(fd); return -5; }
/* Read pixel data */
sceKernelLseek(fd, data_off, 0);
int stride_bmp = ((w * (bpp / 8) + 3) & ~3); /* BMP rows are 4-byte aligned */
unsigned long row_sz = stride_bmp;
/* Read row by row, convert BGR(A) → RGBA, flip if needed */
/* Allocate row buffer on heap for large textures */
unsigned char *row_buf = (unsigned char*)alloc_fn(stride_bmp + 16, 64);
if (!row_buf) { sceKernelClose(fd); return -6; }
unsigned char *dst = (unsigned char*)pixels;
for (int y = 0; y < h; y++) {
int src_y = y; /* no flip: bottom-up */
sceKernelLseek(fd, data_off + (long)src_y * stride_bmp, 0);
int rd = sceKernelRead(fd, row_buf, row_sz);
if (rd < (int)(w * (bpp / 8))) break;
unsigned char *d = dst + (long)y * w * 4;
for (int x = 0; x < w; x++) {
int sx = x; /* no flip — correct UV mapping */
if (bpp == 24) {
d[x*4+0] = row_buf[sx*3+2]; /* R */
d[x*4+1] = row_buf[sx*3+1]; /* G */
d[x*4+2] = row_buf[sx*3+0]; /* B */
d[x*4+3] = 255; /* A */
} else { /* 32bpp BGRA */
d[x*4+0] = row_buf[sx*4+2]; /* R */
d[x*4+1] = row_buf[sx*4+1]; /* G */
d[x*4+2] = row_buf[sx*4+0]; /* B */
d[x*4+3] = row_buf[sx*4+3]; /* A */
}
}
}
sceKernelClose(fd);
out->pixels = pixels;
out->width = w;
out->height = h;
out->size = out_size;
return 0;
}
-8198
View File
File diff suppressed because it is too large. Load diff
-3071
View File
File diff suppressed because it is too large. Load diff
-126
View File
@@ -1,126 +0,0 @@
#pragma once
/* Types come from <stdint.h> which main.c includes before this header.
Do NOT re-typedef uint32_t / uint8_t / int64_t here — that conflicts with
the OpenOrbis SDK's <stdint.h> when building via the SDK toolchain. */
#include <stdint.h>
extern int sceKernelAllocateDirectMemory(long, long, unsigned long, unsigned long, int, unsigned long*);
extern int sceKernelMapDirectMemory(void**, unsigned long, int, int, long, unsigned long);
extern unsigned int sceKernelUsleep(unsigned int);
extern int sceKernelOpen(const char*, int, unsigned short);
extern long sceKernelRead(int, void*, unsigned long);
extern long sceKernelWrite(int, const void*, unsigned long);
extern int sceKernelFsync(int);
extern int sceKernelClose(int);
extern long sceKernelLseek(int, long, int);
extern uint64_t sceKernelGetProcessTime(void);
extern int sceVideoOutOpen(int, int, int, void*);
extern void sceVideoOutSetBufferAttribute(void*, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t);
extern int sceVideoOutRegisterBuffers(int, int, void *const*, int, const void*);
extern int sceVideoOutSubmitFlip(int, int, uint32_t, int64_t);
extern void sceVideoOutSetFlipRate(int, int);
/* Flip status — OpenOrbis canonical layout (verified). flipArg lets us wait
for a SPECIFIC frame's flip to complete (the proper PS4 sync), and
numFlipPending bounds the queue. */
typedef struct {
uint64_t num;
uint64_t ptime;
uint64_t stime;
int64_t flipArg;
uint64_t reserved[2];
int32_t numGpuFlipPending;
int32_t numFlipPending;
int32_t currentBuffer;
uint32_t reserved1;
} OrbisVideoOutFlipStatus;
extern int sceVideoOutGetFlipStatus(int, OrbisVideoOutFlipStatus*);
/* Vblank status: count (offset 0) = number of vblanks since sceVideoOutOpen.
Used to tell whether the display vblank itself stalls at ~8s, or whether the
vblank keeps ticking but the flip-completion worker stops being woken. */
typedef struct {
uint64_t count;
uint64_t processTime;
uint64_t tsc;
uint64_t _reserved;
uint8_t flags;
uint8_t pad[7];
} OrbisVideoOutVblankStatus;
extern int sceVideoOutGetVblankStatus(int, OrbisVideoOutVblankStatus*);
extern int sceVideoOutUnregisterBuffers(int, int);
extern int sceVideoOutClose(int);
/* System service event — used to detect the quit/terminate request so we can
exit cleanly instead of letting the OS time out (~1 min) and crash.
The event struct is large (8192-byte SDK layout); we only read eventType,
the first int32. ReceiveEvent returns 0 when an event was dequeued. */
extern int sceSystemServiceReceiveEvent(void* event);
/* Event-driven flip pacing: register a flip event queue at startup and block on
it each frame until the flip completes (canonical PS4 vsync pacing). */
typedef uint64_t OrbisKernelEqueue;
struct kevent_t { uintptr_t ident; short filter; uint16_t flags; uint32_t fflags;
intptr_t data; void* udata; };
extern int sceKernelCreateEqueue(OrbisKernelEqueue*, const char*);
extern int sceKernelDeleteEqueue(OrbisKernelEqueue);
extern int sceKernelWaitEqueue(OrbisKernelEqueue, struct kevent_t*, int, int*, unsigned int*);
extern int sceVideoOutAddFlipEvent(OrbisKernelEqueue, int, void*);
extern int sceVideoOutDeleteFlipEvent(OrbisKernelEqueue, int);
/* Buffer flip-done label base; label[i] is at base + i*8. */
extern int sceVideoOutGetBufferLabelAddress(int, void**);
/* Emits a 7-dword WAIT_REG_MEM(label[bufIdx] == 0) into the command buffer.
size_dw must be 7 or it returns -1 and emits nothing. */
extern int sceGnmInsertWaitFlipDone(uint32_t*, uint32_t, int, int);
/* Vblank event: the display-hardware vblank IRQ, delivered every 16.6ms
independent of the VideoOut flip-worker thread. On a FAT console
(sceKernelIsNeoMode()==0) the flip worker polls on a 500ms fallback, so
pacing on the flip-completion event degrades to 500ms/frame. The vblank
event is the correct, always-running pace source. */
extern int sceVideoOutAddVblankEvent(OrbisKernelEqueue, int, void*);
extern int sceVideoOutDeleteVblankEvent(OrbisKernelEqueue, int);
extern int sceGnmSubmitCommandBuffers(uint32_t, void**, uint32_t*, uint32_t*, uint32_t);
/* Canonical submit+flip: patches the prepareFlip packet at the end of the last
DCB so the GPU writes the VO buffer label and flips at end-of-pipe (the
handshake the standalone sceVideoOutSubmitFlip skipped). */
extern int sceGnmSubmitAndFlipCommandBuffers(uint32_t count, void** dcb_addrs,
uint32_t* dcb_sizes, void** ccb_addrs,
uint32_t* ccb_sizes, int vo_handle,
int buf_idx, uint32_t flip_mode,
int64_t flip_arg);
extern int sceGnmSubmitDone(void);
/* Returns 1 when the driver's in-flight counter is 0 (GPU quiesced).
The game spins on this after SubmitDone before tearing down. */
extern int sceGnmAreSubmitsAllowed(void);
extern int printf(const char*, ...);
extern int scePadInit(void);
extern int scePadOpen(int, int, int, void*);
extern int scePadRead(int, void*, int);
extern int scePadReadState(int, void*);
extern int sceUserServiceInitialize(void*);
extern int sceUserServiceGetInitialUser(int*);
typedef struct { uint32_t format; uint32_t tiling; uint32_t aspect; uint32_t width; uint32_t height; uint32_t pitch; } SceVideoOutBufferAttribute;
struct OrbisPadData {
uint32_t buttons;
uint8_t lx, ly, rx, ry;
uint8_t l2, r2;
uint8_t _pad0[2];
float quat[4], accel[3], angvel[3];
uint8_t _rest[200];
};
#define PAD_L3 0x0002
#define PAD_R3 0x0004
#define PAD_OPTIONS 0x0008
#define PAD_UP 0x0010
#define PAD_RIGHT 0x0020
#define PAD_DOWN 0x0040
#define PAD_LEFT 0x0080
#define PAD_L1 0x0400
#define PAD_R1 0x0800
#define PAD_TRI 0x1000
#define PAD_CROSS 0x4000
/* Memory freeing */
extern int sceKernelMunmap(void *addr, unsigned long len);
extern int sceKernelReleaseDirectMemory(long phys_addr, unsigned long len);
-461
View File
@@ -1,461 +0,0 @@
#pragma once
/*
* obj_loader.h — 3-pass streaming OBJ loader
*
* NEVER loads the full 2.1GB file. Streams 64MB chunks.
* Pass 1: count verts + faces
* Pass 2: read positions (162MB)
* Pass 3: emit triangulated faces to VB (2.6GB)
* Peak: 162MB + 2.6GB + 64MB = 2.82GB
*/
#define OBJ_STRIDE 12
#define OBJ_BG_VERTS 6
#define OBJ_IDENT_SIZE 64
#define OBJ_BG_SUN_SIZE 16
#define OBJ_BG_SIZE (OBJ_BG_VERTS * OBJ_STRIDE * 4)
#define OBJ_MVP_OFF (OBJ_IDENT_SIZE + OBJ_BG_SUN_SIZE + OBJ_BG_SIZE)
#define OBJ_MVP_SIZE 64
#define OBJ_SUN_SIZE 16
#define OBJ_DATA_OFF (OBJ_MVP_OFF + OBJ_MVP_SIZE + OBJ_SUN_SIZE)
#define OBJ_CHUNK (64 * 1024 * 1024)
typedef struct {
void *vb_base; float *verts; uint32_t *ib_base;
int num_verts, num_tris, num_indices, indexed;
unsigned long vb_size, ib_size;
} ObjMesh;
static void *obj_talloc(unsigned long size, long *phys) {
unsigned long a = 0x4000;
size = (size + a - 1) & ~(a - 1); if (size < a) size = a;
long ph = 0;
if (sceKernelAllocateDirectMemory(0, 0x600000000ULL, size, a, 3, &ph) != 0) return 0;
void *addr = 0;
if (sceKernelMapDirectMemory(&addr, size, 3, 0, ph, a) != 0) return 0;
*phys = ph; return addr;
}
static void obj_tfree(void *addr, long phys, unsigned long size) {
unsigned long a = 0x4000;
size = (size + a - 1) & ~(a - 1); if (size < a) size = a;
if (addr) sceKernelMunmap(addr, size);
if (phys >= 0) sceKernelReleaseDirectMemory(phys, size);
}
static float obj_atof(const char **pp, const char *end) {
const char *p = *pp;
while (p<end&&(*p==' '||*p=='\t')) p++;
float s=1; if(p<end&&*p=='-'){s=-1;p++;}else if(p<end&&*p=='+')p++;
float v=0; while(p<end&&*p>='0'&&*p<='9'){v=v*10+(*p-'0');p++;}
if(p<end&&*p=='.'){p++;float f=.1f;while(p<end&&*p>='0'&&*p<='9'){v+=(*p-'0')*f;f*=.1f;p++;}}
if(p<end&&(*p=='e'||*p=='E')){p++;int es=1,ex=0;if(p<end&&*p=='-'){es=-1;p++;}else if(p<end&&*p=='+')p++;while(p<end&&*p>='0'&&*p<='9'){ex=ex*10+(*p-'0');p++;}float m=1;for(int i=0;i<ex;i++)m*=10;v=es>0?v*m:v/m;}
*pp=p; return s*v;
}
static int obj_atoi(const char **pp, const char *end) {
const char *p=*pp;while(p<end&&(*p==' '||*p=='\t'))p++;
int s=1;if(p<end&&*p=='-'){s=-1;p++;}int v=0;
while(p<end&&*p>='0'&&*p<='9'){v=v*10+(*p-'0');p++;}*pp=p;return s*v;
}
static const char *obj_memchr(const char *s, char c, int n) {
for(int i=0;i<n;i++) if(s[i]==c) return s+i;
return 0;
}
static float obj_sqrtf(float x){if(x<=0)return 0;float r=x;for(int i=0;i<8;i++)r=.5f*(r+x/r);return r;}
/* MTL material table */
#define MTL_MAX 32
#define MTL_NAME_LEN 48
typedef struct { char name[MTL_NAME_LEN]; float r, g, b; } MtlEntry;
typedef struct { MtlEntry m[MTL_MAX]; int n; } MtlTable;
static int mtl_name_eq(const char *a, const char *b) {
while (*a && *b && *a == *b) { a++; b++; }
return (*a == 0 || *a == '\n' || *a == '\r') && (*b == 0 || *b == '\n' || *b == '\r');
}
static void mtl_load(const char *obj_path, MtlTable *t) {
t->n = 0;
/* Build MTL path: try .obj → .mtl */
char mtl_path[256];
int i = 0;
while (obj_path[i] && i < 250) { mtl_path[i] = obj_path[i]; i++; }
mtl_path[i] = 0;
/* Replace .obj → .mtl */
if (i > 4 && mtl_path[i-4] == '.') {
mtl_path[i-3] = 'm'; mtl_path[i-2] = 't'; mtl_path[i-1] = 'l';
}
int fd = sceKernelOpen(mtl_path, 0, 0);
if (fd < 0) return;
/* Read entire MTL (typically <4KB) */
char buf[4096];
int len = sceKernelRead(fd, buf, sizeof(buf)-1);
sceKernelClose(fd);
if (len <= 0) return;
buf[len] = 0;
int cur = -1;
const char *lp = buf;
while (lp < buf + len) {
const char *le = lp;
while (le < buf + len && *le != '\n') le++;
if (lp[0]=='n' && lp[1]=='e' && lp[2]=='w' && lp[3]=='m' && t->n < MTL_MAX) {
cur = t->n++;
const char *nm = lp + 7;
int j = 0;
while (j < MTL_NAME_LEN-1 && nm+j < le && nm[j] != '\r' && nm[j] != '\n')
{ t->m[cur].name[j] = nm[j]; j++; }
t->m[cur].name[j] = 0;
t->m[cur].r = 0.8f; t->m[cur].g = 0.8f; t->m[cur].b = 0.8f;
} else if (lp[0]=='K' && lp[1]=='d' && lp[2]==' ' && cur >= 0) {
const char *p = lp + 3;
t->m[cur].r = obj_atof(&p, le);
t->m[cur].g = obj_atof(&p, le);
t->m[cur].b = obj_atof(&p, le);
}
lp = (le < buf + len) ? le + 1 : le;
}
}
static int mtl_find(const MtlTable *t, const char *name) {
for (int i = 0; i < t->n; i++)
if (mtl_name_eq(t->m[i].name, name)) return i;
return -1;
}
static int obj_emit_face(const char *p, const char *end,
const float *px, const float *py, const float *pz,
const float *nnx, const float *nny, const float *nnz,
int nv, int nn, float *out) {
int fv[64],fn[64],fvc=0;
while(p<end&&*p!='\n'&&*p!='\r'&&fvc<64){
while(p<end&&(*p==' '||*p=='\t'))p++;
if(p>=end||*p=='\n'||*p=='\r')break;
int vi=obj_atoi(&p,end);
int ni=0;
if(p<end&&*p=='/'){p++;if(p<end&&*p!='/')obj_atoi(&p,end);if(p<end&&*p=='/'){p++;ni=obj_atoi(&p,end);}}
if(vi<0)vi=nv+vi+1; vi--;
if(ni<0)ni=nn+ni+1; ni--;
if(vi>=0&&vi<nv){fv[fvc]=vi;fn[fvc]=ni;fvc++;}
}
if(fvc<3)return 0;
float e1x=px[fv[1]]-px[fv[0]],e1y=py[fv[1]]-py[fv[0]],e1z=pz[fv[1]]-pz[fv[0]];
float e2x=px[fv[2]]-px[fv[0]],e2y=py[fv[2]]-py[fv[0]],e2z=pz[fv[2]]-pz[fv[0]];
float nx=e1y*e2z-e1z*e2y,ny=e1z*e2x-e1x*e2z,nz=e1x*e2y-e1y*e2x;
float nl=obj_sqrtf(nx*nx+ny*ny+nz*nz);if(nl>1e-4f){nx/=nl;ny/=nl;nz/=nl;}
/* Store normalized face normal — PS shader computes lighting dynamically */
int n=0;
for(int t=0;t<fvc-2;t++){
int idx[3]={fv[0],fv[t+1],fv[t+2]};
int nidx[3]={fn[0],fn[t+1],fn[t+2]};
for(int k=0;k<3;k++){
float*o=out+n*OBJ_STRIDE;int ii=idx[k];
o[0]=px[ii];o[1]=py[ii];o[2]=pz[ii];o[3]=1;
/* Use per-vertex normal (vn) if available, else face normal */
float vnx,vny,vnz;
if(nnx && nidx[k]>=0 && nidx[k]<nn){
vnx=nnx[nidx[k]]; vny=nny[nidx[k]]; vnz=nnz[nidx[k]];
} else {
vnx=nx; vny=ny; vnz=nz;
}
o[4]=vnx;o[5]=vny;o[6]=vnz;o[7]=0; /* normal for per-pixel Phong */
n++;}}
return n;
}
/* Stream a file pass: calls line_fn for each complete line.
* line_fn(line_start, line_end, user_data). Returns 0 to continue. */
typedef int (*obj_line_fn)(const char*, const char*, void*);
static void obj_stream_pass(const char *path, obj_line_fn fn, void *ud) {
int fd = sceKernelOpen(path, 0, 0);
if (fd < 0) return;
long ck_ph; unsigned long ck_sz = OBJ_CHUNK + 65536;
char *ck = (char*)obj_talloc(ck_sz, &ck_ph);
if (!ck) { sceKernelClose(fd); return; }
int left = 0;
for (;;) {
long r = sceKernelRead(fd, ck + left, OBJ_CHUNK);
if (r <= 0) {
/* Process any remaining data */
if (left > 0) { ck[left] = '\n'; fn(ck, ck + left, ud); }
break;
}
long clen = left + r; ck[clen] = 0;
const char *cp = ck, *ce = ck + clen;
while (cp < ce) {
const char *eol = cp; while (eol < ce && *eol != '\n') eol++;
if (eol >= ce) {
left = (int)(ce - cp); if (left > 65536) left = 65536;
for (int i = 0; i < left; i++) ck[i] = cp[i]; break;
}
fn(cp, eol, ud);
cp = eol + 1; left = 0;
}
if (r < OBJ_CHUNK) break;
}
sceKernelClose(fd);
obj_tfree(ck, ck_ph, ck_sz);
}
/* Pass 1 context: count */
typedef struct { int nv; int nn; int nt; int nf_tri; } P1Ctx;
static int p1_line(const char *s, const char *e, void *ud) {
P1Ctx *c = (P1Ctx*)ud;
if (*s == 'v' && s+1 < e && s[1] == ' ') c->nv++;
else if (*s == 'v' && s+1 < e && s[1] == 't' && s+2 < e && s[2] == ' ') c->nt++;
else if (*s == 'f' && s+1 < e && (s[1]==' '||s[1]=='\t')) {
const char *p = s+2; int fvc = 0;
while (p<e) { while(p<e&&(*p==' '||*p=='\t'))p++; if(p<e&&*p!='\n'&&*p!='\r'){fvc++;while(p<e&&*p!=' '&&*p!='\t'&&*p!='\n'&&*p!='\r')p++;} else break; }
if (fvc >= 3) c->nf_tri += fvc - 2;
}
return 0;
}
/* Pass 2 context: read positions */
typedef struct { float *px, *py, *pz, *nnx, *nny, *nnz, *tu, *tv; int vi, ni, ti, maxv, maxn, maxt; } P2Ctx;
static int p2_line(const char *s, const char *e, void *ud) {
P2Ctx *c = (P2Ctx*)ud;
if (*s == 'v' && s+1 < e && s[1] == 't' && s+2 < e && s[2] == ' ' && c->tu && c->ti < c->maxt) {
s += 3;
c->tu[c->ti] = obj_atof(&s, e);
c->tv[c->ti] = obj_atof(&s, e);
c->ti++;
} else if (*s == 'v' && s+1 < e && s[1] == ' ' && c->vi < c->maxv) {
s += 2;
c->px[c->vi] = obj_atof(&s, e);
c->py[c->vi] = obj_atof(&s, e);
c->pz[c->vi] = obj_atof(&s, e);
c->vi++;
} else if (*s == 'v' && s+2 < e && s[1] == 'n' && s[2] == ' ' && c->nnx && c->ni < c->maxn) {
s += 3;
c->nnx[c->ni] = obj_atof(&s, e);
c->nny[c->ni] = obj_atof(&s, e);
c->nnz[c->ni] = obj_atof(&s, e);
c->ni++;
}
return 0;
}
/* Pass 3 context: emit faces */
typedef struct { const float *px, *py, *pz, *snx, *sny, *snz, *tu, *tv; int nv, nt; float *out; int nout; float cr,cg,cb; const MtlTable *mtl; } P3Ctx;
static int p3_emit_smooth_uv(const char *p, const char *end,
const float *px, const float *py, const float *pz,
const float *snx, const float *sny, const float *snz,
const float *tu, const float *tv, int nv, int nt, float *out) {
int fv[64], ft[64], fvc = 0;
while(p<end&&*p!='\n'&&*p!='\r'&&fvc<64){
while(p<end&&(*p==' '||*p=='\t'))p++;
if(p>=end||*p=='\n'||*p=='\r')break;
int vi=obj_atoi(&p,end);
int ti=-1;
if(p<end&&*p=='/'){p++;if(p<end&&*p!='/')ti=obj_atoi(&p,end);if(p<end&&*p=='/'){p++;obj_atoi(&p,end);}}
if(vi<0)vi=nv+vi+1; vi--;
if(ti>0)ti--; else if(ti==0)ti=-1;
if(vi>=0&&vi<nv){fv[fvc]=vi;ft[fvc]=ti;fvc++;}
}
if(fvc<3)return 0;
int n=0;
for(int t=0;t<fvc-2;t++){
int idx[3]={fv[0],fv[t+1],fv[t+2]};
int tidx[3]={ft[0],ft[t+1],ft[t+2]};
for(int k=0;k<3;k++){
float*o=out+n*OBJ_STRIDE; int ii=idx[k]; int ti=tidx[k];
o[0]=px[ii];o[1]=py[ii];o[2]=pz[ii];o[3]=1;
o[4]=snx[ii];o[5]=sny[ii];o[6]=snz[ii];o[7]=0;
o[8]=(ti>=0&&ti<nt)?tu[ti]:0;
o[9]=(ti>=0&&ti<nt)?tv[ti]:0;
o[10]=0; o[11]=0;
n++;
}
}
return n;
}
static int p3_line(const char *s, const char *e, void *ud) {
P3Ctx *c = (P3Ctx*)ud;
if (s[0]=='u' && s[1]=='s' && s[2]=='e' && s[5]=='l' && s[6]==' ') {
/* usemtl name */
const char *nm = s + 7;
int mi = mtl_find(c->mtl, nm);
if (mi >= 0) { c->cr = c->mtl->m[mi].r; c->cg = c->mtl->m[mi].g; c->cb = c->mtl->m[mi].b; }
}
if (*s == 'f' && s+1 < e && (s[1]==' '||s[1]=='\t')) {
int base = c->nout;
c->nout += p3_emit_smooth_uv(s+2, e, c->px, c->py, c->pz, c->snx, c->sny, c->snz, c->tu, c->tv, c->nv, c->nt, c->out + c->nout * OBJ_STRIDE);
/* UVs stored per-vertex from vt indices */
}
return 0;
}
/* ================================================================ */
/* Progress callback: called between passes with (pass 1-3, detail string, user_data) */
typedef void (*obj_progress_fn)(int pass, const char *msg, void *ud);
/* Smooth normal accumulation callback */
typedef struct { const float *px,*py,*pz; float *snx,*sny,*snz; int nv; } SNCtx;
static int sn_line(const char *s, const char *e, void *ud) {
SNCtx *c = (SNCtx*)ud;
if (s[0]!='f' || (s[1]!=' ' && s[1]!='\t')) return 0;
const char *fp=s+2;
int afv[64],afvc=0;
while(fp<e&&afvc<64){
while(fp<e&&(*fp==' '||*fp=='\t'))fp++;
if(fp>=e||*fp=='\n'||*fp=='\r')break;
int avi=obj_atoi(&fp,e);
if(fp<e&&*fp=='/'){fp++;if(fp<e&&*fp!='/')obj_atoi(&fp,e);if(fp<e&&*fp=='/'){fp++;obj_atoi(&fp,e);}}
if(avi<0)avi=c->nv+avi+1; avi--;
if(avi>=0&&avi<c->nv)afv[afvc++]=avi;
}
if(afvc<3)return 0;
float e1x=c->px[afv[1]]-c->px[afv[0]],e1y=c->py[afv[1]]-c->py[afv[0]],e1z=c->pz[afv[1]]-c->pz[afv[0]];
float e2x=c->px[afv[2]]-c->px[afv[0]],e2y=c->py[afv[2]]-c->py[afv[0]],e2z=c->pz[afv[2]]-c->pz[afv[0]];
float fnx=e1y*e2z-e1z*e2y,fny=e1z*e2x-e1x*e2z,fnz=e1x*e2y-e1y*e2x;
float fl=obj_sqrtf(fnx*fnx+fny*fny+fnz*fnz);
if(fl<1e-6f)return 0; /* skip degenerate faces */
fnx/=fl;fny/=fl;fnz/=fl; /* normalize: equal weight per face */
for(int i=0;i<afvc;i++){c->snx[afv[i]]+=fnx;c->sny[afv[i]]+=fny;c->snz[afv[i]]+=fnz;}
return 0;
}
static int obj_load_file(const char *path,
void *(*alloc_fn)(unsigned long, unsigned long),
ObjMesh *out,
obj_progress_fn progress, void *progress_ud) {
out->vb_base=0;out->ib_base=0;out->verts=0;
out->num_verts=0;out->num_tris=0;out->num_indices=0;
out->indexed=0;out->vb_size=0;out->ib_size=0;
if (progress) progress(0, "Opening file", progress_ud);
/* ==== PASS 1: count ==== */
P1Ctx p1 = {0, 0, 0, 0};
obj_stream_pass(path, p1_line, &p1);
if (p1.nv == 0 || p1.nf_tri == 0) return -4;
/* ==== Alloc positions (162MB for 13.5M verts) ==== */
unsigned long pos_sz = (unsigned long)p1.nv * 4;
long px_ph, py_ph, pz_ph;
float *px = (float*)obj_talloc(pos_sz, &px_ph);
float *py = (float*)obj_talloc(pos_sz, &py_ph);
float *pz = (float*)obj_talloc(pos_sz, &pz_ph);
if (!px || !py || !pz) return -5;
/* Vertex normals (vn) for smooth shading */
int nn = p1.nn;
unsigned long nrm_sz = nn > 0 ? (unsigned long)nn * 4 : 0;
long nnx_ph = -1, nny_ph = -1, nnz_ph = -1;
float *nnx = 0, *nny = 0, *nnz = 0;
if (nn > 0) {
nnx = (float*)obj_talloc(nrm_sz, &nnx_ph);
nny = (float*)obj_talloc(nrm_sz, &nny_ph);
nnz = (float*)obj_talloc(nrm_sz, &nnz_ph);
}
if (progress) progress(1, "Counting done", progress_ud);
/* Alloc UVs */
int nti = p1.nt > 0 ? p1.nt : 1;
long tu_ph = 0, tv_ph = 0;
float *tu = (float*)obj_talloc(nti * 4, &tu_ph);
float *tv = (float*)obj_talloc(nti * 4, &tv_ph);
if (!tu || !tv) { obj_tfree(px,px_ph,pos_sz); obj_tfree(py,py_ph,pos_sz); obj_tfree(pz,pz_ph,pos_sz); return -8; }
/* ==== PASS 2: read positions + UVs ==== */
P2Ctx p2 = { px, py, pz, nnx, nny, nnz, tu, tv, 0, 0, 0, p1.nv, nn, nti };
obj_stream_pass(path, p2_line, &p2);
/* Auto-scale */
int vi = p2.vi;
float mnx=px[0],mxx=px[0],mny=py[0],mxy=py[0],mnz=pz[0],mxz=pz[0];
for(int i=1;i<vi;i++){if(px[i]<mnx)mnx=px[i];if(px[i]>mxx)mxx=px[i];if(py[i]<mny)mny=py[i];if(py[i]>mxy)mxy=py[i];if(pz[i]<mnz)mnz=pz[i];if(pz[i]>mxz)mxz=pz[i];}
float cx=(mnx+mxx)*.5f,cy=(mny+mxy)*.5f,cz=(mnz+mxz)*.5f;
float rng=mxx-mnx;if(mxy-mny>rng)rng=mxy-mny;if(mxz-mnz>rng)rng=mxz-mnz;
float sc=(rng>1e-4f)?1.8f/rng:1;
for(int i=0;i<vi;i++){px[i]=(px[i]-cx)*sc;py[i]=(py[i]-cy)*sc;pz[i]=(pz[i]-cz)*sc;}
/* ==== Alloc VB (2.6GB for 27M tris) ==== */
/* Memory: 162MB pos + 2.6GB VB = 2.76GB. Fits! */
unsigned long vb_size = OBJ_DATA_OFF + (unsigned long)p1.nf_tri * 3 * OBJ_STRIDE * 4;
void *vb = alloc_fn(vb_size, 0x1000);
if (!vb) {
obj_tfree(tu, tu_ph, nti*4); obj_tfree(tv, tv_ph, nti*4);
obj_tfree(px, px_ph, pos_sz);
obj_tfree(py, py_ph, pos_sz);
obj_tfree(pz, pz_ph, pos_sz);
return -6;
}
float *verts = (float*)((char*)vb + OBJ_DATA_OFF);
if (progress) progress(2, "Positions loaded", progress_ud);
/* ==== Compute smooth vertex normals ==== */
unsigned long snrm_sz = (unsigned long)vi * 4;
long snx_ph=-1, sny_ph=-1, snz_ph=-1;
float *snx = (float*)obj_talloc(snrm_sz, &snx_ph);
float *sny = (float*)obj_talloc(snrm_sz, &sny_ph);
float *snz = (float*)obj_talloc(snrm_sz, &snz_ph);
if (!snx||!sny||!snz) return -7;
for(int i=0;i<vi;i++){snx[i]=0;sny[i]=0;snz[i]=0;}
/* Pass 3a: accumulate via obj_stream_pass */
{ SNCtx snctx = { px, py, pz, snx, sny, snz, vi };
obj_stream_pass(path, sn_line, &snctx); }
for(int i=0;i<vi;i++){
float l=obj_sqrtf(snx[i]*snx[i]+sny[i]*sny[i]+snz[i]*snz[i]);
if(l>1e-6f){snx[i]/=l;sny[i]/=l;snz[i]/=l;}
}
/* ==== PASS 3: emit faces ==== */
/* Load MTL */
MtlTable mtl_table;
mtl_load(path, &mtl_table);
/* Hardcoded Controller materials as fallback */
if (mtl_table.n == 0) {
struct { const char *n; float r,g,b; } hc[] = {
{"Metal bits",0.70f,0.74f,0.74f}, {"Shiny black",0.02f,0.02f,0.02f},
{"Blue",0.08f,0.48f,0.94f}, {"Green",0.04f,0.96f,0.16f},
{"Red",0.96f,0.10f,0.04f}, {"Yellow",0.96f,0.81f,0.04f},
{"Logo black",0.02f,0.02f,0.02f}, {"Shiny white",1.0f,1.0f,1.0f},
{"Analogs",1.0f,1.0f,1.0f}, {"Analogs bump",1.0f,1.0f,1.0f},
{"Dull white",1.0f,1.0f,1.0f},
{"Front buttons transparent",0.8f,0.8f,0.8f},
{"Front button inserts",0.02f,0.02f,0.02f}, {0,0,0,0}
};
for (int hi=0; hc[hi].n && mtl_table.n < MTL_MAX; hi++) {
int mi = mtl_table.n++;
const char *s = hc[hi].n; int j=0;
while (s[j] && j < MTL_NAME_LEN-1) { mtl_table.m[mi].name[j] = s[j]; j++; }
mtl_table.m[mi].name[j] = 0;
mtl_table.m[mi].r = hc[hi].r;
mtl_table.m[mi].g = hc[hi].g;
mtl_table.m[mi].b = hc[hi].b;
}
}
P3Ctx p3 = { px, py, pz, snx, sny, snz, tu, tv, vi, nti, verts, 0, 0.8f, 0.8f, 0.8f, &mtl_table };
obj_stream_pass(path, p3_line, &p3);
/* Free positions */
obj_tfree(tu, tu_ph, nti*4); obj_tfree(tv, tv_ph, nti*4);
obj_tfree(px, px_ph, pos_sz);
obj_tfree(py, py_ph, pos_sz);
obj_tfree(pz, pz_ph, pos_sz);
if (nnx) obj_tfree(nnx, nnx_ph, nrm_sz);
if (nny) obj_tfree(nny, nny_ph, nrm_sz);
if (nnz) obj_tfree(nnz, nnz_ph, nrm_sz);
obj_tfree(snx, snx_ph, snrm_sz);
obj_tfree(sny, sny_ph, snrm_sz);
obj_tfree(snz, snz_ph, snrm_sz);
if (progress) progress(3, "Faces done", progress_ud);
out->vb_base = vb; out->verts = verts; out->ib_base = 0;
out->num_verts = p3.nout; out->num_tris = p3.nout / 3;
out->num_indices = 0; out->indexed = 0;
out->vb_size = vb_size; out->ib_size = 0;
return 0;
}
-247
View File
@@ -1,247 +0,0 @@
/* ply_loader.h — ASCII PLY loader
* Supports: element vertex N (x,y,z + optional nx,ny,nz)
* element face M (property list uchar int vertex_index/vertex_indices)
*/
static int ply_load_file(const char *path, void *(*alloc_fn)(unsigned long,unsigned long),
ObjMesh *out, void (*cb)(int,const char*,void*), void *cb_ud) {
int fd = sceKernelOpen(path, 0, 0);
if (fd < 0) return -1;
/* Read header (ASCII, line by line) */
char hdr[8192]; int hdr_len = 0;
int r = sceKernelRead(fd, hdr, sizeof(hdr)-1);
if (r <= 0) { sceKernelClose(fd); return -2; }
hdr[r] = 0;
sceKernelClose(fd);
/* Parse header */
int nv = 0, nf = 0, is_binary = 0;
int has_nx = 0; /* has vertex normals */
int prop_count = 0; /* properties per vertex */
int nx_idx = -1, ny_idx = -1, nz_idx = -1;
int in_vertex = 0, in_face = 0;
int header_bytes = 0;
const char *lp = hdr;
while (*lp) {
const char *le = lp;
while (*le && *le != '\n' && *le != '\r') le++;
int ll = le - lp;
if (ll >= 10 && lp[0]=='e' && lp[1]=='n' && lp[2]=='d' && lp[3]=='_') {
header_bytes = (le - hdr);
while (header_bytes < r && (hdr[header_bytes]=='\n'||hdr[header_bytes]=='\r')) header_bytes++;
break;
}
if (ll >= 6 && lp[0]=='f' && lp[1]=='o' && lp[2]=='r' && lp[3]=='m' && lp[4]=='a' && lp[5]=='t') {
if (ll > 11 && lp[7]=='b') is_binary = 1; /* binary_little_endian or binary_big_endian */
}
if (ll >= 14 && lp[0]=='e' && lp[1]=='l' && lp[2]=='e' && lp[3]=='m') {
/* element vertex N or element face N */
const char *ep = lp + 8; /* skip "element " */
if (ep[0]=='v') {
in_vertex = 1; in_face = 0; prop_count = 0;
while (ep < le && *ep != ' ') ep++; ep++;
nv = obj_atoi(&ep, le);
} else if (ep[0]=='f') {
in_vertex = 0; in_face = 1;
while (ep < le && *ep != ' ') ep++; ep++;
nf = obj_atoi(&ep, le);
} else { in_vertex = 0; in_face = 0; }
}
if (ll >= 8 && lp[0]=='p' && lp[1]=='r' && lp[2]=='o' && in_vertex) {
/* property float x/y/z/nx/ny/nz */
const char *pp = lp;
/* Find last word (property name) */
const char *name = le - 1;
while (name > lp && *name != ' ' && *name != '\t') name--;
name++;
if (name[0]=='n' && name[1]=='x') { has_nx = 1; nx_idx = prop_count; }
if (name[0]=='n' && name[1]=='y') { ny_idx = prop_count; }
if (name[0]=='n' && name[1]=='z') { nz_idx = prop_count; }
prop_count++;
}
while (*le == '\n' || *le == '\r') le++;
lp = le;
}
if (nv == 0 || nf == 0 || is_binary) {
/* Binary PLY not supported yet */
return is_binary ? -10 : -3;
}
if (cb) cb(0, "Reading PLY", cb_ud);
/* Re-read file from after header */
fd = sceKernelOpen(path, 0, 0);
sceKernelLseek(fd, header_bytes, 0);
/* Alloc positions */
unsigned long pos_sz = (unsigned long)nv * 4;
long px_ph, py_ph, pz_ph;
float *px = (float*)obj_talloc(pos_sz, &px_ph);
float *py = (float*)obj_talloc(pos_sz, &py_ph);
float *pz = (float*)obj_talloc(pos_sz, &pz_ph);
if (!px||!py||!pz) { sceKernelClose(fd); return -4; }
/* Read vertices */
char line[4096];
int vi = 0;
/* Simple line reader */
char rbuf[65536]; int rbuf_len = 0;
int phase = 0; /* 0=verts, 1=faces */
int fi = 0;
/* Count triangles in faces (fan triangulation) */
/* First pass: read vertices, count face triangles */
int nf_tri = 0;
/* Read all data into memory (for simplicity with ASCII PLY) */
long fsize = sceKernelLseek(fd, 0, 2);
sceKernelLseek(fd, header_bytes, 0);
long data_size = fsize - header_bytes;
long data_ph;
char *data = (char*)obj_talloc(data_size + 1, &data_ph);
if (!data) { sceKernelClose(fd); return -5; }
long total = 0;
while (total < data_size) {
int rd = sceKernelRead(fd, data + total, data_size - total > 65536 ? 65536 : data_size - total);
if (rd <= 0) break;
total += rd;
}
data[total] = 0;
sceKernelClose(fd);
if (cb) cb(1, "Parsing vertices", cb_ud);
/* Parse vertices */
const char *dp = data, *de = data + total;
for (vi = 0; vi < nv && dp < de; vi++) {
float props[16]; int pi = 0;
while (dp < de && pi < prop_count && pi < 16) {
while (dp < de && (*dp==' '||*dp=='\t')) dp++;
if (dp >= de || *dp == '\n' || *dp == '\r') break;
props[pi++] = obj_atof(&dp, de);
}
while (dp < de && *dp != '\n') dp++;
if (dp < de) dp++;
px[vi] = pi > 0 ? props[0] : 0;
py[vi] = pi > 1 ? props[1] : 0;
pz[vi] = pi > 2 ? props[2] : 0;
}
/* Count face triangles */
const char *face_start = dp;
const char *tp = dp;
for (fi = 0; fi < nf && tp < de; fi++) {
while (tp < de && (*tp==' '||*tp=='\t')) tp++;
int fc = 0;
if (tp < de) { fc = obj_atoi(&tp, de); }
if (fc >= 3) nf_tri += fc - 2;
while (tp < de && *tp != '\n') tp++;
if (tp < de) tp++;
}
if (cb) cb(2, "Emitting triangles", cb_ud);
/* Alloc VB */
unsigned int nverts_out = nf_tri * 3;
unsigned long vb_size = OBJ_DATA_OFF + (unsigned long)nverts_out * OBJ_STRIDE * 4;
void *vb = alloc_fn(vb_size, 0x1000);
if (!vb) { obj_tfree(data, data_ph, data_size+1); return -6; }
/* Auto-scale */
float minx=1e30f,miny=1e30f,minz=1e30f,maxx=-1e30f,maxy=-1e30f,maxz=-1e30f;
for (int i=0;i<vi;i++){
if(px[i]<minx)minx=px[i]; if(py[i]<miny)miny=py[i]; if(pz[i]<minz)minz=pz[i];
if(px[i]>maxx)maxx=px[i]; if(py[i]>maxy)maxy=py[i]; if(pz[i]>maxz)maxz=pz[i];
}
float ccx=(minx+maxx)*0.5f, ccy=(miny+maxy)*0.5f, ccz=(minz+maxz)*0.5f;
float ddx=maxx-minx, ddy=maxy-miny, ddz=maxz-minz;
float maxd=ddx>ddy?ddx:ddy; if(ddz>maxd)maxd=ddz;
float sc = maxd > 1e-6f ? 1.8f / maxd : 1.0f;
for (int i=0;i<vi;i++){px[i]=(px[i]-ccx)*sc;py[i]=(py[i]-ccy)*sc;pz[i]=(pz[i]-ccz)*sc;}
/* Compute smooth normals */
long snx_ph, sny_ph, snz_ph;
float *snx=(float*)obj_talloc(pos_sz,&snx_ph);
float *sny=(float*)obj_talloc(pos_sz,&sny_ph);
float *snz=(float*)obj_talloc(pos_sz,&snz_ph);
if(snx&&sny&&snz){
for(int i=0;i<vi;i++){snx[i]=0;sny[i]=0;snz[i]=0;}
/* Accumulate face normals */
dp = face_start;
for (fi=0;fi<nf&&dp<de;fi++){
while(dp<de&&(*dp==' '||*dp=='\t'))dp++;
int fc=obj_atoi(&dp,de);
int fv[64]; int fvc=0;
for(int i=0;i<fc&&i<64;i++){
while(dp<de&&(*dp==' '||*dp=='\t'))dp++;
fv[fvc++]=obj_atoi(&dp,de);
}
while(dp<de&&*dp!='\n')dp++;
if(dp<de)dp++;
if(fvc>=3&&fv[0]>=0&&fv[0]<vi&&fv[1]>=0&&fv[1]<vi&&fv[2]>=0&&fv[2]<vi){
float e1x=px[fv[1]]-px[fv[0]],e1y=py[fv[1]]-py[fv[0]],e1z=pz[fv[1]]-pz[fv[0]];
float e2x=px[fv[2]]-px[fv[0]],e2y=py[fv[2]]-py[fv[0]],e2z=pz[fv[2]]-pz[fv[0]];
float fnx=e1y*e2z-e1z*e2y,fny=e1z*e2x-e1x*e2z,fnz=e1x*e2y-e1y*e2x;
float fl=obj_sqrtf(fnx*fnx+fny*fny+fnz*fnz);
if(fl>1e-6f){fnx/=fl;fny/=fl;fnz/=fl;}
for(int i=0;i<fvc;i++){
if(fv[i]>=0&&fv[i]<vi){snx[fv[i]]+=fnx;sny[fv[i]]+=fny;snz[fv[i]]+=fnz;}
}
}
}
for(int i=0;i<vi;i++){
float l=obj_sqrtf(snx[i]*snx[i]+sny[i]*sny[i]+snz[i]*snz[i]);
if(l>1e-6f){snx[i]/=l;sny[i]/=l;snz[i]/=l;}
}
}
/* Emit triangles */
float *verts = (float*)((char*)vb + OBJ_DATA_OFF);
int nout = 0;
dp = face_start;
for (fi=0;fi<nf&&dp<de;fi++){
while(dp<de&&(*dp==' '||*dp=='\t'))dp++;
int fc=obj_atoi(&dp,de);
int fv[64]; int fvc=0;
for(int i=0;i<fc&&i<64;i++){
while(dp<de&&(*dp==' '||*dp=='\t'))dp++;
fv[fvc++]=obj_atoi(&dp,de);
}
while(dp<de&&*dp!='\n')dp++;
if(dp<de)dp++;
if(fvc<3)continue;
for(int t=0;t<fvc-2;t++){
int idx[3]={fv[0],fv[t+1],fv[t+2]};
for(int k=0;k<3;k++){
int ii=idx[k]; if(ii<0||ii>=vi)continue;
float*o=verts+nout*OBJ_STRIDE;
o[0]=px[ii];o[1]=py[ii];o[2]=pz[ii];o[3]=1;
if(snx){o[4]=snx[ii];o[5]=sny[ii];o[6]=snz[ii];}
else{o[4]=0;o[5]=1;o[6]=0;}
o[7]=0;
o[8]=0.8f;o[9]=0.8f;o[10]=0.8f;o[11]=1.0f;
nout++;
}
}
}
/* Cleanup */
if(snx){obj_tfree(snx,snx_ph,pos_sz);obj_tfree(sny,sny_ph,pos_sz);obj_tfree(snz,snz_ph,pos_sz);}
obj_tfree(px,px_ph,pos_sz);obj_tfree(py,py_ph,pos_sz);obj_tfree(pz,pz_ph,pos_sz);
obj_tfree(data,data_ph,data_size+1);
/* Identity matrix */
float *id=(float*)vb;
for(int i=0;i<16;i++)id[i]=0; id[0]=1;id[5]=1;id[10]=1;id[15]=1;
out->vb_base=vb; out->verts=verts; out->ib_base=0;
out->num_verts=nout; out->num_tris=nout/3;
out->vb_size=vb_size; out->indexed=0;
return 0;
}
-397
View File
@@ -1,397 +0,0 @@
// ============================================================================
// pm4.h — PM4 Command Buffer Builder for PS4 GPU
// ============================================================================
//
// WHAT IS PM4?
// PM4 (Packet Manager 4) is AMD's protocol for sending commands to the GPU.
// Instead of calling GPU functions directly, you build a command buffer
// (a list of PM4 packets) and submit the whole thing at once.
//
// Think of it like writing a to-do list for the GPU:
// "Set this register to X, set that register to Y, now draw 36 triangles"
//
// HOW PM4 PACKETS WORK:
// Every packet starts with a header dword:
// Bits [31:30] = 3 (always, means "type 3 packet")
// Bits [29:16] = count of body dwords minus 1
// Bits [15:8] = opcode (what command)
// Bits [7:0] = 0 (predicate, always 0 for us)
//
// After the header come the body dwords (register offset + values).
//
// GPU REGISTER TYPES:
// The PS4 GPU has thousands of registers, organized in 3 groups:
//
// CONTEXT registers (0xA000-0xAFFF): Drawing state
// Scissors, viewport, depth test, color buffer, blend mode, etc.
// Changed between draw calls (e.g., switch depth mode mid-frame).
//
// SH registers (0x2C00-0x2CFF): Shader state
// Shader program address, resource descriptors, user data.
// Set before each draw to configure which shaders to use.
//
// UCONFIG registers (0xC000-0xCFFF): Global GPU config
// Primitive type (triangles/lines), instance count.
// Rarely changes.
//
// REGISTER OFFSETS:
// All offsets below are relative to their base (already subtracted).
// The PM4 helper functions add the base automatically.
// Values verified against shadPS4's regs.cpp static_asserts.
//
// ============================================================================
// PM4 command buffer builder for PS4 GFX ring
// Register offsets verified against shadPS4 regs.cpp static_asserts
#pragma once
#include <stdint.h>
// ============================================================================
// PM4 packet header helpers
// ============================================================================
// Type 3 packet: [31:30]=11, [29:16]=count-1, [15:8]=opcode, [7:0]=predicate
static inline uint32_t pm4_type3(uint32_t opcode, uint32_t count) {
return (3u << 30) | (((count)-1) << 16) | ((opcode) << 8);
}
// ============================================================================
// PM4 opcodes (from shadPS4 pm4_opcodes.h)
// ============================================================================
#define PM4_CONTEXT_CONTROL 0x28
#define PM4_SET_CONTEXT_REG 0x69
#define PM4_SET_SH_REG 0x76
#define PM4_SET_UCONFIG_REG 0x79
#define PM4_DRAW_INDEX_AUTO 0x2D
#define PM4_NUM_INSTANCES 0x2F
#define PM4_EVENT_WRITE_EOP 0x47
#define PM4_NOP 0x10
#define PM4_ACQUIRE_MEM 0x58
#define PM4_WRITE_DATA 0x37
// Register bases
#define CTX_REG_BASE 0xA000u
#define SH_REG_BASE 0x2C00u
#define UCONFIG_REG_BASE 0xC000u
// ============================================================================
// Context register offsets (register - 0xA000)
// From shadPS4 regs.cpp static_asserts
// ============================================================================
#define CTX_DEPTH_RENDER_CONTROL 0x000
#define CTX_DEPTH_VIEW 0x002
#define CTX_DEPTH_RENDER_OVERRIDE 0x003
#define CTX_DEPTH_HTILE_DATA_BASE 0x005
#define CTX_SCREEN_SCISSOR 0x00C // 2 dwords
#define CTX_DB_Z_INFO 0x010
#define CTX_DB_STENCIL_INFO 0x011
#define CTX_DB_Z_READ_BASE 0x012
#define CTX_DB_STENCIL_READ_BASE 0x013
#define CTX_DB_Z_WRITE_BASE 0x014
#define CTX_DB_STENCIL_WRITE_BASE 0x015
#define CTX_DB_DEPTH_SIZE 0x016
#define CTX_DB_DEPTH_SLICE 0x017
#define CTX_WINDOW_SCISSOR 0x081 // 2 dwords
#define CTX_COLOR_TARGET_MASK 0x08E
#define CTX_COLOR_SHADER_MASK 0x08F
#define CTX_GENERIC_SCISSOR 0x090 // 2 dwords
#define CTX_VIEWPORT_SCISSOR0 0x094 // 2 dwords
#define CTX_INDEX_OFFSET 0x102
#define CTX_STENCIL_CONTROL 0x10B
#define CTX_STENCIL_REF_FRONT 0x10C
#define CTX_STENCIL_REF_BACK 0x10D
#define CTX_VIEWPORT0 0x10F // 6 dwords (xscale,xoff,yscale,yoff,zscale,zoff)
#define CTX_PS_INPUT_CNTL_0 0x191
#define CTX_VS_OUTPUT_CONFIG 0x1B1
#define CTX_PS_INPUT_ENA 0x1B3
#define CTX_PS_INPUT_ADDR 0x1B4
#define CTX_NUM_INTERP 0x1B6
#define CTX_SHADER_POS_FORMAT 0x1C3
#define CTX_Z_EXPORT_FORMAT 0x1C4
#define CTX_COLOR_EXPORT_FORMAT 0x1C5
#define CTX_BLEND_CONTROL0 0x1E0
#define CTX_DEPTH_CONTROL 0x200
#define CTX_COLOR_CONTROL 0x202
#define CTX_CLIPPER_CONTROL 0x204
#define CTX_VIEWPORT_CONTROL 0x206
#define CTX_VS_OUTPUT_CONTROL 0x207
#define CTX_MODE_CONTROL 0x292
#define CTX_INDEX_SIZE 0x29D
#define CTX_STAGE_ENABLE 0x2D5
#define CTX_AA_CONFIG 0x2F8
#define CTX_CB_COLOR0_BASE 0x318
#define CTX_CB_COLOR0_PITCH 0x319
#define CTX_CB_COLOR0_SLICE 0x31A
#define CTX_CB_COLOR0_VIEW 0x31B
#define CTX_CB_COLOR0_INFO 0x31C
#define CTX_CB_COLOR0_ATTRIB 0x31D
// SH register offsets (register - 0x2C00)
#define SH_PS_PGM_LO 0x08
#define SH_PS_PGM_HI 0x09
#define SH_PS_PGM_RSRC1 0x0A
#define SH_PS_PGM_RSRC2 0x0B
#define SH_PS_USER_DATA_0 0x0C
#define SH_VS_PGM_LO 0x48
#define SH_VS_PGM_HI 0x49
#define SH_VS_PGM_RSRC1 0x4A
#define SH_VS_PGM_RSRC2 0x4B
#define SH_VS_USER_DATA_0 0x4C
// UCONFIG register offsets (register - 0xC000)
#define UCFG_PRIMITIVE_TYPE 0x242
#define UCFG_NUM_INSTANCES 0x24D
// ============================================================================
// PM4 command buffer builder (simple linear writer)
// ============================================================================
struct PM4Builder {
uint32_t* buf;
uint32_t off;
uint32_t cap;
};
static inline void pm4_init(struct PM4Builder* b, uint32_t* buffer, uint32_t capacity_dwords) {
b->buf = buffer;
b->off = 0;
b->cap = capacity_dwords;
}
static inline void pm4_emit(struct PM4Builder* b, uint32_t val) {
if (b->off < b->cap) b->buf[b->off++] = val;
}
static inline void pm4_emit_f(struct PM4Builder* b, float val) {
union { float f; uint32_t u; } conv;
conv.f = val;
pm4_emit(b, conv.u);
}
// --- Packet emitters ---
/* Default hardware-state init — the exact sequence sceGnmDrawInitDefaultHardwareState
emits on real PS4. Copied verbatim from shadPS4's gnmdriver.cpp (ClearContextState)
and gnmdriver_init.h (base InitSequence, the non-Neo / pre-1.75-SDK variant — correct
for a base FAT PS4).
Real PS4 firmware requires this register-defaults block before any draw: it puts the
scan converter, viewport, clip, VGT, and color-buffer registers into a known-good
state. Homebrew that skips it leaves those context registers undefined -> GPU hang /
black screen on hardware. shadPS4 self-initializes its register tracking, so the
omission is invisible there. Emitted once at command-buffer start, before draw state. */
static inline void pm4_init_default_hw_state(struct PM4Builder* b) {
/* ClearContextState preamble (gnmdriver.cpp ClearStateSequence) */
static const uint32_t clear_state[] = {
0xc0012800u, 0x80000000u, 0x80000000u, 0xc0001200u, 0u, 0xc0055800u,
0x2ec47fc0u, 0xffffffffu, 0u, 0u, 0u, 10u,
};
for (unsigned i = 0; i < sizeof(clear_state)/sizeof(clear_state[0]); i++)
pm4_emit(b, clear_state[i]);
/* Base InitSequence register defaults (gnmdriver_init.h), minus the 2-dword
IT_CLEAR_STATE header which ClearContextState already covers above. */
static const uint32_t init_seq[] = {
0xc0017600u, 0x216u, 0xffffffffu,
0xc0017600u, 0x217u, 0xffffffffu,
0xc0017600u, 0x215u, 0u,
0xc0016900u, 0x2f9u, 0x2du,
0xc0016900u, 0x282u, 8u,
0xc0016900u, 0x280u, 0x80008u,
0xc0016900u, 0x281u, 0xffff0000u,
0xc0016900u, 0x204u, 0u,
0xc0016900u, 0x206u, 0x43fu,
0xc0016900u, 0x83u, 0xffffu,
0xc0016900u, 0x317u, 0x10u,
0xc0016900u, 0x2fau, 0x3f800000u,
0xc0016900u, 0x2fcu, 0x3f800000u,
0xc0016900u, 0x2fbu, 0x3f800000u,
0xc0016900u, 0x2fdu, 0x3f800000u,
0xc0016900u, 0x202u, 0xcc0010u,
0xc0016900u, 0x30eu, 0xffffffffu,
0xc0016900u, 0x30fu, 0xffffffffu,
0xc0002f00u, 1u,
0xc0017600u, 7u, 0x1ffu,
0xc0017600u, 0x46u, 0x1ffu,
0xc0017600u, 0x87u, 0x1ffu,
0xc0017600u, 0xc7u, 0x1ffu,
0xc0017600u, 0x107u, 0u,
0xc0017600u, 0x147u, 0x1ffu,
0xc0016900u, 0x1b1u, 2u,
0xc0016900u, 0x101u, 0u,
0xc0016900u, 0x100u, 0xffffffffu,
0xc0016900u, 0x103u, 0u,
0xc0016900u, 0x284u, 0u,
0xc0016900u, 0x290u, 0u,
0xc0016900u, 0x2aeu, 0u,
0xc0016900u, 0x292u, 0u,
0xc0016900u, 0x293u, 0x6000000u,
0xc0016900u, 0x2f8u, 0u,
0xc0016900u, 0x2deu, 0x1e9u,
0xc0036900u, 0x295u, 0x100u, 0x100u, 4u,
0xc0017900u, 0x200u, 0xe0000000u,
};
for (unsigned i = 0; i < sizeof(init_seq)/sizeof(init_seq[0]); i++)
pm4_emit(b, init_seq[i]);
}
static inline void pm4_context_control(struct PM4Builder* b) {
pm4_emit(b, pm4_type3(PM4_CONTEXT_CONTROL, 2));
pm4_emit(b, 0x80000000u); // load_control: LOAD_ENABLE
pm4_emit(b, 0x80000000u); // shadow_enable: SHADOW_ENABLE
}
static inline void pm4_set_context_reg(struct PM4Builder* b, uint32_t offset, uint32_t value) {
pm4_emit(b, pm4_type3(PM4_SET_CONTEXT_REG, 2));
pm4_emit(b, offset);
pm4_emit(b, value);
}
static inline void pm4_set_context_regs(struct PM4Builder* b, uint32_t offset,
const uint32_t* values, uint32_t count) {
pm4_emit(b, pm4_type3(PM4_SET_CONTEXT_REG, count + 1));
pm4_emit(b, offset);
for (uint32_t i = 0; i < count; i++) pm4_emit(b, values[i]);
}
static inline void pm4_set_sh_reg(struct PM4Builder* b, uint32_t offset, uint32_t value) {
pm4_emit(b, pm4_type3(PM4_SET_SH_REG, 2));
pm4_emit(b, offset);
pm4_emit(b, value);
}
static inline void pm4_set_sh_regs(struct PM4Builder* b, uint32_t offset,
const uint32_t* values, uint32_t count) {
pm4_emit(b, pm4_type3(PM4_SET_SH_REG, count + 1));
pm4_emit(b, offset);
for (uint32_t i = 0; i < count; i++) pm4_emit(b, values[i]);
}
static inline void pm4_set_uconfig_reg(struct PM4Builder* b, uint32_t offset, uint32_t value) {
pm4_emit(b, pm4_type3(PM4_SET_UCONFIG_REG, 2));
pm4_emit(b, offset);
pm4_emit(b, value);
}
static inline void pm4_draw_index_auto(struct PM4Builder* b, uint32_t index_count) {
pm4_emit(b, pm4_type3(PM4_DRAW_INDEX_AUTO, 2));
pm4_emit(b, index_count);
pm4_emit(b, 2); // draw_initiator: source_select=AUTO
}
static inline void pm4_event_write_eop(struct PM4Builder* b,
volatile uint32_t* fence_addr,
uint32_t fence_value) {
uint64_t addr = (uint64_t)(uintptr_t)fence_addr;
pm4_emit(b, pm4_type3(PM4_EVENT_WRITE_EOP, 5));
pm4_emit(b, 0x0504u); // CACHE_FLUSH_TS(4), event_index=5
pm4_emit(b, (uint32_t)(addr & 0xFFFFFFFFu)); // address_lo
pm4_emit(b, (uint32_t)(addr >> 32) | 0x20000000u); // addr_hi | data_sel=1(Data32) | int_sel=0: CPU-polled fence, no per-frame EOP IRQ (we pace on vblank)
pm4_emit(b, fence_value); // data_lo
pm4_emit(b, 0); // data_hi
}
static inline void pm4_nop(struct PM4Builder* b, uint32_t count) {
if (count == 0) return;
pm4_emit(b, pm4_type3(PM4_NOP, count));
for (uint32_t i = 0; i < count; i++) pm4_emit(b, 0);
}
/* prepareFlip packet — the real PS4 flip handshake.
sceGnmSubmitAndFlipCommandBuffers -> the marker-patch pass (gnm fn 0xcb0)
requires the DCB to END with a 64-dword TYPE3 NOP whose header is 0xc03e1000
(pm4_type3(NOP,63) — our pm4_type3 takes the payload count and subtracts 1,
so 63 yields 0xc03e1000) and whose first payload dword is a valid marker.
It reads dcb[size_dw - 0x40], so this MUST be the last 64 dwords of the DCB.
Block layout the patcher reads:
+0x00 header 0xc03e1000
+0x04 marker
+0x08 p0 +0x0c p1 +0x10 p2 +0x14 p3 +0x18 p4
+0xe8 is where it writes an EOP (dwords 58..63 — the block's tail)
EVERY valid marker (0x68750777/78/80/81) makes gnm call
sceVideoOutSubmitEopFlip and emit WRITE_DATA(vo_label[bufIdx] = 1). The tag
only selects the trailing packet:
0x68750777 -> NOP 0xc0391000 (what we used before)
0x68750778 -> NOP 0xc0341000 + a 2nd WRITE_DATA (the game's own throttle)
0x68750780 -> EOP 0xc0044700, addr=0, DATA_SEL=0, INT_SEL=1 <-- IRQ only
0x68750781 -> EOP 0xc0044700, caller addr, DATA_SEL=1, INT_SEL=2
We use 0x68750780: flip + label write + an interrupt-only EOP.
Why: on GFX7 (Liverpool is GFX7) amdgpu sizes its fence array as
num_hw_submission*2 = 256*2 = 512, and slots are only freed by
amdgpu_fence_process() running off the EOP interrupt
(gfx_v7_0_ring_emit_fence_gfx emits INT_SEL(int_sel ? 2 : 0)). Our measured
wall is exactly 512 submits with the GPU completing every one — the
signature of fences never being processed. Our own EOP is INT_SEL=0 and
fires no interrupt; this marker makes the driver's own EOP fire one.
p3 = EVENT_TYPE(4 = CACHE_FLUSH_AND_INV_TS), p4 = 0 cache-action bits
-> patched dword1 = 4 | (0<<12) | EVENT_INDEX(5)<<8 = 0x504, the same event
encoding our own EOP and the game's per-submit EOP both use. */
#define PM4_PREPARE_FLIP_TAG 0x68750780u
static inline void pm4_prepare_flip(struct PM4Builder* b) {
pm4_emit(b, pm4_type3(PM4_NOP, 63)); // +0x00 = 0xc03e1000
pm4_emit(b, PM4_PREPARE_FLIP_TAG); // +0x04 marker
pm4_emit(b, 0); // +0x08 p0
pm4_emit(b, 0); // +0x0c p1
pm4_emit(b, 0); // +0x10 p2
pm4_emit(b, 4); // +0x14 p3 = EVENT_TYPE
pm4_emit(b, 0); // +0x18 p4 = cache actions
for (uint32_t i = 0; i < 57; i++) pm4_emit(b, 0); // 64 dwords total
}
/* PM4 DMA_DATA (opcode 0x50): fill GPU memory with a constant value.
Unlike CPU memset, this goes through the rasterizer's FillBuffer path which
invalidates any cached Vulkan image view for the target memory range. Required
when the target is also sampled as a texture elsewhere in the frame (e.g.,
shadow_depth: written by shadow CB, sampled by main floor PS). CPU memset
would leave stale cached pixels in shadPS4's texture cache, producing ghost
silhouettes from previous frames.
Layout per PS4 PM4 DMA_DATA packet:
header = type3(0x50, 6)
dw1 = control: src_sel=2(Data) at [30:29], dst_sel=0(Memory) at [21:20]
dw2 = src data (value to fill)
dw3 = src_addr_hi (unused for Data source)
dw4 = dst_addr_lo
dw5 = dst_addr_hi
dw6 = command: num_bytes at [20:0] */
static inline void pm4_dma_fill(struct PM4Builder* b, void* dst,
uint32_t num_bytes, uint32_t fill_value) {
uint64_t daddr = (uint64_t)(uintptr_t)dst;
pm4_emit(b, pm4_type3(0x50, 6));
pm4_emit(b, (2u << 29) | (0u << 20)); /* src_sel=Data, dst_sel=Memory */
pm4_emit(b, fill_value); /* fill data */
pm4_emit(b, 0); /* src_addr_hi (unused) */
pm4_emit(b, (uint32_t)(daddr & 0xFFFFFFFFu)); /* dst_addr_lo */
pm4_emit(b, (uint32_t)(daddr >> 32)); /* dst_addr_hi */
pm4_emit(b, num_bytes & 0x1FFFFFu); /* command.num_bytes */
}
#define CTX_POLYGON_CONTROL 0x205 // PA_SU_SC_MODE_CNTL
/* IT_INDEX_TYPE (opcode 0x2A): set index buffer format */
static inline void pm4_index_type(struct PM4Builder* b, uint32_t type) {
/* type: 0 = uint16, 1 = uint32 */
pm4_emit(b, 0xC0002A00);
pm4_emit(b, type);
}
/* IT_DRAW_INDEX_2 (opcode 0x27): indexed draw */
static inline void pm4_acquire_mem(struct PM4Builder* b, uint32_t coher_cntl) {
/* Flush CB + invalidate TC between render-to-texture phases.
GCN Sea Islands PM4 spec: ACQUIRE_MEM(0x58), 6 body dwords */
pm4_emit(b, pm4_type3(PM4_ACQUIRE_MEM, 6));
pm4_emit(b, coher_cntl); /* CP_COHER_CNTL */
pm4_emit(b, 0xFFFFFFFF); /* CP_COHER_SIZE = full range */
pm4_emit(b, 0); /* CP_COHER_SIZE_HI */
pm4_emit(b, 0); /* CP_COHER_BASE_LO */
pm4_emit(b, 0); /* CP_COHER_BASE_HI */
pm4_emit(b, 10); /* POLL_INTERVAL */
}
static inline void pm4_draw_index_2(struct PM4Builder* b, uint32_t max_size,
uint64_t index_base, uint32_t index_count) {
pm4_emit(b, 0xC0042700);
pm4_emit(b, max_size);
pm4_emit(b, (uint32_t)(index_base & 0xFFFFFFFF));
pm4_emit(b, (uint32_t)(index_base >> 32));
pm4_emit(b, index_count);
pm4_emit(b, 0); /* draw_initiator: SOURCE_SELECT=DMA (indexed) */
}
-122
View File
@@ -1,122 +0,0 @@
/* stl_loader.h — Binary + ASCII STL loader
* Produces same output as obj_loader: smooth vertex normals, VB at OBJ_DATA_OFF
*
* Binary STL: 80-byte header, uint32 tri_count, then per-tri:
* float[3] normal, float[3] v1, float[3] v2, float[3] v3, uint16 attrib (50 bytes)
* ASCII STL: "solid name" ... "facet normal" ... "vertex" ... "endsolid"
*/
#define STL_TRI_SIZE 50
static int stl_is_binary(const char *path) {
int fd = sceKernelOpen(path, 0, 0);
if (fd < 0) return -1;
char buf[84];
int r = sceKernelRead(fd, buf, 84);
sceKernelClose(fd);
if (r < 84) return 0;
/* If header doesn't start with "solid" or has non-ASCII, likely binary */
if (buf[0]!='s'||buf[1]!='o'||buf[2]!='l'||buf[3]!='i'||buf[4]!='d') return 1;
/* Could be ASCII "solid ..." — check if tri count makes sense */
unsigned int ntri = *(unsigned int*)(buf + 80);
{ int fd2 = sceKernelOpen(path,0,0); long fsize = fd2>=0 ? sceKernelLseek(fd2, 0, 2) : 0; if(fd2>=0) sceKernelClose(fd2);
/* Binary size = 84 + ntri*50 */
if (fsize > 0 && fsize == 84 + (long)ntri * 50) return 1; }
return 0;
}
static int stl_load_binary(const char *path, void *(*alloc_fn)(unsigned long,unsigned long),
ObjMesh *out, void (*cb)(int,const char*,void*), void *cb_ud) {
int fd = sceKernelOpen(path, 0, 0);
if (fd < 0) return -1;
/* Read header + count */
char hdr[84];
sceKernelRead(fd, hdr, 84);
unsigned int ntri = *(unsigned int*)(hdr + 80);
if (ntri == 0 || ntri > 50000000) { sceKernelClose(fd); return -2; }
if (cb) cb(0, "Reading STL", cb_ud);
/* Read all triangle data */
unsigned long data_sz = (unsigned long)ntri * STL_TRI_SIZE;
long data_ph;
char *data = (char*)obj_talloc(data_sz, &data_ph);
if (!data) { sceKernelClose(fd); return -3; }
unsigned long total = 0;
while (total < data_sz) {
int r = sceKernelRead(fd, data + total, data_sz - total > 65536 ? 65536 : data_sz - total);
if (r <= 0) break;
total += r;
}
sceKernelClose(fd);
/* Extract positions: 3 verts per tri, find unique by hashing */
unsigned int nverts = ntri * 3;
if (cb) cb(1, "Computing normals", cb_ud);
/* Alloc VB */
unsigned long vb_size = OBJ_DATA_OFF + (unsigned long)nverts * OBJ_STRIDE * 4;
void *vb = alloc_fn(vb_size, 0x1000);
if (!vb) { obj_tfree(data, data_ph, data_sz); return -4; }
/* For smooth normals: accumulate face normals per unique position.
* STL has no shared vertices, so we weld by position.
* Simple approach for <50M tris: just use face normals (flat shading)
* and let the smooth normal pass in main handle it if needed.
* Actually: emit raw triangles with face normals for now,
* smooth normal computation uses position-based welding. */
float *verts = (float*)((char*)vb + OBJ_DATA_OFF);
float minx=1e30f,miny=1e30f,minz=1e30f,maxx=-1e30f,maxy=-1e30f,maxz=-1e30f;
for (unsigned int t = 0; t < ntri; t++) {
const float *tri = (const float*)(data + t * STL_TRI_SIZE);
float nx = tri[0], ny = tri[1], nz = tri[2]; /* face normal from file */
float nl = obj_sqrtf(nx*nx+ny*ny+nz*nz);
if (nl < 1e-6f) {
/* Compute from vertices */
float e1x=tri[7]-tri[4],e1y=tri[8]-tri[5],e1z=tri[9]-tri[6];
float e2x=tri[10]-tri[4],e2y=tri[11]-tri[5],e2z=tri[12]-tri[6];
nx=e1y*e2z-e1z*e2y; ny=e1z*e2x-e1x*e2z; nz=e1x*e2y-e1y*e2x;
nl=obj_sqrtf(nx*nx+ny*ny+nz*nz);
}
if (nl>1e-6f){nx/=nl;ny/=nl;nz/=nl;}
for (int v = 0; v < 3; v++) {
float px = tri[3+v*3], py = tri[4+v*3], pz = tri[5+v*3];
if(px<minx)minx=px; if(py<miny)miny=py; if(pz<minz)minz=pz;
if(px>maxx)maxx=px; if(py>maxy)maxy=py; if(pz>maxz)maxz=pz;
int idx = (t*3+v) * OBJ_STRIDE;
verts[idx]=px; verts[idx+1]=py; verts[idx+2]=pz; verts[idx+3]=1;
verts[idx+4]=nx; verts[idx+5]=ny; verts[idx+6]=nz; verts[idx+7]=0;
verts[idx+8]=0.8f; verts[idx+9]=0.8f; verts[idx+10]=0.8f; verts[idx+11]=1.0f;
}
}
obj_tfree(data, data_ph, data_sz);
/* Auto-scale to ±0.9 */
if (cb) cb(2, "Scaling", cb_ud);
float cx=(minx+maxx)*0.5f, cy=(miny+maxy)*0.5f, cz=(minz+maxz)*0.5f;
float dx=maxx-minx, dy=maxy-miny, dz=maxz-minz;
float maxd=dx>dy?dx:dy; if(dz>maxd)maxd=dz;
float sc = maxd > 1e-6f ? 1.8f / maxd : 1.0f;
for (unsigned int i = 0; i < nverts; i++) {
int idx = i * OBJ_STRIDE;
verts[idx] = (verts[idx]-cx)*sc;
verts[idx+1] = (verts[idx+1]-cy)*sc;
verts[idx+2] = (verts[idx+2]-cz)*sc;
}
/* Copy BG/MVP header from identity template */
float *id = (float*)vb;
for(int i=0;i<16;i++) id[i]=0; id[0]=1;id[5]=1;id[10]=1;id[15]=1;
out->vb_base = vb; out->verts = verts; out->ib_base = 0;
out->num_verts = nverts; out->num_tris = ntri;
out->vb_size = vb_size; out->indexed = 0;
return 0;
}