没有设备的 DirectX9 window
DirectX9 without device window
我一直认为我们需要创建一个window才能创建一个DirectX9 Device。实际上,这就是我对 CreateDevice 方法中 official documentation 的理解:
hFocusWindow [in] Type: HWND (...) For windowed mode, this parameter may be NULL only if the hDeviceWindow member of pPresentationParameters is set to a valid, non-NULL value.
现在,我试了一下,似乎下面的代码对我有用(例如,我得到了一个有效的设备指针,我可以用它来做 soem 渲染目标渲染,即使我没有随时提供任何 window 句柄):
#include <windows.h>
#include <stdio.h>
#include <iostream>
#include <sstream>
#include <chrono>
#include <thread>
#if defined(_MSC_VER)
#pragma warning(disable : 4005)
#endif
#include <d3d9.h>
#include <d3dx9.h>
#include <DxErr.h>
int (WINAPIV * __vsnprintf)(char *, size_t, const char*, va_list) = _vsnprintf;
#define DEBUG_MSG(msg) std::cout << msg << std::endl;
#define ERROR_MSG(msg) std::cout << "[ERROR] " << msg << std::endl;
#define THROW_MSG(msg) { \
std::ostringstream os; \
os.precision(9); \
os << std::fixed << "[FATAL] " << msg << " (at " << __FILE__ <<":"<<__LINE__<<")"; \
DEBUG_MSG(os.str()); \
throw std::runtime_error(os.str()); \
}
#define CHECK(cond,msg) if(!(cond)) { THROW_MSG(msg); return; }
#define CHECK_RET(cond,ret,msg) if(!(cond)) { THROW_MSG(msg); return ret; }
#define CHECK_RESULT(val,msg) { HRESULT hr = (val); if(FAILED(hr)) { THROW_MSG(msg << ", err=" << DXGetErrorString(hr) << ", desc=" << DXGetErrorDescription(hr)); return; } }
#define CHECK_RESULT_RET(val,ret,msg) { HRESULT hr = (val); if(FAILED(hr)) { THROW_MSG(msg << ", err=" << DXGetErrorString(hr) << ", desc=" << DXGetErrorDescription(hr)); return ret; } }
#define SAFERELEASE(x) if(x) { x->Release(); x = NULL; }
IDirect3D9Ex* d3dEx = nullptr;
IDirect3DDevice9* deviceEx = nullptr;
IDirect3DSurface9* renderSurface1 = nullptr;
HANDLE renderSurfaceHandle1 = nullptr;
IDirect3DSurface9* renderSurface2 = nullptr;
HANDLE renderSurfaceHandle2 = nullptr;
bool testCycle() {
CHECK_RET(d3dEx==nullptr, false,"Invalid D3D context.");
CHECK_RET(deviceEx==nullptr, false,"Invalid D3D device.");
// Create dedicated device:
DEBUG_MSG("Creating Direct3D9Ex context");
CHECK_RESULT_RET(Direct3DCreate9Ex(D3D_SDK_VERSION, (IDirect3D9Ex **)&d3dEx),
false, "Cannot create Direct3D9Ex context" );
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = TRUE;
d3dpp.BackBufferCount = 1;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferWidth = 200;
d3dpp.BackBufferHeight = 200;
d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8;
d3dpp.hDeviceWindow = NULL;
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
DEBUG_MSG("Creating Device9Ex.");
CHECK_RESULT_RET(d3dEx->CreateDevice(0, D3DDEVTYPE_HAL, NULL,
D3DCREATE_MULTITHREADED | D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_PUREDEVICE,
&d3dpp, &deviceEx), false, "Cannot create Device9Ex.");
DEBUG_MSG("Setting lighting render state");
CHECK_RESULT_RET(deviceEx->SetRenderState( D3DRS_LIGHTING, FALSE ),
false, "Cannot set lighting render state.");
int width = 512;
int height = 256;
DEBUG_MSG("Creating SDI render surfaces of size "<<width<<"x"<<height);
CHECK_RESULT_RET(deviceEx->CreateRenderTarget(width, height,
D3DFMT_A8R8G8B8, D3DMULTISAMPLE_NONE, 0,
TRUE, &renderSurface1, &renderSurfaceHandle1),
false, "Cannot create Render surface 1 for SDIOutput");
CHECK_RESULT_RET(deviceEx->CreateRenderTarget(width, height,
D3DFMT_A8R8G8B8, D3DMULTISAMPLE_NONE, 0,
TRUE, &renderSurface2, &renderSurfaceHandle2),
false, "Cannot create Render surface 2 for SDIOutput");
CHECK_RET(renderSurfaceHandle1 != nullptr, false, "Invalid shared handle for surface 1");
CHECK_RET(renderSurfaceHandle2 != nullptr, false, "Invalid shared handle for surface 2");
DEBUG_MSG("Initial render of SDI surface 1")
CHECK_RESULT_RET(deviceEx->SetRenderTarget(0, renderSurface1), false, "Cannot set render target 1");
CHECK_RESULT_RET(deviceEx->BeginScene(),false, "Cannot begin scene 1");
CHECK_RESULT_RET(deviceEx->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(255,0,0), 1.0f, 0), false, "Cannot clear scene 1");
CHECK_RESULT_RET(deviceEx->EndScene(), false, "Cannot end scene 1");
DEBUG_MSG("Initial render of SDI surface 2")
CHECK_RESULT_RET(deviceEx->SetRenderTarget(0, renderSurface2), false, "Cannot set render target 2");
CHECK_RESULT_RET(deviceEx->BeginScene(), false, "Cannot begin scene 2");
CHECK_RESULT_RET(deviceEx->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,255,0), 1.0f, 0), false, "Cannot clear scene 2");
CHECK_RESULT_RET(deviceEx->EndScene(), false, "Cannot end scene 2");
DEBUG_MSG("Test cycle performed successfully.")
return true;
}
void releaseResources()
{
DEBUG_MSG("Releasing resources.");
SAFERELEASE(renderSurface1);
SAFERELEASE(renderSurface2);
renderSurfaceHandle1 = nullptr;
renderSurfaceHandle2 = nullptr;
SAFERELEASE(deviceEx);
SAFERELEASE(d3dEx);
}
#ifdef WINDOWS_APP
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
#else
int main(int argc, char *argv[]) {
#endif
testCycle();
releaseResources();
DEBUG_MSG("Exiting.");
return 0;
}
那么,有人对此有解释的开头吗,是文档不正确还是我遗漏了一点? :-)
文档告诉您必须做什么,但没有具体说明如果您违反规则可能会发生什么。
它似乎对您有用,但您有没有费心在 multi-monitor 设置上测试它?或者当另一个 DirectX 应用程序是 运行 时?这只是两个随机变化。
文档已经告诉你发生了什么:
每 MSDN
For a windowed-mode application, this handle will be the default target window for Present. If this handle is NULL, the focus window will be taken.
所以它使用的是您调用 Present
时 window 恰好处于焦点的任何内容。
请注意,此歧义已通过 DXGI 为 Direct3D 10 或更高版本修复。 window 句柄仅在创建交换链时需要,并且 Direct3D 设备可以独立于交换链创建(除非您使用有点笨拙的辅助函数 D3D11CreateDeviceAndSwapChain
,它同时执行两个操作).参见 Anatomy of Direct3D 11 Create Device。
我一直认为我们需要创建一个window才能创建一个DirectX9 Device。实际上,这就是我对 CreateDevice 方法中 official documentation 的理解:
hFocusWindow [in] Type: HWND (...) For windowed mode, this parameter may be NULL only if the hDeviceWindow member of pPresentationParameters is set to a valid, non-NULL value.
现在,我试了一下,似乎下面的代码对我有用(例如,我得到了一个有效的设备指针,我可以用它来做 soem 渲染目标渲染,即使我没有随时提供任何 window 句柄):
#include <windows.h>
#include <stdio.h>
#include <iostream>
#include <sstream>
#include <chrono>
#include <thread>
#if defined(_MSC_VER)
#pragma warning(disable : 4005)
#endif
#include <d3d9.h>
#include <d3dx9.h>
#include <DxErr.h>
int (WINAPIV * __vsnprintf)(char *, size_t, const char*, va_list) = _vsnprintf;
#define DEBUG_MSG(msg) std::cout << msg << std::endl;
#define ERROR_MSG(msg) std::cout << "[ERROR] " << msg << std::endl;
#define THROW_MSG(msg) { \
std::ostringstream os; \
os.precision(9); \
os << std::fixed << "[FATAL] " << msg << " (at " << __FILE__ <<":"<<__LINE__<<")"; \
DEBUG_MSG(os.str()); \
throw std::runtime_error(os.str()); \
}
#define CHECK(cond,msg) if(!(cond)) { THROW_MSG(msg); return; }
#define CHECK_RET(cond,ret,msg) if(!(cond)) { THROW_MSG(msg); return ret; }
#define CHECK_RESULT(val,msg) { HRESULT hr = (val); if(FAILED(hr)) { THROW_MSG(msg << ", err=" << DXGetErrorString(hr) << ", desc=" << DXGetErrorDescription(hr)); return; } }
#define CHECK_RESULT_RET(val,ret,msg) { HRESULT hr = (val); if(FAILED(hr)) { THROW_MSG(msg << ", err=" << DXGetErrorString(hr) << ", desc=" << DXGetErrorDescription(hr)); return ret; } }
#define SAFERELEASE(x) if(x) { x->Release(); x = NULL; }
IDirect3D9Ex* d3dEx = nullptr;
IDirect3DDevice9* deviceEx = nullptr;
IDirect3DSurface9* renderSurface1 = nullptr;
HANDLE renderSurfaceHandle1 = nullptr;
IDirect3DSurface9* renderSurface2 = nullptr;
HANDLE renderSurfaceHandle2 = nullptr;
bool testCycle() {
CHECK_RET(d3dEx==nullptr, false,"Invalid D3D context.");
CHECK_RET(deviceEx==nullptr, false,"Invalid D3D device.");
// Create dedicated device:
DEBUG_MSG("Creating Direct3D9Ex context");
CHECK_RESULT_RET(Direct3DCreate9Ex(D3D_SDK_VERSION, (IDirect3D9Ex **)&d3dEx),
false, "Cannot create Direct3D9Ex context" );
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = TRUE;
d3dpp.BackBufferCount = 1;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferWidth = 200;
d3dpp.BackBufferHeight = 200;
d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8;
d3dpp.hDeviceWindow = NULL;
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
DEBUG_MSG("Creating Device9Ex.");
CHECK_RESULT_RET(d3dEx->CreateDevice(0, D3DDEVTYPE_HAL, NULL,
D3DCREATE_MULTITHREADED | D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_PUREDEVICE,
&d3dpp, &deviceEx), false, "Cannot create Device9Ex.");
DEBUG_MSG("Setting lighting render state");
CHECK_RESULT_RET(deviceEx->SetRenderState( D3DRS_LIGHTING, FALSE ),
false, "Cannot set lighting render state.");
int width = 512;
int height = 256;
DEBUG_MSG("Creating SDI render surfaces of size "<<width<<"x"<<height);
CHECK_RESULT_RET(deviceEx->CreateRenderTarget(width, height,
D3DFMT_A8R8G8B8, D3DMULTISAMPLE_NONE, 0,
TRUE, &renderSurface1, &renderSurfaceHandle1),
false, "Cannot create Render surface 1 for SDIOutput");
CHECK_RESULT_RET(deviceEx->CreateRenderTarget(width, height,
D3DFMT_A8R8G8B8, D3DMULTISAMPLE_NONE, 0,
TRUE, &renderSurface2, &renderSurfaceHandle2),
false, "Cannot create Render surface 2 for SDIOutput");
CHECK_RET(renderSurfaceHandle1 != nullptr, false, "Invalid shared handle for surface 1");
CHECK_RET(renderSurfaceHandle2 != nullptr, false, "Invalid shared handle for surface 2");
DEBUG_MSG("Initial render of SDI surface 1")
CHECK_RESULT_RET(deviceEx->SetRenderTarget(0, renderSurface1), false, "Cannot set render target 1");
CHECK_RESULT_RET(deviceEx->BeginScene(),false, "Cannot begin scene 1");
CHECK_RESULT_RET(deviceEx->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(255,0,0), 1.0f, 0), false, "Cannot clear scene 1");
CHECK_RESULT_RET(deviceEx->EndScene(), false, "Cannot end scene 1");
DEBUG_MSG("Initial render of SDI surface 2")
CHECK_RESULT_RET(deviceEx->SetRenderTarget(0, renderSurface2), false, "Cannot set render target 2");
CHECK_RESULT_RET(deviceEx->BeginScene(), false, "Cannot begin scene 2");
CHECK_RESULT_RET(deviceEx->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,255,0), 1.0f, 0), false, "Cannot clear scene 2");
CHECK_RESULT_RET(deviceEx->EndScene(), false, "Cannot end scene 2");
DEBUG_MSG("Test cycle performed successfully.")
return true;
}
void releaseResources()
{
DEBUG_MSG("Releasing resources.");
SAFERELEASE(renderSurface1);
SAFERELEASE(renderSurface2);
renderSurfaceHandle1 = nullptr;
renderSurfaceHandle2 = nullptr;
SAFERELEASE(deviceEx);
SAFERELEASE(d3dEx);
}
#ifdef WINDOWS_APP
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
#else
int main(int argc, char *argv[]) {
#endif
testCycle();
releaseResources();
DEBUG_MSG("Exiting.");
return 0;
}
那么,有人对此有解释的开头吗,是文档不正确还是我遗漏了一点? :-)
文档告诉您必须做什么,但没有具体说明如果您违反规则可能会发生什么。
它似乎对您有用,但您有没有费心在 multi-monitor 设置上测试它?或者当另一个 DirectX 应用程序是 运行 时?这只是两个随机变化。
文档已经告诉你发生了什么:
每 MSDN
For a windowed-mode application, this handle will be the default target window for Present. If this handle is NULL, the focus window will be taken.
所以它使用的是您调用 Present
时 window 恰好处于焦点的任何内容。
请注意,此歧义已通过 DXGI 为 Direct3D 10 或更高版本修复。 window 句柄仅在创建交换链时需要,并且 Direct3D 设备可以独立于交换链创建(除非您使用有点笨拙的辅助函数 D3D11CreateDeviceAndSwapChain
,它同时执行两个操作).参见 Anatomy of Direct3D 11 Create Device。