Homebrew Homebrew Development

MasterFeizz

Well-Known Member
Member
Joined
Oct 15, 2015
Messages
1,098
Trophies
1
Age
29
XP
3,710
Country
United States
I must have misinterpreted your response as I took it as my thread example shouldn't work. I was just going by your suggestion to @Badda to use svcSleepThread(1) but I see that isn't sufficient in my case. I've just tried setting both threads to sleep for 100000000 and the Home button works!

I'll see if I can augment this into my code and if not I'll fallback on your suggestion to put it in a common function. I could probably just write a wrapper for hidScanInput to manage that for me since I know that's called everywhere regardless if a frame is rendered.

Thanks for taking the time to help me out!

Trust me, do the wrapper.

--------------------- MERGED ---------------------------

Okay, I got it working to the point where it will not request another update if it's already busy.
However, one thing I don't understand is that as a test I cannot block until it finishes.

Code:
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <3ds.h>

int ShowError( const char* ErrorText, ... );
void RenderThread( void* Pram );
void Cleanup( void );

Handle RenderThreadReadyEvent = 0;
Handle RenderThreadUpdateEvent = 0;

Handle RenderThreadBusyMutex = 0;

Thread RenderThreadHandle = NULL;

volatile bool RenderThreadRun = false;

int ShowError( const char* ErrorText, ... ) {
    static char Buffer[ 1024 ];
    errorConf Err;
    va_list Argp;

    va_start( Argp, ErrorText );
        vsnprintf( Buffer, sizeof( Buffer ), ErrorText, Argp );
    va_end( Argp );

    errorInit( &Err, ERROR_TEXT_WORD_WRAP, CFG_LANGUAGE_EN );
    errorText( &Err, Buffer );
    errorDisp( &Err );

    return 1;
}

void RenderThread( void* Pram ) {
    gfxInitDefault( );
    consoleInit( GFX_BOTTOM, NULL );

    svcSignalEvent( RenderThreadReadyEvent );

    while ( RenderThreadRun ) {
        svcWaitSynchronization( RenderThreadUpdateEvent, INT64_MAX );
        svcClearEvent( RenderThreadUpdateEvent );

        svcWaitSynchronization( RenderThreadBusyMutex, INT64_MAX );
            svcSleepThread( 1 * ( 1e+9 ) );
        svcReleaseMutex( RenderThreadBusyMutex );
    }

    gfxExit( );

    threadExit( 0 );
}

void Cleanup( void ) {
    if ( RenderThreadHandle ) {
        RenderThreadRun = false;

        threadJoin( RenderThreadHandle, UINT64_MAX );
        threadFree( RenderThreadHandle );
    }

    svcCloseHandle( RenderThreadReadyEvent );
    svcCloseHandle( RenderThreadUpdateEvent );
    svcCloseHandle( RenderThreadBusyMutex );
}

int main( void ) {
    uint32_t Keys_Down = 0;
    uint64_t a, b = 0;

    atexit( Cleanup );

    svcCreateEvent( &RenderThreadReadyEvent, RESET_ONESHOT );
    svcCreateEvent( &RenderThreadUpdateEvent, RESET_ONESHOT );
    svcCreateMutex( &RenderThreadBusyMutex, false );

    RenderThreadRun = true;
    RenderThreadHandle = threadCreate( RenderThread, NULL, 4096, 0x20, 1, false );

    if ( RenderThreadHandle == NULL ) {
        return ShowError( "Failed to create rendering thread" );
    }

    // Wait for render thread to complete init
    svcWaitSynchronization( RenderThreadReadyEvent, INT64_MAX );
    svcClearEvent( RenderThreadReadyEvent );

    printf( "Render thread ready.\n" );

    while ( aptMainLoop( ) ) {
        gspWaitForVBlank( );
        gfxSwapBuffers( );

        hidScanInput( );
        Keys_Down = hidKeysDown( );

        if ( Keys_Down & KEY_A ) {
            if ( svcWaitSynchronization( RenderThreadBusyMutex, 0 ) != 0 ) {
                printf( "Render thread busy, try again later.\n" );
            } else {
                // Clear mutex in case we locked it while peeking at it
                svcReleaseMutex( RenderThreadBusyMutex );

                a = svcGetSystemTick( );
                    // Tell the render thread to do the thing
                    svcSignalEvent( RenderThreadUpdateEvent );

                    // Wait for render thread to finish
                    svcWaitSynchronization( RenderThreadBusyMutex, INT64_MAX );
                    svcReleaseMutex( RenderThreadBusyMutex );
                b = svcGetSystemTick( );

                svcReleaseMutex( RenderThreadBusyMutex );

                printf( "Operation took %.2fms\n", ( ( float ) ( b - a ) ) / ( float ) CPU_TICKS_PER_MSEC );
            }
        }

        if ( Keys_Down & KEY_START ) {
            break;
        }
    }

    return 0;
}

I would have thought that between the calls to svcGetSystemTick() it would signal the event and block until the render thread clears the busy mutex.

Shouldn't even work without APT_SetAppCpuTimeLimit, but otherwise seems ok.
 
  • Like
Reactions: TarableCode

TarableCode

Well-Known Member
Member
Joined
Mar 2, 2016
Messages
184
Trophies
0
Age
37
XP
319
Country
Canada
Yeah I do my basic testing on citra to spare the hinges on my n3DS and move it to hardware every few times or so.
I don't get why it's not blocking for the duration of the mutex lock though,
svcWaitSynchronization( RenderThreadReadyEvent, INT64_MAX ) seems to return instantly.

Normally this won't be a problem since I'm not planning to block, but for testing it really should so I can measure how speedy it runs.
 

MasterFeizz

Well-Known Member
Member
Joined
Oct 15, 2015
Messages
1,098
Trophies
1
Age
29
XP
3,710
Country
United States
Yeah I do my basic testing on citra to spare the hinges on my n3DS and move it to hardware every few times or so.
I don't get why it's not blocking for the duration of the mutex lock though,
svcWaitSynchronization( RenderThreadReadyEvent, INT64_MAX ) seems to return instantly.

Normally this won't be a problem since I'm not planning to block, but for testing it really should so I can measure how speedy it runs.
If you are testing on citra, then that's probably the problem.
 
  • Like
Reactions: TarableCode

TarableCode

Well-Known Member
Member
Joined
Mar 2, 2016
Messages
184
Trophies
0
Age
37
XP
319
Country
Canada
That was it lol.
*sigh*

Okay so I've been trying to teach myself arm assembly in an attempt to speed up graphics conversion.
I kind of succeeded in a sort of way.

The speed difference between (on o3ds hardware at 30% syscore):
Code:
void Unpack8BPP( const uint8_t* PixelsIn, uint16_t* PixelsOut, size_t InputLength ) {
    while ( InputLength-- ) {
        *PixelsOut++ = ConversionTable_8BPP[ *PixelsIn++ ];
    }
}

This code gives proper output and takes roughly 100ms.

Code:
.arm
.align 2

.extern ConversionTable_8BPP

@ r0: Src
@ r1: Dst
@ r2: Length
@ ----------
@ r3: Table pointer
@.global Unpack8BPP
Unpack8BPP:
    push {r3-r7}
    ldr r3, =ConversionTable_8BPP
Unpack8BPP_Loop:
    ldr r4, [r0], #4    @ AABBCCDD

    mov r5, r4, lsr #16
    and r5, r5, #0xFF

    mov r6, r4, lsr #8
    and r6, r6, #0xFF

    and r7, r4, #0xFF
    mov r4, r4, lsr #24

    @ 54ms
    ldrh r7, [r7,+r3]
    ldrh r6, [r6,+r3]
    ldrh r5, [r5,+r3]
    ldrh r4, [r4,+r3]

    strh r7, [r1], #2
    strh r6, [r1], #2
    strh r5, [r1], #2
    strh r4, [r1], #2

    subs r2, r2, #4
    bne Unpack8BPP_Loop

    pop {r3-r7}
    bx lr

And this takes only ~55ms, but the colours are inverted.

The conversion table is set up by:
Code:
uint16_t ConversionTable_8BPP[ 256 ];

void Init_8BPP( void ) {
    int r = 0;
    int g = 0;
    int b = 0;
    int i = 0;

    for ( i = 0; i < 256; i++ ) {
        r = ( ( uint16_t* ) CLUT_reds )[ i ] >> 11;
        g = ( ( uint16_t* ) CLUT_greens )[ i ] >> 10;
        b = ( ( uint16_t* ) CLUT_blues )[ i ] >> 11;

        ConversionTable_8BPP[ i ] = RGB565( r, g, b );
    }
}

I can understand the assembly straight up not working, but inverted?!
I'm sure my assembly is garbage, but I don't understand why the garbage has molded itself into this form.
 

HiSaturnV

Well-Known Member
Newcomer
Joined
Jan 29, 2021
Messages
45
Trophies
0
Age
24
Website
saturnsh2x2.ml
XP
488
Country
United States
Is it possible to work with raw pixel data in Citro2D? I've taken a look at various examples online, however, all examples I've seen only load textures from a t3x file. Specifically, I was wondering how one would go about loading pixel data into a buffer, having C2D_Image's tex parameter point to said buffer, configuring the subtex parameter properly (I'm lost on this bit in particular), and then drawing the image to the screen. Citro3D documentation is rather sparse, and there don't appear to be any Citro2D functions that automate this process any.
 
  • Like
Reactions: YugamiSekai

DD2XAlpha

Well-Known Member
Newcomer
Joined
Apr 26, 2016
Messages
48
Trophies
0
Age
26
XP
365
Country
Mexico
Hello, I've a project in my mind but I don't know a ton of C (I know C#, python and Javascript xd) and I am trying to request a JSON from an API and I was using the examples given in devkitPro but I wasn't successful when trying to download it hahahaha
 

scoliono

New Member
Newbie
Joined
Mar 27, 2022
Messages
1
Trophies
0
Age
21
Location
San Jose, CA
XP
29
Country
United States
Is it possible to work with raw pixel data in Citro2D? I've taken a look at various examples online, however, all examples I've seen only load textures from a t3x file. Specifically, I was wondering how one would go about loading pixel data into a buffer, having C2D_Image's tex parameter point to said buffer, configuring the subtex parameter properly (I'm lost on this bit in particular), and then drawing the image to the screen. Citro3D documentation is rather sparse, and there don't appear to be any Citro2D functions that automate this process any.
I was wondering how to do this, too. Then I noticed Universal Updater loads images over the network so I was able to find an example. I can't post the link, but look at the implementation of Screenshot::ConvertFromBuffer() in source/utils/screenshot.cpp.
 
  • Like
Reactions: cearp

HiSaturnV

Well-Known Member
Newcomer
Joined
Jan 29, 2021
Messages
45
Trophies
0
Age
24
Website
saturnsh2x2.ml
XP
488
Country
United States
I was wondering how to do this, too. Then I noticed Universal Updater loads images over the network so I was able to find an example. I can't post the link, but look at the implementation of Screenshot::ConvertFromBuffer() in source/utils/screenshot.cpp.
Oh, I originally asked this question here when I was working on getting the Sonic CD port to use the GPU instead of rendering everything in software. Since then I've managed to figure it out as well (check out the code for texture loading here). Thanks for the tip, though, I might check that implementation out.
 

Pikachuk

Well-Known Member
Member
Joined
Mar 19, 2016
Messages
767
Trophies
0
Age
23
Location
Bordeaux
XP
745
Country
France
I've forked and started to maintain the long abandonned godot 3ds port by porting it already from godot 2.1 to godot 2.1.7 RC and fixing issues that makes it now Python 3 compatible

however I'm right now having an issue where I keep getting VFP compilation errors even though I clearly have
'-mfloat-abi=hard' enabled

this is the link to my fork
https://github.com/SeleDreams/godot-3ds

does anyone knows how I could fix it ?
 

Site & Scene News

Popular threads in this forum

General chit-chat
Help Users
    K3Nv2 @ K3Nv2: https://youtu.be/MddR6PTmGKg?si=mU2EO5hoE7XXSbSr