RetroArch/wiiu/shader_utils.c
2022-11-22 06:08:17 +01:00

479 lines
13 KiB
C

#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <wiiu/gx2.h>
#include <wiiu/system/memory.h>
#include <wiiu/shader_utils.h>
#include <wiiu/wiiu_dbg.h>
/* this is a hack for elf builds since their data section is below 0x10000000
* and thus can't be accessed by the GX2 hardware */
#ifndef GX2_CAN_ACCESS_DATA_SECTION
typedef struct
{
void *vs_program;
void *ps_program;
void *gs_program;
void *gs_copy_program;
} org_programs_t;
#endif
void GX2InitShader(GX2Shader *shader)
{
if (shader->fs.program)
return;
shader->fs.size = GX2CalcFetchShaderSizeEx(shader->vs.attribVarCount,
GX2_FETCH_SHADER_TESSELLATION_NONE, GX2_TESSELLATION_MODE_DISCRETE);
#ifdef GX2_CAN_ACCESS_DATA_SECTION
shader->fs.program = MEM2_alloc(shader->fs.size, GX2_SHADER_ALIGNMENT);
#else
shader->fs.program = MEM2_alloc(shader->fs.size + sizeof(org_programs_t), GX2_SHADER_ALIGNMENT);
#endif
GX2InitFetchShaderEx(&shader->fs, (uint8_t *)shader->fs.program,
shader->vs.attribVarCount,
shader->attribute_stream,
GX2_FETCH_SHADER_TESSELLATION_NONE, GX2_TESSELLATION_MODE_DISCRETE);
GX2Invalidate(GX2_INVALIDATE_MODE_CPU_SHADER, shader->fs.program, shader->fs.size);
#ifndef GX2_CAN_ACCESS_DATA_SECTION
org_programs_t *org = (org_programs_t *)(shader->fs.program + shader->fs.size);
org->vs_program = shader->vs.program;
org->ps_program = shader->ps.program;
org->gs_program = shader->gs.program;
org->gs_copy_program = shader->gs.copyProgram;
shader->vs.program = MEM2_alloc(shader->vs.size, GX2_SHADER_ALIGNMENT);
memcpy(shader->vs.program, org->vs_program, shader->vs.size);
GX2Invalidate(GX2_INVALIDATE_MODE_CPU_SHADER, shader->vs.program, shader->vs.size);
shader->ps.program = MEM2_alloc(shader->ps.size, GX2_SHADER_ALIGNMENT);
memcpy(shader->ps.program, org->ps_program, shader->ps.size);
GX2Invalidate(GX2_INVALIDATE_MODE_CPU_SHADER, shader->ps.program, shader->ps.size);
if (org->gs_program)
{
shader->gs.program = MEM2_alloc(shader->gs.size, GX2_SHADER_ALIGNMENT);
memcpy(shader->gs.program, org->gs_program, shader->gs.size);
GX2Invalidate(GX2_INVALIDATE_MODE_CPU_SHADER, shader->gs.program, shader->gs.size);
shader->gs.copyProgram = MEM2_alloc(shader->gs.copyProgramSize, GX2_SHADER_ALIGNMENT);
memcpy(shader->gs.copyProgram, org->gs_copy_program, shader->gs.copyProgramSize);
GX2Invalidate(GX2_INVALIDATE_MODE_CPU_SHADER, shader->gs.copyProgram, shader->gs.copyProgramSize);
}
#endif
}
void GX2DestroyShader(GX2Shader *shader)
{
#ifndef GX2_CAN_ACCESS_DATA_SECTION
MEM2_free(shader->vs.program);
MEM2_free(shader->ps.program);
MEM2_free(shader->gs.program);
MEM2_free(shader->gs.copyProgram);
org_programs_t *org = (org_programs_t *)(shader->fs.program + shader->fs.size);
shader->vs.program = org->vs_program;
shader->ps.program = org->ps_program;
shader->gs.program = org->gs_program;
shader->gs.copyProgram = org->gs_copy_program;
#endif
MEM2_free(shader->fs.program);
shader->fs.program = NULL;
}
void GX2SetShader(GX2Shader *shader)
{
GX2SetVertexShader(&shader->vs);
GX2SetPixelShader(&shader->ps);
GX2SetFetchShader(&shader->fs);
if (shader->gs.program)
GX2SetGeometryShader(&shader->gs);
}
#if 0
static void dump_vs_data(GX2VertexShader* vs)
{
unsigned i;
DEBUG_INT(vs->size);
DEBUG_VAR(vs->mode);
DEBUG_INT(vs->uniformBlockCount);
for (i = 0; i < vs->uniformBlockCount; i++)
{
DEBUG_STR(vs->uniformBlocks[i].name);
DEBUG_INT(vs->uniformBlocks[i].offset);
DEBUG_INT(vs->uniformBlocks[i].size);
}
DEBUG_INT(vs->uniformVarCount);
for (i = 0; i < vs->uniformVarCount; i++)
{
DEBUG_STR(vs->uniformVars[i].name);
DEBUG_INT(vs->uniformVars[i].offset);
DEBUG_INT(vs->uniformVars[i].type);
DEBUG_INT(vs->uniformVars[i].count);
DEBUG_INT(vs->uniformVars[i].block);
}
DEBUG_INT(vs->initialValueCount);
for (i = 0; i < vs->initialValueCount; i++)
{
DEBUG_INT(vs->initialValues[i].offset);
DEBUG_FLOAT(vs->initialValues[i].value[0]);
DEBUG_FLOAT(vs->initialValues[i].value[1]);
DEBUG_FLOAT(vs->initialValues[i].value[2]);
DEBUG_FLOAT(vs->initialValues[i].value[3]);
}
DEBUG_INT(vs->loopVarCount);
for (i = 0; i < vs->loopVarCount; i++)
{
DEBUG_INT(vs->loopVars[i].offset);
DEBUG_VAR(vs->loopVars[i].value);
}
DEBUG_INT(vs->samplerVarCount);
for (i = 0; i < vs->samplerVarCount; i++)
{
DEBUG_STR(vs->samplerVars[i].name);
DEBUG_INT(vs->samplerVars[i].type);
DEBUG_INT(vs->samplerVars[i].location);
}
for (i = 0; i < vs->attribVarCount; i++)
{
DEBUG_STR(vs->attribVars[i].name);
DEBUG_VAR(vs->attribVars[i].type);
DEBUG_INT(vs->attribVars[i].location);
DEBUG_INT(vs->attribVars[i].count);
}
}
static void dump_ps_data(GX2PixelShader* ps)
{
unsigned i;
DEBUG_INT(ps->size);
DEBUG_VAR(ps->mode);
DEBUG_INT(ps->uniformBlockCount);
for (i = 0; i < ps->uniformBlockCount; i++)
{
DEBUG_STR(ps->uniformBlocks[i].name);
DEBUG_INT(ps->uniformBlocks[i].offset);
DEBUG_INT(ps->uniformBlocks[i].size);
}
DEBUG_INT(ps->uniformVarCount);
for (i = 0; i < ps->uniformVarCount; i++)
{
DEBUG_STR(ps->uniformVars[i].name);
DEBUG_INT(ps->uniformVars[i].offset);
DEBUG_INT(ps->uniformVars[i].type);
DEBUG_INT(ps->uniformVars[i].count);
DEBUG_INT(ps->uniformVars[i].block);
}
DEBUG_INT(ps->initialValueCount);
for (i = 0; i < ps->initialValueCount; i++)
{
DEBUG_INT(ps->initialValues[i].offset);
DEBUG_FLOAT(ps->initialValues[i].value[0]);
DEBUG_FLOAT(ps->initialValues[i].value[1]);
DEBUG_FLOAT(ps->initialValues[i].value[2]);
DEBUG_FLOAT(ps->initialValues[i].value[3]);
}
DEBUG_INT(ps->loopVarCount);
for (i = 0; i < ps->loopVarCount; i++)
{
DEBUG_INT(ps->loopVars[i].offset);
DEBUG_VAR(ps->loopVars[i].value);
}
DEBUG_INT(ps->samplerVarCount);
for (i = 0; i < ps->samplerVarCount; i++)
{
DEBUG_STR(ps->samplerVars[i].name);
DEBUG_INT(ps->samplerVars[i].type);
DEBUG_INT(ps->samplerVars[i].location);
}
}
static void check_shader_verbose(u32 *shader, u32 shader_size, u32 *org, u32 org_size, const char *name)
{
unsigned i;
printf("%s :\n", name);
DEBUG_VAR(shader_size);
DEBUG_VAR(org_size);
if (shader_size != org_size)
printf("size mismatch : 0x%08lX should be 0x%08lX\n", shader_size, org_size);
for (i = 0; i < shader_size / 4; i += 4)
{
printf("0x%08lX 0x%08lX 0x%08lX 0x%08lX 0x%08lX 0x%08lX 0x%08lX 0x%08lX\n",
shader[i], shader[i + 1], shader[i + 2], shader[i + 3],
org[i], org[i + 1], org[i + 2], org[i + 3]);
}
for (i = 0; i < shader_size / 4; i++)
{
if (shader[i] != org[i])
printf("%i(%X): 0x%08lX(0x%08lX) should be 0x%08lX(0x%08lX) \n", i, i, shader[i], __builtin_bswap32(shader[i]), org[i],
__builtin_bswap32(org[i]));
}
}
static void check_shader(const void *shader_, u32 shader_size, const void *org_, u32 org_size, const char *name)
{
unsigned i;
bool different = false;
u32 *shader = (u32 *)shader_;
u32 *org = (u32 *)org_;
printf("%-20s : ", name);
if (shader_size != org_size)
{
different = true;
printf("\nsize mismatch : 0x%08lX should be 0x%08lX", shader_size, org_size);
}
for (i = 0; i < shader_size / 4; i++)
{
if (shader[i] != org[i])
{
different = true;
printf("\n%i(%X): 0x%08lX(0x%08lX) should be 0x%08lX(0x%08lX)", i, i, shader[i], __builtin_bswap32(shader[i]), org[i],
__builtin_bswap32(org[i]));
}
}
if (!different)
printf("no errors");
printf("\n");
}
#endif
#define MAKE_MAGIC(c0,c1,c2,c3) ((c0 << 24) |(c1 << 16) |(c2 << 8) |(c3 << 0))
#define GFD_FILE_MAJOR_VERSION 7
#define GFD_FILE_GPU_VERSION 2
#define GFD_BLOCK_MAJOR_VERSION 1
#define GFD_FILE_MAGIC MAKE_MAGIC('G','f','x','2')
#define GFD_BLOCK_MAGIC MAKE_MAGIC('B','L','K','{')
#define GFD_BLOCK_RELOCATIONS_MAGIC MAKE_MAGIC('}','B','L','K')
#define GFD_RELOCATIONS_TYPE_MASK 0xFFF00000
#define GFD_RELOCATIONS_VALUE_MASK (~GFD_RELOCATIONS_TYPE_MASK)
#define GFD_RELOCATIONS_DATA 0xD0600000
#define GFD_RELOCATIONS_TEXT 0xCA700000
typedef enum
{
GFD_BLOCK_TYPE_END_OF_FILE = 1,
GFD_BLOCK_TYPE_PADDING = 2,
GFD_BLOCK_TYPE_VERTEX_SHADER_HEADER = 3,
GFD_BLOCK_TYPE_VERTEX_SHADER_PROGRAM = 5,
GFD_BLOCK_TYPE_PIXEL_SHADER_HEADER = 6,
GFD_BLOCK_TYPE_PIXEL_SHADER_PROGRAM = 7,
} GFDBlockType;
typedef struct
{
uint32_t magic;
uint32_t headerSize;
uint32_t majorVersion;
uint32_t minorVersion;
uint32_t gpuVersion;
uint32_t align;
uint32_t unk1;
uint32_t unk2;
} GFDFileHeader;
typedef struct
{
uint32_t magic;
uint32_t headerSize;
uint32_t majorVersion;
uint32_t minorVersion;
GFDBlockType type;
uint32_t dataSize;
uint32_t id;
uint32_t index;
} GFDBlockHeader;
typedef struct
{
uint32_t magic;
uint32_t headerSize;
uint32_t unk1;
uint32_t dataSize;
uint32_t dataOffset;
uint32_t textSize;
uint32_t textOffset;
uint32_t patchBase;
uint32_t patchCount;
uint32_t patchOffset;
} GFDRelocationHeader;
typedef struct
{
GFDBlockHeader header;
u8 data[];
} GFDBlock;
void gfd_free(GFDFile* gfd)
{
if (gfd)
{
MEM2_free(gfd->data);
free(gfd);
}
}
static bool gfd_relocate_block(GFDBlock* block)
{
unsigned i;
GFDRelocationHeader* rel = (GFDRelocationHeader*)(block->data + block->header.dataSize) - 1;
if (rel->magic != GFD_BLOCK_RELOCATIONS_MAGIC)
return false;
if ((rel->patchOffset & GFD_RELOCATIONS_TYPE_MASK) != GFD_RELOCATIONS_DATA)
return false;
u32* patches = (u32*)(block->data + (rel->patchOffset & GFD_RELOCATIONS_VALUE_MASK));
for (i = 0; i < rel->patchCount; i++)
{
if (patches[i])
{
if ((patches[i] & GFD_RELOCATIONS_TYPE_MASK) != GFD_RELOCATIONS_DATA)
return false;
u32* ptr = (u32*)(block->data + (patches[i] & GFD_RELOCATIONS_VALUE_MASK));
if ((((*ptr) & GFD_RELOCATIONS_TYPE_MASK) != GFD_RELOCATIONS_DATA) &&
(((*ptr) & GFD_RELOCATIONS_TYPE_MASK) != GFD_RELOCATIONS_TEXT))
return false;
*ptr = (u32)block->data + ((*ptr) & GFD_RELOCATIONS_VALUE_MASK);
}
}
return true;
}
GFDFile *gfd_open(const char *filename)
{
int size;
GFDFile* gfd = (GFDFile*)calloc(1, sizeof(*gfd));
FILE *fp = fopen(filename, "rb");
if (!fp)
goto error;
fseek(fp, 0, SEEK_END);
size = ftell(fp);
fseek(fp, 0, SEEK_SET);
gfd->data = MEM2_alloc(size, GX2_SHADER_ALIGNMENT);
fread(gfd->data, 1, size, fp);
fclose(fp);
GFDFileHeader *header = (GFDFileHeader *)gfd->data;
if (header->magic != GFD_FILE_MAGIC)
goto error;
if (header->headerSize != sizeof(GFDFileHeader))
goto error;
if (header->majorVersion != GFD_FILE_MAJOR_VERSION)
goto error;
if (header->gpuVersion != GFD_FILE_GPU_VERSION)
goto error;
if (!header->align)
goto error;
GFDBlock *block = (GFDBlock *)(gfd->data + header->headerSize);
while (block->header.type != GFD_BLOCK_TYPE_END_OF_FILE)
{
if (block->header.magic != GFD_BLOCK_MAGIC)
goto error;
if (block->header.headerSize != sizeof(GFDBlockHeader))
goto error;
if (block->header.majorVersion != GFD_BLOCK_MAJOR_VERSION)
goto error;
switch (block->header.type)
{
case GFD_BLOCK_TYPE_VERTEX_SHADER_HEADER:
if (gfd->vs)
continue;
gfd->vs = (GX2VertexShader*)block->data;
if (!gfd_relocate_block(block))
goto error;
break;
case GFD_BLOCK_TYPE_VERTEX_SHADER_PROGRAM:
if (gfd->vs->program)
continue;
GX2Invalidate(GX2_INVALIDATE_MODE_CPU_SHADER, block->data, block->header.dataSize);
gfd->vs->program = block->data;
break;
case GFD_BLOCK_TYPE_PIXEL_SHADER_HEADER:
if (gfd->ps)
continue;
gfd->ps = (GX2PixelShader*)block->data;
if (!gfd_relocate_block(block))
goto error;
break;
case GFD_BLOCK_TYPE_PIXEL_SHADER_PROGRAM:
if (gfd->ps->program)
continue;
GX2Invalidate(GX2_INVALIDATE_MODE_CPU_SHADER, block->data, block->header.dataSize);
gfd->ps->program = block->data;
break;
default:
break;
}
block = (GFDBlock *)((u8 *)block + block->header.headerSize + block->header.dataSize);
}
if (!gfd->vs)
goto error;
if (!gfd->vs->program)
goto error;
if (!gfd->ps)
goto error;
if (!gfd->ps->program)
goto error;
return gfd;
error:
gfd_free(gfd);
return NULL;
}