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 - The member 'updateSize' in structure 'D3D_RenderData' will be failed in multi-thread.
Summary: The member 'updateSize' in structure 'D3D_RenderData' will be failed in multi...
Status: NEW
Alias: None
Product: SDL
Classification: Unclassified
Component: render (show other bugs)
Version: 2.0.12
Hardware: x86_64 Windows 10
: P2 normal
Assignee: Sam Lantinga
QA Contact: Sam Lantinga
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2020-04-21 12:19 UTC by elite_jwp
Modified: 2020-04-21 12:24 UTC (History)
0 users

See Also:


Attachments
bug's description (5.16 KB, text/plain)
2020-04-21 12:19 UTC, elite_jwp
Details
modified bug report (6.10 KB, text/plain)
2020-04-21 12:23 UTC, elite_jwp
Details
snapshot of wrong scene (1.10 MB, image/png)
2020-04-21 12:24 UTC, elite_jwp
Details

Note You need to log in before you can comment on or make changes to this bug.
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