Exemplo n.º 1
0
/* queue buffer swap when GPU ready with rendering to buf */
int etna_bswap_queue_swap(etna_bswap_buffers *bufs)
{
    etna_flush(bufs->ctx); /* must flush before swap to make sure signal happens after all current commands processed */
    if(viv_event_queue_signal(bufs->buf[bufs->backbuffer].sig_id_ready, gcvKERNEL_PIXEL) != 0)
    {
#ifdef DEBUG
        fprintf(stderr, "Unable to queue framebuffer sync signal\n");
#endif
        return ETNA_INTERNAL_ERROR;
    }
    bufs->backbuffer = (bufs->backbuffer + 1) % ETNA_BSWAP_NUM_BUFFERS;
    return ETNA_OK;
}
Exemplo n.º 2
0
/* internal (non-inline) part of etna_reserve 
 * - commit current command buffer (if there is a current command buffer)
 * - signify when current command buffer becomes available using a signal
 * - switch to next command buffer 
 */
int _etna_reserve_internal(etna_ctx *ctx, size_t n)
{
    int status;
#ifdef DEBUG
    printf("Buffer full\n");
#endif
    if(ctx->cur_buf != -1)
    {
        /* Otherwise, if there is something to be committed left in the current command buffer, commit it */
        if((status = etna_flush(ctx)) != ETNA_OK)
            return status;
        /* Queue signal to signify when buffer is available again */
        if(viv_event_queue_signal(ctx->cmdbuf_sig[ctx->cur_buf], gcvKERNEL_COMMAND) != 0)
            return ETNA_INTERNAL_ERROR;
    }

    /* Move on to next buffer if not enough free in current one */
    status = switch_next_buffer(ctx);
    return status;
}
Exemplo n.º 3
0
int etna_finish(etna_ctx *ctx)
{
    int status;
    if(ctx == NULL)
        return ETNA_INVALID_ADDR;
    if((status = etna_flush(ctx)) != ETNA_OK)
        return status;
    /* Submit event queue with SIGNAL, fromWhere=gcvKERNEL_PIXEL (wait for pixel engine to finish) */
    if(viv_event_queue_signal(ctx->sig_id, gcvKERNEL_PIXEL) != 0)
    {
        return ETNA_INTERNAL_ERROR;
    }

    /* Wait for signal */
    if(viv_user_signal_wait(ctx->sig_id, SIG_WAIT_INDEFINITE) != 0)
    {
        return ETNA_INTERNAL_ERROR;
    }

    return ETNA_OK;
}
int main(int argc, char **argv)
{
    int rv;
    struct viv_conn *conn = 0;
    rv = viv_open(VIV_HW_3D, &conn);
    if(rv!=0)
    {
        fprintf(stderr, "Error opening device\n");
        exit(1);
    }
    printf("Succesfully opened device\n");
    viv_show_chip_info(conn);
    
    gcsHAL_INTERFACE id = {};
    id.command = gcvHAL_ATTACH;
    if((viv_invoke(conn, &id)) != gcvSTATUS_OK)
    {
        #ifdef DEBUG
        fprintf(stderr, "Error attaching to GPU\n");
        #endif
        exit(1);
    }
    gckCONTEXT context = id.u.Attach.context;

    /* allocate command buffer (blob uses four command buffers, but we don't even fill one) */
    viv_addr_t buf0_physical = 0;
    void *buf0_logical = 0;
    if(viv_alloc_contiguous(conn, 0x20000, &buf0_physical, &buf0_logical, NULL)!=0)
    {
        fprintf(stderr, "Error allocating host memory\n");
        exit(1);
    }
    printf("Allocated buffer: phys=%08x log=%08x\n", (uint32_t)buf0_physical, (uint32_t)buf0_logical);

    /* allocate main render target */
    gcuVIDMEM_NODE_PTR color_surface_node = 0;
    if(viv_alloc_linear_vidmem(conn, 0x1ab000, 0x40, gcvSURF_RENDER_TARGET, gcvPOOL_DEFAULT, &color_surface_node, NULL)!=0)
    {
        fprintf(stderr, "Error allocating render target buffer memory\n");
        exit(1);
    }
    printf("Allocated render target node: node=%08x\n", (uint32_t)color_surface_node);
    viv_addr_t color_surface_physical = 0;
    void *color_surface_logical = 0;
    if(viv_lock_vidmem(conn, color_surface_node, &color_surface_physical, &color_surface_logical)!=0)
    {
        fprintf(stderr, "Error locking render target memory\n");
        exit(1);
    }
    printf("Locked render target: phys=%08x log=%08x\n", (uint32_t)color_surface_physical, (uint32_t)color_surface_logical);

    /* allocate tile status for main render target */
    gcuVIDMEM_NODE_PTR color_status_node = 0;
    if(viv_alloc_linear_vidmem(conn, 0x1b00, 0x40, gcvSURF_TILE_STATUS, gcvPOOL_DEFAULT, &color_status_node, NULL)!=0)
    {
        fprintf(stderr, "Error allocating render target tile status memory\n");
        exit(1);
    }
    printf("Allocated render target tile status node: node=%08x\n", (uint32_t)color_status_node);
    viv_addr_t color_status_physical = 0;
    void *color_status_logical = 0;
    if(viv_lock_vidmem(conn, color_status_node, &color_status_physical, &color_status_logical)!=0)
    {
        fprintf(stderr, "Error locking render target memory\n");
        exit(1);
    }
    printf("Locked render target ts: phys=%08x log=%08x\n", (uint32_t)color_status_physical, (uint32_t)color_status_logical);

    /* allocate depth for main render target */
    gcuVIDMEM_NODE_PTR depth_surface_node = 0;
    if(viv_alloc_linear_vidmem(conn, 0xd1000, 0x40, gcvSURF_DEPTH, gcvPOOL_DEFAULT, &depth_surface_node, NULL)!=0)
    {
        fprintf(stderr, "Error allocating depth memory\n");
        exit(1);
    }
    printf("Allocated depth node: node=%08x\n", (uint32_t)depth_surface_node);
    viv_addr_t depth_surface_physical = 0;
    void *depth_surface_logical = 0;
    if(viv_lock_vidmem(conn, depth_surface_node, &depth_surface_physical, &depth_surface_logical)!=0)
    {
        fprintf(stderr, "Error locking depth target memory\n");
        exit(1);
    }
    printf("Locked depth target: phys=%08x log=%08x\n", (uint32_t)depth_surface_physical, (uint32_t)depth_surface_logical);
    
    /* allocate depth ts for main render target */
    gcuVIDMEM_NODE_PTR depth_status_node = 0;
    if(viv_alloc_linear_vidmem(conn, 0xe00, 0x40, gcvSURF_TILE_STATUS, gcvPOOL_DEFAULT, &depth_status_node, NULL)!=0)
    {
        fprintf(stderr, "Error allocating depth memory\n");
        exit(1);
    }
    printf("Allocated depth ts node: node=%08x\n", (uint32_t)depth_status_node);
    viv_addr_t depth_status_physical = 0;
    void *depth_status_logical = 0;
    if(viv_lock_vidmem(conn, depth_status_node, &depth_status_physical, &depth_status_logical)!=0)
    {
        fprintf(stderr, "Error locking depth target ts memory\n");
        exit(1);
    }
    printf("Locked depth ts target: phys=%08x log=%08x\n", (uint32_t)depth_status_physical, (uint32_t)depth_status_logical);

    /* allocate vertex buffer */
    gcuVIDMEM_NODE_PTR vtx_node = 0;
    if(viv_alloc_linear_vidmem(conn, 0x100000, 0x40, gcvSURF_VERTEX, gcvPOOL_DEFAULT, &vtx_node, NULL)!=0)
    {
        fprintf(stderr, "Error allocating vertex memory\n");
        exit(1);
    }
    printf("Allocated vertex node: node=%08x\n", (uint32_t)vtx_node);
    viv_addr_t vtx_physical = 0;
    void *vtx_logical = 0;
    if(viv_lock_vidmem(conn, vtx_node, &vtx_physical, &vtx_logical)!=0)
    {
        fprintf(stderr, "Error locking vertex memory\n");
        exit(1);
    }
    printf("Locked vertex memory: phys=%08x log=%08x\n", (uint32_t)vtx_physical, (uint32_t)vtx_logical);

    /* allocate tile status for aux render target */
    gcuVIDMEM_NODE_PTR rs_dest_node = 0;
    if(viv_alloc_linear_vidmem(conn, 0x1a0000, 0x40, gcvSURF_BITMAP, gcvPOOL_DEFAULT, &rs_dest_node, NULL)!=0)
    {
        fprintf(stderr, "Error allocating aux render target tile status memory\n");
        exit(1);
    }
    printf("Allocated aux render target tile status node: node=%08x\n", (uint32_t)rs_dest_node);
    viv_addr_t rs_dest_physical = 0;
    void *rs_dest_logical = 0;
    if(viv_lock_vidmem(conn, rs_dest_node, &rs_dest_physical, &rs_dest_logical)!=0)
    {
        fprintf(stderr, "Error locking aux ts render target memory\n");
        exit(1);
    }
    printf("Locked aux render target ts: phys=%08x log=%08x\n", (uint32_t)rs_dest_physical, (uint32_t)rs_dest_logical);
    
    int texture_size[] = {0x100000, 0x040000, 0x010000, 0x004000, 0x001000, 0x000400, 0x000200, 0x000100, 0x000100, 0x000100};
    gcuVIDMEM_NODE_PTR text_lod[10];
    viv_addr_t text_lod_physical[10];
    void* text_lod_logical[10];
    for(int idx=0; idx<10; idx++)
    {
        if (viv_alloc_linear_vidmem(conn, texture_size[idx], 0x40, gcvSURF_TEXTURE, gcvPOOL_DEFAULT, &text_lod[idx], NULL) != 0)
        {
            fprintf(stderr, "Error locking texture nr %d\n", idx);
            exit(1);
        }
        if(viv_lock_vidmem(conn, text_lod[idx], &text_lod_physical[idx], &text_lod_logical[idx]) != 0)
        {
            fprintf(stderr, "Error locking texture memory\n");
            exit(1);
        }
        printf("Locked texture target nr %d: phys=%08x log=%08x\n", idx, (uint32_t)text_lod_physical[idx], (uint32_t)text_lod_logical[idx]);
    }
    
    /* Interleave companion cube vertex data into ADDR_I */
    //memset(vtx_logical, 0, 0x5ef80);
    float *vertices_array = companion_vertices_array();
    float *texture_coordinates_array = companion_texture_coordinates_array();
    float *normals_array = companion_normals_array();
    int dest_idx = 0;
    for(int vert=0; vert<COMPANION_ARRAY_COUNT*3; ++vert)
    {
        ((float*)vtx_logical)[dest_idx] = vertices_array[vert];
        dest_idx++;
    }
    for(int vert=0; vert<COMPANION_ARRAY_COUNT*3; ++vert)
    {
        ((float*)vtx_logical)[dest_idx] = normals_array[vert];
        dest_idx++;
    }
    for(int vert=0; vert<COMPANION_ARRAY_COUNT*2; ++vert)
    {
        ((float*)vtx_logical)[dest_idx] = texture_coordinates_array[vert];
        dest_idx++;
    }
    
    /* Fill in texture (convert from RGB linear to tiled) */
    #if 1
    #define TILE_WIDTH (4)
    #define TILE_HEIGHT (4)
    #define TILE_WORDS (TILE_WIDTH*TILE_HEIGHT)
    unsigned ytiles = COMPANION_TEXTURE_HEIGHT / TILE_HEIGHT;
    unsigned xtiles = COMPANION_TEXTURE_WIDTH / TILE_WIDTH;
    unsigned dst_stride = xtiles * TILE_WORDS;
    
    for(unsigned ty=0; ty<ytiles; ++ty)
    {
        for(unsigned tx=0; tx<xtiles; ++tx)
        {
            unsigned ofs = ty * dst_stride + tx * TILE_WORDS;
            for(unsigned y=0; y<TILE_HEIGHT; ++y)
            {
                for(unsigned x=0; x<TILE_WIDTH; ++x)
                {
                    unsigned srcy = ty*TILE_HEIGHT + y;
                    unsigned srcx = tx*TILE_WIDTH + x;
                    unsigned src_ofs = (srcy*COMPANION_TEXTURE_WIDTH+srcx)*3;
                    unsigned r,g,b,a;
                    r = ((uint8_t*)companion_texture)[src_ofs+0];
                    g = ((uint8_t*)companion_texture)[src_ofs+1];
                    b = ((uint8_t*)companion_texture)[src_ofs+2];
                    a = 255;
                    
                    ((uint32_t*)text_lod_logical[0])[ofs] = ((a&0xFF) << 24) | ((b&0xFF) << 16) | ((g&0xFF) << 8) | (r&0xFF);
                    //((uint32_t*)text_lod_logical[0])[ofs] = 0xff00ff00;
                    ofs += 1;
                }
            }
        }
    }
    #endif
    #if 0
    int texfd = open("/data/mine/texture.raw", O_RDONLY); 
    read(texfd, text_lod_logical[0], 512*512*4);
    close(texfd);
    #endif
    
    struct _gcoCMDBUF commandBuffer = {
        .object = {
            .type = gcvOBJ_COMMANDBUFFER
        },
        .physical = (void*)buf0_physical,
        .logical = (void*)buf0_logical,
        .bytes = 0x20000,
        .startOffset = 0x0,
    };

    commandBuffer.free = commandBuffer.bytes - 0x8; /* Always keep 0x8 at end of buffer for kernel driver */
    /* Set addresses in first command buffer */
    cmdbuf1[31] = cmdbuf1[81] = cmdbuf1[103] = color_status_physical; //H
    cmdbuf1[32] = cmdbuf1[104] = color_surface_physical; //G
    cmdbuf1[41] = cmdbuf1[135] = cmdbuf1[155] = depth_status_physical; //J
    cmdbuf1[42] = cmdbuf1[156] = depth_surface_physical; //I
    cmdbuf1[191] = text_lod_physical[0]; //L - base bitmap
    cmdbuf1[193] = text_lod_physical[1]; //M

    /* Submit first command buffer */
    commandBuffer.startOffset = 0;
    memcpy((void*)((size_t)commandBuffer.logical + commandBuffer.startOffset), cmdbuf1, sizeof(cmdbuf1));
    commandBuffer.offset = commandBuffer.startOffset + sizeof(cmdbuf1);
    commandBuffer.free -= sizeof(cmdbuf1) + 0x08;
    printf("[1] startOffset=%08x, offset=%08x, free=%08x\n", (uint32_t)commandBuffer.startOffset, (uint32_t)commandBuffer.offset, (uint32_t)commandBuffer.free);
    if(viv_commit(conn, &commandBuffer, context) != 0)
    {
        fprintf(stderr, "Error committing first command buffer\n");
        exit(1);
    }

    /* Create signal */
    int sig_id = 0;
    if(viv_user_signal_create(conn, 0, &sig_id) != 0) /* automatic resetting signal */
    {
        fprintf(stderr, "Cannot create user signal\n");
        exit(1);
    }
    printf("Created user signal %i\n", sig_id);
    
    /* Queue and wait for signal */
    if(viv_event_queue_signal(conn, sig_id, gcvKERNEL_PIXEL) != 0)
    {
        fprintf(stderr, "Cannot queue GPU signal\n");
        exit(1);
    }
    if(viv_user_signal_wait(conn, sig_id, VIV_WAIT_INDEFINITE) != 0)
    {
        fprintf(stderr, "Cannot wait for signal\n");
        exit(1);
    }

    //generate LOD
    unsigned stride[] = {0x1000, 0x800, 0x400, 0x200, 0x100, 0x100, 0x100, 0x100, 0x100};
    unsigned height[] = { 256, 128,  64,  32,  16,   8,   8,   8};
    unsigned width[] =  { 256, 128,  64,  32,  32,  32,  32,  32};
    /* 33 - stride src
     35 - stride dst  *
     39 - pad but allways different - garbage?
     45 - source addr
     47 - dest addr
     49 - height width*/
    for (int idx=0; idx<8; idx++)
    {
        /* Submit command buffer 2 */
        cmdbuf2[33] = stride[idx];
        cmdbuf2[35] = stride[idx + 1];
        cmdbuf2[45] = text_lod_physical[idx + 1]; //idx 0 - 1 done in first cmdbuf
        cmdbuf2[47] = text_lod_physical[idx + 2];
        cmdbuf2[49] = (height[idx] << 16) | width[idx];

        commandBuffer.startOffset = commandBuffer.offset + 0x08; /* Make space for LINK */
        memcpy((void*)((size_t)commandBuffer.logical + commandBuffer.startOffset), cmdbuf2, sizeof(cmdbuf2));
        commandBuffer.offset = commandBuffer.startOffset + sizeof(cmdbuf2);
        commandBuffer.free -= sizeof(cmdbuf2) + 0x08;
        printf("[2,%d] startOffset=%08x, offset=%08x, free=%08x\n", idx, (uint32_t)commandBuffer.startOffset, (uint32_t)commandBuffer.offset, (uint32_t)commandBuffer.free);
        if(viv_commit(conn, &commandBuffer, context) != 0)
        {
            fprintf(stderr, "Error committing second command buffer\n");
            exit(1);
        }
    }

    /* Submit command buffer 3 */
    commandBuffer.startOffset = commandBuffer.offset + 0x08;
    memcpy((void*)((size_t)commandBuffer.logical + commandBuffer.startOffset), cmdbuf3, sizeof(cmdbuf3));
    commandBuffer.offset = commandBuffer.startOffset + sizeof(cmdbuf3);
    commandBuffer.free -= sizeof(cmdbuf3) + 0x08;
    printf("[3] startOffset=%08x, offset=%08x, free=%08x\n", (uint32_t)commandBuffer.startOffset, (uint32_t)commandBuffer.offset, (uint32_t)commandBuffer.free);
    if(viv_commit(conn, &commandBuffer, context) != 0)
    {
        fprintf(stderr, "Error committing third command buffer\n");
        exit(1);
    }

    /* Submit command buffer 4 */
    cmdbuf4[67] = vtx_physical; //A
    cmdbuf4[68] = vtx_physical+0x239d0; //A
    cmdbuf4[69] = vtx_physical+(2*0x239d0); //A
    cmdbuf4[87] = text_lod_physical[0];
    cmdbuf4[89] = text_lod_physical[1];
    cmdbuf4[91] = text_lod_physical[2];
    cmdbuf4[93] = text_lod_physical[3];
    cmdbuf4[95] = text_lod_physical[4];
    cmdbuf4[97] = text_lod_physical[5];
    cmdbuf4[99] = text_lod_physical[6];
    cmdbuf4[101] = text_lod_physical[7];
    cmdbuf4[103] = text_lod_physical[8];
    cmdbuf4[105] = text_lod_physical[9];
    cmdbuf4[153] = cmdbuf4[155] = color_surface_physical;//G
    cmdbuf4[165] = cmdbuf4[167] = depth_surface_physical;//I    

    commandBuffer.startOffset = commandBuffer.offset + 0x08;
    memcpy((void*)((size_t)commandBuffer.logical + commandBuffer.startOffset), cmdbuf4, sizeof(cmdbuf4));
    commandBuffer.offset = commandBuffer.startOffset + sizeof(cmdbuf4);
    commandBuffer.free -= sizeof(cmdbuf4) + 0x08;
    printf("[4] startOffset=%08x, offset=%08x, free=%08x\n", (uint32_t)commandBuffer.startOffset, (uint32_t)commandBuffer.offset, (uint32_t)commandBuffer.free);
    if(viv_commit(conn, &commandBuffer, context) != 0)
    {
        fprintf(stderr, "Error committing command buffer 4\n");
        exit(1);
    }

    /* Submit event, and wait */
    if(viv_event_queue_signal(conn, sig_id, gcvKERNEL_PIXEL) != 0)
    {
        fprintf(stderr, "Cannot queue GPU signal\n");
        exit(1);
    }
    if(viv_user_signal_wait(conn, sig_id, VIV_WAIT_INDEFINITE) != 0)
    {
        fprintf(stderr, "Cannot wait for signal\n");
        exit(1);
    }

    /* Submit command buffer 5 */
    cmdbuf5[35] = cmdbuf5[37] = color_surface_physical;
    
    commandBuffer.startOffset = commandBuffer.offset + 0x08;
    memcpy((void*)((size_t)commandBuffer.logical + commandBuffer.startOffset), cmdbuf5, sizeof(cmdbuf5));
    commandBuffer.offset = commandBuffer.startOffset + sizeof(cmdbuf5);
    commandBuffer.free -= sizeof(cmdbuf5) + 0x08;
    printf("[5] startOffset=%08x, offset=%08x, free=%08x\n", (uint32_t)commandBuffer.startOffset, (uint32_t)commandBuffer.offset, (uint32_t)commandBuffer.free);
    if(viv_commit(conn, &commandBuffer, context) != 0)
    {
        fprintf(stderr, "Error committing command buffer 5\n");
        exit(1);
    }
    
    
    
    cmdbuf6[35] = color_surface_physical;
    cmdbuf6[37] = rs_dest_physical;
    
    commandBuffer.startOffset = commandBuffer.offset + 0x08;
    memcpy((void*)((size_t)commandBuffer.logical + commandBuffer.startOffset), cmdbuf6, sizeof(cmdbuf6));
    commandBuffer.offset = commandBuffer.startOffset + sizeof(cmdbuf6);
    commandBuffer.free -= sizeof(cmdbuf6) + 0x08;
    printf("[6] startOffset=%08x, offset=%08x, free=%08x\n", (uint32_t)commandBuffer.startOffset, (uint32_t)commandBuffer.offset, (uint32_t)commandBuffer.free);
    if(viv_commit(conn, &commandBuffer, context) != 0)
    {
        fprintf(stderr, "Error committing command buffer 5\n");
        exit(1);
    }
    
    
    /* Allocate bitmap memory, map */
    gcuVIDMEM_NODE_PTR bmp_node = 0;
    if(viv_alloc_linear_vidmem(conn, 0x177000, 0x40, gcvSURF_BITMAP, gcvPOOL_DEFAULT, &bmp_node, NULL)!=0)
    {
        fprintf(stderr, "Error allocating bitmap status memory\n");
        exit(1);
    }
    printf("Allocated bitmap node: node=%08x\n", (uint32_t)bmp_node);
    viv_addr_t bmp_physical = 0; // ADDR_J 
    void *bmp_logical = 0;
    if(viv_lock_vidmem(conn, bmp_node, &bmp_physical, &bmp_logical)!=0)
    {
        fprintf(stderr, "Error locking bmp memory\n");
        exit(1);
    }
    memset(bmp_logical, 0xff, 0x177000); // clear previous result
    printf("Locked bmp: phys=%08x log=%08x\n", (uint32_t)bmp_physical, (uint32_t)bmp_logical);
     
     
    /* Submit command buffer 7 */
    cmdbuf7[0x19] = rs_dest_physical; //color_surface_physical or rs_dest_physical
    cmdbuf7[0x1b] = bmp_physical;

    commandBuffer.startOffset = commandBuffer.offset + 0x08;
    memcpy((void*)((size_t)commandBuffer.logical + commandBuffer.startOffset), cmdbuf7, sizeof(cmdbuf7));
    commandBuffer.offset = commandBuffer.startOffset + sizeof(cmdbuf7);
    commandBuffer.free -= sizeof(cmdbuf7) + 0x08;
    printf("[7] startOffset=%08x, offset=%08x, free=%08x\n", (uint32_t)commandBuffer.startOffset, (uint32_t)commandBuffer.offset, (uint32_t)commandBuffer.free);
    if(viv_commit(conn, &commandBuffer, context) != 0)
    {
        fprintf(stderr, "Error committing command buffer 5\n");
        exit(1);
    }

    /* Submit event queue with SIGNAL, fromWhere=gcvKERNEL_PIXEL */
    if(viv_event_queue_signal(conn, sig_id, gcvKERNEL_PIXEL) != 0)
    {
        fprintf(stderr, "Cannot queue GPU signal\n");
        exit(1);
    }
    /* Wait for signal */
    if(viv_user_signal_wait(conn, sig_id, VIV_WAIT_INDEFINITE) != 0)
    {
        fprintf(stderr, "Cannot wait for signal\n");
        exit(1);
    }

    bmp_dump32(bmp_logical, 800, 480, false, "/home/linaro/replay.bmp");
    /* Unlock video memory */
    if(viv_unlock_vidmem(conn, bmp_node, gcvSURF_BITMAP, 1) != 0)
    {
        fprintf(stderr, "Cannot unlock vidmem\n");
        exit(1);
    }
    /*
    for(int x=0; x<0x700; ++x)
    {
        uint32_t value = ((uint32_t*)rt_ts_logical)[x];
        printf("Sample ts: %x %08x\n", x*4, value);
    }*/
    //printf("Contextbuffer used %i\n", *contextBuffer.inUse);
    viv_close(conn);
    return 0;
}
Exemplo n.º 5
0
int main(int argc, char **argv)
{
    int rv;
    fb_info fb;
    rv = fb_open(0, &fb);
    if(rv!=0)
    {
        exit(1);
    }
    fb_set_buffer(&fb, 0);
    rv = viv_open();
    if(rv!=0)
    {
        fprintf(stderr, "Error opening device\n");
        exit(1);
    }
    printf("Succesfully opened device\n");

    /* allocate command buffer (blob uses four command buffers, but we don't even fill one) */
    viv_addr_t buf0_physical = 0;
    void *buf0_logical = 0;
    if(viv_alloc_contiguous(0x8000, &buf0_physical, &buf0_logical, NULL)!=0)
    {
        fprintf(stderr, "Error allocating host memory\n");
        exit(1);
    }
    printf("Allocated buffer: phys=%08x log=%08x\n", (uint32_t)buf0_physical, (uint32_t)buf0_logical);

    /* allocate main render target */
    gcuVIDMEM_NODE_PTR rt_node = 0;
    if(viv_alloc_linear_vidmem(0x70000, 0x40, gcvSURF_RENDER_TARGET, gcvPOOL_DEFAULT, &rt_node, NULL)!=0)
    {
        fprintf(stderr, "Error allocating render target buffer memory\n");
        exit(1);
    }
    printf("Allocated render target node: node=%08x\n", (uint32_t)rt_node);
    
    viv_addr_t rt_physical = 0;
    void *rt_logical = 0;
    if(viv_lock_vidmem(rt_node, &rt_physical, &rt_logical)!=0)
    {
        fprintf(stderr, "Error locking render target memory\n");
        exit(1);
    }
    printf("Locked render target: phys=%08x log=%08x\n", (uint32_t)rt_physical, (uint32_t)rt_logical);
    memset(rt_logical, 0xff, 0x70000); /* clear previous result just in case, test that clearing works */

    /* allocate tile status for main render target */
    gcuVIDMEM_NODE_PTR rt_ts_node = 0;
    if(viv_alloc_linear_vidmem(0x700, 0x40, gcvSURF_TILE_STATUS, gcvPOOL_DEFAULT, &rt_ts_node, NULL)!=0)
    {
        fprintf(stderr, "Error allocating render target tile status memory\n");
        exit(1);
    }
    printf("Allocated render target tile status node: node=%08x\n", (uint32_t)rt_ts_node);
    
    viv_addr_t rt_ts_physical = 0;
    void *rt_ts_logical = 0;
    if(viv_lock_vidmem(rt_ts_node, &rt_ts_physical, &rt_ts_logical)!=0)
    {
        fprintf(stderr, "Error locking render target memory\n");
        exit(1);
    }
    printf("Locked render target ts: phys=%08x log=%08x\n", (uint32_t)rt_ts_physical, (uint32_t)rt_ts_logical);

    /* allocate depth for main render target */
    gcuVIDMEM_NODE_PTR z_node = 0;
    if(viv_alloc_linear_vidmem(0x38000, 0x40, gcvSURF_DEPTH, gcvPOOL_DEFAULT, &z_node, NULL)!=0)
    {
        fprintf(stderr, "Error allocating depth memory\n");
        exit(1);
    }
    printf("Allocated depth node: node=%08x\n", (uint32_t)z_node);
    
    viv_addr_t z_physical = 0;
    void *z_logical = 0;
    if(viv_lock_vidmem(z_node, &z_physical, &z_logical)!=0)
    {
        fprintf(stderr, "Error locking depth target memory\n");
        exit(1);
    }
    printf("Locked depth target: phys=%08x log=%08x\n", (uint32_t)z_physical, (uint32_t)z_logical);

    /* allocate depth ts for main render target */
    gcuVIDMEM_NODE_PTR z_ts_node = 0;
    if(viv_alloc_linear_vidmem(0x400, 0x40, gcvSURF_TILE_STATUS, gcvPOOL_DEFAULT, &z_ts_node, NULL)!=0)
    {
        fprintf(stderr, "Error allocating depth memory\n");
        exit(1);
    }
    printf("Allocated depth ts node: node=%08x\n", (uint32_t)z_ts_node);
    
    viv_addr_t z_ts_physical = 0;
    void *z_ts_logical = 0;
    if(viv_lock_vidmem(z_ts_node, &z_ts_physical, &z_ts_logical)!=0)
    {
        fprintf(stderr, "Error locking depth target ts memory\n");
        exit(1);
    }
    printf("Locked depth ts target: phys=%08x log=%08x\n", (uint32_t)z_ts_physical, (uint32_t)z_ts_logical);

    /* allocate vertex buffer */
    gcuVIDMEM_NODE_PTR vtx_node = 0;
    if(viv_alloc_linear_vidmem(0x60000, 0x40, gcvSURF_VERTEX, gcvPOOL_DEFAULT, &vtx_node, NULL)!=0)
    {
        fprintf(stderr, "Error allocating vertex memory\n");
        exit(1);
    }
    printf("Allocated vertex node: node=%08x\n", (uint32_t)vtx_node);
    
    viv_addr_t vtx_physical = 0;
    void *vtx_logical = 0;
    if(viv_lock_vidmem(vtx_node, &vtx_physical, &vtx_logical)!=0)
    {
        fprintf(stderr, "Error locking vertex memory\n");
        exit(1);
    }
    printf("Locked vertex memory: phys=%08x log=%08x\n", (uint32_t)vtx_physical, (uint32_t)vtx_logical);

    /* allocate aux render target */
    gcuVIDMEM_NODE_PTR aux_rt_node = 0;
    if(viv_alloc_linear_vidmem(0x4000, 0x40, gcvSURF_RENDER_TARGET, gcvPOOL_SYSTEM /*why?*/, &aux_rt_node, NULL)!=0)
    {
        fprintf(stderr, "Error allocating aux render target buffer memory\n");
        exit(1);
    }
    printf("Allocated aux render target node: node=%08x\n", (uint32_t)aux_rt_node);
    
    viv_addr_t aux_rt_physical = 0;
    void *aux_rt_logical = 0;
    if(viv_lock_vidmem(aux_rt_node, &aux_rt_physical, &aux_rt_logical)!=0)
    {
        fprintf(stderr, "Error locking aux render target memory\n");
        exit(1);
    }
    printf("Locked aux render target: phys=%08x log=%08x\n", (uint32_t)aux_rt_physical, (uint32_t)aux_rt_logical);

    /* allocate tile status for aux render target */
    gcuVIDMEM_NODE_PTR aux_rt_ts_node = 0;
    if(viv_alloc_linear_vidmem(0x100, 0x40, gcvSURF_TILE_STATUS, gcvPOOL_DEFAULT, &aux_rt_ts_node, NULL)!=0)
    {
        fprintf(stderr, "Error allocating aux render target tile status memory\n");
        exit(1);
    }
    printf("Allocated aux render target tile status node: node=%08x\n", (uint32_t)aux_rt_ts_node);
    
    viv_addr_t aux_rt_ts_physical = 0;
    void *aux_rt_ts_logical = 0;
    if(viv_lock_vidmem(aux_rt_ts_node, &aux_rt_ts_physical, &aux_rt_ts_logical)!=0)
    {
        fprintf(stderr, "Error locking aux ts render target memory\n");
        exit(1);
    }
    printf("Locked aux render target ts: phys=%08x log=%08x\n", (uint32_t)aux_rt_ts_physical, (uint32_t)aux_rt_ts_logical);

    /* Phew, now we got all the memory we need.
     * Write interleaved attribute vertex stream.
     * Unlike the GL example we only do this once, not every time glDrawArrays is called, the same would be accomplished
     * from GL by using a vertex buffer object.
     */
    for(int vert=0; vert<NUM_VERTICES; ++vert)
    {
        int src_idx = vert * COMPONENTS_PER_VERTEX;
        int dest_idx = vert * COMPONENTS_PER_VERTEX * 3;
        for(int comp=0; comp<COMPONENTS_PER_VERTEX; ++comp)
        {
            ((float*)vtx_logical)[dest_idx+comp+0] = vVertices[src_idx + comp]; /* 0 */
            ((float*)vtx_logical)[dest_idx+comp+3] = vNormals[src_idx + comp]; /* 1 */
            ((float*)vtx_logical)[dest_idx+comp+6] = vColors[src_idx + comp]; /* 2 */
        }
    }
    /*
    for(int idx=0; idx<NUM_VERTICES*3*3; ++idx)
    {
        printf("%i %f\n", idx, ((float*)vtx_logical)[idx]);
    }*/

    /* Load the command buffer and send the commit command. */
    /* First build context state map */
    size_t stateCount = 0x1d00;
    uint32_t *contextMap = malloc(stateCount * 4);
    memset(contextMap, 0, stateCount*4);
    for(int idx=0; idx<sizeof(contextbuf_addr)/sizeof(address_index_t); ++idx)
    {
        contextMap[contextbuf_addr[idx].address / 4] = contextbuf_addr[idx].index;
    }

    struct _gcoCMDBUF commandBuffer = {
        .object = {
            .type = gcvOBJ_COMMANDBUFFER
        },
        //.os = (_gcoOS*)0xbf7488,
        //.hardware = (_gcoHARDWARE*)0x402694e0,
        .physical = (void*)buf0_physical,
        .logical = (void*)buf0_logical,
        .bytes = 0x8000,
        .startOffset = 0x0,
        //.offset = 0xac0,
        //.free = 0x7520,
        //.hintTable = (unsigned int*)0x0, // Used when gcdSECURE
        //.hintIndex = (unsigned int*)0x58,  // Used when gcdSECURE
        //.hintCommit = (unsigned int*)0xffffffff // Used when gcdSECURE
    };
    struct _gcoCONTEXT contextBuffer = {
        .object = {
            .type = gcvOBJ_CONTEXT
        },
        //.os = (_gcoOS*)0xbf7488,
        //.hardware = (_gcoHARDWARE*)0x402694e0,
        .id = 0x0, // Actual ID will be returned here
        .map = contextMap,
        .stateCount = stateCount,
        //.hint = (unsigned char*)0x0, // Used when gcdSECURE
        //.hintValue = 2, // Used when gcdSECURE
        //.hintCount = 0xca, // Used when gcdSECURE
        .buffer = contextbuf,
        .pipe3DIndex = 0x2d6, // XXX should not be hardcoded
        .pipe2DIndex = 0x106e,
        .linkIndex = 0x1076,
        .inUseIndex = 0x1078,
        .bufferSize = 0x41e4,
        .bytes = 0x0, // Number of bytes at physical, logical
        .physical = (void*)0x0,
        .logical = (void*)0x0,
        .link = (void*)0x0, // Logical address of link
        .initialPipe = 0x1,
        .entryPipe = 0x0,
        .currentPipe = 0x0,
        .postCommit = 1,
        .inUse = (int*)0x0, // Logical address of inUse
        .lastAddress = 0xffffffff, // Not used by kernel
        .lastSize = 0x2, // Not used by kernel
        .lastIndex = 0x106a, // Not used by kernel
        .lastFixed = 0, // Not used by kernel
        //.hintArray = (unsigned int*)0x0, // Used when gcdSECURE
        //.hintIndex = (unsigned int*)0x0  // Used when gcdSECURE
    };
    commandBuffer.free = commandBuffer.bytes - 0x8; /* Always keep 0x8 at end of buffer for kernel driver */
    /* Set addresses in first command buffer */
    cmdbuf1[0x57] = cmdbuf1[0x67] = cmdbuf1[0x9f] = cmdbuf1[0xbb] = cmdbuf1[0xd9] = cmdbuf1[0xfb] = rt_physical;
    cmdbuf1[0x65] = cmdbuf1[0x9d] = cmdbuf1[0xb9] = cmdbuf1[0xd7] = cmdbuf1[0xe5] = cmdbuf1[0xf9] = rt_ts_physical;
    cmdbuf1[0x6d] = cmdbuf1[0x7f] = z_physical;
    cmdbuf1[0x7d] = z_ts_physical;
    cmdbuf1[0x87] = cmdbuf1[0xa3] = cmdbuf1[0xc1] = aux_rt_ts_physical;
    cmdbuf1[0x89] = cmdbuf1[0x8f] = cmdbuf1[0x93] 
        = cmdbuf1[0xa5] = cmdbuf1[0xab] = cmdbuf1[0xaf] 
        = cmdbuf1[0xc3] = cmdbuf1[0xc9] = cmdbuf1[0xcd] = aux_rt_physical;
    cmdbuf1[0x1f3] = cmdbuf1[0x215] = cmdbuf1[0x237] 
        = cmdbuf1[0x259] = cmdbuf1[0x27b] = cmdbuf1[0x29d] = vtx_physical;

    /* Submit first command buffer */
    commandBuffer.startOffset = 0;
    memcpy((void*)((size_t)commandBuffer.logical + commandBuffer.startOffset), cmdbuf1, sizeof(cmdbuf1));
    commandBuffer.offset = commandBuffer.startOffset + sizeof(cmdbuf1);
    commandBuffer.free -= sizeof(cmdbuf1) + 0x18;
    printf("[1] startOffset=%08x, offset=%08x, free=%08x\n", (uint32_t)commandBuffer.startOffset, (uint32_t)commandBuffer.offset, (uint32_t)commandBuffer.free);
    if(viv_commit(&commandBuffer, &contextBuffer) != 0)
    {
        fprintf(stderr, "Error committing first command buffer\n");
        exit(1);
    }

    /* After the first COMMIT, allocate contiguous memory for context and set
     * bytes, physical, logical, link, inUse */
    printf("Context assigned index: %i\n", (uint32_t)contextBuffer.id);
    viv_addr_t cbuf0_physical = 0;
    void *cbuf0_logical = 0;
    size_t cbuf0_bytes = 0;
    if(viv_alloc_contiguous(contextBuffer.bufferSize, &cbuf0_physical, &cbuf0_logical, &cbuf0_bytes)!=0)
    {
        fprintf(stderr, "Error allocating contiguous host memory for context\n");
        exit(1);
    }
    printf("Allocated buffer (size 0x%x) for context: phys=%08x log=%08x\n", (int)cbuf0_bytes, (int)cbuf0_physical, (int)cbuf0_logical);
    contextBuffer.bytes = cbuf0_bytes; /* actual size of buffer */
    contextBuffer.physical = (void*)cbuf0_physical;
    contextBuffer.logical = cbuf0_logical;
    contextBuffer.link = ((uint32_t*)cbuf0_logical) + contextBuffer.linkIndex;
    contextBuffer.inUse = (gctBOOL*)(((uint32_t*)cbuf0_logical) + contextBuffer.inUseIndex);
    *contextBuffer.inUse = 0;

    /* Submit second command buffer, with updated context.
     * Second command buffer fills the background.
     */
    cmdbuf2[0x1d] = cmdbuf2[0x1f] = rt_physical;
    commandBuffer.startOffset = commandBuffer.offset + 0x18; /* Make space for LINK */
    memcpy((void*)((size_t)commandBuffer.logical + commandBuffer.startOffset), cmdbuf2, sizeof(cmdbuf2));
    commandBuffer.offset = commandBuffer.startOffset + sizeof(cmdbuf2);
    commandBuffer.free -= sizeof(cmdbuf2) + 0x18;
    printf("[2] startOffset=%08x, offset=%08x, free=%08x\n", (uint32_t)commandBuffer.startOffset, (uint32_t)commandBuffer.offset, (uint32_t)commandBuffer.free);
    if(viv_commit(&commandBuffer, &contextBuffer) != 0)
    {
        fprintf(stderr, "Error committing second command buffer\n");
        exit(1);
    }

    /* Submit third command buffer, with updated context
     * Third command buffer does some cache flush trick?
     * It can be left out without any visible harm.
     **/
    cmdbuf3[0x9] = aux_rt_ts_physical;
    cmdbuf3[0xb] = cmdbuf3[0x11] = cmdbuf3[0x15] = aux_rt_physical;
    cmdbuf3[0x1f] = rt_ts_physical;
    cmdbuf3[0x21] = rt_physical;
    commandBuffer.startOffset = commandBuffer.offset + 0x18;
    memcpy((void*)((size_t)commandBuffer.logical + commandBuffer.startOffset), cmdbuf3, sizeof(cmdbuf3));
    commandBuffer.offset = commandBuffer.startOffset + sizeof(cmdbuf3);
    commandBuffer.free -= sizeof(cmdbuf3) + 0x18;
    printf("[3] startOffset=%08x, offset=%08x, free=%08x\n", (uint32_t)commandBuffer.startOffset, (uint32_t)commandBuffer.offset, (uint32_t)commandBuffer.free);
    if(viv_commit(&commandBuffer, &contextBuffer) != 0)
    {
        fprintf(stderr, "Error committing third command buffer\n");
        exit(1);
    }

    /* Submit event queue with SIGNAL, fromWhere=gcvKERNEL_PIXEL (wait for pixel engine to finish) */
    int sig_id = 0;
    if(viv_user_signal_create(0, &sig_id) != 0) /* automatic resetting signal */
    {
        fprintf(stderr, "Cannot create user signal\n");
        exit(1);
    }
    printf("Created user signal %i\n", sig_id);
    if(viv_event_queue_signal(sig_id, gcvKERNEL_PIXEL) != 0)
    {
        fprintf(stderr, "Cannot queue GPU signal\n");
        exit(1);
    }

    /* Wait for signal */
    if(viv_user_signal_wait(sig_id, SIG_WAIT_INDEFINITE) != 0)
    {
        fprintf(stderr, "Cannot wait for signal\n");
        exit(1);
    }

    /* Allocate video memory for BITMAP, lock */
    gcuVIDMEM_NODE_PTR bmp_node = 0;
    if(viv_alloc_linear_vidmem(0x5dc00, 0x40, gcvSURF_BITMAP, gcvPOOL_DEFAULT, &bmp_node, NULL)!=0)
    {
        fprintf(stderr, "Error allocating bitmap status memory\n");
        exit(1);
    }
    printf("Allocated bitmap node: node=%08x\n", (uint32_t)bmp_node);
    
    viv_addr_t bmp_physical = 0;
    void *bmp_logical = 0;
    if(viv_lock_vidmem(bmp_node, &bmp_physical, &bmp_logical)!=0)
    {
        fprintf(stderr, "Error locking bmp memory\n");
        exit(1);
    }
    memset(bmp_logical, 0xff, 0x5dc00); /* clear previous result */
    printf("Locked bmp: phys=%08x log=%08x\n", (uint32_t)bmp_physical, (uint32_t)bmp_logical);

    /* Submit fourth command buffer, updating context.
     * Fourth command buffer copies render result to bitmap, detiling along the way. 
     */
    cmdbuf4[0x0f] = fb.fb_fix.line_length;
    cmdbuf4[0x19] = rt_physical;
    cmdbuf4[0x1b] = fb.physical[0];
    fb_set_buffer(&fb, 0);
    /* XXX gcvHAL_MAP_USER_MEMORY to get dma-able address, or does this work as-is? */
    commandBuffer.startOffset = commandBuffer.offset + 0x18;
    memcpy((void*)((size_t)commandBuffer.logical + commandBuffer.startOffset), cmdbuf4, sizeof(cmdbuf4));
    commandBuffer.offset = commandBuffer.startOffset + sizeof(cmdbuf4);
    commandBuffer.free -= sizeof(cmdbuf4) + 0x18;
    printf("[4] startOffset=%08x, offset=%08x, free=%08x\n", (uint32_t)commandBuffer.startOffset, (uint32_t)commandBuffer.offset, (uint32_t)commandBuffer.free);
    if(viv_commit(&commandBuffer, &contextBuffer) != 0)
    {
        fprintf(stderr, "Error committing fourth command buffer\n");
        exit(1);
    }

    /* Submit event queue with SIGNAL, fromWhere=gcvKERNEL_PIXEL */
    if(viv_event_queue_signal(sig_id, gcvKERNEL_PIXEL) != 0)
    {
        fprintf(stderr, "Cannot queue GPU signal\n");
        exit(1);
    }

    /* Wait for signal */
    if(viv_user_signal_wait(sig_id, SIG_WAIT_INDEFINITE) != 0)
    {
        fprintf(stderr, "Cannot wait for signal\n");
        exit(1);
    }
    bmp_dump32(bmp_logical, 400, 240, false, "/mnt/sdcard/replay.bmp");
    /* Unlock video memory */
    if(viv_unlock_vidmem(bmp_node, gcvSURF_BITMAP, 1) != 0)
    {
        fprintf(stderr, "Cannot unlock vidmem\n");
        exit(1);
    }
    /*
    for(int x=0; x<0x700; ++x)
    {
        uint32_t value = ((uint32_t*)rt_ts_logical)[x];
        printf("Sample ts: %x %08x\n", x*4, value);
    }*/
    printf("Contextbuffer used %i\n", *contextBuffer.inUse);

    viv_close();
    return 0;
}
Exemplo n.º 6
0
int main(int argc, char **argv)
{
    int rv;
    struct viv_conn *conn = 0;
    rv = viv_open(VIV_HW_3D, &conn);
    if(rv!=0)
    {
        fprintf(stderr, "Error opening device\n");
        exit(1);
    }
    printf("Succesfully opened device\n");
    viv_show_chip_info(conn);
    
    /* allocate command buffer (blob uses four command buffers, but we don't even fill one) */
    viv_addr_t buf0_physical = 0;
    void *buf0_logical = 0;
    if(viv_alloc_contiguous(conn, 0x20000, &buf0_physical, &buf0_logical, NULL)!=0)
    {
        fprintf(stderr, "Error allocating host memory\n");
        exit(1);
    }
    printf("Allocated buffer: phys=%08x log=%08x\n", (uint32_t)buf0_physical, (uint32_t)buf0_logical);
    
    /* allocate main render target */
    gcuVIDMEM_NODE_PTR color_surface_node = 0;
    if(viv_alloc_linear_vidmem(conn, 0x73000, 0x40, gcvSURF_RENDER_TARGET, gcvPOOL_SYSTEM /*why?*/, &color_surface_node, NULL)!=0)
    {
        fprintf(stderr, "Error allocating render target buffer memory\n");
        exit(1);
    }
    printf("Allocated render target node: node=%08x\n", (uint32_t)color_surface_node);
    
    viv_addr_t color_surface_physical = 0;
    void *color_surface_logical = 0;
    if(viv_lock_vidmem(conn, color_surface_node, &color_surface_physical, &color_surface_logical)!=0)
    {
        fprintf(stderr, "Error locking render target memory\n");
        exit(1);
    }
    printf("Locked render target: phys=%08x log=%08x\n", (uint32_t)color_surface_physical, (uint32_t)color_surface_logical);
    
    /* allocate tile status for main render target */
    gcuVIDMEM_NODE_PTR color_status_node = 0;
    if(viv_alloc_linear_vidmem(conn, 0x800, 0x40, gcvSURF_TILE_STATUS, gcvPOOL_DEFAULT, &color_status_node, NULL)!=0)
    {
        fprintf(stderr, "Error allocating render target tile status memory\n");
        exit(1);
    }
    printf("Allocated render target tile status node: node=%08x\n", (uint32_t)color_status_node);
    
    viv_addr_t color_status_physical = 0;
    void *color_status_logical = 0;
    if(viv_lock_vidmem(conn, color_status_node, &color_status_physical, &color_status_logical)!=0)
    {
        fprintf(stderr, "Error locking render target memory\n");
        exit(1);
    }
    printf("Locked render target ts: phys=%08x log=%08x\n", (uint32_t)color_status_physical, (uint32_t)color_status_logical);
    
    /* allocate depth for main render target */
    gcuVIDMEM_NODE_PTR depth_surface_node = 0;
    if(viv_alloc_linear_vidmem(conn, 0x45000, 0x40, gcvSURF_DEPTH, gcvPOOL_DEFAULT, &depth_surface_node, NULL)!=0)
    {
        fprintf(stderr, "Error allocating depth memory\n");
        exit(1);
    }
    printf("Allocated depth node: node=%08x\n", (uint32_t)depth_surface_node);
    
    viv_addr_t depth_surface_physical = 0;
    void *depth_surface_logical = 0;
    if(viv_lock_vidmem(conn, depth_surface_node, &depth_surface_physical, &depth_surface_logical)!=0)
    {
        fprintf(stderr, "Error locking depth target memory\n");
        exit(1);
    }
    printf("Locked depth target: phys=%08x log=%08x\n", (uint32_t)depth_surface_physical, (uint32_t)depth_surface_logical);
    
    /* allocate depth ts for main render target */
    gcuVIDMEM_NODE_PTR depth_status_node = 0;
    if(viv_alloc_linear_vidmem(conn, 0x500, 0x40, gcvSURF_TILE_STATUS, gcvPOOL_DEFAULT, &depth_status_node, NULL)!=0)
    {
        fprintf(stderr, "Error allocating depth memory\n");
        exit(1);
    }
    printf("Allocated depth ts node: node=%08x\n", (uint32_t)depth_status_node);
    
    viv_addr_t depth_status_physical = 0;
    void *depth_status_logical = 0;
    if(viv_lock_vidmem(conn, depth_status_node, &depth_status_physical, &depth_status_logical)!=0)
    {
        fprintf(stderr, "Error locking depth target ts memory\n");
        exit(1);
    }
    printf("Locked depth ts target: phys=%08x log=%08x\n", (uint32_t)depth_status_physical, (uint32_t)depth_status_logical);
    
    /* allocate tile status for aux render target */
    gcuVIDMEM_NODE_PTR rs_dest_node = 0;
    if(viv_alloc_linear_vidmem(conn, 0x70000, 0x40, gcvSURF_BITMAP, gcvPOOL_DEFAULT, &rs_dest_node, NULL)!=0)
    {
        fprintf(stderr, "Error allocating aux render target tile status memory\n");
        exit(1);
    }
    printf("Allocated aux render target tile status node: node=%08x\n", (uint32_t)rs_dest_node);
    
    viv_addr_t rs_dest_physical = 0;
    void *rs_dest_logical = 0;
    if(viv_lock_vidmem(conn, rs_dest_node, &rs_dest_physical, &rs_dest_logical)!=0)
    {
        fprintf(stderr, "Error locking aux ts render target memory\n");
        exit(1);
    }
    printf("Locked aux render target ts: phys=%08x log=%08x\n", (uint32_t)rs_dest_physical, (uint32_t)rs_dest_logical);
    
    /* allocate vertex buffer */
    gcuVIDMEM_NODE_PTR vtx_node = 0;
    if(viv_alloc_linear_vidmem(conn, 0x100000, 0x40, gcvSURF_VERTEX, gcvPOOL_DEFAULT, &vtx_node, NULL)!=0)
    {
        fprintf(stderr, "Error allocating vertex memory\n");
        exit(1);
    }
    printf("Allocated vertex node: node=%08x\n", (uint32_t)vtx_node);
    
    viv_addr_t vtx_physical = 0;
    void *vtx_logical = 0;
    if(viv_lock_vidmem(conn, vtx_node, &vtx_physical, &vtx_logical)!=0)
    {
        fprintf(stderr, "Error locking vertex memory\n");
        exit(1);
    }
    printf("Locked vertex memory: phys=%08x log=%08x\n", (uint32_t)vtx_physical, (uint32_t)vtx_logical);
    
    /* Phew, now we got all the memory we need.
     * Write interleaved attribute vertex stream.
     * Unlike the GL example we only do this once, not every time glDrawArrays is called, the same would be accomplished
     * from GL by using a vertex buffer object.
     */
    int dest_idx = 0;
    int v_src_idx = 0;
    int n_src_idx = 0;
    int c_src_idx = 0;
    for(int jj=0; jj<DRAW_COUNT; jj++)
    {
        for(int vert=0; vert<VERTICES_PER_DRAW*3; ++vert)
        {
            ((float*)vtx_logical)[dest_idx] = vVertices[v_src_idx];
            dest_idx++;
            v_src_idx++;
        }
        for(int vert=0; vert<VERTICES_PER_DRAW*3; ++vert)
        {
            ((float*)vtx_logical)[dest_idx] = vNormals[n_src_idx];
            dest_idx++;
            n_src_idx++;
        }
        for(int vert=0; vert<VERTICES_PER_DRAW*3; ++vert)
        {
            ((float*)vtx_logical)[dest_idx] = vColors[c_src_idx];
            dest_idx++;
            c_src_idx++;
        }
    }
    
    /*
     *    for(int idx=0; idx<NUM_VERTICES*3*3; ++idx)
     *    {
     *        printf("%i %f\n", idx, ((float*)vtx_logical)[idx]);
}*/
    
    /* Load the command buffer and send the commit command. */
    /* First build context state map */
    size_t stateCount = 0x1d00;
    uint32_t *contextMap = malloc(stateCount * 4);
    memset(contextMap, 0, stateCount*4);
    for(int idx=0; idx<sizeof(contextbuf_addr)/sizeof(address_index_t); ++idx)
    {
        contextMap[contextbuf_addr[idx].address / 4] = contextbuf_addr[idx].index;
    }
    
    struct _gcoCMDBUF commandBuffer = {
        .object = {
            .type = gcvOBJ_COMMANDBUFFER
        },
        //.os = (_gcoOS*)0xbf7488,
        //.hardware = (_gcoHARDWARE*)0x402694e0,
        .physical = (void*)buf0_physical,
        .logical = (void*)buf0_logical,
        .bytes = 0x20000,
        .startOffset = 0x0,
        //.offset = 0xac0,
        //.free = 0x7520,
        //.hintTable = (unsigned int*)0x0, // Used when gcdSECURE
        //.hintIndex = (unsigned int*)0x58,  // Used when gcdSECURE
        //.hintCommit = (unsigned int*)0xffffffff // Used when gcdSECURE
    };
    
    gcsHAL_INTERFACE id = {};
    id.command = gcvHAL_ATTACH;
    if((viv_invoke(conn, &id)) != gcvSTATUS_OK)
    {
        #ifdef DEBUG
        fprintf(stderr, "Error attaching to GPU\n");
        #endif
        exit(1);
    }
    else
    {
        fprintf(stderr, "gcvHAL_ATTACHed to GPU\n");
    }
    gckCONTEXT context = id.u.Attach.context;
    
    commandBuffer.free = commandBuffer.bytes - 0x8; /* Always keep 0x8 at end of buffer for kernel driver */
    /* Set addresses in first command buffer */
    cmdbuf1[37] = cmdbuf1[87] = cmdbuf1[109] = color_status_physical;
    cmdbuf1[38] = cmdbuf1[110] = cmdbuf1[213] = cmdbuf1[215] = color_surface_physical;
    cmdbuf1[47] = depth_status_physical; //ADDR_J */   0x500 gcvSURF_TILE_STATUS
    cmdbuf1[48] = cmdbuf1[225] = cmdbuf1[227] = depth_surface_physical; //DDR_I */       0x45000 gcvSURF_DEPTH
    cmdbuf1[169] = vtx_physical;
    cmdbuf1[170] = vtx_physical + 0x030;
    cmdbuf1[171] = vtx_physical + 0x060;
    
    cmdbuf1[413] = vtx_physical + 0x060;
    cmdbuf1[414] = vtx_physical + 0x090;
    cmdbuf1[415] = vtx_physical + 0x0c0;
    
    cmdbuf1[435] = vtx_physical + 0x0c0;
    cmdbuf1[436] = vtx_physical + 0x0f0;
    cmdbuf1[437] = vtx_physical + 0x120;
    
    cmdbuf1[457] = vtx_physical + 0x120;
    cmdbuf1[458] = vtx_physical + 0x150;
    cmdbuf1[459] = vtx_physical + 0x180;
    
    cmdbuf1[479] = vtx_physical + 0x180;
    cmdbuf1[480] = vtx_physical + 0x1b0;
    cmdbuf1[481] = vtx_physical + 0x1e0;
    
    cmdbuf1[501] = vtx_physical + 0x1e0;
    cmdbuf1[502] = vtx_physical + 0x210;
    cmdbuf1[503] = vtx_physical + 0x240;
    
    /* Submit first command buffer */
    commandBuffer.startOffset = 0;
    memcpy((void*)((size_t)commandBuffer.logical + commandBuffer.startOffset), cmdbuf1, sizeof(cmdbuf1));
    commandBuffer.offset = commandBuffer.startOffset + sizeof(cmdbuf1);
    commandBuffer.free -= sizeof(cmdbuf1) + 0x08;
    printf("[1] startOffset=%08x, offset=%08x, free=%08x\n", (uint32_t)commandBuffer.startOffset, (uint32_t)commandBuffer.offset, (uint32_t)commandBuffer.free);
    if(viv_commit(conn, &commandBuffer, context) != 0)
    {
        fprintf(stderr, "Error committing first command buffer\n");
        exit(1);
    }
    
    /*
     * What does it do? Can be skipped.
     */
    cmdbuf2[35] = color_surface_physical;
    cmdbuf2[37] = color_surface_physical;
    commandBuffer.startOffset = commandBuffer.offset + 0x08; /* Make space for LINK */
    memcpy((void*)((size_t)commandBuffer.logical + commandBuffer.startOffset), cmdbuf2, sizeof(cmdbuf2));
    commandBuffer.offset = commandBuffer.startOffset + sizeof(cmdbuf2);
    commandBuffer.free -= sizeof(cmdbuf2) + 0x08;
    
    printf("[2] startOffset=%08x, offset=%08x, free=%08x\n", (uint32_t)commandBuffer.startOffset, (uint32_t)commandBuffer.offset, (uint32_t)commandBuffer.free);
    if(viv_commit(conn, &commandBuffer, context) != 0)
    {
        fprintf(stderr, "Error committing second command buffer\n");
        exit(1);
    }
    
    /* Submit third command buffer - SWAP_RB=1 - swaps red and blue
     **/
    cmdbuf3[35] = color_surface_physical;
    cmdbuf3[37] = rs_dest_physical;
    commandBuffer.startOffset = commandBuffer.offset + 0x08;
    memcpy((void*)((size_t)commandBuffer.logical + commandBuffer.startOffset), cmdbuf3, sizeof(cmdbuf3));
    commandBuffer.offset = commandBuffer.startOffset + sizeof(cmdbuf3);
    commandBuffer.free -= sizeof(cmdbuf3) + 0x08;
    printf("[3] startOffset=%08x, offset=%08x, free=%08x\n", (uint32_t)commandBuffer.startOffset, (uint32_t)commandBuffer.offset, (uint32_t)commandBuffer.free);
    if(viv_commit(conn, &commandBuffer, context) != 0)
    {
        fprintf(stderr, "Error committing third command buffer\n");
        exit(1);
    }
    
    /* Submit event queue with SIGNAL, fromWhere=gcvKERNEL_PIXEL (wait for pixel engine to finish) */
    int sig_id = 0;
    if(viv_user_signal_create(conn, 0, &sig_id) != 0) /* automatic resetting signal */
    {
        fprintf(stderr, "Cannot create user signal\n");
        exit(1);
    }
    printf("Created user signal %i\n", sig_id);
    if(viv_event_queue_signal(conn, sig_id, gcvKERNEL_PIXEL) != 0)
    {
        fprintf(stderr, "Cannot queue GPU signal\n");
        exit(1);
    }
    
    /* Wait for signal */
    if(viv_user_signal_wait(conn, sig_id, VIV_WAIT_INDEFINITE) != 0)
    {
        fprintf(stderr, "Cannot wait for signal\n");
        exit(1);
    }
    
    /* Allocate video memory for BITMAP, lock */
    gcuVIDMEM_NODE_PTR bmp_node = 0;
    if(viv_alloc_linear_vidmem(conn, 0x5dc00, 0x40, gcvSURF_BITMAP, gcvPOOL_DEFAULT, &bmp_node, NULL)!=0)
    {
        fprintf(stderr, "Error allocating bitmap status memory\n");
        exit(1);
    }
    printf("Allocated bitmap node: node=%08x\n", (uint32_t)bmp_node);
    
    viv_addr_t bmp_physical = 0;
    void *bmp_logical = 0;
    if(viv_lock_vidmem(conn, bmp_node, &bmp_physical, &bmp_logical)!=0)
    {
        fprintf(stderr, "Error locking bmp memory\n");
        exit(1);
    }
    memset(bmp_logical, 0xff, 0x5dc00); /* clear previous result */
    printf("Locked bmp: phys=%08x log=%08x\n", (uint32_t)bmp_physical, (uint32_t)bmp_logical);
    
    /* Submit fourth command buffer, updating context.
     * Fourth command buffer copies render result to bitmap, detiling along the way. 
     */
    /* color_surface_physical = cmdbuf2 or cmdbuf1 result, rs_dest_physical - cmdbuf3 result
     * FIXME rs_dest_physical result is bad... why?
     * turning off source tilling in cmdbuf4 helps but don't solve problem. */
    cmdbuf4[0x19] = rs_dest_physical; //color_surface_physical rs_dest_physical
    cmdbuf4[0x1b] = bmp_physical;
    commandBuffer.startOffset = commandBuffer.offset + 0x08;
    memcpy((void*)((size_t)commandBuffer.logical + commandBuffer.startOffset), cmdbuf4, sizeof(cmdbuf4));
    commandBuffer.offset = commandBuffer.startOffset + sizeof(cmdbuf4);
    commandBuffer.free -= sizeof(cmdbuf4) + 0x08;
    printf("[4] startOffset=%08x, offset=%08x, free=%08x\n", (uint32_t)commandBuffer.startOffset, (uint32_t)commandBuffer.offset, (uint32_t)commandBuffer.free);
    if(viv_commit(conn, &commandBuffer, context) != 0)
    {
        fprintf(stderr, "Error committing fourth command buffer\n");
        exit(1);
    }
    
    /* Submit event queue with SIGNAL, fromWhere=gcvKERNEL_PIXEL */
    if(viv_event_queue_signal(conn, sig_id, gcvKERNEL_PIXEL) != 0)
    {
        fprintf(stderr, "Cannot queue GPU signal\n");
        exit(1);
    }
    
    /* Wait for signal */
    if(viv_user_signal_wait(conn, sig_id, VIV_WAIT_INDEFINITE) != 0)
    {
        fprintf(stderr, "Cannot wait for signal\n");
        exit(1);
    }
    bmp_dump32(bmp_logical, 400, 240, false, "/home/linaro/replay.bmp");
    /* Unlock video memory */
    if(viv_unlock_vidmem(conn, bmp_node, gcvSURF_BITMAP, 1) != 0)
    {
        fprintf(stderr, "Cannot unlock vidmem\n");
        exit(1);
    }
    /*
     *    for(int x=0; x<0x700; ++x)
     *    {
     *        uint32_t value = ((uint32_t*)rt_ts_logical)[x];
     *        printf("Sample ts: %x %08x\n", x*4, value);
}*/
    //printf("Contextbuffer used %i\n", *contextBuffer.inUse);
    
    viv_close(conn);
    return 0;
}