direct3d.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. #undef interface
  2. #define interface struct
  3. #include <d3d9.h>
  4. #undef interface
  5. static LRESULT CALLBACK VideoDirect3D9_WindowProcedure(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
  6. if(msg == WM_SYSKEYDOWN && wparam == VK_F4) return false;
  7. return DefWindowProc(hwnd, msg, wparam, lparam);
  8. }
  9. struct VideoDirect3D : VideoDriver {
  10. VideoDirect3D& self = *this;
  11. VideoDirect3D(Video& super) : VideoDriver(super) { construct(); }
  12. ~VideoDirect3D() { destruct(); }
  13. auto create() -> bool override {
  14. return initialize();
  15. }
  16. auto driver() -> string override { return "Direct3D 9.0"; }
  17. auto ready() -> bool override { return _ready; }
  18. auto hasFullScreen() -> bool override { return true; }
  19. auto hasMonitor() -> bool override { return true; }
  20. auto hasExclusive() -> bool override { return true; }
  21. auto hasContext() -> bool override { return true; }
  22. auto hasBlocking() -> bool override { return true; }
  23. auto hasShader() -> bool override { return true; }
  24. auto setFullScreen(bool fullScreen) -> bool override { return initialize(); }
  25. auto setMonitor(string monitor) -> bool override { return initialize(); }
  26. auto setExclusive(bool exclusive) -> bool override { return initialize(); }
  27. auto setContext(uintptr context) -> bool override { return initialize(); }
  28. auto setBlocking(bool blocking) -> bool override { return true; }
  29. auto setShader(string shader) -> bool override { return updateFilter(); }
  30. auto focused() -> bool override {
  31. if(self.fullScreen && self.exclusive) return true;
  32. auto focused = GetFocus();
  33. return _context == focused || IsChild(_context, focused);
  34. }
  35. auto clear() -> void override {
  36. if(_lost && !recover()) return;
  37. D3DSURFACE_DESC surfaceDescription;
  38. _texture->GetLevelDesc(0, &surfaceDescription);
  39. _texture->GetSurfaceLevel(0, &_surface);
  40. if(_surface) {
  41. D3DLOCKED_RECT lockedRectangle;
  42. _surface->LockRect(&lockedRectangle, 0, D3DLOCK_NOSYSLOCK | D3DLOCK_DISCARD);
  43. memory::fill(lockedRectangle.pBits, lockedRectangle.Pitch * surfaceDescription.Height);
  44. _surface->UnlockRect();
  45. _surface->Release();
  46. _surface = nullptr;
  47. }
  48. //clear primary display and all backbuffers
  49. for(uint n : range(3)) {
  50. _device->Clear(0, 0, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0x00, 0x00, 0x00), 1.0f, 0);
  51. _device->Present(0, 0, 0, 0);
  52. }
  53. }
  54. auto size(uint& width, uint& height) -> void {
  55. if(_lost && !recover()) return;
  56. RECT rectangle;
  57. GetClientRect(_context, &rectangle);
  58. width = rectangle.right - rectangle.left;
  59. height = rectangle.bottom - rectangle.top;
  60. //if output size changed, driver must be re-initialized.
  61. //failure to do so causes scaling issues on some video drivers.
  62. if(width != _windowWidth || height != _windowHeight) initialize();
  63. }
  64. auto acquire(uint32_t*& data, uint& pitch, uint width, uint height) -> bool override {
  65. if(_lost && !recover()) return false;
  66. uint windowWidth, windowHeight;
  67. size(windowWidth, windowHeight);
  68. if(width != _inputWidth || height != _inputHeight) {
  69. resize(_inputWidth = width, _inputHeight = height);
  70. }
  71. D3DSURFACE_DESC surfaceDescription;
  72. _texture->GetLevelDesc(0, &surfaceDescription);
  73. _texture->GetSurfaceLevel(0, &_surface);
  74. D3DLOCKED_RECT lockedRectangle;
  75. _surface->LockRect(&lockedRectangle, 0, D3DLOCK_NOSYSLOCK | D3DLOCK_DISCARD);
  76. pitch = lockedRectangle.Pitch;
  77. return data = (uint32_t*)lockedRectangle.pBits;
  78. }
  79. auto release() -> void override {
  80. _surface->UnlockRect();
  81. _surface->Release();
  82. _surface = nullptr;
  83. }
  84. auto output(uint width, uint height) -> void override {
  85. if(_lost && !recover()) return;
  86. if(!width) width = _windowWidth;
  87. if(!height) height = _windowHeight;
  88. _device->BeginScene();
  89. //center output within window
  90. uint x = (_windowWidth - width) / 2;
  91. uint y = (_windowHeight - height) / 2;
  92. setVertex(0, 0, _inputWidth, _inputHeight, _textureWidth, _textureHeight, x, y, width, height);
  93. _device->SetTexture(0, _texture);
  94. _device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
  95. _device->EndScene();
  96. if(self.blocking) {
  97. D3DRASTER_STATUS status;
  98. while(true) { //wait for a previous vblank to finish, if necessary
  99. _device->GetRasterStatus(0, &status);
  100. if(!status.InVBlank) break;
  101. }
  102. while(true) { //wait for next vblank to begin
  103. _device->GetRasterStatus(0, &status);
  104. if(status.InVBlank) break;
  105. }
  106. }
  107. if(_device->Present(0, 0, 0, 0) == D3DERR_DEVICELOST) _lost = true;
  108. }
  109. private:
  110. auto construct() -> void {
  111. WNDCLASS windowClass{};
  112. windowClass.cbClsExtra = 0;
  113. windowClass.cbWndExtra = 0;
  114. windowClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
  115. windowClass.hCursor = LoadCursor(0, IDC_ARROW);
  116. windowClass.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
  117. windowClass.hInstance = GetModuleHandle(0);
  118. windowClass.lpfnWndProc = VideoDirect3D9_WindowProcedure;
  119. windowClass.lpszClassName = L"VideoDirect3D9_Window";
  120. windowClass.lpszMenuName = 0;
  121. windowClass.style = CS_HREDRAW | CS_VREDRAW;
  122. RegisterClass(&windowClass);
  123. }
  124. auto destruct() -> void {
  125. terminate();
  126. }
  127. auto recover() -> bool {
  128. if(!_device) return false;
  129. if(_lost) {
  130. if(_vertexBuffer) { _vertexBuffer->Release(); _vertexBuffer = nullptr; }
  131. if(_surface) { _surface->Release(); _surface = nullptr; }
  132. if(_texture) { _texture->Release(); _texture = nullptr; }
  133. if(_device->Reset(&_presentation) != D3D_OK) return false;
  134. }
  135. _lost = false;
  136. _device->SetDialogBoxMode(false);
  137. _device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
  138. _device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
  139. _device->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
  140. _device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
  141. _device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
  142. _device->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
  143. _device->SetRenderState(D3DRS_LIGHTING, false);
  144. _device->SetRenderState(D3DRS_ZENABLE, false);
  145. _device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
  146. _device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
  147. _device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
  148. _device->SetRenderState(D3DRS_ALPHABLENDENABLE, false);
  149. _device->SetVertexShader(nullptr);
  150. _device->SetFVF(D3DFVF_XYZRHW | D3DFVF_TEX1);
  151. _device->CreateVertexBuffer(sizeof(Vertex) * 4, _vertexUsage, D3DFVF_XYZRHW | D3DFVF_TEX1,
  152. (D3DPOOL)_vertexPool, &_vertexBuffer, nullptr);
  153. _textureWidth = 0;
  154. _textureHeight = 0;
  155. resize(_inputWidth = 256, _inputHeight = 256);
  156. updateFilter();
  157. clear();
  158. return true;
  159. }
  160. auto resize(uint width, uint height) -> void {
  161. if(_textureWidth >= width && _textureHeight >= height) return;
  162. _textureWidth = bit::round(max(width, _textureWidth));
  163. _textureHeight = bit::round(max(height, _textureHeight));
  164. if(_capabilities.MaxTextureWidth < _textureWidth || _capabilities.MaxTextureWidth < _textureHeight) return;
  165. if(_texture) _texture->Release();
  166. _device->CreateTexture(_textureWidth, _textureHeight, 1, _textureUsage, D3DFMT_X8R8G8B8,
  167. (D3DPOOL)_texturePool, &_texture, nullptr);
  168. }
  169. auto updateFilter() -> bool {
  170. if(!_device) return false;
  171. if(_lost && !recover()) return false;
  172. auto filter = self.shader == "Blur" ? D3DTEXF_LINEAR : D3DTEXF_POINT;
  173. _device->SetSamplerState(0, D3DSAMP_MINFILTER, filter);
  174. _device->SetSamplerState(0, D3DSAMP_MAGFILTER, filter);
  175. return true;
  176. }
  177. //(x,y) screen coordinates, in pixels
  178. //(u,v) texture coordinates, betweeen 0.0 (top, left) to 1.0 (bottom, right)
  179. auto setVertex(
  180. uint32_t px, uint32_t py, uint32_t pw, uint32_t ph,
  181. uint32_t tw, uint32_t th,
  182. uint32_t x, uint32_t y, uint32_t w, uint32_t h
  183. ) -> void {
  184. Vertex vertex[4];
  185. vertex[0].x = vertex[2].x = (double)(x - 0.5);
  186. vertex[1].x = vertex[3].x = (double)(x + w - 0.5);
  187. vertex[0].y = vertex[1].y = (double)(y - 0.5);
  188. vertex[2].y = vertex[3].y = (double)(y + h - 0.5);
  189. //Z-buffer and RHW are unused for 2D blit, set to normal values
  190. vertex[0].z = vertex[1].z = vertex[2].z = vertex[3].z = 0.0;
  191. vertex[0].rhw = vertex[1].rhw = vertex[2].rhw = vertex[3].rhw = 1.0;
  192. double rw = (double)w / (double)pw * (double)tw;
  193. double rh = (double)h / (double)ph * (double)th;
  194. vertex[0].u = vertex[2].u = (double)(px ) / rw;
  195. vertex[1].u = vertex[3].u = (double)(px + w) / rw;
  196. vertex[0].v = vertex[1].v = (double)(py ) / rh;
  197. vertex[2].v = vertex[3].v = (double)(py + h) / rh;
  198. LPDIRECT3DVERTEXBUFFER9* vertexPointer = nullptr;
  199. _vertexBuffer->Lock(0, sizeof(Vertex) * 4, (void**)&vertexPointer, 0);
  200. memory::copy<Vertex>(vertexPointer, vertex, 4);
  201. _vertexBuffer->Unlock();
  202. _device->SetStreamSource(0, _vertexBuffer, 0, sizeof(Vertex));
  203. }
  204. auto initialize() -> bool {
  205. terminate();
  206. if(!self.fullScreen && !self.context) return false;
  207. auto monitor = Video::monitor(self.monitor);
  208. _monitorX = monitor.x;
  209. _monitorY = monitor.y;
  210. _monitorWidth = monitor.width;
  211. _monitorHeight = monitor.height;
  212. _exclusive = self.exclusive && self.fullScreen;
  213. //Direct3D exclusive mode targets the primary monitor only
  214. if(_exclusive) {
  215. POINT point{0, 0}; //the primary monitor always starts at (0,0)
  216. HMONITOR monitor = MonitorFromPoint(point, MONITOR_DEFAULTTOPRIMARY);
  217. MONITORINFOEX info{};
  218. info.cbSize = sizeof(MONITORINFOEX);
  219. GetMonitorInfo(monitor, &info);
  220. _monitorX = info.rcMonitor.left;
  221. _monitorY = info.rcMonitor.top;
  222. _monitorWidth = info.rcMonitor.right - info.rcMonitor.left;
  223. _monitorHeight = info.rcMonitor.bottom - info.rcMonitor.top;
  224. }
  225. if(self.fullScreen) {
  226. _context = _window = CreateWindowEx(WS_EX_TOPMOST, L"VideoDirect3D9_Window", L"", WS_VISIBLE | WS_POPUP,
  227. _monitorX, _monitorY, _monitorWidth, _monitorHeight,
  228. nullptr, nullptr, GetModuleHandle(0), nullptr);
  229. } else {
  230. _context = (HWND)self.context;
  231. }
  232. RECT rectangle;
  233. GetClientRect(_context, &rectangle);
  234. _windowWidth = rectangle.right - rectangle.left;
  235. _windowHeight = rectangle.bottom - rectangle.top;
  236. _instance = Direct3DCreate9(D3D_SDK_VERSION);
  237. if(!_instance) return false;
  238. memory::fill(&_presentation, sizeof(_presentation));
  239. _presentation.Flags = D3DPRESENTFLAG_VIDEO;
  240. _presentation.SwapEffect = D3DSWAPEFFECT_DISCARD;
  241. _presentation.BackBufferCount = 1;
  242. _presentation.MultiSampleType = D3DMULTISAMPLE_NONE;
  243. _presentation.MultiSampleQuality = 0;
  244. _presentation.EnableAutoDepthStencil = false;
  245. _presentation.AutoDepthStencilFormat = D3DFMT_UNKNOWN;
  246. _presentation.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
  247. _presentation.hDeviceWindow = _context;
  248. _presentation.Windowed = !_exclusive;
  249. _presentation.BackBufferFormat = _exclusive ? D3DFMT_X8R8G8B8 : D3DFMT_UNKNOWN;
  250. _presentation.BackBufferWidth = _exclusive ? _monitorWidth : 0;
  251. _presentation.BackBufferHeight = _exclusive ? _monitorHeight : 0;
  252. _presentation.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
  253. if(_instance->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, _context,
  254. D3DCREATE_FPU_PRESERVE | D3DCREATE_SOFTWARE_VERTEXPROCESSING, &_presentation, &_device) != D3D_OK) {
  255. return false;
  256. }
  257. _device->GetDeviceCaps(&_capabilities);
  258. if(_capabilities.Caps2 & D3DCAPS2_DYNAMICTEXTURES) {
  259. _textureUsage = D3DUSAGE_DYNAMIC;
  260. _texturePool = D3DPOOL_DEFAULT;
  261. _vertexUsage = D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC;
  262. _vertexPool = D3DPOOL_DEFAULT;
  263. } else {
  264. _textureUsage = 0;
  265. _texturePool = D3DPOOL_MANAGED;
  266. _vertexUsage = D3DUSAGE_WRITEONLY;
  267. _vertexPool = D3DPOOL_MANAGED;
  268. }
  269. _lost = false;
  270. return _ready = recover();
  271. }
  272. auto terminate() -> void {
  273. _ready = false;
  274. if(_vertexBuffer) { _vertexBuffer->Release(); _vertexBuffer = nullptr; }
  275. if(_surface) { _surface->Release(); _surface = nullptr; }
  276. if(_texture) { _texture->Release(); _texture = nullptr; }
  277. if(_device) { _device->Release(); _device = nullptr; }
  278. if(_instance) { _instance->Release(); _instance = nullptr; }
  279. if(_window) { DestroyWindow(_window); _window = nullptr; }
  280. _context = nullptr;
  281. }
  282. struct Vertex {
  283. float x, y, z, rhw; //screen coordinates
  284. float u, v; //texture coordinates
  285. };
  286. bool _ready = false;
  287. HWND _window = nullptr;
  288. HWND _context = nullptr;
  289. LPDIRECT3D9 _instance = nullptr;
  290. LPDIRECT3DDEVICE9 _device = nullptr;
  291. LPDIRECT3DVERTEXBUFFER9 _vertexBuffer = nullptr;
  292. D3DPRESENT_PARAMETERS _presentation = {};
  293. D3DCAPS9 _capabilities = {};
  294. LPDIRECT3DTEXTURE9 _texture = nullptr;
  295. LPDIRECT3DSURFACE9 _surface = nullptr;
  296. bool _exclusive = false;
  297. bool _lost = true;
  298. uint _windowWidth;
  299. uint _windowHeight;
  300. uint _textureWidth;
  301. uint _textureHeight;
  302. int _monitorX;
  303. int _monitorY;
  304. int _monitorWidth;
  305. int _monitorHeight;
  306. uint _inputWidth;
  307. uint _inputHeight;
  308. uint32_t _textureUsage;
  309. uint32_t _texturePool;
  310. uint32_t _vertexUsage;
  311. uint32_t _vertexPool;
  312. };