C++ - Metal

sample code/learn-metal/00-window.cpp 분석

사전 지식

모든 macOS 애플리케이션은 NSApplication 클래스의 인스턴스이다. 이 객체는 다음과 같은 기능을 수행한다.

  • main event loop 제공
  • event를 받아 적절한 객체에게 전달
  • window와 메뉴 관리, autorelease pool(메모리 관리 매커니즘) 생성
  • 애플리케이션의 상태(시작, 종료, 숨겨짐, 활성화 등)를 전달받는 delegate 객체를 가진다.

Metal-cpp는 Cocoa와 Cocoa Touch의 object allocation policy를 따른다. 즉, Metal object들은 object ownership에 의해 관리된다. 즉, 객체에 대한 참조를 추적(reference counting)하여 객체에 대한 모든 참조가 끊어지면(owner가 없으면) 객체를 메모리에서 해제한다.

main function

애플리케이션의 동작 환경을 설정한다.

int main( int argc, char* argv[] )
{
    // 메모리 관리를 위한 autoreleasePool 초기화.
    // 객체가 더 이상 사용되지 않으면 자동으로 해제한다.
    NS::AutoreleasePool* pAutoreleasePool = NS::AutoreleasePool::alloc()->init();

    // Delegate 객체 생성
    MyAppDelegate del;

    /* 모든 애플리케이션은 하나의 NSApplication 인스턴스를 사용한다. 이 인스턴스는 다음과 같은 기능을 제공한다.
     * main event 관리.
     * 앱의 윈도우와 메뉴 관리(추적), 이벤트를 적절한 객체(autorelease pools 포함)에 전달.
     * 사용자로부터의 입력을 받는다. */
    NS::Application* pSharedApplication = NS::Application::sharedApplication();
    /* 모든 NSApplication은 하나의 delegate 객체를 가진다. 이 객체를 통해 앱의 상태(시작, 종료 등)를 전달받는다.
     * delegate를 설정하고 delegate method를 구현하여 앱의 행동을 customize할수 있다. */
    pSharedApplication->setDelegate( &del );
    pSharedApplication->run();

    pAutoreleasePool->release();

    return 0;
}

Application Delegate

Application delegate는 애플리케이션이 macOS system으로부터 특정 notification을 전달받았을때의 행동을 정의(customize)할수 있게 해준다.

MyAppDelegate 클래스

class MyAppDelegate : public NS::ApplicationDelegate
{
    public:
        ~MyAppDelegate();

        NS::Menu* createMenuBar();

        virtual void applicationWillFinishLaunching( NS::Notification* pNotification ) override;
        virtual void applicationDidFinishLaunching( NS::Notification* pNotification ) override;
        virtual bool applicationShouldTerminateAfterLastWindowClosed( NS::Application* pSender ) override;

    private:
        NS::Window* _pWindow;
        MTK::View* _pMtkView;
        MTL::Device* _pDevice;
        MyMTKViewDelegate* _pViewDelegate = nullptr;
};

applicationWillFinishLaunching

기본 클래스인 ApplicationDelegate에 선언된 virtual function이다.
이 함수는 앱의 초기화가 거의 완료되었을때 호출된다. 주로 menu bar를 설정하거나 main event loop가 시작되기 전에 필요한 object들을 준비하기 위해 사용한다.

void MyAppDelegate::applicationWillFinishLaunching( NS::Notification* pNotification )
{
    NS::Menu* pMenu = createMenuBar();
    NS::Application* pApp = reinterpret_cast< NS::Application* >( pNotification->object() );
    pApp->setMainMenu( pMenu );
    pApp->setActivationPolicy( NS::ActivationPolicy::ActivationPolicyRegular );
}

applicationDidFinishLaunching

애플리케이션의 main event loop가 시작되었지만 아직 어떠한 event도 처리되지 않았을때 호출된다. window와 metal-related object들을 준비(set up)하기 위해 사용한다.

void MyAppDelegate::applicationDidFinishLaunching( NS::Notification* pNotification )
{
    CGRect frame = (CGRect){ {100.0, 100.0}, {512.0, 512.0} };

    // window 객체 초기화
    _pWindow = NS::Window::alloc()->init(
        frame,
        NS::WindowStyleMaskClosable|NS::WindowStyleMaskTitled,
        NS::BackingStoreBuffered,
        false );

    _pDevice = MTL::CreateSystemDefaultDevice();

    // Metal object들을 생성(create), 계산(configure)하는데 특화된 view 초기화
    _pMtkView = MTK::View::alloc()->init( frame, _pDevice );
    _pMtkView->setColorPixelFormat( MTL::PixelFormat::PixelFormatBGRA8Unorm_sRGB );
    // R, G, B, alpha(불투명도)
    _pMtkView->setClearColor( MTL::ClearColor::Make( 1.0, 0.0, 0.0, 1.0 ) );

    // View delegate를 생성하고 설정
    _pViewDelegate = new MyMTKViewDelegate( _pDevice );
    _pMtkView->setDelegate( _pViewDelegate );

    // Window에 content view를 설정해주면 window는 view delegate를 소유하게 된다.
    // view object들은 window의 content area에 정확히 맞춰지도록 크기가 조정된다.
    _pWindow->setContentView( _pMtkView );
    _pWindow->setTitle( NS::String::string( "00 - Window", NS::StringEncoding::UTF8StringEncoding ) );

    _pWindow->makeKeyAndOrderFront( nullptr );

    // Application을 활성화(activate)한다.
    NS::Application* pApp = reinterpret_cast< NS::Application* >( pNotification->object() );
    pApp->activateIgnoringOtherApps( true );
}
  • (CGRect) - 원점이 (100,100)이고 크기가 (512,512)인 CGRect 구조체 초기화
  • CreateSystemDefaultDevice - metal device에 대한 포인터 반환
  • NS::Window
    • NS::WindowStyleMask... - 윈도우 스타일을 설정하는 bit mask이다. 윈도우가 어떻게 보일지, 어떤 기능을 할지 결정한다.
    • NS::BackingStoreBuffered - display buffer를 사용하여 어떻게 drawing이 진행되야 할지 window에게 알려준다.
  • MTK::View 객체 - Metal을 사용하여 화면에 그리고싶은 content를 그릴때 사용되는 핵심 detail을 다루는 객체. 이 객체를 통해 어떤 것들을 어떻게 그릴지 제어할수 있다. 픽셀의 format을 설정할수 있고, 스크린을 정해진(preset) 색으로 초기화(clear)할수도 있다.
  • ViewDelegate
  • setContentView - 윈도우에게 화면에 출력(display)할 view를 설정한다.
  • makeKeyAndOrderFront - 윈도우를 screen list의 맨 앞으로 설정

applicationShouldTerminateAfterLastWindowClosed

모든 window들이 닫혔을때 애플리케이션이 main event loop를 벗어나도록 한다.

bool MyAppDelegate::applicationShouldTerminateAfterLastWindowClosed( NS::Application* pSender )
{
    return true;
}

createMenuBar

사용자가 윈도우를 조작할수 있는 menu bar(닫기, 최소화, 최대화 버튼이 있는 바)를 설정한다. 또한 키보드 입력을 통해서도 여러 가지 상호작용을 실행할수 있다.

  • Menu instance - 애플리케이션의 menu를 관리한다. 이 인스턴스를 통해 MenuItem객체들을 추가할수 있다. MenuItem 객체들은 command functionality같은 것이다.
  • Menu::alloc()->init() - NS::String 객체를 전달하여 메뉴의 이름을 정할수 있다.
  • registerActionCallback() - callback function 인스턴스를 생성한다.

    method selector
    Objective-C에서 다른 객체의 메서드를 호출할때 사용되는 개념이다. Objective-C에서 객체 간 통신은 메세지 전송을 통해 이루어지며 메세지의 내용은 메서드의 이름과 매개변수의 타입(함수의 signiture)이다. 이때 메서드의 이름과 매개변수 타입 정보를 메서드 선택자라고 한다.
    Java의 함수형 인터페이스와 유사하다.

  • addItem(const String* pTitle, SEL pSelector, const String* pKeyEquivalent)
    • pTitle - MenuItem 인스턴스의 이름
    • pSelector - callback을 가리키는 selector
    • pKeyEquivalent - callback을 실행하기 위해 눌려져야하는 키
  • setKeyEquivalentModifierMask - callback action key와 함께 눌려져야하는 키
    • NS::EventModifierFlagCommand - command key

reference

Tags:

Categories:

Updated:

Comments