diff options
author | Alon Levy <alevy@redhat.com> | 2011-05-31 15:50:47 +0300 |
---|---|---|
committer | Alon Levy <alevy@redhat.com> | 2011-08-26 16:22:12 +0300 |
commit | b768cfd9daa2150c5a8ee6c302593b7f71ff4e48 (patch) | |
tree | 23db751d745706b9efd70f1a2a5ffb600a39ca11 /src | |
parent | 9d1213a011076df5acb9e53e9dcad47e1e1316f0 (diff) |
gallium/drivers/remote: tr_context.c builds
TODO: split this large commit or improve commit message
Contains changes required to get tr_context to build:
updates of API used from old vgallium to current mesa.
All new functions left with an empty implementation debug_print-ing
"implement me"
Diffstat (limited to 'src')
-rw-r--r-- | src/gallium/drivers/remote/remote_messages.h | 19 | ||||
-rw-r--r-- | src/gallium/drivers/remote/tr_context.c | 339 | ||||
-rw-r--r-- | src/gallium/drivers/remote/tr_context.h | 3 | ||||
-rw-r--r-- | src/gallium/drivers/remote/tr_screen.c | 35 |
4 files changed, 340 insertions, 56 deletions
diff --git a/src/gallium/drivers/remote/remote_messages.h b/src/gallium/drivers/remote/remote_messages.h index adcae265d9..588c0e05f6 100644 --- a/src/gallium/drivers/remote/remote_messages.h +++ b/src/gallium/drivers/remote/remote_messages.h @@ -394,7 +394,6 @@ struct remreq_set_constant_buffer { struct message_header base; uint32_t pipe; unsigned int shader, index; - unsigned buffer_size; uint32_t buffer; }; @@ -426,7 +425,7 @@ struct remreq_set_sampler_textures { struct opaque_pipe_vertex_element { - unsigned pitch, max_index, buffer_offset; + unsigned stride, buffer_offset; uint32_t buffer; }; @@ -481,8 +480,11 @@ struct remreq_clear { struct message_header base; uint32_t pipe; - uint32_t surface; - unsigned clearValue; + uint32_t has_rgba; + float rgba[4]; + unsigned buffers; + double depth; + unsigned stencil; }; @@ -492,7 +494,6 @@ struct remreq_flush { struct message_header base; uint32_t pipe; - unsigned flags; }; @@ -612,7 +613,7 @@ struct remreq_texture_blanket { uint32_t screen; struct pipe_resource templat; uint32_t handle; - unsigned pitch; + unsigned stride; uint32_t buffer; }; @@ -858,4 +859,10 @@ struct remreq_swap_buffers { #define REMREQ_SWAP_BUFFERS 76 +// Adding new messages for vGallium porting. +// All of this should be autogenerated. +// Currently for learning purposes doing this on a required basis - not +// implementing a call until it is actually reached. + + #endif diff --git a/src/gallium/drivers/remote/tr_context.c b/src/gallium/drivers/remote/tr_context.c index 6aab497a01..80d8f5afe3 100644 --- a/src/gallium/drivers/remote/tr_context.c +++ b/src/gallium/drivers/remote/tr_context.c @@ -26,10 +26,15 @@ **************************************************************************/ #include "util/u_memory.h" -#include "pipe/p_screen.h" +#include "util/u_inlines.h" + #include "tgsi/tgsi_parse.h" + +#include "pipe/p_screen.h" #include "pipe/p_shader_tokens.h" -#include "util/u_inlines.h" + +#include "pipe/p_format.h" +#include "pipe/p_screen.h" #include "tr_screen.h" #include "tr_context.h" @@ -130,7 +135,7 @@ remote_context_draw_arrays(struct pipe_context *_pipe, static INLINE boolean remote_context_draw_elements(struct pipe_context *_pipe, - struct pipe_buffer *indexBuffer, + struct pipe_resource *indexBuffer, unsigned indexSize, unsigned mode, unsigned start, unsigned count) @@ -169,7 +174,7 @@ remote_context_draw_elements(struct pipe_context *_pipe, static INLINE boolean remote_context_draw_range_elements(struct pipe_context *_pipe, - struct pipe_buffer *indexBuffer, + struct pipe_resource *indexBuffer, unsigned indexSize, unsigned minIndex, unsigned maxIndex, @@ -210,6 +215,17 @@ remote_context_draw_range_elements(struct pipe_context *_pipe, } +static INLINE void +remote_context_draw_vbo(struct pipe_context *_pipe, + const struct pipe_draw_info *info) +{ +#if 0 + ALLOC_OUT_MESSAGE(draw_vbo, message); + + message->base.opcode = REMREQ_DRAW_VBO; +#endif + DBG("Draw VBO info->mode %x\n", handle, info->mode); +} static INLINE struct pipe_query * remote_context_create_query(struct pipe_context *_pipe, @@ -301,7 +317,7 @@ static INLINE boolean remote_context_get_query_result(struct pipe_context *_pipe, struct pipe_query *query, boolean wait, - uint64 *presult) + void *presult) { DBG("Get query result for query %u\n", QUERY_HANDLE(query)); @@ -316,7 +332,7 @@ remote_context_get_query_result(struct pipe_context *_pipe, QUEUE_AND_WAIT(message, get_query_result, reply); if(reply) { - *presult = reply->result; + (*(uint64_t*)presult) = reply->result; DBG("Query result: %lu\n", reply->result); boolean done = reply->done; free_message(reply); @@ -893,6 +909,7 @@ remote_context_set_viewport_state(struct pipe_context *_pipe, } +#if 0 static INLINE void remote_context_set_sampler_textures(struct pipe_context *_pipe, unsigned num_textures, @@ -925,6 +942,7 @@ remote_context_set_sampler_textures(struct pipe_context *_pipe, enqueue_message(header); } +#endif static INLINE void @@ -935,14 +953,14 @@ remote_context_set_vertex_buffers(struct pipe_context *_pipe, // Each vertex_buffer points to a pipe_buffer, which here is actually // a remote_pipe_buffer. Flatten those references and send an array // which gives the buffer by opaque handle - + DBG("Set vertex buffers: %d buffers\n", num_buffers); - + struct remreq_set_vertex_buffers* header = - (struct remreq_set_vertex_buffers*) - allocate_message_memory( - sizeof(struct remreq_set_vertex_buffers) + - (num_buffers * sizeof(struct opaque_pipe_vertex_element))); + (struct remreq_set_vertex_buffers*) + allocate_message_memory( + sizeof(struct remreq_set_vertex_buffers) + + (num_buffers * sizeof(struct opaque_pipe_vertex_element))); PANIC_IF_NULL(header); @@ -951,17 +969,16 @@ remote_context_set_vertex_buffers(struct pipe_context *_pipe, header->num_buffers = num_buffers; struct opaque_pipe_vertex_element* data = - (struct opaque_pipe_vertex_element*) + (struct opaque_pipe_vertex_element*) (((char*)header) + sizeof(struct remreq_set_vertex_buffers)); unsigned i; for(i = 0; i < num_buffers; i++) { - data[i].pitch = buffers[i].pitch; - data[i].max_index = buffers[i].max_index; - data[i].buffer_offset = buffers[i].buffer_offset; - data[i].buffer = buffers[i].buffer ? BUFFER_HANDLE(buffers[i].buffer) : 0; - DBG("Buffer slot %d assigned buffer handle %u\n", i, data[i].buffer); + data[i].stride = buffers[i].stride; + data[i].buffer_offset = buffers[i].buffer_offset; + data[i].buffer = buffers[i].buffer ? BUFFER_HANDLE(buffers[i].buffer) : 0; + DBG("Buffer slot %d assigned buffer handle %u\n", i, data[i].buffer); } enqueue_message(header); @@ -1055,8 +1072,10 @@ remote_context_surface_fill(struct pipe_context *_pipe, static INLINE void remote_context_clear(struct pipe_context *_pipe, - struct pipe_surface *surface, - unsigned clearValue) + unsigned buffers, + const float *rgba, + double depth, + unsigned stencil) { DBG("Clear surface %u with %x\n", SURFACE_HANDLE(surface), clearValue); @@ -1065,8 +1084,16 @@ remote_context_clear(struct pipe_context *_pipe, message->base.opcode = REMREQ_CLEAR; message->pipe = PIPE_HANDLE(_pipe); - message->surface = SURFACE_HANDLE(surface); - message->clearValue = clearValue; + message->buffers = buffers; + message->has_rgba = rgba != NULL ? 1 : 0; + if (rgba) { + message->rgba[0] = rgba[0]; + message->rgba[1] = rgba[1]; + message->rgba[2] = rgba[2]; + message->rgba[3] = rgba[3]; + } + message->depth = depth; + message->stencil = stencil; enqueue_message(message); @@ -1075,8 +1102,7 @@ remote_context_clear(struct pipe_context *_pipe, static INLINE void remote_context_flush(struct pipe_context *_pipe, - unsigned flags, - struct pipe_fence_handle **fence) + struct pipe_fence_handle **fence) { DBG("Flush\n"); @@ -1085,7 +1111,6 @@ remote_context_flush(struct pipe_context *_pipe, message->base.opcode = REMREQ_FLUSH; message->pipe = PIPE_HANDLE(_pipe); - message->flags = flags; /* !!! I *think* fence is out-only */ @@ -1111,9 +1136,223 @@ remote_context_destroy(struct pipe_context *_pipe) FREE((struct remote_context*)_pipe); } +static INLINE void +remote_context_bind_fragment_sampler_states(struct pipe_context *_pipe, + unsigned num_states, + void **states) +{ + DBG("context_bind_fragment_sampler_states %u\n", PIPE_HANDLE(_pipe)); +} + +static INLINE void +remote_context_bind_vertex_sampler_states(struct pipe_context *_pipe, + unsigned num_states, + void **states) +{ + DBG("UNIMPLEMENTED %s\n", __FUNCTION__); +} + +static INLINE void * +remote_context_create_vertex_elements_state(struct pipe_context *_pipe, + unsigned num_elements, + const struct pipe_vertex_element *elements) +{ + DBG("UNIMPLEMENTED %s\n", __FUNCTION__); + return NULL; +} + +static INLINE void +remote_context_bind_vertex_elements_state(struct pipe_context *_pipe, + void *state) +{ + DBG("UNIMPLEMENTED %s\n", __FUNCTION__); +} + +static INLINE void +remote_context_delete_vertex_elements_state(struct pipe_context *_pipe, + void *state) +{ + DBG("UNIMPLEMENTED %s\n", __FUNCTION__); +} + +static INLINE void +remote_context_set_stencil_ref(struct pipe_context *_pipe, + const struct pipe_stencil_ref *state) +{ + DBG("UNIMPLEMENTED %s\n", __FUNCTION__); +} + +static INLINE void +remote_context_set_sample_mask(struct pipe_context *_pipe, + unsigned sample_mask) +{ + DBG("UNIMPLEMENTED %s\n", __FUNCTION__); +} + +static INLINE void +remote_context_set_fragment_sampler_views(struct pipe_context *_pipe, + unsigned num, + struct pipe_sampler_view **views) +{ + DBG("UNIMPLEMENTED %s\n", __FUNCTION__); +} + +static INLINE void +remote_context_set_vertex_sampler_views(struct pipe_context *_pipe, + unsigned num, + struct pipe_sampler_view **views) +{ + DBG("UNIMPLEMENTED %s\n", __FUNCTION__); +} + +static struct pipe_sampler_view * +remote_create_sampler_view(struct pipe_context *_pipe, + struct pipe_resource *_resource, + const struct pipe_sampler_view *templ) +{ + DBG("UNIMPLEMENTED %s\n", __FUNCTION__); + return NULL; +} + +static void +remote_sampler_view_destroy(struct pipe_context *_pipe, + struct pipe_sampler_view *_view) +{ + DBG("UNIMPLEMENTED %s\n", __FUNCTION__); +} + + +static struct pipe_surface * +remote_create_surface(struct pipe_context *_pipe, + struct pipe_resource *_resource, + const struct pipe_surface *surf_tmpl) +{ + DBG("UNIMPLEMENTED %s\n", __FUNCTION__); + return NULL; +} + + +static void +remote_surface_destroy(struct pipe_context *_pipe, + struct pipe_surface *_surface) +{ + DBG("UNIMPLEMENTED %s\n", __FUNCTION__); +} + + +static INLINE void +remote_context_set_index_buffer(struct pipe_context *_pipe, + const struct pipe_index_buffer *ib) +{ + DBG("UNIMPLEMENTED %s\n", __FUNCTION__); +} + +static INLINE void +remote_context_resource_copy_region(struct pipe_context *_pipe, + struct pipe_resource *dst, + unsigned dst_level, + unsigned dstx, unsigned dsty, unsigned dstz, + struct pipe_resource *src, + unsigned src_level, + const struct pipe_box *src_box) +{ + DBG("UNIMPLEMENTED %s\n", __FUNCTION__); +} + +static INLINE void +remote_context_clear_render_target(struct pipe_context *_pipe, + struct pipe_surface *dst, + const float *rgba, + unsigned dstx, unsigned dsty, + unsigned width, unsigned height) +{ + DBG("UNIMPLEMENTED %s\n", __FUNCTION__); +} + + +static INLINE void +remote_context_clear_depth_stencil(struct pipe_context *_pipe, + struct pipe_surface *dst, + unsigned clear_flags, + double depth, + unsigned stencil, + unsigned dstx, unsigned dsty, + unsigned width, unsigned height) +{ + DBG("UNIMPLEMENTED %s\n", __FUNCTION__); +} + + +static struct pipe_transfer * +remote_context_get_transfer(struct pipe_context *_context, + struct pipe_resource *_resource, + unsigned level, + unsigned usage, + const struct pipe_box *box) +{ + DBG("UNIMPLEMENTED %s\n", __FUNCTION__); + return NULL; +} + + +static void +remote_context_transfer_destroy(struct pipe_context *_context, + struct pipe_transfer *_transfer) +{ + DBG("UNIMPLEMENTED %s\n", __FUNCTION__); +} + + +static void * +remote_context_transfer_map(struct pipe_context *_context, + struct pipe_transfer *_transfer) +{ + DBG("UNIMPLEMENTED %s\n", __FUNCTION__); + return NULL; +} + + +static void +remote_context_transfer_flush_region( struct pipe_context *_context, + struct pipe_transfer *_transfer, + const struct pipe_box *box) +{ + DBG("UNIMPLEMENTED %s\n", __FUNCTION__); +} + +static void +remote_context_transfer_unmap(struct pipe_context *_context, + struct pipe_transfer *_transfer) +{ + DBG("UNIMPLEMENTED %s\n", __FUNCTION__); +} + + +static void +remote_context_transfer_inline_write(struct pipe_context *_context, + struct pipe_resource *_resource, + unsigned level, + unsigned usage, + const struct pipe_box *box, + const void *data, + unsigned stride, + unsigned layer_stride) +{ + DBG("UNIMPLEMENTED %s\n", __FUNCTION__); +} + + +static void remote_redefine_user_buffer(struct pipe_context *_context, + struct pipe_resource *_resource, + unsigned offset, unsigned size) +{ + DBG("UNIMPLEMENTED %s\n", __FUNCTION__); +} + struct pipe_context * -remote_context_create(struct pipe_screen *screen) +remote_context_create(struct pipe_screen *screen, + struct pipe_context *pipe) { struct remote_context *tr_ctx; @@ -1122,14 +1361,10 @@ remote_context_create(struct pipe_screen *screen) tr_ctx = CALLOC_STRUCT(remote_context); if(!tr_ctx) goto error1; - tr_ctx->base.winsys = screen->winsys; tr_ctx->base.screen = screen; tr_ctx->base.destroy = remote_context_destroy; - tr_ctx->base.set_edgeflags = remote_context_set_edgeflags; - tr_ctx->base.draw_arrays = remote_context_draw_arrays; - tr_ctx->base.draw_elements = remote_context_draw_elements; - tr_ctx->base.draw_range_elements = remote_context_draw_range_elements; + tr_ctx->base.draw_vbo = remote_context_draw_vbo; // new tr_ctx->base.create_query = remote_context_create_query; tr_ctx->base.destroy_query = remote_context_destroy_query; tr_ctx->base.begin_query = remote_context_begin_query; @@ -1139,7 +1374,8 @@ remote_context_create(struct pipe_screen *screen) tr_ctx->base.bind_blend_state = remote_context_bind_blend_state; tr_ctx->base.delete_blend_state = remote_context_delete_blend_state; tr_ctx->base.create_sampler_state = remote_context_create_sampler_state; - tr_ctx->base.bind_sampler_states = remote_context_bind_sampler_states; + tr_ctx->base.bind_fragment_sampler_states = remote_context_bind_fragment_sampler_states; // new + tr_ctx->base.bind_vertex_sampler_states = remote_context_bind_vertex_sampler_states; // new tr_ctx->base.delete_sampler_state = remote_context_delete_sampler_state; tr_ctx->base.create_rasterizer_state = remote_context_create_rasterizer_state; tr_ctx->base.bind_rasterizer_state = remote_context_bind_rasterizer_state; @@ -1153,20 +1389,52 @@ remote_context_create(struct pipe_screen *screen) tr_ctx->base.create_vs_state = remote_context_create_vs_state; tr_ctx->base.bind_vs_state = remote_context_bind_vs_state; tr_ctx->base.delete_vs_state = remote_context_delete_vs_state; + tr_ctx->base.create_vertex_elements_state = remote_context_create_vertex_elements_state; // new + tr_ctx->base.bind_vertex_elements_state = remote_context_bind_vertex_elements_state; // new + tr_ctx->base.delete_vertex_elements_state = remote_context_delete_vertex_elements_state; // new tr_ctx->base.set_blend_color = remote_context_set_blend_color; + tr_ctx->base.set_stencil_ref = remote_context_set_stencil_ref; // new tr_ctx->base.set_clip_state = remote_context_set_clip_state; + tr_ctx->base.set_sample_mask = remote_context_set_sample_mask; // new tr_ctx->base.set_constant_buffer = remote_context_set_constant_buffer; tr_ctx->base.set_framebuffer_state = remote_context_set_framebuffer_state; tr_ctx->base.set_polygon_stipple = remote_context_set_polygon_stipple; tr_ctx->base.set_scissor_state = remote_context_set_scissor_state; tr_ctx->base.set_viewport_state = remote_context_set_viewport_state; - tr_ctx->base.set_sampler_textures = remote_context_set_sampler_textures; + tr_ctx->base.set_fragment_sampler_views = remote_context_set_fragment_sampler_views; // new + tr_ctx->base.set_vertex_sampler_views = remote_context_set_vertex_sampler_views; // new + tr_ctx->base.create_sampler_view = remote_create_sampler_view; // new + tr_ctx->base.sampler_view_destroy = remote_sampler_view_destroy; // new + tr_ctx->base.create_surface = remote_create_surface; // new + tr_ctx->base.surface_destroy = remote_surface_destroy; // new tr_ctx->base.set_vertex_buffers = remote_context_set_vertex_buffers; + tr_ctx->base.set_index_buffer = remote_context_set_index_buffer; // new + tr_ctx->base.resource_copy_region = remote_context_resource_copy_region; // new + tr_ctx->base.clear = remote_context_clear; + tr_ctx->base.clear_render_target = remote_context_clear_render_target; // new + tr_ctx->base.clear_depth_stencil = remote_context_clear_depth_stencil; // new + tr_ctx->base.flush = remote_context_flush; + + tr_ctx->base.get_transfer = remote_context_get_transfer; // new + tr_ctx->base.transfer_destroy = remote_context_transfer_destroy; // new + tr_ctx->base.transfer_map = remote_context_transfer_map; // new + tr_ctx->base.transfer_unmap = remote_context_transfer_unmap; // new + tr_ctx->base.transfer_flush_region = remote_context_transfer_flush_region; // new + tr_ctx->base.transfer_inline_write = remote_context_transfer_inline_write; // new + tr_ctx->base.redefine_user_buffer = remote_redefine_user_buffer; // new + +#if 0 + // Old (vgallium 2009) + tr_ctx->base.set_edgeflags = remote_context_set_edgeflags; + tr_ctx->base.draw_arrays = remote_context_draw_arrays; + tr_ctx->base.draw_elements = remote_context_draw_elements; + tr_ctx->base.draw_range_elements = remote_context_draw_range_elements; + tr_ctx->base.bind_sampler_states = remote_context_bind_sampler_states; + tr_ctx->base.set_sampler_textures = remote_context_set_sampler_textures; tr_ctx->base.set_vertex_elements = remote_context_set_vertex_elements; tr_ctx->base.surface_copy = remote_context_surface_copy; tr_ctx->base.surface_fill = remote_context_surface_fill; - tr_ctx->base.clear = remote_context_clear; - tr_ctx->base.flush = remote_context_flush; +#endif memset(&tr_ctx->current_framebuffer_state, 0, sizeof(struct pipe_framebuffer_state)); @@ -1192,3 +1460,4 @@ remote_context_create(struct pipe_screen *screen) error1: return NULL; } +/* vim: set sw=3 ts=8 sts=3 expandtab: */ diff --git a/src/gallium/drivers/remote/tr_context.h b/src/gallium/drivers/remote/tr_context.h index e0959e8993..600d389586 100644 --- a/src/gallium/drivers/remote/tr_context.h +++ b/src/gallium/drivers/remote/tr_context.h @@ -59,7 +59,8 @@ remote_context(struct pipe_context *pipe) struct pipe_context * -remote_context_create(struct pipe_screen *screen); +remote_context_create(struct pipe_screen *screen, + struct pipe_context *pipe); #ifdef __cplusplus diff --git a/src/gallium/drivers/remote/tr_screen.c b/src/gallium/drivers/remote/tr_screen.c index ab9b7a482c..4de287a55e 100644 --- a/src/gallium/drivers/remote/tr_screen.c +++ b/src/gallium/drivers/remote/tr_screen.c @@ -25,7 +25,9 @@ * **************************************************************************/ +#ifdef USE_XEN #include <xen/sys/gntmem.h> +#endif #include <sys/types.h> #include <sys/socket.h> #include <sys/un.h> @@ -37,7 +39,7 @@ #include <unistd.h> #include <signal.h> -#include "pipe/p_inlines.h" +#include "util/u_inlines.h" #include "pipe/p_defines.h" #include "util/u_memory.h" @@ -47,7 +49,7 @@ #include "remote_messages.h" #include "remote_debug.h" #include "tr_screen.h" -#include "tr_winsys.h" +//#include "tr_winsys.h" /* A few of the objects dealt with here are refcounted. Of course, the remote side is refcounting too. Since functions like pipe_texture_reference and so on in some cases just adjust the refcount variable without making a callback, @@ -540,26 +542,30 @@ remote_screen_create(struct pipe_winsys* winsys) DBG("Creating new screen\n"); - if(!tr_scr) { - printf("Alloc failed in remote_screen_create!\n"); - return NULL; - } + if(!tr_scr) { + debug_printf("Alloc failed in remote_screen_create!\n"); + return NULL; + } tr_scr->base.winsys = winsys; tr_scr->base.destroy = remote_screen_destroy; tr_scr->base.get_name = remote_screen_get_name; tr_scr->base.get_vendor = remote_screen_get_vendor; tr_scr->base.get_param = remote_screen_get_param; + tr_scr->base.get_shader_param = remote_screen_get_shader_param; tr_scr->base.get_paramf = remote_screen_get_paramf; tr_scr->base.is_format_supported = remote_screen_is_format_supported; - tr_scr->base.texture_create = remote_screen_texture_create; - tr_scr->base.texture_blanket = remote_screen_texture_blanket; - tr_scr->base.texture_release = remote_screen_texture_release; - tr_scr->base.get_tex_surface = remote_screen_get_tex_surface; - tr_scr->base.tex_surface_release = remote_screen_tex_surface_release; - tr_scr->base.surface_map = remote_screen_surface_map; - tr_scr->base.surface_unmap = remote_screen_surface_unmap; - + assert(screen->context_create); + tr_scr->base.context_create = remote_screen_context_create; + tr_scr->base.resource_create = remote_screen_resource_create; + tr_scr->base.resource_from_handle = remote_screen_resource_from_handle; + tr_scr->base.resource_get_handle = remote_screen_resource_get_handle; + tr_scr->base.resource_destroy = remote_screen_resource_destroy; + tr_scr->base.user_buffer_create = remote_screen_user_buffer_create; + tr_scr->base.fence_reference = remote_screen_fence_reference; + tr_scr->base.fence_signalled = remote_screen_fence_signalled; + tr_scr->base.fence_finish = remote_screen_fence_finish; + tr_scr->base.flush_frontbuffer = remote_screen_flush_frontbuffer; tr_scr->last_query_handle = 1; tr_scr->last_blend_handle = 1; tr_scr->last_dsa_handle = 1; @@ -846,3 +852,4 @@ uint32_t get_fresh_buffer_handle(struct pipe_screen* screen) { return remscr->last_buffer_handle++; } +/* vim: set sw=3 ts=8 sts=3 expandtab: */ |