summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--native/fb/Makefile7
-rw-r--r--native/fb/cube_companion.c466
2 files changed, 469 insertions, 4 deletions
diff --git a/native/fb/Makefile b/native/fb/Makefile
index 1faca47..03f43f8 100644
--- a/native/fb/Makefile
+++ b/native/fb/Makefile
@@ -7,7 +7,7 @@ CFLAGS += $(COMMON_FLAGS)
CXXFLAGS += $(COMMON_FLAGS)
LDFLAGS += -lm
-TARGETS = mip_cube cubemap_sphere stencil_test alpha_blend
+TARGETS = mip_cube cubemap_sphere stencil_test alpha_blend cube_companion
COMPANION_OBJS = ../resources/companion_array.o ../resources/companion_mesh.o ../resources/companion_texture.o
ETNA_OBJS = ../lib/viv.o ../lib/etna.o ../lib/etna_rs.o ../lib/etna_fb.o ../lib/etna_mem.o ../lib/etna_bswap.o ../lib/etna_tex.o ../lib/etna_pipe.o
@@ -27,9 +27,8 @@ mip_cube: mip_cube.o ../lib/write_bmp.o ../lib/esTransform.o ../lib/dds.o $(ETNA
cubemap_sphere: cubemap_sphere.o ../lib/write_bmp.o ../lib/esTransform.o ../lib/dds.o ../lib/esShapes.o $(ETNA_OBJS)
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
-# XXX port this to etna_pipe
-#cube_companion: cube_companion.o ../lib/write_bmp.o ../lib/esTransform.o $(COMPANION_OBJS) $(ETNA_OBJS)
-# $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
+cube_companion: cube_companion.o ../lib/write_bmp.o ../lib/esTransform.o $(COMPANION_OBJS) $(ETNA_OBJS)
+ $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
alpha_blend: alpha_blend.o ../lib/write_bmp.o ../lib/esTransform.o $(ETNA_OBJS)
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
diff --git a/native/fb/cube_companion.c b/native/fb/cube_companion.c
new file mode 100644
index 0000000..cd4dc17
--- /dev/null
+++ b/native/fb/cube_companion.c
@@ -0,0 +1,466 @@
+/*
+ * Copyright (c) 2012-2013 Etnaviv Project
+ *
+ * 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, sub license,
+ * 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 NON-INFRINGEMENT. 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.
+ */
+/* Animated rotating "weighted companion cube", using array or indexed rendering
+ * Exercised in this demo:
+ * - Array and indexed rendering of arbitrary mesh
+ * - Video memory allocation
+ * - Setting up render state
+ * - Depth buffer
+ * - Vertex / fragment shader
+ * - Texturing
+ * - Double-buffered rendering to framebuffer
+ * - Anti-aliasing (MSAA)
+ *
+ * Still TODO:
+ * - Mipmapping (hw generation, if possible)
+ */
+#define INDEXED
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <math.h>
+
+#include <errno.h>
+
+#include "etna_pipe.h"
+
+#include "etna/common.xml.h"
+#include "etna/state.xml.h"
+#include "etna/state_3d.xml.h"
+#include "etna/cmdstream.xml.h"
+
+#include "write_bmp.h"
+#include "viv.h"
+#include "etna.h"
+#include "etna_state.h"
+#include "etna_rs.h"
+#include "etna_fb.h"
+#include "etna_bswap.h"
+#include "etna_tex.h"
+
+#include "esTransform.h"
+#include "dds.h"
+#include "companion.h"
+
+/*********************************************************************/
+#define INDEXED /* Used indexed rendering */
+#define INDEX_BUFFER_SIZE 0x8000
+#define VERTEX_BUFFER_SIZE 0x60000
+uint32_t vs[] = {
+ 0x01831009, 0x00000000, 0x00000000, 0x203fc048,
+ 0x02031009, 0x00000000, 0x00000000, 0x203fc058,
+ 0x07841003, 0x39000800, 0x00000050, 0x00000000,
+ 0x07841002, 0x39001800, 0x00aa0050, 0x00390048,
+ 0x07841002, 0x39002800, 0x01540050, 0x00390048,
+ 0x07841002, 0x39003800, 0x01fe0050, 0x00390048,
+ 0x03851003, 0x29004800, 0x000000d0, 0x00000000,
+ 0x03851002, 0x29005800, 0x00aa00d0, 0x00290058,
+ 0x03811002, 0x29006800, 0x015400d0, 0x00290058,
+ 0x07851003, 0x39007800, 0x00000050, 0x00000000,
+ 0x07851002, 0x39008800, 0x00aa0050, 0x00390058,
+ 0x07851002, 0x39009800, 0x01540050, 0x00390058,
+ 0x07801002, 0x3900a800, 0x01fe0050, 0x00390058,
+ 0x0401100c, 0x00000000, 0x00000000, 0x003fc008,
+ 0x03801002, 0x69000800, 0x01fe00c0, 0x00290038,
+ 0x03831005, 0x29000800, 0x01480040, 0x00000000,
+ 0x0383100d, 0x00000000, 0x00000000, 0x00000038,
+ 0x03801003, 0x29000800, 0x014801c0, 0x00000000,
+ 0x00801005, 0x29001800, 0x01480040, 0x00000000,
+ 0x0380108f, 0x3fc06800, 0x00000050, 0x203fc068,
+ 0x04001009, 0x00000000, 0x00000000, 0x200000b8,
+ 0x01811009, 0x00000000, 0x00000000, 0x00150028,
+ 0x02041001, 0x2a804800, 0x00000000, 0x003fc048,
+ 0x02041003, 0x2a804800, 0x00aa05c0, 0x00000002,
+};
+uint32_t ps[] = { /* texture sampling */
+ 0x07811003, 0x00000800, 0x01c800d0, 0x00000000,
+ 0x07821018, 0x15002f20, 0x00000000, 0x00000000,
+ 0x07811003, 0x39001800, 0x01c80140, 0x00000000,
+};
+
+const struct etna_shader_program shader = {
+ .num_inputs = 3,
+ .inputs = {{.vs_reg=0},{.vs_reg=1},{.vs_reg=2}},
+ .num_varyings = 2,
+ .varyings = {
+ {.num_components=4, .special=ETNA_VARYING_VSOUT, .pa_attributes=0x200, .vs_reg=0}, /* color */
+ {.num_components=2, .special=ETNA_VARYING_VSOUT, .pa_attributes=0x200, .vs_reg=1} /* texcoord */
+ },
+ .vs_code_size = sizeof(vs)/4,
+ .vs_code = (uint32_t*)vs,
+ .vs_pos_out_reg = 4, // t4 out
+ .vs_load_balancing = 0xf3f0542, /* depends on number of inputs/outputs/varyings? XXX how exactly */
+ .vs_num_temps = 6,
+ .vs_uniforms_size = 12*4,
+ .vs_uniforms = (uint32_t*)(const float[12*4]){
+ [19] = 2.0f, /* u4.w */
+ [23] = 20.0f, /* u5.w */
+ [27] = 0.0f, /* u6.w */
+ [45] = 0.5f, /* u11.y */
+ [44] = 1.0f, /* u11.x */
+ },
+ .ps_code_size = sizeof(ps)/4,
+ .ps_code = (uint32_t*)ps,
+ .ps_color_out_reg = 1, // t1 out
+ .ps_num_temps = 3,
+ .ps_uniforms_size = 1*4,
+ .ps_uniforms = (uint32_t*)(const float[1*4]){
+ [0] = 1.0f,
+ },
+};
+
+inline void convert_r8g8b8_to_b8g8r8x8(uint32_t *dst, const uint8_t *src, unsigned num_pixels)
+{
+ for(unsigned idx=0; idx<num_pixels; ++idx)
+ {
+ dst[idx] = ((0xFF) << 24) | (src[idx*3+0] << 16) | (src[idx*3+1] << 8) | src[idx*3+2];
+ }
+}
+
+int main(int argc, char **argv)
+{
+ int rv;
+ int width = 256;
+ int height = 256;
+
+ fb_info fb;
+ rv = fb_open(0, &fb);
+ if(rv!=0)
+ {
+ exit(1);
+ }
+ width = fb.fb_var.xres;
+ height = fb.fb_var.yres;
+
+ rv = viv_open();
+ if(rv!=0)
+ {
+ fprintf(stderr, "Error opening device\n");
+ exit(1);
+ }
+ printf("Succesfully opened device\n");
+
+ etna_ctx *ctx = 0;
+ struct pipe_context *pipe = 0;
+ etna_bswap_buffers *buffers = 0;
+ if(etna_create(&ctx) != ETNA_OK ||
+ etna_bswap_create(ctx, &buffers, (etna_set_buffer_cb_t)&fb_set_buffer, (etna_copy_buffer_cb_t)&etna_fb_copy_buffer, &fb) != ETNA_OK ||
+ (pipe = etna_new_pipe_context(ctx)) == NULL)
+ {
+ printf("Unable to create etna context\n");
+ exit(1);
+ }
+
+ /* Convert and upload embedded texture */
+ struct pipe_resource *tex_resource = etna_pipe_create_2d(pipe, ETNA_IS_TEXTURE, PIPE_FORMAT_B8G8R8X8_UNORM,
+ COMPANION_TEXTURE_WIDTH, COMPANION_TEXTURE_HEIGHT, 0);
+ void *temp = malloc(COMPANION_TEXTURE_WIDTH * COMPANION_TEXTURE_HEIGHT * 4);
+ convert_r8g8b8_to_b8g8r8x8(temp, (const uint8_t*)companion_texture, COMPANION_TEXTURE_WIDTH * COMPANION_TEXTURE_HEIGHT);
+ etna_pipe_inline_write(pipe, tex_resource, 0, 0, temp, COMPANION_TEXTURE_WIDTH * COMPANION_TEXTURE_HEIGHT * 4);
+ free(temp);
+
+ /* resources */
+ struct pipe_resource *rt_resource = etna_pipe_create_2d(pipe, ETNA_IS_RENDER_TARGET, PIPE_FORMAT_B8G8R8X8_UNORM, width, height, 0);
+ struct pipe_resource *z_resource = etna_pipe_create_2d(pipe, ETNA_IS_RENDER_TARGET, PIPE_FORMAT_Z16_UNORM, width, height, 0);
+
+ /* bind render target to framebuffer */
+ etna_fb_bind_resource(&fb, rt_resource);
+
+ /* geometry */
+ struct pipe_resource *vtx_resource = etna_pipe_create_buffer(pipe, ETNA_IS_VERTEX, VERTEX_BUFFER_SIZE);
+ struct pipe_resource *idx_resource = etna_pipe_create_buffer(pipe, ETNA_IS_INDEX, INDEX_BUFFER_SIZE);
+
+ float *vtx_logical = etna_pipe_get_resource_ptr(pipe, vtx_resource, 0, 0);
+ assert(vtx_logical);
+ float *idx_logical = etna_pipe_get_resource_ptr(pipe, idx_resource, 0, 0);
+ assert(idx_logical);
+#ifndef INDEXED
+ printf("Interleaving vertices...\n");
+ float *vertices_array = companion_vertices_array();
+ float *texture_coordinates_array =
+ companion_texture_coordinates_array();
+ float *normals_array = companion_normals_array();
+ assert(COMPANION_ARRAY_COUNT*(3+3+2)*sizeof(float) < VERTEX_BUFFER_SIZE);
+ for(int vert=0; vert<COMPANION_ARRAY_COUNT; ++vert)
+ {
+ int dest_idx = vert * (3 + 3 + 2);
+ for(int comp=0; comp<3; ++comp)
+ ((float*)vtx_logical)[dest_idx+comp+0] = vertices_array[vert*3 + comp]; /* 0 */
+ for(int comp=0; comp<3; ++comp)
+ ((float*)vtx_logical)[dest_idx+comp+3] = normals_array[vert*3 + comp]; /* 1 */
+ for(int comp=0; comp<2; ++comp)
+ ((float*)vtx_logical)[dest_idx+comp+6] = texture_coordinates_array[vert*2 + comp]; /* 2 */
+ }
+#else
+ printf("Interleaving vertices and copying index buffer...\n");
+ assert(COMPANION_VERTEX_COUNT*(3+3+2)*sizeof(float) < VERTEX_BUFFER_SIZE);
+ for(int vert=0; vert<COMPANION_VERTEX_COUNT; ++vert)
+ {
+ int dest_idx = vert * (3 + 3 + 2);
+ for(int comp=0; comp<3; ++comp)
+ ((float*)vtx_logical)[dest_idx+comp+0] = companion_vertices[vert][comp]; /* 0 */
+ for(int comp=0; comp<3; ++comp)
+ ((float*)vtx_logical)[dest_idx+comp+3] = companion_normals[vert][comp]; /* 1 */
+ for(int comp=0; comp<2; ++comp)
+ ((float*)vtx_logical)[dest_idx+comp+6] = companion_texture_coordinates[vert][comp]; /* 2 */
+ }
+ assert(COMPANION_TRIANGLE_COUNT*3*sizeof(unsigned short) < INDEX_BUFFER_SIZE);
+ memcpy(idx_logical, &companion_triangles[0][0], COMPANION_TRIANGLE_COUNT*3*sizeof(unsigned short));
+#endif
+ struct pipe_vertex_buffer vertex_buffer_desc = {
+ .stride = (3 + 3 + 2)*4,
+ .buffer_offset = 0,
+ .buffer = vtx_resource,
+ .user_buffer = 0
+ };
+ struct pipe_index_buffer index_buffer_desc = {
+ .index_size = sizeof(unsigned short),
+ .offset = 0,
+ .buffer = idx_resource,
+ .user_buffer = 0
+ };
+ struct pipe_vertex_element pipe_vertex_elements[] = {
+ { /* positions */
+ .src_offset = 0,
+ .instance_divisor = 0,
+ .vertex_buffer_index = 0,
+ .src_format = PIPE_FORMAT_R32G32B32_FLOAT
+ },
+ { /* normals */
+ .src_offset = 0xc,
+ .instance_divisor = 0,
+ .vertex_buffer_index = 0,
+ .src_format = PIPE_FORMAT_R32G32B32_FLOAT
+ },
+ { /* texture coord */
+ .src_offset = 0x18,
+ .instance_divisor = 0,
+ .vertex_buffer_index = 0,
+ .src_format = PIPE_FORMAT_R32G32_FLOAT
+ }
+ };
+ void *vertex_elements = pipe->create_vertex_elements_state(pipe,
+ sizeof(pipe_vertex_elements)/sizeof(pipe_vertex_elements[0]), pipe_vertex_elements);
+
+ /* compile other gallium3d states */
+ void *blend = pipe->create_blend_state(pipe, &(struct pipe_blend_state) {
+ .rt[0] = {
+ .blend_enable = 0,
+ .rgb_func = PIPE_BLEND_ADD,
+ .rgb_src_factor = PIPE_BLENDFACTOR_ONE,
+ .rgb_dst_factor = PIPE_BLENDFACTOR_ZERO,
+ .alpha_func = PIPE_BLEND_ADD,
+ .alpha_src_factor = PIPE_BLENDFACTOR_ONE,
+ .alpha_dst_factor = PIPE_BLENDFACTOR_ZERO,
+ .colormask = 0xf
+ }
+ });
+
+ void *sampler = pipe->create_sampler_state(pipe, &(struct pipe_sampler_state) {
+ .wrap_s = PIPE_TEX_WRAP_CLAMP_TO_EDGE,
+ .wrap_t = PIPE_TEX_WRAP_CLAMP_TO_EDGE,
+ .wrap_r = PIPE_TEX_WRAP_CLAMP_TO_EDGE,
+ .min_img_filter = PIPE_TEX_FILTER_LINEAR,
+ .min_mip_filter = PIPE_TEX_MIPFILTER_LINEAR,
+ .mag_img_filter = PIPE_TEX_FILTER_LINEAR,
+ .normalized_coords = 1,
+ .lod_bias = 0.0f,
+ .min_lod = 0.0f, .max_lod=1000.0f
+ });
+
+ void *rasterizer = pipe->create_rasterizer_state(pipe, &(struct pipe_rasterizer_state){
+ .flatshade = 0,
+ .light_twoside = 1,
+ .clamp_vertex_color = 1,
+ .clamp_fragment_color = 1,
+ .front_ccw = 1,
+ .cull_face = PIPE_FACE_BACK, /**< PIPE_FACE_x */
+ .fill_front = PIPE_POLYGON_MODE_FILL, /**< PIPE_POLYGON_MODE_x */
+ .fill_back = PIPE_POLYGON_MODE_FILL, /**< PIPE_POLYGON_MODE_x */
+ .offset_point = 0,
+ .offset_line = 0,
+ .offset_tri = 0,
+ .scissor = 0,
+ .poly_smooth = 1,
+ .poly_stipple_enable = 0,
+ .point_smooth = 0,
+ .sprite_coord_mode = 0, /**< PIPE_SPRITE_COORD_ */
+ .point_quad_rasterization = 0, /** points rasterized as quads or points */
+ .point_size_per_vertex = 0, /**< size computed in vertex shader */
+ .multisample = 0,
+ .line_smooth = 0,
+ .line_stipple_enable = 0,
+ .line_last_pixel = 0,
+ .flatshade_first = 0,
+ .gl_rasterization_rules = 1,
+ .rasterizer_discard = 0,
+ .depth_clip = 0,
+ .clip_plane_enable = 0,
+ .line_stipple_factor = 0,
+ .line_stipple_pattern = 0,
+ .sprite_coord_enable = 0,
+ .line_width = 1.0f,
+ .point_size = 1.0f,
+ .offset_units = 0.0f,
+ .offset_scale = 0.0f,
+ .offset_clamp = 0.0f
+ });
+
+ void *dsa = pipe->create_depth_stencil_alpha_state(pipe, &(struct pipe_depth_stencil_alpha_state){
+ .depth = {
+ .enabled = 1,
+ .writemask = 1,
+ .func = PIPE_FUNC_LESS /* GL default */
+ },
+ .stencil[0] = {
+ .enabled = 0
+ },
+ .stencil[1] = {
+ .enabled = 0
+ },
+ .alpha = {
+ .enabled = 0
+ }
+ });
+
+ struct pipe_sampler_view *sampler_view = pipe->create_sampler_view(pipe, tex_resource, &(struct pipe_sampler_view){
+ .format = tex_resource->format,
+ .u.tex.first_level = 0,
+ .u.tex.last_level = 0,
+ .swizzle_r = PIPE_SWIZZLE_RED,
+ .swizzle_g = PIPE_SWIZZLE_GREEN,
+ .swizzle_b = PIPE_SWIZZLE_BLUE,
+ .swizzle_a = PIPE_SWIZZLE_ALPHA,
+ });
+ struct pipe_surface *cbuf = pipe->create_surface(pipe, rt_resource, &(struct pipe_surface){
+ .texture = rt_resource,
+ .format = rt_resource->format,
+ .u.tex.level = 0
+ });
+ struct pipe_surface *zsbuf = pipe->create_surface(pipe, z_resource, &(struct pipe_surface){
+ .texture = z_resource,
+ .format = z_resource->format,
+ .u.tex.level = 0
+ });
+
+ /* bind */
+ pipe->bind_blend_state(pipe, blend);
+ pipe->bind_fragment_sampler_states(pipe, 1, &sampler);
+ pipe->bind_rasterizer_state(pipe, rasterizer);
+ pipe->bind_depth_stencil_alpha_state(pipe, dsa);
+ pipe->bind_vertex_elements_state(pipe, vertex_elements);
+
+ pipe->set_blend_color(pipe, &(struct pipe_blend_color){
+ .color = {0.0f,0.0f,0.0f,1.0f}
+ });
+ pipe->set_stencil_ref(pipe, &(struct pipe_stencil_ref){
+ .ref_value[0] = 0xff,
+ .ref_value[1] = 0xff
+ });
+ pipe->set_sample_mask(pipe, 0xf);
+ pipe->set_framebuffer_state(pipe, &(struct pipe_framebuffer_state){
+ .width = width,
+ .height = height,
+ .nr_cbufs = 1,
+ .cbufs[0] = cbuf,
+ .zsbuf = zsbuf
+ });
+ pipe->set_scissor_state(pipe, &(struct pipe_scissor_state){
+ .minx = 0,
+ .miny = 0,
+ .maxx = 65535,
+ .maxy = 65535
+ });
+ pipe->set_viewport_state(pipe, &(struct pipe_viewport_state){
+ .scale = {width/2.0f, height/2.0f, 0.5f, 1.0f},
+ .translate = {width/2.0f, height/2.0f, 0.5f, 1.0f}
+ });
+ pipe->set_fragment_sampler_views(pipe, 1, &sampler_view);
+ pipe->set_vertex_buffers(pipe, 0, 1, &vertex_buffer_desc);
+ pipe->set_index_buffer(pipe, &index_buffer_desc);
+
+ void *shader_state = pipe->create_etna_shader_state(pipe, &shader);
+ pipe->bind_etna_shader_state(pipe, shader_state);
+
+ for(int frame=0; frame<1000; ++frame)
+ {
+ if(frame%50 == 0)
+ printf("*** FRAME %i ****\n", frame);
+ /* Compute transform matrices in the same way as cube egl demo */
+ ESMatrix modelview, projection, modelviewprojection;
+ ESMatrix inverse, normal;
+ esMatrixLoadIdentity(&modelview);
+ esTranslate(&modelview, 0.0f, 0.0f, -9.0f);
+ esRotate(&modelview, 45.0f, 1.0f, 0.0f, 0.0f);
+ esRotate(&modelview, 45.0f, 0.0f, 1.0f, 0.0f);
+ esRotate(&modelview, frame*0.5f, 0.0f, 0.0f, 1.0f);
+ esScale(&modelview, 0.475f, 0.475f, 0.475f);
+ GLfloat aspect = (GLfloat)(height) / (GLfloat)(width);
+ esMatrixLoadIdentity(&projection);
+ esFrustum(&projection, -2.8f, +2.8f, -2.8f * aspect, +2.8f * aspect, 6.0f, 10.0f);
+ esMatrixLoadIdentity(&modelviewprojection);
+ esMatrixMultiply(&modelviewprojection, &modelview, &projection);
+ esMatrixInverse3x3(&inverse, &modelview);
+ esMatrixTranspose(&normal, &inverse);
+
+ /* Clear render target */
+ pipe->clear(pipe, PIPE_CLEAR_COLOR | PIPE_CLEAR_DEPTHSTENCIL, &(const union pipe_color_union) {
+ .f = {0.2, 0.2, 0.2, 1.0}
+ }, 1.0, 0xff);
+
+ pipe->set_etna_uniforms(pipe, shader_state, PIPE_SHADER_VERTEX, 0, 16, (uint32_t*)&modelviewprojection.m[0][0]);
+ pipe->set_etna_uniforms(pipe, shader_state, PIPE_SHADER_VERTEX, 16, 3, (uint32_t*)&normal.m[0][0]); /* u4.xyz */
+ pipe->set_etna_uniforms(pipe, shader_state, PIPE_SHADER_VERTEX, 20, 3, (uint32_t*)&normal.m[1][0]); /* u5.xyz */
+ pipe->set_etna_uniforms(pipe, shader_state, PIPE_SHADER_VERTEX, 24, 3, (uint32_t*)&normal.m[2][0]); /* u6.xyz */
+ pipe->set_etna_uniforms(pipe, shader_state, PIPE_SHADER_VERTEX, 28, 16, (uint32_t*)&modelview.m[0][0]);
+
+ pipe->draw_vbo(pipe, &(struct pipe_draw_info){
+#ifdef INDEXED
+ .indexed = 1,
+#else
+ .indexed = 0,
+#endif
+ .mode = PIPE_PRIM_TRIANGLES,
+ .start = 0,
+ .count = COMPANION_TRIANGLE_COUNT * 3
+ });
+
+ etna_swap_buffers(buffers);
+ }
+#ifdef DUMP
+ bmp_dump32(fb.logical[1-backbuffer], width, height, false, "/mnt/sdcard/fb.bmp");
+ printf("Dump complete\n");
+#endif
+ etna_bswap_free(buffers);
+ etna_free(ctx);
+ viv_close();
+ return 0;
+}