| /* |
| * Copyright (c) 2012 Rob Clark <robdclark@gmail.com> |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice (including the next |
| * paragraph) shall be included in all copies or substantial portions of the |
| * Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| * SOFTWARE. |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <stdint.h> |
| #include <unistd.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| #include <string.h> |
| |
| #include "redump.h" |
| #include "disasm.h" |
| #include "io.h" |
| |
| #define ASCII_XOR 0xff |
| #include "util.h" |
| |
| struct pgm_header { |
| uint32_t size; |
| uint32_t unknown1; |
| uint32_t unknown2; |
| uint32_t revision; |
| uint32_t unknown4; |
| uint32_t unknown5; |
| uint32_t unknown6; |
| uint32_t unknown7; |
| uint32_t unknown8; |
| uint32_t num_attribs; |
| uint32_t num_uniforms; |
| uint32_t num_samplers; |
| uint32_t num_varyings; |
| uint32_t num_uniformblocks; |
| }; |
| |
| struct vs_header { |
| uint32_t unknown1; /* seems to be # of sections up to and including shader */ |
| uint32_t unknown2; /* seems to be low byte or so of SQ_PROGRAM_CNTL */ |
| uint32_t unknown3; |
| uint32_t unknown4; |
| uint32_t unknown5; |
| uint32_t unknown6; |
| uint32_t unknown7; |
| uint32_t unknown8; |
| uint32_t unknown9; /* seems to be # of sections following shader */ |
| }; |
| |
| struct fs_header { |
| uint32_t unknown1; |
| }; |
| /* |
| // Covers a lot of type_info |
| // varying, attribute, uniform, sampler |
| type_info & 0xFF |
| if ((type_info >> 8) == 0x8b) // vector |
| 0x50 = vec2 |
| 0x51 = vec3 |
| 0x52 = vec4 |
| 0x53 = ivec2 |
| 0x54 = ivec3 |
| 0x55 = ivec4 |
| 0x56 = bool // Why is this in vector? |
| 0x57 = bvec2 |
| 0x58 = bvec3 |
| 0x59 = bvec4 |
| 0x5a = mat2 |
| 0x5b = mat3 |
| 0x5c = mat4 |
| 0x5a = mat2x2 // Same as mat2 |
| 0x65 = mat2x3 |
| 0x66 = mat2x4 |
| 0x67 = mat3x2 |
| 0x5b = mat3x3 // Same as mat3 |
| 0x68 = mat3x4 |
| 0x69 = mat4x2 |
| 0x6a = mat4x3 |
| 0x5c = mat4x4 // same as mat4 |
| 0x5e = sampler2D |
| 0x5f = sampler3D |
| 0x60 = samplerCube // XXX: Doesn't work |
| 0x62 = sampler2DShadow |
| 0xc6 = uvec2 |
| 0xc7 = uvec3 |
| 0xc8 = uvec4 |
| else if ((type_info >> 8) == 0x8d) // GLES3 samplers |
| 0xC1 = sampler2DArray |
| 0xC4 = sampler2DArrayShadow |
| 0xC5 = samplerCubeShadow |
| 0xCA = isampler2D |
| 0xCB = isampler3D |
| 0xCC = isamplerCube |
| 0xD2 = usampler2D |
| 0xD3 = usampler3D |
| 0xD4 = usamplerCube |
| 0xD7 = isampler2DArray |
| 0xD7 = usampler2DArray // Is the same as isampler2DArray? |
| else // 0x14 = single |
| 0x04 = int |
| 0x05 = uint |
| 0x06 = float |
| */ |
| struct attribute { |
| uint32_t type_info; |
| uint32_t reg; /* seems to be the register the fetch instruction loads to */ |
| uint32_t const_idx; /* the CONST() indx value for sampler */ |
| uint32_t unknown2; |
| uint32_t unknown3; |
| uint32_t unknown4; |
| uint32_t unknown5; |
| char name[]; |
| }; |
| |
| struct uniform { |
| uint32_t type_info; |
| uint32_t unknown2; |
| uint32_t unknown3; |
| uint32_t unknown4; |
| uint32_t const_base; /* const base register (for uniforms that take more than one const reg, ie. matrices) */ |
| uint32_t unknown6; |
| uint32_t const_reg; /* the const register holding the value */ |
| uint32_t unknown7; |
| uint32_t unknown8; |
| uint32_t unknown9; |
| union { |
| struct { |
| char name[1]; |
| } v1; |
| struct { |
| uint32_t unknown10; |
| uint32_t unknown11; |
| uint32_t unknown12; |
| char name[]; |
| } v2; |
| }; |
| }; |
| |
| struct uniformblockmember { |
| uint32_t type_info; |
| uint32_t is_array; |
| uint32_t array_size; /* elements in the array */ |
| uint32_t unknown2; /* Same as array_size */ |
| uint32_t unknown3; /* Seems to be a offset within UBO in vertex (by components) */ |
| uint32_t unknown4; |
| uint32_t unknown5; /* Seems to be a offset within UBO in fragment (by vec4) */ |
| uint32_t unknown6; |
| uint32_t unknown7; |
| uint32_t unknown8; |
| uint32_t unknown9; /* UBO block index? */ |
| uint32_t unknown10; |
| uint32_t unknown11; |
| uint32_t unknown12; |
| char name[]; |
| }; |
| |
| struct uniformblock |
| { |
| uint32_t type_info; |
| uint32_t unknown1; |
| uint32_t unknown2; |
| uint32_t unknown3; |
| uint32_t unknown4; |
| uint32_t num_members; |
| uint32_t num_members2; |
| uint32_t unknown5; |
| uint32_t unknown6; |
| uint32_t unknown7; |
| char name[]; |
| }; |
| |
| |
| struct sampler { |
| uint32_t type_info; |
| uint32_t is_array; |
| uint32_t array_size; /* elements in the array */ |
| uint32_t unknown4; /* same as array_size */ |
| uint32_t unknown5; |
| uint32_t unknown6; |
| uint32_t const_idx; /* the CONST() indx value for the sampler */ |
| uint32_t unknown7; |
| char name[]; |
| }; |
| |
| struct varying { |
| uint32_t type_info; |
| uint32_t unknown2; |
| uint32_t unknown3; |
| uint32_t reg; /* the register holding the value (on entry to the shader) */ |
| char name[]; |
| }; |
| |
| struct output { |
| uint32_t type_info; |
| uint32_t unknown2; |
| uint32_t unknown3; |
| uint32_t unknown4; |
| uint32_t unknown5; |
| uint32_t unknown6; |
| uint32_t unknown7; |
| uint32_t unknown8; |
| char name[]; |
| }; |
| |
| struct constant { |
| uint32_t unknown1; |
| uint32_t unknown2; |
| uint32_t unknown3; |
| uint32_t const_idx; |
| float val[]; |
| }; |
| |
| struct state { |
| char *buf; |
| int sz; |
| struct pgm_header *hdr; |
| struct attribute *attribs[32]; /* don't really know the upper limit.. */ |
| struct uniform *uniforms[32]; |
| struct sampler *samplers[32]; |
| struct varying *varyings[32]; |
| struct { |
| struct uniformblock *header; |
| struct uniformblockmember **members; /* GL ES 3.0 spec mandates minimum 16K support. a3xx supports 65K */ |
| } uniformblocks[24]; /* Maximum a330 supports */ |
| struct output *outputs[0]; /* I guess only one?? */ |
| }; |
| |
| static const char *infile; |
| static int full_dump = 1; |
| static int dump_shaders = 0; |
| static int gpu_id; |
| |
| static char *find_sect_end(char *buf, int sz) |
| { |
| uint8_t *ptr = (uint8_t *)buf; |
| uint8_t *end = ptr + sz - 3; |
| |
| while (ptr < end) { |
| uint32_t d = 0; |
| |
| d |= ptr[0] << 0; |
| d |= ptr[1] << 8; |
| d |= ptr[2] << 16; |
| d |= ptr[3] << 24; |
| |
| /* someone at QC likes baseball */ |
| if (d == 0xba5eba11) |
| return (char *)ptr; |
| |
| ptr++; |
| } |
| return NULL; |
| } |
| |
| static void *next_sect(struct state *state, int *sect_size) |
| { |
| char *end = find_sect_end(state->buf, state->sz); |
| void *sect; |
| |
| if (!end) |
| return NULL; |
| |
| *sect_size = end - state->buf; |
| |
| /* copy the section to keep things nicely 32b aligned: */ |
| sect = malloc(ALIGN(*sect_size, 4)); |
| memcpy(sect, state->buf, *sect_size); |
| |
| state->sz -= *sect_size + 4; |
| state->buf = end + 4; |
| |
| return sect; |
| } |
| |
| static int valid_type(uint32_t type_info) |
| { |
| switch ((type_info >> 8) & 0xff) { |
| case 0x8b: /* vector */ |
| case 0x8d: /* GLES3 samplers */ |
| case 0x14: /* float */ |
| return 1; |
| default: |
| return 0; |
| } |
| } |
| |
| #if 0 |
| static int valid_uniformblock(uint32_t type_info) |
| { |
| if (type_info == 0x128) |
| return 1; |
| return 0; |
| } |
| #endif |
| |
| static void dump_attribute(struct attribute *attrib) |
| { |
| printf("\tR%d, CONST(%d): %s\n", attrib->reg, |
| attrib->const_idx, attrib->name); |
| } |
| |
| static inline int is_uniform_v2(struct uniform *uniform) |
| { |
| /* TODO maybe this should be based on revision #? */ |
| if (uniform->v2.unknown10 == 0) |
| return 1; |
| return 0; |
| } |
| |
| static void dump_uniform(struct uniform *uniform) |
| { |
| char *name = is_uniform_v2(uniform) ? uniform->v2.name : uniform->v1.name; |
| if (uniform->const_reg == -1) { |
| printf("\tC%d+: %s\n", uniform->const_base, name); |
| } else { |
| printf("\tC%d: %s\n", uniform->const_reg, name); |
| } |
| } |
| |
| static void dump_sampler(struct sampler *sampler) |
| { |
| printf("\tCONST(%d): %s\n", sampler->const_idx, sampler->name); |
| } |
| |
| static void dump_varying(struct varying *varying) |
| { |
| printf("\tR%d: %s\n", varying->reg, varying->name); |
| } |
| |
| static void dump_uniformblock(struct uniformblock *uniformblock) |
| { |
| printf("\tUniform Block: %s(%d)\n", uniformblock->name, uniformblock->num_members); |
| } |
| |
| static void dump_uniformblockmember(struct uniformblockmember *member) |
| { |
| printf("Uniform Block member: %s\n", member->name); |
| } |
| |
| static void dump_output(struct output *output) |
| { |
| printf("\tR?: %s\n", output->name); |
| } |
| |
| static void dump_constant(struct constant *constant) |
| { |
| printf("\tC%d: %f, %f, %f, %f\n", constant->const_idx, |
| constant->val[0], constant->val[1], |
| constant->val[2], constant->val[3]); |
| } |
| |
| /* dump attr/uniform/sampler/varying/const summary: */ |
| static void dump_short_summary(struct state *state, int nconsts, |
| struct constant **constants) |
| { |
| int i; |
| |
| /* dump attr/uniform/sampler/varying/const summary: */ |
| for (i = 0; i < state->hdr->num_varyings; i++) { |
| dump_varying(state->varyings[i]); |
| } |
| for (i = 0; i < state->hdr->num_attribs; i++) { |
| dump_attribute(state->attribs[i]); |
| } |
| for (i = 0; i < state->hdr->num_uniforms; i++) { |
| dump_uniform(state->uniforms[i]); |
| } |
| for (i = 0; i < state->hdr->num_samplers; i++) { |
| dump_sampler(state->samplers[i]); |
| } |
| for (i = 0; i < nconsts - 1; i++) { |
| if (constants[i]->unknown2 == 0) { |
| dump_constant(constants[i]); |
| } |
| } |
| printf("\n"); |
| } |
| |
| static void dump_raw_shader(uint32_t *dwords, uint32_t sizedwords, int n, char *ext) |
| { |
| static char filename[256]; |
| int fd; |
| |
| if (!dump_shaders) |
| return; |
| |
| sprintf(filename, "%.*s-%d.%s", (int)strlen(infile)-3, infile, n, ext); |
| fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, 0644); |
| write(fd, dwords, sizedwords * 4); |
| } |
| |
| static void dump_shaders_a2xx(struct state *state) |
| { |
| int i, sect_size; |
| uint8_t *ptr; |
| |
| /* dump vertex shaders: */ |
| for (i = 0; i < 3; i++) { |
| struct vs_header *vs_hdr = next_sect(state, §_size); |
| struct constant *constants[32]; |
| int j, level = 0; |
| |
| printf("\n"); |
| |
| if (full_dump) { |
| printf("#######################################################\n"); |
| printf("######## VS%d HEADER: (size %d)\n", i, sect_size); |
| dump_hex((void *)vs_hdr, sect_size); |
| } |
| |
| for (j = 0; j < (int)vs_hdr->unknown1 - 1; j++) { |
| constants[j] = next_sect(state, §_size); |
| if (full_dump) { |
| printf("######## VS%d CONST: (size=%d)\n", i, sect_size); |
| dump_constant(constants[j]); |
| dump_hex((char *)constants[j], sect_size); |
| } |
| } |
| |
| ptr = next_sect(state, §_size); |
| printf("######## VS%d SHADER: (size=%d)\n", i, sect_size); |
| if (full_dump) { |
| dump_hex(ptr, sect_size); |
| level = 1; |
| } else { |
| dump_short_summary(state, vs_hdr->unknown1 - 1, constants); |
| } |
| disasm_a2xx((uint32_t *)(ptr + 32), (sect_size - 32) / 4, level+1, MESA_SHADER_VERTEX); |
| dump_raw_shader((uint32_t *)(ptr + 32), (sect_size - 32) / 4, i, "vo"); |
| free(ptr); |
| |
| for (j = 0; j < vs_hdr->unknown9; j++) { |
| ptr = next_sect(state, §_size); |
| if (full_dump) { |
| printf("######## VS%d CONST?: (size=%d)\n", i, sect_size); |
| dump_hex(ptr, sect_size); |
| } |
| free(ptr); |
| } |
| |
| for (j = 0; j < vs_hdr->unknown1 - 1; j++) { |
| free(constants[j]); |
| } |
| |
| free(vs_hdr); |
| } |
| |
| /* dump fragment shaders: */ |
| for (i = 0; i < 1; i++) { |
| struct fs_header *fs_hdr = next_sect(state, §_size); |
| struct constant *constants[32]; |
| int j, level = 0; |
| |
| printf("\n"); |
| |
| if (full_dump) { |
| printf("#######################################################\n"); |
| printf("######## FS%d HEADER: (size %d)\n", i, sect_size); |
| dump_hex((void *)fs_hdr, sect_size); |
| } |
| |
| for (j = 0; j < fs_hdr->unknown1 - 1; j++) { |
| constants[j] = next_sect(state, §_size); |
| if (full_dump) { |
| printf("######## FS%d CONST: (size=%d)\n", i, sect_size); |
| dump_constant(constants[j]); |
| dump_hex((char *)constants[j], sect_size); |
| } |
| } |
| |
| ptr = next_sect(state, §_size); |
| printf("######## FS%d SHADER: (size=%d)\n", i, sect_size); |
| if (full_dump) { |
| dump_hex(ptr, sect_size); |
| level = 1; |
| } else { |
| dump_short_summary(state, fs_hdr->unknown1 - 1, constants); |
| } |
| disasm_a2xx((uint32_t *)(ptr + 32), (sect_size - 32) / 4, level+1, MESA_SHADER_FRAGMENT); |
| dump_raw_shader((uint32_t *)(ptr + 32), (sect_size - 32) / 4, i, "fo"); |
| free(ptr); |
| |
| for (j = 0; j < fs_hdr->unknown1 - 1; j++) { |
| free(constants[j]); |
| } |
| |
| free(fs_hdr); |
| } |
| } |
| |
| static void dump_shaders_a3xx(struct state *state) |
| { |
| int i, j; |
| |
| /* dump vertex shaders: */ |
| for (i = 0; i < 2; i++) { |
| int instrs_size, hdr_size, sect_size, nconsts = 0, level = 0, compact = 0; |
| uint8_t *vs_hdr; |
| struct constant *constants[32]; |
| uint8_t *instrs = NULL; |
| |
| vs_hdr = next_sect(state, &hdr_size); |
| printf("hdr_size=%d\n", hdr_size); |
| |
| /* seems like there are two cases, either: |
| * 1) 152 byte header, |
| * 2) zero or more 32 byte compiler const sections |
| * 3) followed by shader instructions |
| * or, if there are no compiler consts, this can be |
| * all smashed in one large section |
| */ |
| int n; |
| if (state->hdr->revision >= 0xb) |
| n = 160; |
| else if (state->hdr->revision >= 7) |
| n = 156; |
| else |
| n = 152; |
| if (hdr_size > n) { |
| instrs = &vs_hdr[n]; |
| instrs_size = hdr_size - n; |
| hdr_size = n; |
| compact = 1; |
| } else { |
| while (1) { |
| void *ptr = next_sect(state, §_size); |
| |
| if ((sect_size != 32) && (sect_size != 44)) { |
| /* end of constants: */ |
| instrs = ptr; |
| instrs_size = sect_size; |
| break; |
| } |
| dump_hex_ascii(ptr, sect_size, 0); |
| constants[nconsts++] = ptr; |
| } |
| } |
| |
| printf("\n"); |
| |
| if (full_dump) { |
| printf("#######################################################\n"); |
| printf("######## VS%d HEADER: (size %d)\n", i, hdr_size); |
| dump_hex((void *)vs_hdr, hdr_size); |
| for (j = 0; j < nconsts; j++) { |
| printf("######## VS%d CONST: (size=%d)\n", i, (int)sizeof(constants[i])); |
| dump_constant(constants[j]); |
| dump_hex((char *)constants[j], sizeof(constants[j])); |
| } |
| } |
| |
| printf("######## VS%d SHADER: (size=%d)\n", i, instrs_size); |
| if (full_dump) { |
| dump_hex(instrs, instrs_size); |
| level = 1; |
| } else { |
| dump_short_summary(state, nconsts, constants); |
| } |
| |
| if (!compact) { |
| if (state->hdr->revision >= 7) { |
| instrs += ALIGN(instrs_size, 8) - instrs_size; |
| instrs_size = ALIGN(instrs_size, 8); |
| } |
| instrs += 32; |
| instrs_size -= 32; |
| } |
| |
| disasm_a3xx((uint32_t *)instrs, instrs_size / 4, level+1, stdout, gpu_id); |
| dump_raw_shader((uint32_t *)instrs, instrs_size / 4, i, "vo3"); |
| free(vs_hdr); |
| } |
| |
| /* dump fragment shaders: */ |
| for (i = 0; i < 1; i++) { |
| int instrs_size, hdr_size, sect_size, nconsts = 0, level = 0, compact = 0; |
| uint8_t *fs_hdr; |
| struct constant *constants[32]; |
| uint8_t *instrs = NULL; |
| |
| fs_hdr = next_sect(state, &hdr_size); |
| |
| printf("hdr_size=%d\n", hdr_size); |
| /* two cases, similar to vertex shader, but magic # is 200 |
| * (or 208 for newer?).. |
| */ |
| int n; |
| if (state->hdr->revision >= 0xb) |
| n = 256; |
| else if (state->hdr->revision >= 8) |
| n = 208; |
| else if (state->hdr->revision == 7) |
| n = 204; |
| else |
| n = 200; |
| |
| if (hdr_size > n) { |
| instrs = &fs_hdr[n]; |
| instrs_size = hdr_size - n; |
| hdr_size = n; |
| compact = 1; |
| } else { |
| while (1) { |
| void *ptr = next_sect(state, §_size); |
| |
| if ((sect_size != 32) && (sect_size != 44)) { |
| /* end of constants: */ |
| instrs = ptr; |
| instrs_size = sect_size; |
| break; |
| } |
| |
| dump_hex_ascii(ptr, sect_size, 0); |
| constants[nconsts++] = ptr; |
| } |
| } |
| |
| printf("\n"); |
| |
| if (full_dump) { |
| printf("#######################################################\n"); |
| printf("######## FS%d HEADER: (size %d)\n", i, hdr_size); |
| dump_hex((void *)fs_hdr, hdr_size); |
| for (j = 0; j < nconsts; j++) { |
| printf("######## FS%d CONST: (size=%d)\n", i, (int)sizeof(constants[i])); |
| dump_constant(constants[j]); |
| dump_hex((char *)constants[j], sizeof(constants[j])); |
| } |
| } |
| |
| printf("######## FS%d SHADER: (size=%d)\n", i, instrs_size); |
| if (full_dump) { |
| dump_hex(instrs, instrs_size); |
| level = 1; |
| } else { |
| dump_short_summary(state, nconsts, constants); |
| } |
| |
| if (!compact) { |
| if (state->hdr->revision >= 7) { |
| instrs += 44; |
| instrs_size -= 44; |
| } else { |
| instrs += 32; |
| instrs_size -= 32; |
| } |
| } |
| disasm_a3xx((uint32_t *)instrs, instrs_size / 4, level+1, stdout, gpu_id); |
| dump_raw_shader((uint32_t *)instrs, instrs_size / 4, i, "fo3"); |
| free(fs_hdr); |
| } |
| } |
| |
| static void dump_program(struct state *state) |
| { |
| int i, sect_size; |
| uint8_t *ptr; |
| |
| state->hdr = next_sect(state, §_size); |
| |
| printf("######## HEADER: (size %d)\n", sect_size); |
| printf("\tsize: %d\n", state->hdr->size); |
| printf("\trevision: %d\n", state->hdr->revision); |
| printf("\tattributes: %d\n", state->hdr->num_attribs); |
| printf("\tuniforms: %d\n", state->hdr->num_uniforms); |
| printf("\tsamplers: %d\n", state->hdr->num_samplers); |
| printf("\tvaryings: %d\n", state->hdr->num_varyings); |
| printf("\tuniform blocks: %d\n", state->hdr->num_uniformblocks); |
| if (full_dump) |
| dump_hex((void *)state->hdr, sect_size); |
| printf("\n"); |
| |
| /* there seems to be two 0xba5eba11's at the end of the header, possibly |
| * with some other stuff between them: |
| */ |
| ptr = next_sect(state, §_size); |
| if (full_dump) { |
| dump_hex_ascii(ptr, sect_size, 0); |
| } |
| |
| for (i = 0; (i < state->hdr->num_attribs) && (state->sz > 0); i++) { |
| state->attribs[i] = next_sect(state, §_size); |
| |
| /* hmm, for a3xx (or maybe just newer driver version), we have some |
| * extra sections that don't seem useful, so skip these: |
| */ |
| while (!valid_type(state->attribs[i]->type_info)) { |
| dump_hex_ascii(state->attribs[i], sect_size, 0); |
| state->attribs[i] = next_sect(state, §_size); |
| } |
| |
| clean_ascii(state->attribs[i]->name, sect_size - 28); |
| if (full_dump) { |
| printf("######## ATTRIBUTE: (size %d)\n", sect_size); |
| dump_attribute(state->attribs[i]); |
| dump_hex((char *)state->attribs[i], sect_size); |
| } |
| } |
| |
| for (i = 0; (i < state->hdr->num_uniforms) && (state->sz > 0); i++) { |
| state->uniforms[i] = next_sect(state, §_size); |
| |
| /* hmm, for a3xx (or maybe just newer driver version), we have some |
| * extra sections that don't seem useful, so skip these: |
| */ |
| while (!valid_type(state->uniforms[i]->type_info)) { |
| dump_hex_ascii(state->uniforms[i], sect_size, 0); |
| state->uniforms[i] = next_sect(state, §_size); |
| } |
| |
| if (is_uniform_v2(state->uniforms[i])) { |
| clean_ascii(state->uniforms[i]->v2.name, sect_size - 53); |
| } else { |
| clean_ascii(state->uniforms[i]->v1.name, sect_size - 41); |
| } |
| |
| if (full_dump) { |
| printf("######## UNIFORM: (size %d)\n", sect_size); |
| dump_uniform(state->uniforms[i]); |
| dump_hex((char *)state->uniforms[i], sect_size); |
| } |
| } |
| |
| for (i = 0; (i < state->hdr->num_samplers) && (state->sz > 0); i++) { |
| state->samplers[i] = next_sect(state, §_size); |
| |
| /* hmm, for a3xx (or maybe just newer driver version), we have some |
| * extra sections that don't seem useful, so skip these: |
| */ |
| while (!valid_type(state->samplers[i]->type_info)) { |
| dump_hex_ascii(state->samplers[i], sect_size, 0); |
| state->samplers[i] = next_sect(state, §_size); |
| } |
| |
| clean_ascii(state->samplers[i]->name, sect_size - 33); |
| if (full_dump) { |
| printf("######## SAMPLER: (size %d)\n", sect_size); |
| dump_sampler(state->samplers[i]); |
| dump_hex((char *)state->samplers[i], sect_size); |
| } |
| |
| } |
| |
| // These sections show up after all of the other sampler sections |
| // Loops through them all since we don't deal with them |
| if (state->hdr->revision >= 7) { |
| for (i = 0; (i < state->hdr->num_samplers) && (state->sz > 0); i++) { |
| ptr = next_sect(state, §_size); |
| dump_hex_ascii(ptr, sect_size, 0); |
| } |
| } |
| |
| |
| for (i = 0; (i < state->hdr->num_varyings) && (state->sz > 0); i++) { |
| state->varyings[i] = next_sect(state, §_size); |
| |
| /* hmm, for a3xx (or maybe just newer driver version), we have some |
| * extra sections that don't seem useful, so skip these: |
| */ |
| while (!valid_type(state->varyings[i]->type_info)) { |
| dump_hex_ascii(state->varyings[i], sect_size, 0); |
| state->varyings[i] = next_sect(state, §_size); |
| } |
| |
| clean_ascii(state->varyings[i]->name, sect_size - 16); |
| if (full_dump) { |
| printf("######## VARYING: (size %d)\n", sect_size); |
| dump_varying(state->varyings[i]); |
| dump_hex((char *)state->varyings[i], sect_size); |
| } |
| } |
| |
| /* show up again for revision >= 14?? */ |
| if (state->hdr->revision >= 14) { |
| for (i = 0; (i < state->hdr->num_varyings) && (state->sz > 0); i++) { |
| ptr = next_sect(state, §_size); |
| dump_hex_ascii(ptr, sect_size, 0); |
| } |
| } |
| |
| /* not sure exactly which revision started this, but seems at least |
| * rev7 and rev8 implicitly include a new section for gl_FragColor: |
| */ |
| if (state->hdr->revision >= 7) { |
| /* I guess only one? */ |
| state->outputs[0] = next_sect(state, §_size); |
| |
| clean_ascii(state->outputs[0]->name, sect_size - 32); |
| if (full_dump) { |
| printf("######## OUTPUT: (size %d)\n", sect_size); |
| dump_output(state->outputs[0]); |
| dump_hex((char *)state->outputs[0], sect_size); |
| } |
| } |
| |
| for (i = 0; (i < state->hdr->num_uniformblocks) && (state->sz > 0); i++) { |
| state->uniformblocks[i].header = next_sect(state, §_size); |
| |
| clean_ascii(state->uniformblocks[i].header->name, sect_size - 40); |
| if (full_dump) { |
| printf("######## UNIFORM BLOCK: (size %d)\n", sect_size); |
| dump_uniformblock(state->uniformblocks[i].header); |
| dump_hex((char *)state->uniformblocks[i].header, sect_size); |
| } |
| |
| /* |
| * OpenGL ES 3.0 spec mandates a minimum amount of 16K members supported |
| * a330 supports a minimum of 65K |
| */ |
| state->uniformblocks[i].members = malloc(state->uniformblocks[i].header->num_members * sizeof(void*)); |
| |
| int member = 0; |
| for (member = 0; (member < state->uniformblocks[i].header->num_members) && (state->sz > 0); member++) { |
| state->uniformblocks[i].members[member] = next_sect(state, §_size); |
| |
| clean_ascii(state->uniformblocks[i].members[member]->name, sect_size - 56); |
| if (full_dump) { |
| printf("######## UNIFORM BLOCK MEMBER: (size %d)\n", sect_size); |
| dump_uniformblockmember(state->uniformblocks[i].members[member]); |
| dump_hex((char *)state->uniformblocks[i].members[member], sect_size); |
| } |
| } |
| /* |
| * Qualcomm saves the UBO members twice for each UBO |
| * Don't ask me why |
| */ |
| for (member = 0; (member < state->uniformblocks[i].header->num_members) && (state->sz > 0); member++) { |
| state->uniformblocks[i].members[member] = next_sect(state, §_size); |
| |
| clean_ascii(state->uniformblocks[i].members[member]->name, sect_size - 56); |
| if (full_dump) { |
| printf("######## UNIFORM BLOCK MEMBER2: (size %d)\n", sect_size); |
| dump_uniformblockmember(state->uniformblocks[i].members[member]); |
| dump_hex((char *)state->uniformblocks[i].members[member], sect_size); |
| } |
| } |
| } |
| |
| if (gpu_id >= 300) { |
| dump_shaders_a3xx(state); |
| } else { |
| dump_shaders_a2xx(state); |
| } |
| |
| if (!full_dump) |
| return; |
| |
| /* dump ascii version of shader program: */ |
| ptr = next_sect(state, §_size); |
| printf("\n#######################################################\n"); |
| printf("######## SHADER SRC: (size=%d)\n", sect_size); |
| dump_ascii(ptr, sect_size); |
| free(ptr); |
| |
| /* dump remaining sections (there shouldn't be any): */ |
| while (state->sz > 0) { |
| ptr = next_sect(state, §_size); |
| printf("######## section (size=%d)\n", sect_size); |
| printf("as hex:\n"); |
| dump_hex(ptr, sect_size); |
| printf("as float:\n"); |
| dump_float(ptr, sect_size); |
| printf("as ascii:\n"); |
| dump_ascii(ptr, sect_size); |
| free(ptr); |
| } |
| /* cleanup the uniform buffer members we allocated */ |
| if (state->hdr->num_uniformblocks > 0) |
| free (state->uniformblocks[i].members); |
| } |
| |
| int main(int argc, char **argv) |
| { |
| enum rd_sect_type type = RD_NONE; |
| enum debug_t debug = PRINT_RAW | PRINT_STATS; |
| void *buf = NULL; |
| int sz; |
| struct io *io; |
| int raw_program = 0; |
| |
| /* lame argument parsing: */ |
| |
| while (1) { |
| if ((argc > 1) && !strcmp(argv[1], "--verbose")) { |
| debug |= PRINT_RAW | PRINT_VERBOSE; |
| argv++; |
| argc--; |
| continue; |
| } |
| if ((argc > 1) && !strcmp(argv[1], "--expand")) { |
| debug |= EXPAND_REPEAT; |
| argv++; |
| argc--; |
| continue; |
| } |
| if ((argc > 1) && !strcmp(argv[1], "--short")) { |
| /* only short dump, original shader, symbol table, and disassembly */ |
| full_dump = 0; |
| argv++; |
| argc--; |
| continue; |
| } |
| if ((argc > 1) && !strcmp(argv[1], "--dump-shaders")) { |
| dump_shaders = 1; |
| argv++; |
| argc--; |
| continue; |
| } |
| if ((argc > 1) && !strcmp(argv[1], "--raw")) { |
| raw_program = 1; |
| argv++; |
| argc--; |
| continue; |
| } |
| if ((argc > 1) && !strcmp(argv[1], "--gpu300")) { |
| gpu_id = 320; |
| argv++; |
| argc--; |
| continue; |
| } |
| break; |
| } |
| |
| if (argc != 2) { |
| fprintf(stderr, "usage: pgmdump [--verbose] [--short] [--dump-shaders] testlog.rd\n"); |
| return -1; |
| } |
| |
| disasm_a2xx_set_debug(debug); |
| disasm_a3xx_set_debug(debug); |
| |
| infile = argv[1]; |
| |
| io = io_open(infile); |
| if (!io) { |
| fprintf(stderr, "could not open: %s\n", infile); |
| return -1; |
| } |
| |
| if (raw_program) |
| { |
| io_readn(io, &sz, 4); |
| free(buf); |
| |
| /* note: allow hex dumps to go a bit past the end of the buffer.. |
| * might see some garbage, but better than missing the last few bytes.. |
| */ |
| buf = calloc(1, sz + 3); |
| io_readn(io, buf + 4, sz); |
| (*(int*)buf) = sz; |
| |
| struct state state = { |
| .buf = buf, |
| .sz = sz, |
| }; |
| printf("############################################################\n"); |
| printf("program:\n"); |
| dump_program(&state); |
| printf("############################################################\n"); |
| return 0; |
| } |
| |
| /* figure out what sort of input we are dealing with: */ |
| if (!(check_extension(infile, ".rd") || check_extension(infile, ".rd.gz"))) { |
| gl_shader_stage shader = ~0; |
| int ret; |
| if (check_extension(infile, ".vo")) { |
| shader = MESA_SHADER_VERTEX; |
| } else if (check_extension(infile, ".fo")) { |
| shader = MESA_SHADER_FRAGMENT; |
| } else if (check_extension(infile, ".vo3")) { |
| } else if (check_extension(infile, ".fo3")) { |
| } else if (check_extension(infile, ".co3")) { |
| } else { |
| fprintf(stderr, "invalid input file: %s\n", infile); |
| return -1; |
| } |
| buf = calloc(1, 100 * 1024); |
| ret = io_readn(io, buf, 100 * 1024); |
| if (ret < 0) { |
| fprintf(stderr, "error: %m"); |
| return -1; |
| } |
| if (shader != ~0) { |
| return disasm_a2xx(buf, ret/4, 0, shader); |
| } else { |
| /* disassembly does not depend on shader stage on a3xx+: */ |
| return disasm_a3xx(buf, ret/4, 0, stdout, gpu_id); |
| } |
| } |
| |
| while ((io_readn(io, &type, sizeof(type)) > 0) && (io_readn(io, &sz, 4) > 0)) { |
| free(buf); |
| |
| /* note: allow hex dumps to go a bit past the end of the buffer.. |
| * might see some garbage, but better than missing the last few bytes.. |
| */ |
| buf = calloc(1, sz + 3); |
| io_readn(io, buf, sz); |
| |
| switch(type) { |
| case RD_TEST: |
| if (full_dump) |
| printf("test: %s\n", (char *)buf); |
| break; |
| case RD_VERT_SHADER: |
| printf("vertex shader:\n%s\n", (char *)buf); |
| break; |
| case RD_FRAG_SHADER: |
| printf("fragment shader:\n%s\n", (char *)buf); |
| break; |
| case RD_PROGRAM: { |
| struct state state = { |
| .buf = buf, |
| .sz = sz, |
| }; |
| printf("############################################################\n"); |
| printf("program:\n"); |
| dump_program(&state); |
| printf("############################################################\n"); |
| break; |
| } |
| case RD_GPU_ID: |
| gpu_id = *((unsigned int *)buf); |
| printf("gpu_id: %d\n", gpu_id); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| io_close(io); |
| |
| return 0; |
| } |
| |