Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/VLC/modules/video_output/win32/   (VLC Media Plyer ©)  Datei vom 23.9.2026 mit Größe 18 kB image not shown  

Quelle  dxgi_swapchain.cpp   Sprache: C

 

/*****************************************************************************
 * dxgi_swapchain.c: DXGI swapchain handled by the display module
 *****************************************************************************
 * Copyright (C) 2014-2021 VLC authors and VideoLAN
 *
 * Authors: Martell Malone <martellmalone@gmail.com>
 *          Steve Lhomme <robux4@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/


#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <cassert>

#include <vlc_es.h>

#include <windows.h>
#include <dcomp.h>

#include <initguid.h>
#include "dxgi_swapchain.h"

#include <dxgi1_6.h>

#include "../../video_chroma/dxgi_fmt.h"

#include <new>

#include <wrl/client.h>

using Microsoft::WRL::ComPtr;

typedef enum video_color_axis {
    COLOR_AXIS_RGB,
    COLOR_AXIS_YCBCR,
} video_color_axis;

typedef enum swapchain_surface_type {
    SWAPCHAIN_SURFACE_HWND,
    SWAPCHAIN_SURFACE_DCOMP,
} swapchain_surface_type;

typedef struct {
    DXGI_COLOR_SPACE_TYPE   dxgi;
    const char              *name;
    video_color_axis        axis;
    video_color_primaries_t primaries;
    video_transfer_func_t   transfer;
    video_color_space_t     color;
    bool                    b_full_range;
} dxgi_color_space;

struct dxgi_swapchain
{
    vlc_object_t           *obj = nullptr;

    const d3d_format_t     *pixelFormat = nullptr;
    const dxgi_color_space *colorspace = nullptr;

    swapchain_surface_type  swapchainSurfaceType;
    union {
        HWND                hwnd;
        struct {
            IDCompositionDevice  *device;
            IDCompositionVisual  *visual;
        } dcomp;
    } swapchainSurface;

    ComPtr<IDXGISwapChain1> dxgiswapChain;   /* DXGI 1.2 swap chain */
    ComPtr<IDXGISwapChain4> dxgiswapChain4;  /* DXGI 1.5 for HDR metadata */
    bool                    send_metadata;
    DXGI_HDR_METADATA_HDR10 hdr10;

    bool                   logged_capabilities = false;
};

#define DXGI_COLOR_RANGE_FULL   1 /* 0-255 */
#define DXGI_COLOR_RANGE_STUDIO 0 /* 16-235 */

#define TRANSFER_FUNC_10    TRANSFER_FUNC_LINEAR
#define TRANSFER_FUNC_22    TRANSFER_FUNC_SRGB
#define TRANSFER_FUNC_2084  TRANSFER_FUNC_SMPTE_ST2084

#define COLOR_PRIMARIES_BT601  COLOR_PRIMARIES_BT601_525

static const dxgi_color_space color_spaces[] = {
#define DXGIMAP(AXIS, RANGE, GAMMA, SITTING, PRIMARIES) \
    { DXGI_COLOR_SPACE_##AXIS##_##RANGE##_G##GAMMA##_##SITTING##_P##PRIMARIES, \
      #AXIS " Rec." #PRIMARIES " gamma:" #GAMMA " range:" #RANGE, \
      COLOR_AXIS_##AXIS, COLOR_PRIMARIES_BT##PRIMARIES, TRANSFER_FUNC_##GAMMA, \
      COLOR_SPACE_BT##PRIMARIES, DXGI_COLOR_RANGE_##RANGE},

    DXGIMAP(RGB,   FULL,     22,    NONE,   709)
    DXGIMAP(YCBCR, STUDIO,   22,    LEFT,   601)
    DXGIMAP(YCBCR, FULL,     22,    LEFT,   601)
    DXGIMAP(RGB,   FULL,     10,    NONE,   709)
    DXGIMAP(RGB,   STUDIO,   22,    NONE,   709)
    DXGIMAP(YCBCR, STUDIO,   22,    LEFT,   709)
    DXGIMAP(YCBCR, FULL,     22,    LEFT,   709)
    DXGIMAP(RGB,   STUDIO,   22,    NONE,  2020)
    DXGIMAP(YCBCR, STUDIO,   22,    LEFT,  2020)
    DXGIMAP(YCBCR, FULL,     22,    LEFT,  2020)
    DXGIMAP(YCBCR, STUDIO,   22, TOPLEFT,  2020)
    DXGIMAP(RGB,   FULL,     22,    NONE,  2020)
    DXGIMAP(RGB,   FULL,   2084,    NONE,  2020)
    DXGIMAP(YCBCR, STUDIO, 2084,    LEFT,  2020)
    DXGIMAP(RGB,   STUDIO, 2084,    NONE,  2020)
    DXGIMAP(YCBCR, STUDIO, 2084, TOPLEFT,  2020)
    DXGIMAP(YCBCR, STUDIO,  HLG, TOPLEFT,  2020)
    DXGIMAP(YCBCR, FULL,    HLG, TOPLEFT,  2020)
    /*DXGIMAP(YCBCR, FULL,     22,    NONE,  2020, 601)*/
    {DXGI_COLOR_SPACE_RESERVED, NULL, COLOR_AXIS_RGB, COLOR_PRIMARIES_UNDEF, TRANSFER_FUNC_UNDEF, COLOR_SPACE_UNDEF, 0},
#undef DXGIMAP
};

static bool canHandleConversion(const dxgi_color_space *src, const dxgi_color_space *dst)
{
    if (src == dst)
        return true;
    if (src->primaries == COLOR_PRIMARIES_BT2020)
        return true; /* we can convert BT2020 to 2020 or 709 */
    if (dst->transfer == TRANSFER_FUNC_BT709)
        return true; /* we can handle anything to 709 */
    return false; /* let Windows do the rest */
}

void DXGI_SelectSwapchainColorspace(dxgi_swapchain *display, const libvlc_video_render_cfg_t *cfg, bool match_display)
{
    HRESULT hr;
    int best = 0;
    int score, best_score = 0;
    UINT support;
    ComPtr<IDXGISwapChain3> dxgiswapChain3;
    ComPtr<IDXGIOutput> dxgiOutput;

    hr = display->dxgiswapChain.As(&dxgiswapChain3);
    if (FAILED(hr)) {
        msg_Warn(display->obj, "could not get a IDXGISwapChain3");
        goto done;
    }

    /* pick the best output based on color support and transfer */
    /* TODO support YUV output later */
    best = -1;
    for (int i=0; color_spaces[i].name; ++i)
    {
        hr = dxgiswapChain3->CheckColorSpaceSupport(color_spaces[i].dxgi, &support);
        if (SUCCEEDED(hr) && support) {
            if (!display->logged_capabilities)
                msg_Dbg(display->obj, "supports colorspace %s", color_spaces[i].name);
            score = 0;
            if (color_spaces[i].primaries == (video_color_primaries_t) cfg->primaries)
                score++;
            if (color_spaces[i].color == (video_color_space_t) cfg->colorspace)
                score += 2; /* we don't want to translate color spaces */
            if (color_spaces[i].transfer == (video_transfer_func_t) cfg->transfer ||
                /* favor 2084 output for HLG source */
                (color_spaces[i].transfer == TRANSFER_FUNC_SMPTE_ST2084 && cfg->transfer == libvlc_video_transfer_func_HLG))
                score++;
            if (color_spaces[i].b_full_range == cfg->full_range)
                score++;
            if (score > best_score || (score && best == -1)) {
                best = i;
                best_score = score;
            }
        }
    }
    display->logged_capabilities = true;

    if (best == -1)
    {
        best = 0;
        msg_Warn(display->obj, "no matching colorspace found force %s", color_spaces[best].name);
    }

    display->dxgiswapChain.As(&display->dxgiswapChain4);

    if (match_display)
    if (SUCCEEDED(display->dxgiswapChain->GetContainingOutput(&dxgiOutput)))
    {
        ComPtr<IDXGIOutput6> dxgiOutput6;
        if (SUCCEEDED(dxgiOutput.As(&dxgiOutput6)))
        {
            DXGI_OUTPUT_DESC1 desc1;
            if (SUCCEEDED(dxgiOutput6->GetDesc1(&desc1 )))
            {
                const dxgi_color_space *csp = NULL;
                for (int i=0; color_spaces[i].name; ++i)
                {
                    if (color_spaces[i].dxgi == desc1.ColorSpace)
                    {
                        if (!canHandleConversion(&color_spaces[best], &color_spaces[i]))
                            msg_Warn(display->obj, "Can't handle conversion to screen format %s", color_spaces[i].name);
                        else
                        {
                            best = i;
                            csp = &color_spaces[i];
                        }
                        break;
                    }
                }

                msg_Dbg(display->obj, "Output max luminance: %.1f, colorspace %s, bits per pixel %d", desc1.MaxFullFrameLuminance, csp?csp->name:"unknown", desc1.BitsPerColor);
                //sys->display.luminance_peak = desc1.MaxFullFrameLuminance;
            }
        }
    }

    hr = dxgiswapChain3->SetColorSpace1(color_spaces[best].dxgi);
    if (SUCCEEDED(hr))
        msg_Dbg(display->obj, "using colorspace %s", color_spaces[best].name);
    else
        msg_Err(display->obj, "Failed to set colorspace %s. (hr=0x%lX)", color_spaces[best].name, hr);
done:
    display->colorspace = &color_spaces[best];
    display->send_metadata = color_spaces[best].transfer == (video_transfer_func_t) cfg->transfer &&
                             color_spaces[best].primaries == (video_color_primaries_t) cfg->primaries &&
                             color_spaces[best].color == (video_color_space_t) cfg->colorspace;
}

static void FillSwapChainDesc(dxgi_swapchain *display, UINT width, UINT height, DXGI_SWAP_CHAIN_DESC1 *out)
{
    ZeroMemory(out, sizeof(*out));
    out->BufferCount = DXGI_SWAP_FRAME_COUNT;
    out->BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    out->SampleDesc.Count = 1;
    out->SampleDesc.Quality = 0;
    out->Width = width;
    out->Height = height;
    out->Format = display->pixelFormat->formatTexture;
    //out->Flags = 512; // DXGI_SWAP_CHAIN_FLAG_YUV_VIDEO;

#if (_WIN32_WINNT < _WIN32_WINNT_WIN10)
    bool isWin10OrGreater = false;
    HMODULE hKernel32 = GetModuleHandle(TEXT("kernel32.dll"));
    if (likely(hKernel32 != NULL))
        isWin10OrGreater = GetProcAddress(hKernel32, "GetSystemCpuSetInformation") != NULL;
    if (isWin10OrGreater)
#endif // !_WIN32_WINNT_WIN10
        out->SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
#if (_WIN32_WINNT < _WIN32_WINNT_WIN10)
    else
    {
#if (_WIN32_WINNT < _WIN32_WINNT_WIN8)
        bool isWin8OrGreater = false;
        if (likely(hKernel32 != NULL))
            isWin8OrGreater = GetProcAddress(hKernel32, "GetCurrentThreadStackLimits") != NULL;
        if (isWin8OrGreater)
#endif // !_WIN32_WINNT_WIN8
            out->SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
#if (_WIN32_WINNT < _WIN32_WINNT_WIN8)
        else
        {
            out->SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
            out->BufferCount = 1;
        }
#endif // !_WIN32_WINNT_WIN8
    }
#endif // !_WIN32_WINNT_WIN10
}

static void DXGI_CreateSwapchainHwnd(dxgi_swapchain *display,
                              IDXGIAdapter *dxgiadapter, IUnknown *pFactoryDevice,
                              UINT width, UINT height)
{
    vlc_assert(display->swapchainSurfaceType == SWAPCHAIN_SURFACE_HWND);
    if (display->swapchainSurface.hwnd == NULL)
    {
        msg_Err(display->obj, "missing a HWND to create the swapchain");
        return;
    }

    DXGI_SWAP_CHAIN_DESC1 scd;
    FillSwapChainDesc(display, width, height, &scd);

    ComPtr<IDXGIFactory2> dxgifactory;
    HRESULT hr = dxgiadapter->GetParent(IID_GRAPHICS_PPV_ARGS(&dxgifactory));
    if (FAILED(hr)) {
        msg_Err(display->obj, "Could not get the DXGI Factory. (hr=0x%lX)", hr);
        return;
    }

    hr = dxgifactory->CreateSwapChainForHwnd(pFactoryDevice,
                                              display->swapchainSurface.hwnd, &scd,
                                              NULL, NULL, &display->dxgiswapChain);

    if (hr == DXGI_ERROR_INVALID_CALL && scd.Format == DXGI_FORMAT_R10G10B10A2_UNORM)
    {
        msg_Warn(display->obj, "10 bits swapchain failed, try 8 bits");
        scd.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
        hr = dxgifactory->CreateSwapChainForHwnd(pFactoryDevice,
                                                  display->swapchainSurface.hwnd, &scd,
                                                  NULL, NULL, &display->dxgiswapChain);
    }
    if (FAILED(hr)) {
        msg_Err(display->obj, "Could not create the SwapChain. (hr=0x%lX)", hr);
    }
}

static void DXGI_CreateSwapchainDComp(dxgi_swapchain *display,
                               IDXGIAdapter *dxgiadapter, IUnknown *pFactoryDevice,
                               UINT width, UINT height)
{
    vlc_assert(display->swapchainSurfaceType == SWAPCHAIN_SURFACE_DCOMP);
    if (!display->swapchainSurface.dcomp.device || !display->swapchainSurface.dcomp.visual)
    {
        msg_Err(display->obj, "missing a HWND to create the swapchain");
        return;
    }

    DXGI_SWAP_CHAIN_DESC1 scd;
    FillSwapChainDesc(display, width, height, &scd);
    scd.Scaling = DXGI_SCALING_STRETCH;
    scd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
    scd.AlphaMode = DXGI_ALPHA_MODE_IGNORE;

    ComPtr<IDXGIFactory2> dxgifactory;
    HRESULT hr = dxgiadapter->GetParent(IID_GRAPHICS_PPV_ARGS(&dxgifactory));
    if (FAILED(hr)) {
        msg_Err(display->obj, "Could not get the DXGI Factory. (hr=0x%lX)", hr);
        return;
    }

    hr = dxgifactory->CreateSwapChainForComposition(pFactoryDevice,
                                                    &scd, NULL, &display->dxgiswapChain);
    if (hr == DXGI_ERROR_INVALID_CALL && scd.Format == DXGI_FORMAT_R10G10B10A2_UNORM)
    {
        msg_Warn(display->obj, "10 bits swapchain failed, try 8 bits");
        scd.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
        hr = dxgifactory->CreateSwapChainForComposition(pFactoryDevice,
                                                        &scd, NULL, &display->dxgiswapChain);
    }
    if (SUCCEEDED(hr)) {
        display->swapchainSurface.dcomp.visual->SetContent(display->dxgiswapChain.Get());
        display->swapchainSurface.dcomp.device->Commit();
    } else {
        msg_Err(display->obj, "Could not create the SwapChain. (hr=0x%lX)", hr);
    }
}

void DXGI_LocalSwapchainSwap( dxgi_swapchain *display )
{
    DXGI_PRESENT_PARAMETERS presentParams = { };

    HRESULT hr = display->dxgiswapChain->Present1(0, 0, &presentParams );
    if ( hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET )
    {
        /* TODO device lost */
        msg_Err( display->obj, "SwapChain Present failed. (hr=0x%lX)", hr );
    }
}

void DXGI_LocalSwapchainSetMetadata( dxgi_swapchain *display, libvlc_video_metadata_type_t type, const void *metadata )
{
    assert(type == libvlc_video_metadata_frame_hdr10);
    if ( type == libvlc_video_metadata_frame_hdr10 && metadata &&
         display->send_metadata && display->dxgiswapChain4.Get() )
    {
        const libvlc_video_frame_hdr10_metadata_t *p_hdr10 = static_cast<const libvlc_video_frame_hdr10_metadata_t *>(metadata);
        DXGI_HDR_METADATA_HDR10 hdr10 = { };
        hdr10.GreenPrimary[0] = p_hdr10->GreenPrimary[0];
        hdr10.GreenPrimary[1] = p_hdr10->GreenPrimary[1];
        hdr10.BluePrimary[0] = p_hdr10->BluePrimary[0];
        hdr10.BluePrimary[1] = p_hdr10->BluePrimary[1];
        hdr10.RedPrimary[0] = p_hdr10->RedPrimary[0];
        hdr10.RedPrimary[1] = p_hdr10->RedPrimary[1];
        hdr10.WhitePoint[0] = p_hdr10->WhitePoint[0];
        hdr10.WhitePoint[1] = p_hdr10->WhitePoint[1];
        hdr10.MaxMasteringLuminance = p_hdr10->MaxMasteringLuminance / 10000;
        hdr10.MinMasteringLuminance = p_hdr10->MinMasteringLuminance;
        hdr10.MaxContentLightLevel = p_hdr10->MaxContentLightLevel;
        hdr10.MaxFrameAverageLightLevel = p_hdr10->MaxFrameAverageLightLevel;
        if (memcmp(&display->hdr10, &hdr10, sizeof(hdr10)))
        {
            memcpy(&display->hdr10, &hdr10, sizeof(hdr10));
            display->dxgiswapChain4->SetHDRMetaData(DXGI_HDR_METADATA_TYPE_HDR10,
                                            sizeof( &display->hdr10 ), &display->hdr10 );
        }
    }
}

dxgi_swapchain *DXGI_CreateLocalSwapchainHandleHwnd(vlc_object_t *o, HWND hwnd)
{
    dxgi_swapchain *display = new (std::nothrow) dxgi_swapchain();
    if (unlikely(display == NULL))
        return NULL;

    display->obj = o;
    display->swapchainSurfaceType = SWAPCHAIN_SURFACE_HWND;
    display->swapchainSurface.hwnd = hwnd;

    return display;
}

dxgi_swapchain *DXGI_CreateLocalSwapchainHandleDComp(vlc_object_t *o, void* dcompDevice, void* dcompVisual)
{
    dxgi_swapchain *display = new (std::nothrow) dxgi_swapchain();
    if (unlikely(display == NULL))
        return NULL;

    display->obj = o;
    display->swapchainSurfaceType = SWAPCHAIN_SURFACE_DCOMP;
    display->swapchainSurface.dcomp.device = static_cast<IDCompositionDevice*>(dcompDevice);
    display->swapchainSurface.dcomp.visual = static_cast<IDCompositionVisual*>(dcompVisual);

    return display;
}

void DXGI_LocalSwapchainCleanupDevice( dxgi_swapchain *display )
{
    delete display;
}

void DXGI_SwapchainUpdateOutput( dxgi_swapchain *display, libvlc_video_output_cfg_t *out )
{
    out->u.dxgi_format    = display->pixelFormat->formatTexture;
    out->full_range     = display->colorspace->b_full_range;
    out->colorspace     = (libvlc_video_color_space_t)     display->colorspace->color;
    out->primaries      = (libvlc_video_color_primaries_t) display->colorspace->primaries;
    out->transfer       = (libvlc_video_transfer_func_t)   display->colorspace->transfer;
    out->orientation    = libvlc_video_orient_top_left;
}

bool DXGI_UpdateSwapChain( dxgi_swapchain *display, IDXGIAdapter *dxgiadapter,
                           IUnknown *pFactoryDevice,
                           const d3d_format_t *newPixelFormat, const libvlc_video_render_cfg_t *cfg,
                           bool match_display )
{
    // 0 dimensions are not allowed, a value of 8 is used otherwise
    UINT width = cfg->width ? cfg->width : 8;
    UINT height = cfg->height ? cfg->height : 8;

    if (display->dxgiswapChain.Get() && display->pixelFormat != newPixelFormat)
    {
        // the pixel format changed, we need a new swapchain
        display->dxgiswapChain.Reset();
        display->logged_capabilities = false;
    }

    if ( !display->dxgiswapChain.Get() )
    {
        display->pixelFormat = newPixelFormat;

        if (display->swapchainSurfaceType == SWAPCHAIN_SURFACE_DCOMP)
            DXGI_CreateSwapchainDComp(display, dxgiadapter, pFactoryDevice,
                                      width, height);
        else // SWAPCHAIN_TARGET_HWND
            DXGI_CreateSwapchainHwnd(display, dxgiadapter, pFactoryDevice,
                                     width, height);

    }
    if ( !display->dxgiswapChain.Get() )
        return false;

    /* TODO detect is the size is the same as the output and switch to fullscreen mode */
    HRESULT hr;
    hr = display->dxgiswapChain->ResizeBuffers(0, width, height,
                                        DXGI_FORMAT_UNKNOWN, 0 );
    if ( FAILED( hr ) ) {
        msg_Err( display->obj, "Failed to resize the backbuffer. (hr=0x%lX)", hr );
        return false;
    }

    DXGI_SelectSwapchainColorspace(display, cfg, match_display);
    return true;
}

Microsoft::WRL::ComPtr<IDXGISwapChain1> & DXGI_GetSwapChain1( dxgi_swapchain *display )
{
    return display->dxgiswapChain;
}

Microsoft::WRL::ComPtr<IDXGISwapChain4> & DXGI_GetSwapChain4( dxgi_swapchain *display )
{
    return display->dxgiswapChain4;
}

const d3d_format_t  *DXGI_GetPixelFormat( dxgi_swapchain *display )
{
    return display->pixelFormat;
}

Messung
CHZG
829610092

¤ Dauer der Verarbeitung: 0.2 Sekunden  (vorverarbeitet am  2026-11-04) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.