We are currently migrating Bugzilla to GitHub issues.
Any changes made to the bug tracker now will be lost, so please do not post new bugs or make changes to them.
When we're done, all bug URLs will redirect to their equivalent location on the new bug tracker.

Bug 5102

Summary: The member 'updateSize' in structure 'D3D_RenderData' will be failed in multi-thread.
Product: SDL Reporter: elite_jwp <elite_jwp>
Component: renderAssignee: Sam Lantinga <slouken>
Status: NEW --- QA Contact: Sam Lantinga <slouken>
Severity: normal    
Priority: P2    
Version: 2.0.12   
Hardware: x86_64   
OS: Windows 10   
Attachments: bug's description
modified bug report
snapshot of wrong scene

Description elite_jwp 2020-04-21 12:19:18 UTC
Created attachment 4314 [details]
bug's description

For the text format's reason, this report can also be found in attachment file.

In my project, a window was created by 'SDL_CreateWindowFrom' in main-thread
and rendered in another sub-thread.
I used d3d as the render's driver and met a issue which can cause 
the video area wrong when adjusting the window's size. A snapshot
of that scene was shown in attachment file.

So I study the codes and try to fix it:

When adjusting the window's size, the render will be notified by 
'SDL_RendererEventWatch' and it will further call 'D3D_WindowEvent'
in which a member named 'updateSize' of D3D_RenderData is set to SDL_TRUE.

In SDL_render.c:
static int SDLCALL SDL_RendererEventWatch(void *userdata, SDL_Event *event)
{
    SDL_Renderer *renderer = (SDL_Renderer *)userdata;
    if (event->type == SDL_WINDOWEVENT) {
        SDL_Window *window = SDL_GetWindowFromID(event->window.windowID);
        if (window == renderer->window) {
            if (renderer->WindowEvent) {
                // here call 'D3D_WindowEvent'
                renderer->WindowEvent(renderer, &event->window);
            }
            ...
        }
    }
    ...
    return 0;
}

In SDL_render_d3d.c:
static void D3D_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event)
{
    D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata;
    if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED) {
        data->updateSize = SDL_TRUE;
    }
}

The member 'updateSize' is used in 'SDL_render_d3d.c:D3D_ActivateRenderer()' to reset
d3d device context after the window was adjusted.

In SDL_render_d3d.c:
static int D3D_ActivateRenderer(SDL_Renderer * renderer)
{
    D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata;
    HRESULT result;
    if (data->updateSize) {
        SDL_Window *window = renderer->window;
        int w, h;
        Uint32 window_flags = SDL_GetWindowFlags(window);
        SDL_GetWindowSize(window, &w, &h);
        data->pparams.BackBufferWidth = w;
        data->pparams.BackBufferHeight = h;
        ...
        if (D3D_Reset(renderer) < 0) {
            return -1;
        }
        data->updateSize = SDL_FALSE;
    }
    ...
    return 0;
}

So, here exists a problem when adjusting the window's size twice in a small interval time.
The pseudo code and the excuting schedule were shown as:

main-thread                                                             sub-thread
(WIN_WindowProc-->SDL_PushEvent-->SDL_RendererEventWatch)                (rendering operations)

first adjusting to size_1:
SDL_RendererEventWatch() {
    D3D_WindowEvent() {
        renderer->driverdata->updateSize = SDL_TRUE;
        }
}

                                                                        D3D_ActivateRenderer()
                                                                        {
                                                                            if (data->updateSize) {
                                                                                ...
second adjusting to size_2:                                                        D3D_Reset(renderer);
SDL_RendererEventWatch() {                                                        ...
    D3D_WindowEvent() {                                                            ...
        renderer->driverdata->updateSize = SDL_TRUE;                            ...
        }                                                                        ...
}                                                                                ...
                                                                                ...
                                                                                ...
                                                                                data->updateSize = SDL_FALSE;
                                                                            }
                                                                            ...
                                                                            return 0;
                                                                        }

In above excuting schedule, the second adjusting to size_2 was wrongly ignored as
the member 'updateSize' was reset to 'SDL_FALSE' in 'D3D_ActivateRenderer' which was 
excuted in sub-thread.

In according to that, I give a suggestion:
1) don't use the member 'updateSize';
2) modify 'D3D_ActivateRenderer()' as below:

static int
D3D_ActivateRenderer(SDL_Renderer * renderer)
{
    D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata;
    HRESULT result;
    SDL_Window *window = renderer->window;
    int w, h;

    SDL_GetWindowSize(window, &w, &h);

    // here compare the new window's size and the device's back buffer's size.
    if (w != data->pparams.BackBufferWidth ||
        h != data->pparams.BackBufferHeight) {
        Uint32 window_flags = SDL_GetWindowFlags(window);

        data->pparams.BackBufferWidth = w;
        data->pparams.BackBufferHeight = h;
        if (window_flags & SDL_WINDOW_FULLSCREEN && (window_flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != SDL_WINDOW_FULLSCREEN_DESKTOP) {
            SDL_DisplayMode fullscreen_mode;
            SDL_GetWindowDisplayMode(window, &fullscreen_mode);
            data->pparams.Windowed = FALSE;
            data->pparams.BackBufferFormat = PixelFormatToD3DFMT(fullscreen_mode.format);
            data->pparams.FullScreen_RefreshRateInHz = fullscreen_mode.refresh_rate;
        } else {
            data->pparams.Windowed = TRUE;
            data->pparams.BackBufferFormat = D3DFMT_UNKNOWN;
            data->pparams.FullScreen_RefreshRateInHz = 0;
        }
        if (D3D_Reset(renderer) < 0) {
            return -1;
        }
    }
    if (data->beginScene) {
        result = IDirect3DDevice9_BeginScene(data->device);
        if (result == D3DERR_DEVICELOST) {
            if (D3D_Reset(renderer) < 0) {
                return -1;
            }
            result = IDirect3DDevice9_BeginScene(data->device);
        }
        if (FAILED(result)) {
            return D3D_SetError("BeginScene()", result);
        }
        data->beginScene = SDL_FALSE;
    }
    return 0;
}
Comment 1 elite_jwp 2020-04-21 12:23:39 UTC
Created attachment 4315 [details]
modified bug report
Comment 2 elite_jwp 2020-04-21 12:24:22 UTC
Created attachment 4316 [details]
snapshot of wrong scene