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.htmlDort 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=401732If 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?