Sign in to follow this  
Rockaway

DX12 Direct2D/DirectWrite Text Resolution

Recommended Posts

Hey,

 

I've just been playing about with Direct2D this afternoon, and I've moved on to using DIrectWrite for outputting my text.

 

All easy enough, but I can't for the life of me figure out how to increase the resolution of the text. Anything I draw to the screen with Direct2D scales nicely, but text doesn't.

 

I have two screens, one at 1680x1050, the other at 3840x2160 and it is quite noticeable on the higher resolution screen.

 

[attachment=33027:D2D_Resolution.jpg]

 

You can see the difference between the window title, and the sample text i've done with DirectWrite.

 

Anyone shed any light on this?

 

The code follows, i've removed the extraneous extra code to focus on the DirectWrite stuff.

 

Thanks.

#pragma once

#include <d2d1.h>
#include <dwrite.h>
#include <string.h>
#include <Windows.h>

    template<class Interface>
    inline void SafeRelease( Interface **ppInterfaceToRelease )
    {
        if ( *ppInterfaceToRelease != NULL )
        {
            ( *ppInterfaceToRelease )->Release( );

            ( *ppInterfaceToRelease ) = NULL;
        }
    }

class Application
{
    // ================================================================
    // --> FIELD
    // ================================================================

        private : FLOAT                  m_dpiX;
        private : FLOAT                  m_dpiY;
        private : HWND                   m_hWnd;
        private : ID2D1Factory*          m_pDirect2dFactory;
        private : ID2D1HwndRenderTarget* m_pRenderTarget;
        private : ID2D1SolidColorBrush*  m_pLightSlateGrayBrush;
        private : IDWriteFactory*        m_pDWriteFactory;
        private : IDWriteTextFormat*     m_pTextFormat;
        private : IDWriteTextLayout*     m_pTextLayout;
                  

    // ================================================================
    // --> CONSTRUCTOR
    // ================================================================

        public : Application( );

    // ================================================================
    // --> METHOD
    // ================================================================
 
        // METHOD -> CreateDeviceIndependentResources()
        private : HRESULT CreateDeviceIndependentResources( );

        // METHOD -> CreateDeviceDependentResources()
        private : HRESULT CreateDeviceDependentResources( );

        // METHOD -> DiscardDeviceDependentResources()
        private : void DiscardDeviceDependentResources( );

        // METHOD -> Initialise()
        public : HRESULT Initialise( HINSTANCE hInstance );

        // METHOD -> MessageLoop()
        public : void MessageLoop( );

        // METHOD -> OnRender()
        private : HRESULT OnRender( );

        // METHOD -> OnRender()
        private: void OnResize( UINT height, UINT width );

        // METHOD -> Procedure()
        private : static LRESULT CALLBACK Procedure( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );

    // ================================================================
    // --> DESTRUCTOR
    // ================================================================

        public : ~Application( );
};
#include "Application.h"

// ================================================================
// CONSTRUCTOR
// ================================================================

    // PUBLIC
    Application::Application( ) : m_hWnd( NULL ),
                                  m_pDirect2dFactory( NULL ),
                                  m_pRenderTarget( NULL ),
                                  m_pLightSlateGrayBrush( NULL ),
                                  m_pDWriteFactory( NULL ),
                                  m_pTextFormat( NULL )
    {

    }

// ================================================================
// METHOD -> CreateDeviceIndependentResources()
// ================================================================

    // PRIVATE
    HRESULT Application::CreateDeviceIndependentResources( )
    {
        HRESULT hr = S_OK;

        hr = D2D1CreateFactory( D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_pDirect2dFactory );

        if ( SUCCEEDED( hr ) )
        {
            hr = DWriteCreateFactory( DWRITE_FACTORY_TYPE_SHARED,
                                      __uuidof( IDWriteFactory ),
                                      reinterpret_cast<IUnknown**>( &m_pDWriteFactory ) );
        }

        if ( SUCCEEDED( hr ) )
        {
            hr = m_pDWriteFactory->CreateTextFormat(
                L"Segoe UI",                // Font family name.
                NULL,                       // Font collection (NULL sets it to use the system font collection).
                DWRITE_FONT_WEIGHT_REGULAR,
                DWRITE_FONT_STYLE_NORMAL,
                DWRITE_FONT_STRETCH_NORMAL,
                12.0f,
                L"en-us",
                &m_pTextFormat
            );  
        }

        return hr;
    }

// ================================================================
// METHOD -> CreateDeviceDependentResources()
// ================================================================

    // PRIVATE
    HRESULT Application::CreateDeviceDependentResources()
    {
        HRESULT hr = S_OK;

        if ( !m_pRenderTarget )
        {
            RECT rc;

            GetClientRect( m_hWnd, &rc );

            D2D1_SIZE_U size = D2D1::SizeU( rc.right - rc.left, rc.bottom - rc.top );

            // Create a Direct2D render target.
            hr = m_pDirect2dFactory->CreateHwndRenderTarget( D2D1::RenderTargetProperties( ),
                                                             D2D1::HwndRenderTargetProperties( m_hWnd, size ),
                                                             &m_pRenderTarget );
           

            if ( SUCCEEDED( hr ) )
            {
                // Create a black brush.
                hr = m_pRenderTarget->CreateSolidColorBrush( D2D1::ColorF( D2D1::ColorF( 0.0F, 0.0F, 0.0F, 1.0F ) ),
                    &m_pLightSlateGrayBrush );
            }
        }

        return hr;
    }

// ================================================================
// METHOD -> DiscardDeviceDependentResources()
// ================================================================

    // PRIVATE
    void Application::DiscardDeviceDependentResources( )
    {
        SafeRelease( &m_pRenderTarget );
        SafeRelease( &m_pLightSlateGrayBrush );
        SafeRelease( &m_pDWriteFactory );
        SafeRelease( &m_pTextFormat );
        SafeRelease( &m_pTextLayout );
    }

// ================================================================
// METHOD -> Initialise()
// ================================================================

    // PUBLIC
    HRESULT Application::Initialise( HINSTANCE hInstance )
    {
        HRESULT hr;

        hr = CreateDeviceIndependentResources( );

        if ( SUCCEEDED( hr ) )
        {
            // Register the window class.
            WNDCLASSEX wcex = { sizeof( WNDCLASSEX ) };

            wcex.style = CS_HREDRAW | CS_VREDRAW;
            wcex.lpfnWndProc = Application::Procedure;
            wcex.cbClsExtra = 0;
            wcex.cbWndExtra = sizeof( LONG_PTR );
            wcex.hInstance = hInstance;
            wcex.hbrBackground = NULL;
            wcex.lpszMenuName = NULL;
            wcex.hCursor = LoadCursor( NULL, IDI_APPLICATION );
            wcex.lpszClassName = L"DirectX 12 Application";

            RegisterClassEx( &wcex );

            m_pDirect2dFactory->GetDesktopDpi( &m_dpiX, &m_dpiY );

            // Create the window.
            m_hWnd = CreateWindow( L"DirectX 12 Application",
                L"DirectX 12 Application",
                WS_OVERLAPPEDWINDOW,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                1024,
                768,
                NULL,
                NULL,
                hInstance,
                this
            );

            hr = m_hWnd ? S_OK : E_FAIL;

            if ( SUCCEEDED( hr ) )
            {
                ShowWindow( m_hWnd, SW_SHOWNORMAL );
                UpdateWindow( m_hWnd );
            }
        }

        return hr;
    }

// ================================================================
// // METHOD -> MessageLoop()
// ================================================================

    // PUBLIC
    void Application::MessageLoop( )
    {
        MSG msg;

        while ( GetMessage( &msg, NULL, 0, 0 ) )
        {
            TranslateMessage( &msg );
            DispatchMessage( &msg );
        }
    }

// ================================================================
// METHOD -> OnRender()
// ================================================================

    // PRIVATE
    HRESULT Application::OnRender( )
    {
        HRESULT hr = S_OK;

        hr = CreateDeviceDependentResources( );

        if ( SUCCEEDED( hr ) )
        {
            m_pRenderTarget->BeginDraw( );

            m_pRenderTarget->SetTransform( D2D1::Matrix3x2F::Identity( ) );

            m_pRenderTarget->Clear( D2D1::ColorF( D2D1::ColorF::White ) );

            D2D1_SIZE_F rtSize = m_pRenderTarget->GetSize( );

            // START : DIRECTWRITE

            // Create a text layout using the text format.
            if ( SUCCEEDED( hr ) )
            {


                D2D1_SIZE_F rtSize = m_pRenderTarget->GetSize( );

                // Align text to centre of layout box.
                m_pTextFormat->SetTextAlignment( DWRITE_TEXT_ALIGNMENT_CENTER );
                m_pTextFormat->SetParagraphAlignment( DWRITE_PARAGRAPH_ALIGNMENT_CENTER );

                const wchar_t* wszText_ = L"DirectX 12 Application";

                UINT32 cTextLength_ = ( UINT32 )wcslen( wszText_ );

                hr = m_pDWriteFactory->CreateTextLayout(
                    wszText_,       // The string to be laid out and formatted.
                    cTextLength_,   // The length of the string.
                    m_pTextFormat,  // The text format to apply to the string (contains font information, etc).
                    300.0F,         // The width of the layout box.
                    100.0F,         // The height of the layout box.
                    &m_pTextLayout  // The IDWriteTextLayout interface pointer.
                );
            }

            m_pRenderTarget->DrawTextLayout(
                D2D1_POINT_2F { ( ( rtSize.width / 2 ) - 150.0F ), ( ( rtSize.height / 2 ) - 50.0F ) },
                m_pTextLayout,
                m_pLightSlateGrayBrush,
                D2D1_DRAW_TEXT_OPTIONS_NONE
            );

            // END : DIRECTWRITE
            // ----------------------------------------------------------------

            hr = m_pRenderTarget->EndDraw( );
        }

        if ( hr == D2DERR_RECREATE_TARGET )
        {
            hr = S_OK;
            DiscardDeviceDependentResources( );
        }

        return hr;
    }

// ================================================================
// METHOD -> OnResize()
// ================================================================

    // PRIVATE
    void Application::OnResize( UINT height, UINT width )
    {
        if ( m_pRenderTarget )
        {
            m_pRenderTarget->Resize( D2D1::SizeU( width, height ) );
        }
    }

// ================================================================
// METHOD -> Procedure()
// ================================================================

    // PRIVATE, STATIC
    LRESULT CALLBACK Application::Procedure( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
    {
        LRESULT result = 0;

        if ( uMsg == WM_CREATE )
        {
            LPCREATESTRUCT pcs = ( LPCREATESTRUCT )lParam;

            Application *pApplication = ( Application * )pcs->lpCreateParams;

            ::SetWindowLongPtrW( hWnd,
                GWLP_USERDATA,
                PtrToUlong( pApplication )
            );

            result = 1;
        }
        else
        {
            Application *pApplication = reinterpret_cast< Application * >( static_cast< LONG_PTR >( ::GetWindowLongPtrW( hWnd, GWLP_USERDATA ) ) );

            bool wasHandled = false;

            if ( pApplication )
            {
                switch ( uMsg )
                {
                    case WM_DESTROY :
                        {
                            PostQuitMessage( 0 );
                        }

                        result = 1;

                        wasHandled = true;

                        break;

                    case WM_DISPLAYCHANGE :
                        {
                            InvalidateRect( hWnd, NULL, FALSE );
                        }

                        result = 0;

                        wasHandled = true;

                        break;

                    case WM_PAINT :
                        {
                            pApplication->OnRender( );

                            ValidateRect( hWnd, NULL );
                        }

                        result = 0;

                        wasHandled = true;

                        break;

                    case WM_SIZE :
                        {
                            UINT height = HIWORD( lParam );
                            UINT width = LOWORD( lParam );
                            
                            pApplication->OnResize( height, width );
                        }

                        result = 0;

                        wasHandled = true;

                        break;
                }
            }

            if ( !wasHandled )
            {
                result = DefWindowProc( hWnd, uMsg, wParam, lParam );
            }
        }

        return result;
    }

    // ================================================================
    // DESTRUCTOR
    // ================================================================

    // PUBLIC
    Application::~Application( )
    {
        SafeRelease( &m_pDirect2dFactory );
        SafeRelease( &m_pRenderTarget );
        SafeRelease( &m_pLightSlateGrayBrush );
        SafeRelease( &m_pDWriteFactory );
        SafeRelease( &m_pTextFormat );
        SafeRelease( &m_pTextLayout );
    }
#include "Application.h"

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int CmdShow )
{
    HeapSetInformation( NULL, HeapEnableTerminationOnCorruption, NULL, 0 );

    if ( SUCCEEDED( CoInitialize( NULL ) ) )
    {
        Application App;

        if ( SUCCEEDED( App.Initialise( hInstance ) ) )
        {
            App.MessageLoop( );
        }

        CoUninitialize( );
    }

    return 0;
}

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this  

  • Forum Statistics

    • Total Topics
      627711
    • Total Posts
      2978761
  • Similar Content

    • By Mr_Fox
      Hi Guys,
      Does anyone know how to grab a video frame on to DX texture easily just using Windows SDK? or just play video on DX texture easily without using 3rd party library?  I know during DX9 ages, there is a DirectShow library to use (though very hard to use). After a brief search, it seems most game dev settled down with Bink and leave all hobbyist dx programmer struggling....
      Having so much fun play with Metal video playback (super easy setup just with AVKit, and you can grab movie frame to your metal texture), I feel there must be a similar easy path for video playback on dx12 but I failed to find it.
      Maybe I missed something? Thanks in advance for anyone who could give me some path to follow
    • By _void_
      Hello guys,
      I have a texture of format DXGI_FORMAT_B8G8R8A8_UNORM_SRGB.
      Is there a way to create shader resource view for the texture so that I could read it as RGBA from the shader instead of reading it specifically as BGRA?
      I would like all the textures to be read as RGBA.
       
      Tx
    • By _void_
      Hello guys,
      I am wondering why D3D12 resource size has type UINT64 while resource view size is limited to UINT32.
      typedef struct D3D12_RESOURCE_DESC { … UINT64                   Width; … } D3D12_RESOURCE_DESC; Vertex buffer view can be described in UINT32 types.
      typedef struct D3D12_VERTEX_BUFFER_VIEW { D3D12_GPU_VIRTUAL_ADDRESS BufferLocation; UINT                      SizeInBytes; UINT                      StrideInBytes; } D3D12_VERTEX_BUFFER_VIEW; For the buffer we can specify offset for the first element as UINT64 but the buffer view should still be defined in UINT32 terms.
      typedef struct D3D12_BUFFER_SRV { UINT64                 FirstElement; UINT                   NumElements; UINT                   StructureByteStride; D3D12_BUFFER_SRV_FLAGS Flags; } D3D12_BUFFER_SRV; Does it really mean that we can create, for instance, structured buffer of floats having MAX_UNIT64 elements (MAX_UNIT64 * sizeof(float) in byte size) but are not be able to create shader resource view which will enclose it completely since we are limited by UINT range?
      Is there a specific reason for this? HLSL is restricted to UINT32 values. Calling function GetDimensions() on the resource of UINT64 size will not be able to produce valid values. I guess, it could be one of the reasons.
       
      Thanks!
    • By pcmaster
      Hello!
      Is it possible to mix ranges of samplers and ranges of SRVs and ranges of UAVs in one root parameter descriptor table? Like so:
      D3D12_DESCRIPTOR_RANGE ranges[3]; D3D12_ROOT_PARAMETER param; param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; param.DescriptorTable.NumDescriptorRanges = 3; param.DescriptorTable.pDescriptorRanges = ranges; range[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; .. range[1].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV; .. range[2].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER; .. I wonder especially about CopyDescriptors, that will need to copy a range of D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER and a range of D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV.
      Thanks if anyone knows (while I try it :))
      .P
    • By Infinisearch
      So I was reading the presentation Practical DirectX 12 - Programming Model and Hardware Capabilities again and finally decided to tackle proper command list submission.  Things mentioned in the document regarding this subject:
      Aim for (per-frame): ● 15-30 Command Lists ● 5-10 ‘ExecuteCommandLists’ calls
      Each ‘ ExecuteCommandLists’ has a fixed CPU overhead ● Underneath this call triggers a flush ● So batch up command lists
      Try to put at least 200μs of GPU work in each ‘ExecuteCommandLists’, preferably 500μs
      Small calls to ‘ExecuteCommandLists’ complete faster than the OS scheduler can submit new ones
      OS takes ~60μs to schedule upcoming work
      So basically I want to estimate how long my draw calls take.  Benchmarking for a particular piece of hardware seems impractical.  So given the stats primitive count, pixel count(approximately how many screen space pixels the call will be rendered to), and some precomputed metric associated with shader ALU complexity(like # of alu ops) do you think that I can get a reasonable estimation of how much time a draw call will take?
      What do you do to take this into account?
      What about other things like transitions?  I can only think of actual measurement in this case.
  • Popular Now