Direct2D Performance DrawLine mit strokeStyle (gestrichelte Linie..)
-
Hallo zusammen,
ich habe in letzter Zeit ein kleines 2DPlotTool geschrieben, dass mir einfaceh Daten x über y anzeigt. Ich habe erstmal GDI+ genutzt mit WinApi genutzt.
Es hat soweit auch gut funktioniert, nur habe ich große Probleme bei der Performance
a) wenn ich das Fenster im Vollbild habe und meine "Pan" Funktion nutze, also den Graphen hin und herschiebe
b) viele Plotpunkte nutzt (viel heißt bereits ca 1000 Plotwerte)In beiden fällen ruckelt die Grafik stark wenn ich den Plot schnell hin und herschiebe (mit der Pan Funktion)
Bei kleinem Fenster und wenigen Daten ist es kein Problem, dann läuft das alles flüssig. Wenn ich aber dann das Fenster maximiere bekomme ich bereits mit wenigen Daten ein Ruckeln in der Bewegung.
Erste Frage daher schonmal: Kann das an GDI+ liegen, dass hier einfach die Größe des zu zeichnenden Bilds entscheident für die Perfomance ist?
Jetzt kam ich eben auf die Idee als Alternative Grafik Api Direct2D zu nutzen und habe mal das Beispiel von msdn getestet.
Wenn ich hier jedoch eine gestrichelte Linie benutze gibt große Perfomance verluste, die SEHR starkes Ruckeln erzeugen..Ich habe in dem Beispiel hier schon double buffering genutzt, hat aber keinen Vorteil gebracht. Nutze ich es falsch?
Gibt es hier bereits bekannte Ursachen, warum das so ruckelt? Macht es daher überhaupt keinen Sinn für mich Direct2D zu nutzen?
Was gibt es noch für alternative APIs um mein PlotTool zu erstellen (das ganze soll in etwa so ähnlich aussehen wie der Matlab Plot wer ihn kennt)Vielen Dank, ich hänge den Code vom Direct2D Beispiel mal an, damit ihr testen könnt ob es bei euch auch ruckelt oder ob ich evtl. einen Fehler drinnen habe...
Grüße
torettoCPPPS: gibt es hier auch sowas wie Spoiler? Dann könnte ich den Code noch verstecken, ist etwas viel
DemoApp.h
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE. // // Copyright (c) Microsoft Corporation. All rights reserved // #pragma once // Modify the following defines if you have to target a platform prior to the ones specified below. // Refer to MSDN for the latest info on corresponding values for different platforms. #ifndef WINVER // Allow use of features specific to Windows 7 or later. #define WINVER 0x0700 // Change this to the appropriate value to target other versions of Windows. #endif #ifndef _WIN32_WINNT // Allow use of features specific to Windows 7 or later. #define _WIN32_WINNT 0x0700 // Change this to the appropriate value to target other versions of Windows. #endif #ifndef UNICODE #define UNICODE #endif // Exclude rarely-used items from Windows headers. #define WIN32_LEAN_AND_MEAN // Windows Header Files: #include <windows.h> // C RunTime Header Files: #include <stdlib.h> #include <malloc.h> #include <memory.h> #include <wchar.h> #include <math.h> #include <d2d1.h> #pragma comment(lib, "d2d1") #include <d2d1helper.h> #include <dwrite.h> #include <wincodec.h> /****************************************************************** * * * Macros * * * ******************************************************************/ template<class Interface> inline void SafeRelease( Interface **ppInterfaceToRelease ) { if (*ppInterfaceToRelease != NULL) { (*ppInterfaceToRelease)->Release(); (*ppInterfaceToRelease) = NULL; } } #ifndef Assert #if defined( DEBUG ) || defined( _DEBUG ) #define Assert(b) do {if (!(b)) {OutputDebugStringA("Assert: " #b "\n");}} while(0) #else #define Assert(b) #endif //DEBUG || _DEBUG #endif #ifndef HINST_THISCOMPONENT EXTERN_C IMAGE_DOS_HEADER __ImageBase; #define HINST_THISCOMPONENT ((HINSTANCE)&__ImageBase) #endif // // DemoApp class declaration // class DemoApp { public: DemoApp(); ~DemoApp(); // Register the window class and call methods for instantiating drawing resources HRESULT Initialize(); // Process and dispatch messages void RunMessageLoop(); private: // Initialize device-independent resources. HRESULT CreateDeviceIndependentResources(); // Initialize device-dependent resources. HRESULT CreateDeviceResources(); // Release device-dependent resource. void DiscardDeviceResources(); // Draw content. HRESULT OnRender(); // Resize the render target. void OnResize( UINT width, UINT height ); // The windows procedure. static LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ); private: HWND m_hwnd; ID2D1Factory* m_pDirect2dFactory; ID2D1HwndRenderTarget* m_pRenderTarget; ID2D1SolidColorBrush* m_pLightSlateGrayBrush; ID2D1SolidColorBrush* m_pCornflowerBlueBrush; ID2D1StrokeStyle* m_strokeStyle; ID2D1BitmapRenderTarget *pCompatibleRenderTarget; };
DemoApp.cpp
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE. // // Copyright (c) Microsoft Corporation. All rights reserved #include "DemoApp.h" // Provides the application entry point. int WINAPI WinMain( HINSTANCE /* hInstance */, HINSTANCE /* hPrevInstance */, LPSTR /* lpCmdLine */, int /* nCmdShow */ ) { // Use HeapSetInformation to specify that the process should // terminate if the heap manager detects an error in any heap used // by the process. // The return value is ignored, because we want to continue running in the // unlikely event that HeapSetInformation fails. HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); if (SUCCEEDED(CoInitialize(NULL))) { { DemoApp app; if (SUCCEEDED(app.Initialize())) { app.RunMessageLoop(); } } CoUninitialize(); } return 0; } // DemoApp constructor DemoApp::DemoApp() : m_hwnd(NULL), m_pDirect2dFactory(NULL), m_pRenderTarget(NULL), m_pLightSlateGrayBrush(NULL), m_pCornflowerBlueBrush(NULL), m_strokeStyle(NULL), pCompatibleRenderTarget(NULL) { } // DemoApp destructor // Releases the application's resources. DemoApp::~DemoApp() { SafeRelease(&m_pDirect2dFactory); SafeRelease(&m_pRenderTarget); SafeRelease(&m_pLightSlateGrayBrush); SafeRelease(&m_pCornflowerBlueBrush); } // Creates the application window and device-independent // resources. HRESULT DemoApp::Initialize() { HRESULT hr; // Initialize device-indpendent resources, such // as the Direct2D factory. hr = CreateDeviceIndependentResources(); if (SUCCEEDED(hr)) { // Register the window class. WNDCLASSEX wcex = { sizeof(WNDCLASSEX) }; wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = DemoApp::WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = sizeof(LONG_PTR); wcex.hInstance = HINST_THISCOMPONENT; wcex.hbrBackground = NULL; wcex.lpszMenuName = NULL; wcex.hCursor = LoadCursor(NULL, IDI_APPLICATION); wcex.lpszClassName = L"D2DDemoApp"; RegisterClassEx(&wcex); // Because the CreateWindow function takes its size in pixels, // obtain the system DPI and use it to scale the window size. FLOAT dpiX, dpiY; // The factory returns the current system DPI. This is also the value it will use // to create its own windows. m_pDirect2dFactory->GetDesktopDpi(&dpiX, &dpiY); // Create the window. m_hwnd = CreateWindow( L"D2DDemoApp", L"Direct2D Demo App", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, static_cast<UINT>(ceil(640.f * dpiX / 96.f)), static_cast<UINT>(ceil(480.f * dpiY / 96.f)), NULL, NULL, HINST_THISCOMPONENT, this ); hr = m_hwnd ? S_OK : E_FAIL; if (SUCCEEDED(hr)) { ShowWindow(m_hwnd, SW_SHOWNORMAL); UpdateWindow(m_hwnd); } } return hr; } // Creates resources that are not bound to a particular device. // Their lifetime effectively extends for the duration of the // application. HRESULT DemoApp::CreateDeviceIndependentResources() { HRESULT hr = S_OK; // Create a Direct2D factory. hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_pDirect2dFactory); return hr; } // Creates resources that are bound to a particular // Direct3D device. These resources need to be recreated // if the Direct3D device dissapears, such as when the display // changes, the window is remoted, etc. HRESULT DemoApp::CreateDeviceResources() { 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 gray brush. hr = m_pRenderTarget->CreateSolidColorBrush( D2D1::ColorF(D2D1::ColorF::Red), &m_pLightSlateGrayBrush ); } if (SUCCEEDED(hr)) { // Create a blue brush. hr = m_pRenderTarget->CreateSolidColorBrush( D2D1::ColorF(D2D1::ColorF::CornflowerBlue), &m_pCornflowerBlueBrush ); // Strokestyle für gestrichelte Linie erzeugen D2D1_STROKE_STYLE_PROPERTIES properties = D2D1::StrokeStyleProperties( D2D1_CAP_STYLE_FLAT, D2D1_CAP_STYLE_FLAT, D2D1_CAP_STYLE_ROUND, D2D1_LINE_JOIN_MITER, 10.0f, D2D1_DASH_STYLE_CUSTOM, 0.0f); //properties.dashStyle = D2D1_DASH_STYLE_DASH; float dashes[] = { 3.0f, 3.0f }; m_pDirect2dFactory->CreateStrokeStyle(properties, dashes, _countof(dashes), &m_strokeStyle); } } return hr; } // Discards device-dependent resources. These resources must be // recreated when the Direct3D device is lost. void DemoApp::DiscardDeviceResources() { SafeRelease(&m_pRenderTarget); SafeRelease(&m_pLightSlateGrayBrush); SafeRelease(&m_pCornflowerBlueBrush); } // Runs the main window message loop. void DemoApp::RunMessageLoop() { MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } } // This method discards device-specific // resources if the Direct3D device dissapears during execution and // recreates the resources the next time it's invoked. HRESULT DemoApp::OnRender() { HRESULT hr = S_OK; hr = CreateDeviceResources(); if (SUCCEEDED(hr)) { D2D1_SIZE_F rtSize = m_pRenderTarget->GetSize(); // Draw a grid background. int width = static_cast<int>(rtSize.width); int height = static_cast<int>(rtSize.height); m_pRenderTarget->CreateCompatibleRenderTarget( D2D1::SizeF(static_cast<float>(width), static_cast<float>(height)), &pCompatibleRenderTarget ); ID2D1SolidColorBrush *pGridBrush = NULL; hr = pCompatibleRenderTarget->CreateSolidColorBrush( D2D1::ColorF(D2D1::ColorF::Red), &pGridBrush ); pCompatibleRenderTarget-> BeginDraw(); // pCompatibleRenderTarget->FillRectangle(D2D1::RectF(50.0f, 50.0f, 70.0f, 70.0f), pGridBrush); for (int x = 0; x < width; x += 50) { pCompatibleRenderTarget->DrawLine( D2D1::Point2F(static_cast<FLOAT>(x), 0.0f), D2D1::Point2F(static_cast<FLOAT>(x), rtSize.height), m_pLightSlateGrayBrush, 1.0f, m_strokeStyle ); } for (int y = 0; y < height; y += 50) { pCompatibleRenderTarget->DrawLine( D2D1::Point2F(0.0f, static_cast<FLOAT>(y)), D2D1::Point2F(rtSize.width, static_cast<FLOAT>(y)), m_pLightSlateGrayBrush, 1.0f, m_strokeStyle ); } // Draw two rectangles. D2D1_RECT_F rectangle1 = D2D1::RectF( rtSize.width/2 - 50.0f, rtSize.height/2 - 50.0f, rtSize.width/2 + 50.0f, rtSize.height/2 + 50.0f ); D2D1_RECT_F rectangle2 = D2D1::RectF( rtSize.width/2 - 100.0f, rtSize.height/2 - 100.0f, rtSize.width/2 + 100.0f, rtSize.height/2 + 100.0f ); // Draw a filled rectangle. pCompatibleRenderTarget->FillRectangle(&rectangle1, m_pLightSlateGrayBrush); // Draw the outline of a rectangle. // m_pRenderTarget->DrawRectangle(&rectangle2, m_pCornflowerBlueBrush); pCompatibleRenderTarget->DrawRectangle(&rectangle2, m_pCornflowerBlueBrush, 5.0, m_strokeStyle); pCompatibleRenderTarget->EndDraw(); // Retrieve the bitmap from the render target. ID2D1Bitmap *pGridBitmap = NULL; pCompatibleRenderTarget->GetBitmap(&pGridBitmap); D2D1_BITMAP_BRUSH_PROPERTIES brushProperties = D2D1::BitmapBrushProperties(D2D1_EXTEND_MODE_WRAP, D2D1_EXTEND_MODE_WRAP); ID2D1BitmapBrush *ppBitmapBrush = NULL; // Create the bitmap brush. m_pRenderTarget->CreateBitmapBrush(pGridBitmap, brushProperties, &ppBitmapBrush); pGridBitmap->Release(); pGridBrush->Release(); pCompatibleRenderTarget->Release(); m_pRenderTarget->BeginDraw(); m_pRenderTarget->SetTransform(D2D1::Matrix3x2F::Identity()); m_pRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::White)); m_pRenderTarget->FillRectangle( D2D1::RectF(0.0f, 0.0f, width, height), ppBitmapBrush); hr = m_pRenderTarget->EndDraw(); } if (hr == D2DERR_RECREATE_TARGET) { hr = S_OK; DiscardDeviceResources(); } return hr; } // If the application receives a WM_SIZE message, this method // resizes the render target appropriately. void DemoApp::OnResize(UINT width, UINT height) { if (m_pRenderTarget) { // Note: This method can fail, but it's okay to ignore the // error here, because the error will be returned again // the next time EndDraw is called. m_pRenderTarget->Resize(D2D1::SizeU(width, height)); } } // Handles window messages. LRESULT CALLBACK DemoApp::WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { LRESULT result = 0; if (message == WM_CREATE) { LPCREATESTRUCT pcs = (LPCREATESTRUCT)lParam; DemoApp *pDemoApp = (DemoApp *)pcs->lpCreateParams; ::SetWindowLongPtrW( hwnd, GWLP_USERDATA, PtrToUlong(pDemoApp) ); result = 1; } else { DemoApp *pDemoApp = reinterpret_cast<DemoApp *>(static_cast<LONG_PTR>( ::GetWindowLongPtrW( hwnd, GWLP_USERDATA ))); bool wasHandled = false; if (pDemoApp) { switch (message) { case WM_SIZE: { UINT width = LOWORD(lParam); UINT height = HIWORD(lParam); pDemoApp->OnResize(width, height); } result = 0; wasHandled = true; break; case WM_DISPLAYCHANGE: { InvalidateRect(hwnd, NULL, FALSE); } result = 0; wasHandled = true; break; case WM_PAINT: { pDemoApp->OnRender(); ValidateRect(hwnd, NULL); } result = 0; wasHandled = true; break; case WM_DESTROY: { PostQuitMessage(0); } result = 1; wasHandled = true; break; } } if (!wasHandled) { result = DefWindowProc(hwnd, message, wParam, lParam); } } return result; }
[/spoiler]
-
GDI+ ist nicht sonderlich schnell und sein Einsatzzweck einfache Fenster und Bildbearbeitung. Große Datenmengen zu zeichnen sollte heute eher mit stark hardwarebeschleunigten Grafik-APIs, wie dem von dir bereits genannten D2D, ausgeführt werden. Warum D2D bei dir ruckelt, kann ich nicht sagen, ich kann nur eine schlechte/ältere Implementation annehmen.
torettoCPP schrieb:
PS: gibt es hier auch sowas wie Spoiler? Dann könnte ich den Code noch verstecken, ist etwas viel
tl;dr
-
Was spricht gegen SFML oder SDL?
Wäre auch deutlich weniger Code...
Direct2D ist nun nicht grade die beste Wahl. Vor allem da es deprecated ist.
-
Danke für die Antworten.
Falls möglich kann ja doch mal jemand einen Blick in den D2D code werfen, ich habe wie gesagt nur das Standard MSDN Beispiel auf Double Buffering erweitert und gestrichelte Linien gezeichnet. Danach ist die Performance _extrem_ in den Keller gegangen..
Sollte per copy/paste in VS 2010 Express direkt so laufen.
Ich habe bisher mit SFML bzw. SDL noch nicht gearbeitet.
Habe gerade mal die Seiten überflogen... ist es hier auch möglich Windows ähnliche Menüs wie "Datei, Bearbeiten, Hilfe" hinzuzufügen? Ich würde gerne den Look and Feel von normalen Windows Fenstern beibehalten.
Worauf setzt SFML auf? OpenGL?Ich hatte ursprünglich das Ziel alles aus der Software rechnen zu lassen, damit ich auch in VMs keinerlei Probleme wegen Hardware-Grafikbeschleunigung bekomme, aber wie es aussieht komme ich da ja nicht drum herum..
-
torettoCPP schrieb:
Ich habe bisher mit SFML bzw. SDL noch nicht gearbeitet.
Habe gerade mal die Seiten überflogen... ist es hier auch möglich Windows ähnliche Menüs wie "Datei, Bearbeiten, Hilfe" hinzuzufügen? Ich würde gerne den Look and Feel von normalen Windows Fenstern beibehalten.
Worauf setzt SFML auf? OpenGL?Ich hatte ursprünglich das Ziel alles aus der Software rechnen zu lassen, damit ich auch in VMs keinerlei Probleme wegen Hardware-Grafikbeschleunigung bekomme, aber wie es aussieht komme ich da ja nicht drum herum..
Ja, setzt auf OpenGL auf. Du kannst wxWidgets für deine Menüs und Buttons etc ausserhalb des SFML Canvas verwenden und ein SFML Canvas für deine Darstellung verwenden. Das funktioniert einwandfrei.
Beispiel:
http://sfml-dev.org/tutorials/1.6/graphics-wxwidgets.phpSollte mit 2.0 ähnlich funktionieren, gibt leider grade kein Sample dazu.
Weiss nicht ob man in QT einen Canvas integrieren kann, wenn man QT nur das Fensterhandle gibt, aber wenn das geht steht auch QT nichts im Wege.