Sidan 2 av 2 FörstaFörsta 12
Resultat 11 till 18 av 18

Ämne: Mina C/C++ libs

  1. #11
    Administrator TheSpaceMans avatar
    Reg.datum
    maj 2012
    Ort
    Eskilstuna
    Inlägg
    298
    Blog Entries
    1
    Next up, single file render engine?
    Nä lite stort för en ensam header fil implementation kanske.


    Men är det någon jag kan tänka mig se skulle kunna släppa en snabb lättanvänd single file game engine med allt man behöver så är det väl du.

  2. #12
    jag har faktiskt en sån lib på g :-) Och ja den blir ganska stor, även om jag försöker hålla dess funktionalitet ganska basic. T.ex. har den bara stöd för *en* ljudström (man får mixa i software och skicka resultatet till lib:en) och enda sättet att få ut grafik är via ett "present" anrop, som bara visar den backbuffer-bitmap man skickar till det. Lite stöd för input och så, men det är typ allt. Så inte en hel spelmotor, men den mest grundläggande funktionalitet man behöver från operativsystemet (windows till en början, men är tänkt att vara cross-plattform).

    Så här ser API:t ut för tillfället
    Kod:
    /*
    ------------------------------------------------------------------------------
              Licensing information can be found at the end of the file.
    ------------------------------------------------------------------------------
    
    
    app.h - v0.1 - Small platform independent base framework for graphical apps.
    
    
    Do this:
        #define APP_IMPLEMENTATION
    before you include this file in *one* C/C++ file to create the implementation.
    */
    
    
    #ifndef app_h
    #define app_h
    
    
    #ifndef APP_U8
        #define APP_U8 unsigned char
    #endif
    #ifndef APP_U16
        #define APP_U16 unsigned short
    #endif
    #ifndef APP_U32
        #define APP_U32 unsigned int
    #endif
    #ifndef APP_U64
        #define APP_U64 unsigned long long
    #endif
    
    
    typedef struct app_t app_t;
    
    
    int app_run( int (*app_proc)( app_t*, void* ), void* user_data );
    int app_run_ctx( int (*app_proc)( app_t*, void* ), void* user_data, void* memctx, void* logctx );
    int app_yield( app_t* app );
    int app_running( app_t* app );
    
    
    void app_title( app_t* app, char const* title );
    char const* app_exe_path( app_t* app );
    char const* app_cmdline( app_t* app );
    
    
    typedef struct app_error_t
        {
        char const* description;
        } app_error_t;
    app_error_t app_get_last_error( app_t* app );
    
    
    APP_U64 app_timer_count( app_t* app );
    APP_U64 app_timer_freq( app_t* app );
    
    
    void app_log( app_t* app, char const* message );
    
    
    typedef struct app_datetime_t
        {
        int year;
        int month;
        int day;
        int day_of_week;
        int hour;
        int minute;
        int second;
        } app_datetime_t;
    app_datetime_t app_datetime_local( app_t* app );
    app_datetime_t app_datetime_utc( app_t* app );
    
    
    void app_console_open( app_t* app );
    void app_console_close( app_t* app );
    
    
    typedef struct app_pointer_t { int handle; } app_pointer_t;
    app_pointer_t app_pointer_create( app_t* app, int width, int height, APP_U32* abgr, int hotspot_x, int hotspot_y, int scale_hw );
    void app_pointer_destroy( app_t* app, app_pointer_t pointer );
    void app_pointer( app_t* app, app_pointer_t pointer );
    extern app_pointer_t app_pointer_none;
    extern app_pointer_t app_pointer_default;
    
    
    typedef enum app_pointermode_t { APP_POINTERMODE_HARDWARE, APP_POINTERMODE_SOFTWARE, } app_pointermode_t;
    app_pointermode_t app_pointermode( app_t* app );
    void app_pointermode_set( app_t* app, app_pointermode_t pointermode );
    
    
    typedef enum app_interpolation_t { APP_INTERPOLATION_NONE, APP_INTERPOLATION_LINEAR, } app_interpolation_t;
    app_interpolation_t app_interpolation( app_t* app );
    void app_interpolation_set( app_t* app, app_interpolation_t interpolation );
    
    
    typedef enum app_screenmode_t { APP_SCREENMODE_WINDOW, APP_SCREENMODE_FULLSCREEN, } app_screenmode_t;
    app_screenmode_t app_screenmode( app_t* app );
    void app_screenmode_set( app_t* app, app_screenmode_t screenmode );
    
    
    typedef enum app_crtmode_t { APP_CRTMODE_ENABLED, APP_CRTMODE_DISABLED, } app_crtmode_t;
    app_crtmode_t app_crtmode( app_t* app );
    void app_crtmode_set( app_t* app, app_crtmode_t crtmode );
    
    
    void app_window_size( app_t* app, int width, int height );
    int app_window_width( app_t* app );
    int app_window_height( app_t* app );
    
    
    void app_present_xbgr32( app_t* app, APP_U32 const* bitmap_data, int bitmap_width, int bitmap_height, APP_U32 xbgr, APP_U32 bgxbgr );
    void app_present_rgb16( app_t* app, APP_U16 const* bitmap_data, int bitmap_width, int bitmap_height, APP_U32 xbgr, APP_U32 bgxbgr );
    
    
    void app_sound_format( app_t* app, int channels, int frequency, int bits_per_sample, int size_in_bytes );
    int app_sound_channels( app_t* app );
    int app_sound_frequency( app_t* app );
    int app_sound_bits_per_sample( app_t* app );
    int app_sound_size( app_t* app );
    
    
    void app_sound_start( app_t* app );
    void app_sound_stop( app_t* app );
    
    
    int app_sound_position( app_t* app );
    void app_sound_position_set( app_t* app, int position );
    
    
    float app_sound_volume( app_t* app );
    void app_sound_volume_set( app_t* app, float volume );
    
    
    void app_sound_update( app_t* app, int byte_offset, int byte_count, void const* data );
    
    
    
    
    typedef enum app_key_t { APP_KEY_INVALID, APP_KEY_LBUTTON, APP_KEY_RBUTTON, APP_KEY_CANCEL, APP_KEY_MBUTTON, 
        APP_KEY_XBUTTON1, APP_KEY_XBUTTON2, APP_KEY_BACK, APP_KEY_TAB, APP_KEY_CLEAR, APP_KEY_RETURN, APP_KEY_SHIFT, 
        APP_KEY_CONTROL, APP_KEY_MENU, APP_KEY_PAUSE, APP_KEY_CAPITAL, APP_KEY_KANA, APP_KEY_HANGUL = APP_KEY_KANA, 
        APP_KEY_JUNJA, APP_KEY_FINAL, APP_KEY_HANJA, APP_KEY_KANJI = APP_KEY_HANJA, APP_KEY_ESCAPE, APP_KEY_CONVERT, 
        APP_KEY_NONCONVERT, APP_KEY_ACCEPT, APP_KEY_MODECHANGE, APP_KEY_SPACE, APP_KEY_PRIOR, APP_KEY_NEXT, APP_KEY_END, 
        APP_KEY_HOME, APP_KEY_LEFT, APP_KEY_UP, APP_KEY_RIGHT, APP_KEY_DOWN, APP_KEY_SELECT, APP_KEY_PRINT, APP_KEY_EXEC, 
        APP_KEY_SNAPSHOT, APP_KEY_INSERT, APP_KEY_DELETE, APP_KEY_HELP, APP_KEY_0, APP_KEY_1, APP_KEY_2, APP_KEY_3, 
        APP_KEY_4, APP_KEY_5, APP_KEY_6, APP_KEY_7, APP_KEY_8, APP_KEY_9, APP_KEY_A, APP_KEY_B, APP_KEY_C, APP_KEY_D, 
        APP_KEY_E, APP_KEY_F, APP_KEY_G, APP_KEY_H, APP_KEY_I, APP_KEY_J, APP_KEY_K, APP_KEY_L, APP_KEY_M, APP_KEY_N, 
        APP_KEY_O, APP_KEY_P, APP_KEY_Q, APP_KEY_R, APP_KEY_S, APP_KEY_T, APP_KEY_U, APP_KEY_V, APP_KEY_W, APP_KEY_X, 
        APP_KEY_Y, APP_KEY_Z, APP_KEY_LWIN, APP_KEY_RWIN, APP_KEY_APPS, APP_KEY_SLEEP, APP_KEY_NUMPAD0, APP_KEY_NUMPAD1, 
        APP_KEY_NUMPAD2, APP_KEY_NUMPAD3, APP_KEY_NUMPAD4, APP_KEY_NUMPAD5, APP_KEY_NUMPAD6, APP_KEY_NUMPAD7, 
        APP_KEY_NUMPAD8, APP_KEY_NUMPAD9, APP_KEY_MULTIPLY, APP_KEY_ADD, APP_KEY_SEPARATOR, APP_KEY_SUBTRACT, 
        APP_KEY_DECIMAL, APP_KEY_DIVIDE, APP_KEY_F1, APP_KEY_F2, APP_KEY_F3, APP_KEY_F4, APP_KEY_F5, APP_KEY_F6, APP_KEY_F7, 
        APP_KEY_F8, APP_KEY_F9, APP_KEY_F10, APP_KEY_F11, APP_KEY_F12, APP_KEY_F13, APP_KEY_F14, APP_KEY_F15, APP_KEY_F16, 
        APP_KEY_F17, APP_KEY_F18, APP_KEY_F19, APP_KEY_F20, APP_KEY_F21, APP_KEY_F22, APP_KEY_F23, APP_KEY_F24, 
        APP_KEY_NUMLOCK, APP_KEY_SCROLL, APP_KEY_LSHIFT, APP_KEY_RSHIFT, APP_KEY_LCONTROL, APP_KEY_RCONTROL, APP_KEY_LMENU, 
        APP_KEY_RMENU, APP_KEY_BROWSER_BACK, APP_KEY_BROWSER_FORWARD, APP_KEY_BROWSER_REFRESH, APP_KEY_BROWSER_STOP, 
        APP_KEY_BROWSER_SEARCH, APP_KEY_BROWSER_FAVORITES, APP_KEY_BROWSER_HOME, APP_KEY_VOLUME_MUTE, APP_KEY_VOLUME_DOWN, 
        APP_KEY_VOLUME_UP, APP_KEY_MEDIA_NEXT_TRACK, APP_KEY_MEDIA_PREV_TRACK, APP_KEY_MEDIA_STOP, APP_KEY_MEDIA_PLAY_PAUSE, 
        APP_KEY_LAUNCH_MAIL, APP_KEY_LAUNCH_MEDIA_SELECT, APP_KEY_LAUNCH_APP1, APP_KEY_LAUNCH_APP2, APP_KEY_OEM_1, 
        APP_KEY_OEM_PLUS, APP_KEY_OEM_COMMA, APP_KEY_OEM_MINUS, APP_KEY_OEM_PERIOD, APP_KEY_OEM_2, APP_KEY_OEM_3, 
        APP_KEY_OEM_4, APP_KEY_OEM_5, APP_KEY_OEM_6, APP_KEY_OEM_7, APP_KEY_OEM_8, APP_KEY_OEM_102, APP_KEY_PROCESSKEY, 
        APP_KEY_ATTN, APP_KEY_CRSEL, APP_KEY_EXSEL, APP_KEY_EREOF, APP_KEY_PLAY, APP_KEY_ZOOM, APP_KEY_NONAME, APP_KEY_PA1, 
        APP_KEY_OEM_CLEAR, } app_key_t;
    
    
    typedef enum app_input_type_t { APP_INPUT_KEY_DOWN, APP_INPUT_KEY_UP, APP_INPUT_DOUBLE_CLICK, APP_INPUT_CHAR, 
        APP_INPUT_MOUSE_MOVE, APP_INPUT_MOUSE_DELTA, APP_INPUT_SCROLL_WHEEL, } app_input_type_t;
    
    
    typedef struct app_input_event_t 
        {
        app_input_type_t type;
        union data_t
            {
            app_key_t key;
            char char_code;
            struct { int x; int y; } mouse_pos;
            struct { float x; float y; } mouse_delta;
            float wheel_delta;
            } data;
        } app_input_event_t;
    
    
    typedef struct app_input_t
        {
        app_input_event_t* events;
        int count;
        } app_input_t;
    
    
    app_input_t app_input( app_t* app );
    
    
    #endif /* app_h */

  3. #13
    Har just publicerat en ny C++ lib, random.hpp https://github.com/mattiasgustavsson...ter/random.hpp

    Jag har postat en del slumptals-bibliotek tidigare, och det här är en vidareutveckling. Dels har jag stöd för flera olika slumptalsgeneratorer (WELL, gamerand, xorshift och den nya PCG). Speciellt spännande är PCG, som jag hörde talas om första gången för bara några månader sen. Den är snabbare, mindre och mer slumpmässig än andra generatorer. Det finns en bra föreläsning om den här: https://www.youtube.com/watch?v=45Oet5qjlms

    Dessutom har den ett gäng hjälpfunktioner för att t.ex. få ut en random unit vector och att göra shuffle på en array. Alla funktioner ger möjlighet för anroparen att välja vilken generator som ska användas - antingen en av de som definieras i libbet, eller en som man själv definierat, så länge den exponerar samma functionalitet som mina generatorer.

    Här är några exempel på hur det kan användas
    Kod:
    // Get a random number N in the range: 0 <= N <= 0xffffffff, using the default generator
    u32 a = random::next();
    
    
    // Get a random number N in the range: 0 <= N <= 0xffffffff, using the WELL generator
    u32 b = random::next<well>(); 
    
    
    // Get a random float X in the range: 0.0f <= X < 1.0f, using the default generator
    float c = random::nextf();
    
    
    // Get a random float X in the range: 0.0f <= X < 1.0f, using the XorShift+ generator
    float d = random::nextf<xorshift>();
    
    
    // Get a random integer N in the range: 10 <= N <= 100
    int e = random::range<gamerand>( 10, 100 );
    
    
    // Get a random float X in the range: 0.0f <= X < 1.0f, distributed along a rough approximation of a bell-curve
    float f = random::bell_curve<pcg>();
    
    
    // Get a random integer N in the range: 3 <= N <= 18, distributed along a rough approximation of a bell-curve
    int g = random::bell_curve( 3, 18 );
    
    
    // Randomize the order of elements in an array
    int arr[ 10 ] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, };
    random::shuffle<pcg>( arr, 10 );
    
    
    // Get a random three-dimensional unit vector 
    vec3 v0 = unit_vector<pcg, vec3>();
    vec3 v1 = unit_vector<vec3>();

  4. #14
    Citat Ursprungligen postat av Mattias Gustavsson Visa inlägg
    Har just publicerat en ny C++ lib, random.hpp https://github.com/mattiasgustavsson...ter/random.hpp
    Finns det någon speciell anledning till att du gör detta på varje seed? Dvs ser till att value är udda innan den skickas in till murmur3_avalanche?
    Kod:
    value = murmur3_avalanche( ( value << 1U ) | 1U );
    För xorshift får seed inte vara 0. Att skicka in ett udda värde till murmur3_avalanche garanterar nog inte det, värdet kan bli jämnt eller 0 ändå.
    Det kanske borde se ut såhär istället:
    Kod:
    void xorshift::seed( u64 value )
        {
        value = murmur3_avalanche( value );
        state_[ 0 ] = ( value << 1ULL ) | 1ULL;
    ...
    De andra, well, pcg, gamerand, kräver inte att seed ska vara udda, 0 tror jag också går bra.


    Jag har en tweakad variant av gamerand som jag brukar använda.
    Den är i princip lika snabb, passerar alla dieharder tests (den vanliga gamerand failar alltid ett par tester):
    Kod:
    u32 gamerand1913::next()
        {
        state_[ 0 ] = ( state_[ 0 ] << 19 ) + ( state_[ 0 ] >> 13 );
        state_[ 0 ] += state_[ 1 ];
        state_[ 1 ] = ( state_[ 1 ] << 13 ) + ( state_[ 1 ] >> 19 );
        state_[ 1 ] += state_[ 0 ];
        return state_[ 0 ];
        }

    Enheten är MiB/s, högre värde är bättre:
    (detta är en gammal benchmark, så PCG är inte med)
    Core i7 EEEBOX RPI2
    gamerand 4371.40 2629.01 1054.31
    gamerand1913 4491.23 2040.86 1054.31
    XorShift128plus 3992.20 2568.03 209.34
    XorShift1024star 2846.42 1610.06 137.78
    Well512 795.80 430.48 124.62
    Mt19937ar 643.62 351.41 74.12

  5. #15
    Citat Ursprungligen postat av madah Visa inlägg
    För xorshift får seed inte vara 0. Att skicka in ett udda värde till murmur3_avalanche garanterar nog inte det, värdet kan bli jämnt eller 0 ändå.
    Det kanske borde se ut såhär istället:
    Jamen det är klart det borde Tack tack (murmur var något jag lade på i efterhand, och alltså på fel ställe :P)

    Citat Ursprungligen postat av madah Visa inlägg
    De andra, well, pcg, gamerand, kräver inte att seed ska vara udda, 0 tror jag också går bra.
    Hmm, bra poäng... vet inte varför jag gör shift-or grejen på de andras seed också.. blev nog av bara farten. Men det ska jag nog plocka bort.

    Citat Ursprungligen postat av madah Visa inlägg
    Jag har en tweakad variant av gamerand som jag brukar använda.
    Intressant! Har du kollat något djupare på hur det kommer sig att din variant är nästan lika snabb, fastän den använder ganska många fler operationer? (jag tänker i form av att inspektera den genererade assembler koden eller så). Tänker ifall det skulle vara något annat än instruktionerna som är flaskhalsen i testet. Men sjysst förbättring hursomhelst - kanske testar den lite och sen byter till den - beroende lite på vad du har för licens på den koden?

    Iallafall, tack för granskningen och tipsen, uppskattas!

  6. #16
    Citat Ursprungligen postat av Mattias Gustavsson Visa inlägg
    Intressant! Har du kollat något djupare på hur det kommer sig att din variant är nästan lika snabb, fastän den använder ganska många fler operationer? (jag tänker i form av att inspektera den genererade assembler koden eller så). Tänker ifall det skulle vara något annat än instruktionerna som är flaskhalsen i testet. Men sjysst förbättring hursomhelst - kanske testar den lite och sen byter till den - beroende lite på vad du har för licens på den koden?

    Iallafall, tack för granskningen och tipsen, uppskattas!
    Gällande licens så släpper jag den gärna helt fri, i både MIT och public domain som du redan hade på den övriga koden.

    Från början så var GameRand helt implementerad i form av SSE (från image of the day) och snabbheten där var att det bara var två instruktioner:
    Kod:
    pshufw mm1, mm0, 0x1E
    paddd mm0, mm1
    Jag har dock aldrig försökt implementera denna eller mätt prestandan, eftersom GameRand var tillräckligt mycket snabbare även i ren kod, men jag kan tänka mig att det inte går att implementera en rotate på 19 och 13 på samma sätt.

    Anledningen till att jag började tweaka var att GameRand ofta var sämre än libc's rand() (sämre slumptal), vilket ofta ses som exempel på den sämsta slumptalsimplementationen som finns. Jag ville se om det gick att få den lite bättre om man bara kunde ändra rotations-konstanten och fortfarande bibehålla samma hastighet.
    Så jag bruteforce:ade detta, loopade igenom alla rotate-värden mellan 1 och 31, och motsvarande för två-rotate varianten. Sen tog jag den som fick minst fail i 10 körningar av dieharder (med olika seed för varje körning).
    (Egentligen borde man nog köra TestU01, men den tar rätt mycket längre tid och jag har inte orkat köra dom testerna.)

    Dock så kan jag inte lämna några garanterier om period-längd eller seeds man bör undvika och sånt. Att använda två rotationer kanske halverar perioden jämfört med en.
    Detta saknas också för GameRand, så man bör absolut undvika dessa om man behöver "bra" slumptal.


    Jag gjorde lite assembler dumps från mitt benchmark-program, detta är från en loop som gör fyra anrop i varje iteration.

    På ARM (RaspberryPi2) blir det samma mängd instruktioner, då ARM verkar ha några specialinstruktioner som kan göra både add och rotate, prestandan är identisk också:
    Kod:
    gcc (Debian 4.6.3-14+rpi1) 4.6.3
    GameRand                        GameRand1913
    =================               =================
    mov     r5, r7                  mov     r5, r7
    mov     lr, r8                  mov     lr, r8
    add     r12, r4, r12, ror #16   add     r12, r4, r12, ror #19
    add     r4, r12, r4             add     r4, r12, r4, ror #13
    str     r12, [lr]               str     r12, [lr]
    add     r12, r4, r12, ror #16   add     r12, r4, r12, ror #19
    add     r4, r12, r4             add     r4, r12, r4, ror #13
    str     r12, [lr, #4]           str     r12, [lr, #4]
    add     r12, r4, r12, ror #16   add     r12, r4, r12, ror #19
    add     r4, r12, r4             add     r4, r12, r4, ror #13
    str     r12, [lr, #8]           str     r12, [lr, #8]
    add     r12, r4, r12, ror #16   add     r12, r4, r12, ror #19
    add     r4, r12, r4             add     r4, r12, r4, ror #13
    str     r12, [lr, #12]          str     r12, [lr, #12]
    add     lr, lr, #16             add     lr, lr, #16
    sub     r5, r5, #4              sub     r5, r5, #4
    cmp     r5, #3                  cmp     r5, #3
    På Intel blir det en extra rotate, men det verkar inte påverka prestandan alls (kanske pga pipelining):
    Kod:
    gcc (Ubuntu 5.2.1-22ubuntu2) 5.2.1 20151010
    GameRand                GameRand1913
    =================       =================
    rol    $0x10,%eax       rol    $0xd,%eax
    add    %ebx,%eax        add    %ebx,%eax
                            ror    $0xd,%ebx
    add    %eax,%ebx        add    %eax,%ebx
    mov    %eax,(%r11)      mov    %eax,(%rdx)
    rol    $0x10,%eax       rol    $0xd,%eax
    add    %ebx,%eax        add    %ebx,%eax
                            ror    $0xd,%ebx
    add    %eax,%ebx        add    %eax,%ebx
    mov    %eax,0x4(%r11)   mov    %eax,0x4(%rdx)
    rol    $0x10,%eax       rol    $0xd,%eax
    add    %ebx,%eax        add    %ebx,%eax
                            ror    $0xd,%ebx
    add    %eax,%ebx        add    %eax,%ebx
    mov    %eax,0x8(%r11)   mov    %eax,0x8(%rdx)
    add    $0x10,%r11       add    $0x10,%rdx
    rol    $0x10,%eax       rol    $0xd,%eax
    add    %ebx,%eax        add    %ebx,%eax
                            ror    $0xd,%ebx
    add    %eax,%ebx        add    %eax,%ebx
    mov    %eax,-0x4(%r11)  mov    %eax,-0x4(%rdx)
    cmp    %r11,%rdx        cmp    %rdx,%r8

    Notera att på en dator jag testade (Intel(R) Celeron(R) CPU J1900 @ 1.99GHz) så var GameRand ~30% snabbare än GameRand1913. Troligast pga Celeron-processorn. Det hade varit intressant att se om det är någon skillnad på AMD.

    Om jag får tid över senare, så hade jag tänkt utöka mitt benchmark-test. Just nu så kör den bara alla slumptalsgeneratorerna i en loop, där allt blir inline. Det hade varit mer intressant att mäta också i samverkan med "riktig" kod, så att inte alla register helt allokeras till genereringen av slumptal.
    Senast redigerat av madah den 2016-02-29 klockan 22:33. Anledning: Celeron istället för Atom

  7. #17
    Väldigt intressanta saker :-) Tack för postning av assemblykoden också, kul att se vad som händer. Det jag i regel använder GameRand till är när jag behöver slumptal under rendering (t.ex. emulera photoshops dissolve effect), där speed är viktigare än kvalitet. Vid tillfälle ska jag testa din variant, och se att den inte ger synliga patterns i den typ av use-case jag använder det till. Men känns som nåt jag skulle vilja byta till :-)

  8. #18

    C++

    Hmm, det är tre år sen jag startade den här tråden

    Jag har fortsatt att jobba på mina libs, men inte postat om dem (forumet var ju tyvärr nere rätt länge). Nu har jag kommit så långt att typ hela min spelmotor består av ett gäng single-header libs. De flesta kan användas fristående, vissa sitter ihop lite, och själva huvud-filen, pixie.hpp, behöver förstås ALLA libs för att bygga.

    Här finns en preview release av all kod: https://github.com/mattiasgustavsson/pixie

    Det finns inte någon dokumentation, knappt några samples, och finns gott om buggar och halvfärdiga features - så jag rekommenderar inte att använda det för ett riktigt projekt ännu, men finns där att titta på för dom som är nyfikna.

    Namn:  pixie_preview.jpg
Visningar: 63
Storlek:  20,1 KB

Bokmärken

Behörigheter för att posta

  • Du får inte posta nya ämnen
  • Du får inte posta svar
  • Du får inte posta bifogade filer
  • Du får inte redigera dina inlägg
  •