D3D Backbuffergröße ändern ohne Reset()-Call



  • Hallöchen!

    Ich habe eine Direct3D-Anwendung die ein Terrain im Fenstermodus zeichnet. Sobald ich von Windows eine WM_SIZE-Nachricht bekomme rufe ich eine Funktion auf um das Client-Direct3Dfenster anzupassen. Die Perspektive an das neue Seitenverhältnis anpassen funktioniert ohne Probleme.

    Wenn ich das Fenster sehr klein erstelle (320x200) und es dann mit der Maus größer ziehe, passt sich zwar die Perspektive an, aber der Backbuffer wird nicht an die neue Auflösung angepasst. Das Bild wird matschig, unscharf, es wird lediglich auf die neue Größe *gestretcht*.

    Ich möchte jedoch das der Viewport, Backbuffer immer exakt genauso größ sind wie dar Clientbereich vom Fenster. Es sollte also 1 gerenderter Pixel auch immer genau einem *echten* Pixel auf dem Bildschirm entsprechen. Nix soll verzerrt sein.

    Nach einer Forumsuche bin ich auf diesen Post gestoßen:
    http://www.c-plusplus.net/forum/viewtopic-var-t-is-230088-and-highlight-is-resize.html

    Dort wird erklärt das man Reset() aufrufen MUSS. Laut SDK hat das aber den Nachteil das alle über das D3Device geladenen Daten und States verloren gehen.

    DirectX SDK March 2009 zu Reset():

    Calling IDirect3DDevice9::Reset causes all texture memory surfaces to be lost, managed textures to be flushed from video memory, and all state information to be lost.

    Das heißt ich müsste bei jedem Resize alle Grafikdaten wie Texturen, Vertexarrays, Indexbuffer, States löschen, dann meine Resize-Funktion aufrufen in der Reset() gecallt wird und dann alles wieder neu laden ... puh! Also ich find des etwas heftig.

    Bei einigen Spielen (z.B. World of Warcraft) ist mir aufgefallen das es eine andere Möglichkeit geben muss. In World of Warcraft wird die Fenstergröße (und damit auch der Backbuffer) mit kleiner Verzögerung direkt angepasst, es gibt keinen Ladebildschirm oder längeres Laden ... man würde doch sicherlich bemerken wenn mehrere hundert Megabyte Daten gelöschen und neu geladen werden.

    Jetzt meine Frage:
    Wie passe ich den Backbuffer an die Größe den Client-fensterbereichs an ohne einen Reset()-Aufruf?

    Ich bin mir zieeeemlich sicher das es möglich ist...



  • du kannst mit SetRenderTarget einen neuen buffer setzen. vielleicht löst das
    dein problem (ungetestet)



  • Häufig passt man den Backbuffer auch der Desktop Auflösung an, bzw man setzt die Größe auf die maximalgröße. Und ansatt den Backbuffer zu verändern wird bloß der Viewport verändert.



  • Erstmal danke für die schnellen Antworten!

    Häufig passt man den Backbuffer auch der Desktop Auflösung an, bzw man setzt die Größe auf die maximalgröße. Und ansatt den Backbuffer zu verändern wird bloß der Viewport verändert.

    Nehmen wir einmal an ich mache das so.
    Würde sich dann der Rechenaufwand für die GPU erhöhen?
    z.B. meine Desktopauflösung ist 1920x1200, den Viewport erstelle ich nun mit 800x600, berechnet die GPU jetzt die vollen 1920x1200 oder nur 800x600 Pixel?

    Schade ist nur das man damit sicherlich etwas video-RAM verschwendet... obwohl das wohl bei den heutigen Killergrafikkarten wohl kaum noch eine Rolle spielt.

    (Ich denke nicht, aber ich frage lieber, da ich erst seit ein paar Tagen versuche mit Direct3D arbeite 🙂 )

    Außerdem habe ich noch das hier gefunden:
    http://www.gamedev.net/community/forums/topic.asp?topic_id=401732

    If you create an additional swapchain and use it instead of the implicit swapchain created during the creation of the device, you won't have to reset the device when the window size changes. Just destroy the swapchain and create a new one with a back buffer of the correct size.

    Dort wird beschrieben wie es ohne Reset()-Aufruf gehen soll (leider blicke ich da nicht ganz durch). Wie *verwende* ich eine neue Swapchain nachdem ich mir eine gebaut habe?

    EDIT:

    Ich habe das mit dem Backbuffer auf Desktopgröße probiert. Das Ergebnis ist nicht ganz das was ich mir vorgestellt habe. Das ist etwas schwer zu beschreiben, deshalb hier ein Screenshot:

    http://img3.imagebanana.com/view/86qf62e4/resizeproblem.png

    bool Cd3d::Init(Cwindow *d3dwnd)
    {
    	this->interf = Direct3DCreate9(D3D_SDK_VERSION);
    	if (!this->interf)
    		return 0;
    
        ZeroMemory(&d3dpp, sizeof(d3dpp));
    
    	d3dpp.Windowed               = d3dwnd->windowed;
        d3dpp.SwapEffect             = D3DSWAPEFFECT_DISCARD;	// discard old frames
    	d3dpp.hDeviceWindow          = d3dwnd->Gethwnd();					// set the window to be used by Direct3D
        d3dpp.BackBufferFormat       = D3DFMT_X8R8G8B8;
    	d3dpp.BackBufferWidth        = GetSystemMetrics(SM_CXSCREEN);//clientrect.right;		
    	d3dpp.BackBufferHeight       = GetSystemMetrics(SM_CYSCREEN);//clientrect.bottom;
        d3dpp.EnableAutoDepthStencil = TRUE;					// automatically run the z-buffer for us
        d3dpp.AutoDepthStencilFormat = D3DFMT_D16;				// 16-bit pixel format for the z-buffer
    	//d3dpp.PresentationInterval   = D3DPRESENT_INTERVAL_IMMEDIATE;	// Vsync off
    
    	if (D3D_OK != this->interf->CreateDevice(	D3DADAPTER_DEFAULT,
    												D3DDEVTYPE_HAL,
    												d3dwnd->Gethwnd(),
    												//D3DCREATE_SOFTWARE_VERTEXPROCESSING,
    												D3DCREATE_HARDWARE_VERTEXPROCESSING,
    												&d3dpp,
    												&this->device))
    		return false;
    
    	this->device->SetRenderState(D3DRS_ZENABLE, TRUE);
    	this->device->SetRenderState(D3DRS_LIGHTING, FALSE);
    	//this->device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
    	this->device->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
    	this->device->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
    	this->device->SetSamplerState( 0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR );
    
    	RECT clientrect;
    	GetClientRect(d3dwnd->Gethwnd(), &clientrect);
    	this->device->GetViewport(&this->viewport);	// viewport is 0,0,1920,1200
    	this->viewport.Width  = clientrect.right;	// set viewport width of clientwindow
    	this->viewport.Height = clientrect.bottom;	// set viewport height of clientwindow
    	this->device->SetViewport(&this->viewport);	// set new viewport
    
    	return true;
    }
    

    Wenn ich den Viewport auf die vollen 1920x1200 setz sieht es so aus als würde es funktionieren, aber würde ich ja unnötig eine zu höhe Auflösung rendern ... das möchte ich nicht.

    EDIT2:
    Habe es hinbekommen über eine zweite Swapchain. Atm keine Lust den Code aufzuräumen deswegen meine komplette class als Lösung. Der Backbuffer vom Device ist 1x1 Pixel -> platzsparend und der Backbuffer der Swapchain jeweils an die Client-Fenstergröße angepaßt. Der Code ist jedoch noch nicht Fullscreen-tauglich, weil (quote DX SDK):

    Note that any given device can support only one full-screen swap chain.

    Es sind mit hoher Warscheinlichkeit noch einige Fehler da drin, wer was findet darf gerne meckern und verbessern ...

    #ifndef __DIRECT3D9_H_
    #define __DIRECT3D9_H_
    
    #include <d3d9.h>
    #include <d3dx9.h>
    #include "window.h"
    
    class Cd3d
    {
    	LPDIRECT3D9				interf;			// Direct3D interface pointer, COM Object
    	bool					wireframemode;
        D3DPRESENT_PARAMETERS	fakeparam;		// init fake params for 1x1 backbuffer
    	LPDIRECT3DSURFACE9		fakebackbuffer;	// 1x1 backbuffer -> low memory usage
    	LPDIRECT3DSURFACE9      fakedepthbuffer;
    	D3DPRESENT_PARAMETERS	scparam;		// swapchain param /w real backbuffer
    	LPDIRECT3DSWAPCHAIN9	swapchain;
    	LPDIRECT3DSURFACE9		scbackbuffer;	// real backbuffer
    	LPDIRECT3DSURFACE9      scdepthbuffer;	// real depthbuffer
    public:
    	LPDIRECT3DDEVICE9		device;			// device class pointer
    	D3DVIEWPORT9			viewport;
    	D3DXMATRIX				worldmatrix;
        D3DXMATRIX				viewmatrix;
        D3DXMATRIX				projmatrix;
    	Cd3d();									// Constructor
    	~Cd3d();								// Destructor
    	bool Init(Cwindow *d3dwnd);
    	void Render();
    	void ToggleWireframeMode();
    	void Resize(int width, int height);
    };
    
    extern Cd3d d3d;
    
    #endif//__DIRECT3D9_H_
    
    #include "direct3D9.h"
    #include "terrain.h"
    #include "camera.h"
    
    #pragma comment (lib, "d3d9.lib")
    #pragma comment (lib, "d3dx9.lib")
    
    #define NEAR_VIEWPLANE 0.1f
    #define FAR_VIEWPLANE 500.0f
    
    Cd3d d3d;
    
    Cd3d::Cd3d()					// Constructor
    {
    	// private:
    	ZeroMemory(&this->interf,sizeof(LPDIRECT3D9));
    	this->wireframemode=false;
    	ZeroMemory(&this->fakeparam,sizeof(D3DPRESENT_PARAMETERS));
    	ZeroMemory(&this->fakebackbuffer,sizeof(LPDIRECT3DSURFACE9));
    	ZeroMemory(&this->fakedepthbuffer,sizeof(LPDIRECT3DSURFACE9));
    	ZeroMemory(&this->scparam,sizeof(D3DPRESENT_PARAMETERS));
    	ZeroMemory(&this->swapchain,sizeof(LPDIRECT3DSWAPCHAIN9));
    	ZeroMemory(&this->scbackbuffer,sizeof(LPDIRECT3DSURFACE9));
    	ZeroMemory(&this->scdepthbuffer,sizeof(LPDIRECT3DSURFACE9));
    	// public:
    	ZeroMemory(&this->device,sizeof(LPDIRECT3DDEVICE9));
    	ZeroMemory(&this->viewport,sizeof(D3DVIEWPORT9));
    	ZeroMemory(&this->worldmatrix,sizeof(D3DXMATRIX));
    	ZeroMemory(&this->viewmatrix,sizeof(D3DXMATRIX));
    	ZeroMemory(&this->projmatrix,sizeof(D3DXMATRIX));
    }
    
    Cd3d::~Cd3d()					// Destructor
    {
    	if (this->fakebackbuffer)	this->fakebackbuffer->Release();
    	if (this->fakedepthbuffer)	this->fakedepthbuffer->Release();
    	if (this->scdepthbuffer)	this->scdepthbuffer->Release();
    	if (this->scbackbuffer)		this->scbackbuffer->Release();
    	if (this->swapchain)		this->swapchain->Release();
    	if (this->device)			this->device->Release();	// close and release the 3D device
    	if (this->interf)			this->interf->Release();	// close and release Direct3D
    }
    
    bool Cd3d::Init(Cwindow *d3dwnd)
    {
    	this->interf = Direct3DCreate9(D3D_SDK_VERSION);
    	if (!this->interf)
    		return 0;
    
    	RECT clientrect;
    	GetClientRect(d3dwnd->Gethwnd(), &clientrect);
    
    	this->fakeparam.Windowed               = d3dwnd->windowed;
        this->fakeparam.SwapEffect             = /*D3DSWAPEFFECT_COPY;*/D3DSWAPEFFECT_DISCARD;	// discard old frames
    	this->fakeparam.hDeviceWindow          = d3dwnd->Gethwnd();					// set the window to be used by Direct3D
        this->fakeparam.BackBufferFormat       = D3DFMT_X8R8G8B8;
    	this->fakeparam.BackBufferWidth        = 1;//GetSystemMetrics(SM_CXSCREEN);//clientrect.right;		
    	this->fakeparam.BackBufferHeight       = 1;//GetSystemMetrics(SM_CYSCREEN);//clientrect.bottom;
        this->fakeparam.EnableAutoDepthStencil = TRUE;					// automatically run the z-buffer for us
        this->fakeparam.AutoDepthStencilFormat = D3DFMT_D16;				// 16-bit pixel format for the z-buffer
    	this->fakeparam.PresentationInterval   = D3DPRESENT_INTERVAL_IMMEDIATE;	// Vsync off
    
    	if (D3D_OK != this->interf->CreateDevice(	D3DADAPTER_DEFAULT,
    												D3DDEVTYPE_HAL,
    												d3dwnd->Gethwnd(),
    												//D3DCREATE_SOFTWARE_VERTEXPROCESSING,
    												D3DCREATE_HARDWARE_VERTEXPROCESSING,
    												&this->fakeparam,
    												&this->device))
    		return false;
    
    	this->device->SetRenderState(D3DRS_ZENABLE, TRUE);
    	this->device->SetRenderState(D3DRS_LIGHTING, FALSE);
    	//this->device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
    	this->device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
    	this->device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
    	this->device->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
    
    	// Create swapchain
    	memcpy_s(&this->scparam,sizeof(D3DPRESENT_PARAMETERS),&this->fakeparam,sizeof(D3DPRESENT_PARAMETERS));
    	this->scparam.BackBufferWidth  = clientrect.right;
    	this->scparam.BackBufferHeight = clientrect.bottom;
    	this->device->CreateAdditionalSwapChain(&this->scparam,&this->swapchain);
    	// Create scbackbuffer
    	this->swapchain->GetBackBuffer(0,D3DBACKBUFFER_TYPE_MONO,&this->scbackbuffer);
    	// Create scdepthbuffer
    	this->device->CreateDepthStencilSurface(clientrect.right,clientrect.bottom,D3DFMT_D24S8,D3DMULTISAMPLE_NONE,0,false,&this->scdepthbuffer,0);
    	// backup backbuffer and depth buffer from device for reset/resize usage
    	this->device->GetBackBuffer(0,0,D3DBACKBUFFER_TYPE_MONO,&this->fakebackbuffer);
    	this->device->GetDepthStencilSurface(&this->fakedepthbuffer);
    	// set rendertarget to scbackbuffer and scdepthbuffer
    	this->device->SetRenderTarget(0,this->scbackbuffer);
    	this->device->SetDepthStencilSurface(this->scdepthbuffer);
    
    	// set viewport
    	this->viewport.X      = 0;
    	this->viewport.Y      = 0;
    	this->viewport.Width  = clientrect.right;
    	this->viewport.Height = clientrect.bottom;
    	this->viewport.MinZ   = 0.0f;
    	this->viewport.MaxZ   = 1.0f;
    	this->device->SetViewport(&this->viewport);
    	return true;
    }
    
    void Cd3d::Render()
    {
        this->device->Clear(0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 40, 100), 1.0f, 0);
    	//this->device->Clear(0, NULL, , D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
        this->device->BeginScene();
    
        // SET UP THE PIPELINE
    	//D3DXMatrixTranslation(&this->worldmatrix, 0.0f, 0.0f, 0.0f);
        //this->device->SetTransform(D3DTS_WORLD, &this->worldmatrix);
    
    	camera.CalculateViewMatrix(&this->viewmatrix);
        this->device->SetTransform(D3DTS_VIEW, &this->viewmatrix);
    
        D3DXMatrixPerspectiveFovLH(&this->projmatrix,
                                   D3DXToRadian(45),    // the horizontal field of view
    							   (FLOAT)this->viewport.Width / (FLOAT)this->viewport.Height, // aspect ratio
                                   NEAR_VIEWPLANE,		// the near view-plane
                                   FAR_VIEWPLANE);		// the far view-plane
        this->device->SetTransform(D3DTS_PROJECTION, &this->projmatrix);    // set the projection
    
    	terrain.RenderTerrain();
    	//if (control.GetMouseLeftBtn())
    	//	terrain.Pick();
    
        this->device->EndScene();
        //this->device->Present(NULL, NULL, NULL, NULL);   // displays the created frame on the screen
    	this->swapchain->Present(0,0,0,0,0);
    }
    
    void Cd3d::ToggleWireframeMode()
    {
    	this->wireframemode=!this->wireframemode;
    	if (this->wireframemode)
    		this->device->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);
    	else
    		this->device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
    }
    
    void Cd3d::Resize(int width, int height)
    {
    	if (!this->device)
    		return;
    	if (height<1)				// prevent divide by zero
    		height=1;
    
    	D3DXMatrixPerspectiveFovLH(&this->projmatrix, D3DXToRadian(45), (float)width/(float)height, NEAR_VIEWPLANE, FAR_VIEWPLANE);
    	this->device->SetTransform(D3DTS_PROJECTION, &this->projmatrix);
    	D3DXMatrixIdentity(&this->projmatrix);
    
    	// delete old swapchain
    	this->scdepthbuffer->Release();
    	this->scbackbuffer->Release();
    	this->swapchain->Release();
    	// update backbuffer parameters
    	this->scparam.BackBufferWidth  = width;
    	this->scparam.BackBufferHeight = height;
    	// create new swapchain
    	this->device->CreateAdditionalSwapChain(&this->scparam,&this->swapchain);
    	this->swapchain->GetBackBuffer(0,D3DBACKBUFFER_TYPE_MONO,&this->scbackbuffer);		// create scbackbuffer
    	this->device->CreateDepthStencilSurface(width,height,D3DFMT_D24S8,D3DMULTISAMPLE_NONE,0,false,&this->scdepthbuffer,0);	// create scdepthbuffer
    	// set rendertarget to scbackbuffer and scdepthbuffer
    	this->device->SetRenderTarget(0,this->scbackbuffer);
    	this->device->SetDepthStencilSurface(this->scdepthbuffer);
    }
    


  • Ich habe das mit dem Backbuffer auf Desktopgröße probiert. Das Ergebnis ist nicht ganz das was ich mir vorgestellt habe. Das ist etwas schwer zu beschreiben, deshalb hier ein Screenshot:

    In das Hauptfenster muss ein Childwindow, welches die selbe Größe hat wie der Backbuffer. Zeichnen tust du dann ins Childfenster.

    Schade ist nur das man damit sicherlich etwas video-RAM verschwendet... obwohl das wohl bei den heutigen Killergrafikkarten wohl kaum noch eine Rolle spielt.

    Ich glaube die 3kb werden dir schon nicht fehlen und es ist nicht direkt eine Speicher verschwendung. Man muss es so sehen: Oft steht man or sder Frage ob man lieber mehr Spiecher verbraucht und dafür nicht mehr so viel gerechnet werden muss oder ob man lieber mehr rechnet und dafür weniger speicher verbraucht 🙂



  • Ich glaube die 3kb werden dir schon nicht fehlen und es ist nicht direkt eine Speicher verschwendung.
    

    Das sind womgl. etwas mehr als nur Kilobytes. Man muss bedenken das der Z-BUffer auch noch dazu kommt.

    Ich habs an einem Beispiel durchgerechnet:

    BackBufferFormat       = D3DFMT_X8R8G8B8
    AutoDepthStencilFormat = D3DFMT_D16
    Backbuffer Auflösung   = 1920x1200      (Desktopauflösung)
    Fenster Auflösung      = 1024x768       (Viewport)
    ----------------------------------------
    Backbuffer Pixel       = 2.304.000
    Viewport Pixel         =   786.432
    ----------------------------------------
    Backbuffer Größe       = 9216000 bytes  = 9000 kilobytes = 9 MB   (32bit/4bytes pro pixel)
    Z-Buffer Größe         = 4608000 bytes  = 4500 kilobytes = 4,5 MB (16bit/2bytes pro pixel)
    
    davon *verwendet* -> Viewport Pixel:
    BackBuffer             = 3145728 bytes  = 3000 kilobytes = 3 MB   (32bit/4bytes pro pixel)
    Z-Buffer               = 1572864 bytes  = 1536 kilobytes = 1,5 MB (16bit/2bytes pro pixel)
    
    ----------------------------------------
    BackBuffer + Z-Buffer    13,5 MB
    davon verwendet        -  4,5 MB
    ----------------------------------------
    verschwendeter V-RAM   =  9,0 MB !!!
    

    Die Swapchain-Methode verschwendet mit einem 1x1 fake Backbuffer und Z-Buffer lediglich 6 bytes. Zudem werde ich die Vermuting nicht los das ansonnten die GPU die vollen 1920x1200 Pixel berechnet ... auch wenn der Viewport kleiner ist - aber ich weiß es nicht. Das wäre dann ganz schlecht -> VRAM verschwendet und GPU mehr belastet.



  • Also keiner den ich kenne hat weniger als 512MB, ich hab selber 1GB RAM auf meienr Karte. Die Frage ist fallen die 9MB überhaupt ins Gewicht? schiebst du heir wirklich solche Mengen Daten durch die Gegend das du den VRAM auch nur ansatzweise soweit belegen kanst das dir das am Ende fehlt?


Anmelden zum Antworten