diff --git a/makefile b/makefile index 646e823..b9a2bc6 100644 --- a/makefile +++ b/makefile @@ -22,6 +22,7 @@ OBJS = \ ui/render/render.obj \ ui/render/gl.obj \ ui/render/glproc.obj \ + ui/render/d3d9.obj \ ui/render/d3d11.obj \ ui/render/d3d12.obj \ ui/render/ui.obj \ @@ -73,6 +74,7 @@ LIBS = \ ole32.lib \ oleaut32.lib \ opengl32.lib \ + d3d9.lib \ d3d11.lib \ d3d12.lib \ dxgi.lib \ diff --git a/parsec.dll b/parsec.dll index 4dc3396..90b0b60 100644 Binary files a/parsec.dll and b/parsec.dll differ diff --git a/ui/include/parsec-dso.h b/ui/include/parsec-dso.h index 1e658f0..6374279 100644 --- a/ui/include/parsec-dso.h +++ b/ui/include/parsec-dso.h @@ -109,6 +109,7 @@ static ParsecStatus ParsecInit(ParsecConfig *cfg, void *reserved, char *path, Pa GETPROC(ctx, ParsecHostSubmitCursor); GETPROC(ctx, ParsecHostSubmitRumble); GETPROC(ctx, ParsecHostGLSubmitFrame); + GETPROC(ctx, ParsecHostD3D9SubmitFrame); GETPROC(ctx, ParsecHostD3D11SubmitFrame); ctx->api.ParsecInit(PARSEC_VER, cfg, reserved, &ctx->ps); @@ -154,6 +155,7 @@ static ParsecStatus ParsecInit(ParsecConfig *cfg, void *reserved, char *path, Pa #define ParsecHostSubmitCursor(dso, ...) dso->api.ParsecHostSubmitCursor(dso->ps, __VA_ARGS__) #define ParsecHostSubmitRumble(dso, ...) dso->api.ParsecHostSubmitRumble(dso->ps, __VA_ARGS__) #define ParsecHostGLSubmitFrame(dso, ...) dso->api.ParsecHostGLSubmitFrame(dso->ps, __VA_ARGS__) +#define ParsecHostD3D9SubmitFrame(dso, ...) dso->api.ParsecHostD3D9SubmitFrame(dso->ps, __VA_ARGS__) #define ParsecHostD3D11SubmitFrame(dso, ...) dso->api.ParsecHostD3D11SubmitFrame(dso->ps, __VA_ARGS__) #if defined(_WIN32) diff --git a/ui/include/parsec.h b/ui/include/parsec.h index 2c61c71..84ff5f3 100644 --- a/ui/include/parsec.h +++ b/ui/include/parsec.h @@ -34,7 +34,7 @@ #define PARSEC_VER \ ((uint32_t) (((uint16_t) PARSEC_VER_MAJOR << 16u) | ((uint16_t) PARSEC_VER_MINOR))) -/// @brief Default ::Parsec instance confgiuration passed to ::ParsecInit. +/// @brief Default ::Parsec instance configuration passed to ::ParsecInit. #define PARSEC_DEFAULTS { \ /*upnp */ 1, \ /*clientPort */ 0, \ @@ -654,7 +654,7 @@ typedef struct ParsecMouseWheelMessage { typedef struct ParsecMouseMotionMessage { int32_t x; ///< The absolute horizontal screen coordinate of the cursor if `relative` is `false`, or the delta (can be negative) if `relative` is `true`. int32_t y; ///< The absolute vertical screen coordinate of the cursor if `relative` is `false`, or the delta (can be negative) if `relative` is `true`. - bool relative; ///< `true` for relative mode, `false` for absolute mode. See above. + bool relative; ///< `true` for relative mode, `false` for absolute mode. See details. uint8_t __pad[3]; } ParsecMouseMotionMessage; @@ -708,9 +708,9 @@ typedef struct ParsecClientConfig { int32_t decoderSoftware; ///< `true` to force decoding of video frames via a software implementation. int32_t mediaContainer; ///< ::ParsecContainer value. int32_t protocol; ///< ::ParsecProtocol value. - int32_t resolutionX; ///< See above. - int32_t resolutionY; ///< See above. - int32_t refreshRate; ///< See above. + int32_t resolutionX; ///< See details. + int32_t resolutionY; ///< See details. + int32_t refreshRate; ///< See details. bool pngCursor; ///< `true` to return compressed PNG cursor images during ::ParsecClientPollEvents, `false` to return a 32-bit RGBA image. uint8_t __pad[3]; } ParsecClientConfig; @@ -826,6 +826,14 @@ typedef struct Parsec Parsec; /// @details Passed to ::ParsecHostGLSubmitFrame. Prevents obligatory include of GL headers. typedef uint32_t ParsecGLuint; +/// @brief D3D9 `IDirect3DDevice9`. +/// @details Passed to ::ParsecHostD3D9SubmitFrame. Prevents obligatory include of d3d9.h. +typedef void ParsecD3D9Device; + +/// @brief D3D9 `IDirect3DSurface9`. +/// @details Passed to ::ParsecHostD3D9SubmitFrame. Prevents obligatory include of d3d9.h. +typedef void ParsecD3D9Surface; + /// @brief D3D11 `ID3D11Device`. /// @details Passed to ::ParsecHostD3D11SubmitFrame. Prevents obligatory include of d3d11.h. typedef void ParsecD3D11Device; @@ -896,6 +904,7 @@ typedef void (*ParsecAudioCallback)(int16_t *pcm, uint32_t frames, void *opaque) #define ParsecHostSubmitCursor (*ParsecHostSubmitCursor) #define ParsecHostSubmitRumble (*ParsecHostSubmitRumble) #define ParsecHostGLSubmitFrame (*ParsecHostGLSubmitFrame) +#define ParsecHostD3D9SubmitFrame (*ParsecHostD3D9SubmitFrame) #define ParsecHostD3D11SubmitFrame (*ParsecHostD3D11SubmitFrame) typedef struct ParsecAPI { @@ -1068,7 +1077,7 @@ ParsecClientSendUserData(Parsec *ps, uint32_t id, char *text); /// ::HOST_GAME requires that the application call ::ParsecHostSubmitAudio and ::ParsecHostD3D11SubmitFrame /// (or similar) to push data to guests, and call ::ParsecHostPollInput to process input from guests. /// @param[in] ps ::Parsec instance returned by ::ParsecInit. -/// @param[in] mode Host mode of operation. See above. +/// @param[in] mode Host mode of operation. See details. /// @param[in] cfg Host configuration. May be updated later via ::ParsecHostSetConfig. May be `NULL` /// to use ::PARSEC_HOST_DEFAULTS. /// @param[in] sessionID Session ID obtained via the [Parsec API](https://github.com/parsec-cloud/parsec-sdk/tree/master/api/public). @@ -1195,6 +1204,14 @@ ParsecHostSubmitRumble(Parsec *ps, uint32_t guestID, uint32_t gamepadID, uint8_t EXPORT ParsecStatus ParsecHostGLSubmitFrame(Parsec *ps, ParsecGLuint frame); +/// @brief Submit rendered D3D9 frame to connected guests. ::HOST_GAME only. Windows only. +/// @param[in] ps ::Parsec instance returned by ::ParsecInit. +/// @param[in] device Cast to `IDirect3DDevice9 *` used within your render loop. +/// @param[in] frame Cast to `IDirect3DSurface9 *` representing a rendered frame. +/// @returns ::PARSEC_OK if the frame was submitted successfully, otherwise a ::ParsecStatus error value. +EXPORT ParsecStatus +ParsecHostD3D9SubmitFrame(Parsec *ps, ParsecD3D9Device *device, ParsecD3D9Surface *frame); + /// @brief Submit rendered D3D11 frame to connected guests. ::HOST_GAME only. Windows only. /// @param[in] ps ::Parsec instance returned by ::ParsecInit. /// @param[in] device Cast to `ID3D11Device *` used within your render loop. @@ -1241,6 +1258,7 @@ ParsecHostD3D11SubmitFrame(Parsec *ps, ParsecD3D11Device *device, ParsecD3D11Dev #undef ParsecHostSubmitCursor #undef ParsecHostSubmitRumble #undef ParsecHostGLSubmitFrame +#undef ParsecHostD3D9SubmitFrame #undef ParsecHostD3D11SubmitFrame #endif diff --git a/ui/render/d3d9.cpp b/ui/render/d3d9.cpp new file mode 100644 index 0000000..6dbe029 --- /dev/null +++ b/ui/render/d3d9.cpp @@ -0,0 +1,401 @@ +#include "d3d9.h" + +#include +#include + +#include +#include + +#include "shaders/d3d9/vs.h" +#include "shaders/d3d9/ps.h" + +#define DUMMY_WIN_CLASS "d3d9_dummy_window" + +struct d3d9 { + D3DDISPLAYMODEEX mode; + IDirect3D9Ex *factory; + IDirect3DDevice9Ex *device; + IDirect3DDevice9 *device_og; + IDirect3DSwapChain9Ex *swap_chain; + IDirect3DVertexShader9 *vs; + IDirect3DPixelShader9 *ps; + IDirect3DVertexBuffer9 *vb; + IDirect3DVertexDeclaration9 *vd; + IDirect3DIndexBuffer9 *ib; + IDirect3DTexture9 *tex; + IDirect3DSurface9 *render_target; + + HWND hwnd; + bool vsync; + bool scene_begun; + bool headless; + enum sampler sampler; + uint32_t w; + uint32_t h; +}; + +void d3d9_set_sampler(struct render_mod *mod, enum sampler sampler) +{ + struct d3d9 *d3d9 = (struct d3d9 *) mod; + + d3d9->sampler = sampler; +} + +static LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + return DefWindowProc(hwnd, uMsg, wParam, lParam); +} + +int32_t d3d9_init(struct render_mod **mod_out, void *window, bool vsync, + uint32_t width, uint32_t height, enum sampler sampler) +{ + struct d3d9 **d3d9_out = (struct d3d9 **) mod_out; + struct d3d9 *d3d9 = *d3d9_out = (struct d3d9 *) calloc(1, sizeof(struct d3d9)); + d3d9->hwnd = (HWND) window; + d3d9->sampler = sampler; + d3d9->vsync = vsync; + + int32_t r = 0; + D3DPRESENT_PARAMETERS pp = {0}; + IDirect3DSwapChain9 *swap_chain = NULL; + + if (!d3d9->hwnd) { + d3d9->headless = true; + + WNDCLASSEX wx = {0}; + wx.cbSize = sizeof(WNDCLASSEX); + wx.lpfnWndProc = (WNDPROC) WndProc; + wx.hInstance = GetModuleHandle(NULL); + wx.lpszClassName = DUMMY_WIN_CLASS; + + ATOM a = RegisterClassEx(&wx); + if (a == 0) {printf("RegisterClassEx=0\n"); r = -1; goto except;} + + d3d9->hwnd = CreateWindowEx(0, DUMMY_WIN_CLASS, NULL, WS_POPUP, + 0, 0, 1, 1, NULL, NULL, NULL, NULL); + if (!d3d9->hwnd) {printf("CreateWindowEx=NULL\n"); r = -1; goto except;} + } + + int32_t e = Direct3DCreate9Ex(D3D_SDK_VERSION, &d3d9->factory); + if (e != D3D_OK) {printf("Direct3DCreate9Ex=%d\n", e); r = -1; goto except;} + + d3d9->mode.Size = sizeof(D3DDISPLAYMODEEX); + e = d3d9->factory->GetAdapterDisplayModeEx(D3DADAPTER_DEFAULT, &d3d9->mode, NULL); + if (e != D3D_OK) {printf("GetAdapterDisplayModeEx=%d\n", e); r = -1; goto except;} + + pp.BackBufferFormat = d3d9->mode.Format; + pp.BackBufferCount = 1; + pp.SwapEffect = D3DSWAPEFFECT_FLIPEX; + pp.hDeviceWindow = d3d9->hwnd; + pp.Windowed = TRUE; + pp.PresentationInterval = D3DPRESENT_INTERVAL_ONE; + + DWORD flags = D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_ENABLE_PRESENTSTATS | + D3DCREATE_PUREDEVICE | D3DCREATE_NOWINDOWCHANGES | D3DCREATE_MULTITHREADED | + D3DCREATE_DISABLE_PSGP_THREADING; + + e = d3d9->factory->CreateDeviceEx(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, d3d9->hwnd, + flags, &pp, NULL, &d3d9->device); + if (e != D3D_OK) {printf("CreateDeviceEx=%d\n", e); r = -1; goto except;} + + e = d3d9->device->QueryInterface(__uuidof(IDirect3DDevice9), (void **) &d3d9->device_og); + if (e != D3D_OK) {printf("QueryInterface=%d\n", e); r = -1; goto except;} + + if (!d3d9->headless) { + e = d3d9->device->GetSwapChain(0, &swap_chain); + if (e != D3D_OK) {printf("GetSwapChain=%d\n", e); r = -1; goto except;} + + e = swap_chain->QueryInterface(__uuidof(IDirect3DSwapChain9Ex), (void **) &d3d9->swap_chain); + if (e != D3D_OK) {printf("QueryInterface=%d\n", e); r = -1; goto except;} + + e = d3d9->device->SetMaximumFrameLatency(1); + if (e != S_OK) {printf("SetMaximumFrameLatency=%d\n", e); r = -1; goto except;} + } + + e = d3d9->device->CreateVertexShader((DWORD *) VS, &d3d9->vs); + if (e != D3D_OK) {printf("CreateVertexShader=%d\n", e); r = -1; goto except;} + + e = d3d9->device->CreatePixelShader((DWORD *) PS, &d3d9->ps); + if (e != D3D_OK) {printf("CreatePixelShader=%d\n", e); r = -1; goto except;} + + //vertex buffer + float vertex_data[] = { + -1.0f, -1.0f, // position0 (bottom-left) + 0.0f, 1.0f, // texcoord0 + -1.0f, 1.0f, // position1 (top-left) + 0.0f, 0.0f, // texcoord1 + 1.0f, 1.0f, // position2 (top-right) + 1.0f, 0.0f, // texcoord2 + 1.0f, -1.0f, // position3 (bottom-right) + 1.0f, 1.0f, // texcoord3 + }; + + e = d3d9->device->CreateVertexBuffer(sizeof(vertex_data), 0, D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, + D3DPOOL_DEFAULT, &d3d9->vb, NULL); + if (e != D3D_OK) {printf("CreateVertexBuffer=%d\n", e); r = -1; goto except;} + + char *ptr = NULL; + e = d3d9->vb->Lock(0, 0, (void **) &ptr, D3DLOCK_DISCARD); + if (e != D3D_OK) {printf("Lock=%d\n", e); r = -1; goto except;} + + memcpy(ptr, vertex_data, sizeof(vertex_data)); + + e = d3d9->vb->Unlock(); + if (e != D3D_OK) {printf("Unlock=%d\n", e); r = -1; goto except;} + + //vertex declaration (input layout) + D3DVERTEXELEMENT9 dec[] = { + {0, 0, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0}, + {0, 2 * sizeof(float), D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0}, + D3DDECL_END() + }; + + e = d3d9->device->CreateVertexDeclaration(dec, &d3d9->vd); + if (e != D3D_OK) {printf("CreateVertexDeclaration=%d\n", e); r = -1; goto except;} + + //index buffer + DWORD index_data[] = { + 0, 1, 2, + 2, 3, 0 + }; + + e = d3d9->device->CreateIndexBuffer(sizeof(index_data), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, D3DFMT_INDEX32, + D3DPOOL_DEFAULT, &d3d9->ib, NULL); + if (e != D3D_OK) {printf("CreateIndexBuffer=%d\n", e); r = -1; goto except;} + + e = d3d9->ib->Lock(0, 0, (void **) &ptr, D3DLOCK_DISCARD); + if (e != D3D_OK) {printf("Lock=%d\n", e); r = -1; goto except;} + + memcpy(ptr, index_data, sizeof(index_data)); + + e = d3d9->ib->Unlock(); + if (e != D3D_OK) {printf("Unlock=%d\n", e); r = -1; goto except;} + + e = d3d9->device->CreateTexture(width, height, 1, D3DUSAGE_DYNAMIC, + D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &d3d9->tex, NULL); + if (e != D3D_OK) {printf("CreateTexture=%d\n", e); r = -1; goto except;} + + except: + + if (swap_chain) + swap_chain->Release(); + + if (r != 0) + d3d9_destroy(mod_out); + + return r; +} + +void d3d9_destroy(struct render_mod **mod_out) +{ + struct d3d9 **d3d9_out = (struct d3d9 **) mod_out; + + if (d3d9_out == NULL || *d3d9_out == NULL) + return; + + struct d3d9 *d3d9 = *d3d9_out; + + if (d3d9->render_target) + d3d9->render_target->Release(); + + if (d3d9->tex) + d3d9->tex->Release(); + + if (d3d9->ib) + d3d9->ib->Release(); + + if (d3d9->vd) + d3d9->vd->Release(); + + if (d3d9->vb) + d3d9->vb->Release(); + + if (d3d9->ps) + d3d9->ps->Release(); + + if (d3d9->vs) + d3d9->vs->Release(); + + if (d3d9->swap_chain) + d3d9->swap_chain->Release(); + + if (d3d9->device_og) + d3d9->device_og->Release(); + + if (d3d9->device) + d3d9->device->Release(); + + if (d3d9->factory) + d3d9->factory->Release(); + + if (d3d9->headless) { + if (d3d9->hwnd) + DestroyWindow(d3d9->hwnd); + + UnregisterClass(DUMMY_WIN_CLASS, GetModuleHandle(NULL)); + } + + free(d3d9); + *d3d9_out = NULL; +} + +void d3d9_get_device(struct render_mod *mod, struct render_device **device, struct render_context **context) +{ + struct d3d9 *d3d9 = (struct d3d9 *) mod; + + *device = (struct render_device *) d3d9->device; + *context = NULL; +} + +static void d3d9_set_viewport(struct d3d9 *d3d9, uint32_t window_w, uint32_t window_h, float ratio) +{ + D3DVIEWPORT9 viewport = {0}; + viewport.Width = window_w; + viewport.Height = lrint((float) viewport.Width / ratio); + viewport.MinZ = 0.0f; + viewport.MaxZ = 0.0f; + + if ((float) window_h / 240.0f < ((float) window_w / ratio) / 256.0f) { + viewport.Height = window_h; + viewport.Width = lrint((float) viewport.Height * ratio); + } + + viewport.X = (viewport.Width > window_w) ? 0 : (window_w - viewport.Width) / 2; + viewport.Y = (viewport.Height > window_h) ? 0 : (window_h - viewport.Height) / 2; + + int32_t e = d3d9->device->SetViewport(&viewport); + if (e != D3D_OK) printf("SetViewport=%d\n", e); +} + +void d3d9_draw(struct render_mod *mod, uint32_t window_w, uint32_t window_h, uint32_t *pixels, uint32_t aspect) +{ + struct d3d9 *d3d9 = (struct d3d9 *) mod; + + D3DLOCKED_RECT rect = {0}; + + D3DVIEWPORT9 viewport = {0}; + viewport.Width = window_w; + viewport.Height = window_h; + + int32_t e = d3d9->device->SetViewport(&viewport); + if (e != D3D_OK) {printf("SetViewport=%d\n", e); goto except;} + + e = d3d9->device->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0); + if (e != D3D_OK) {printf("Clear=%d\n", e); goto except;} + + e = d3d9->device->BeginScene(); + if (e != D3D_OK) {printf("BeginScene=%d\n", e); goto except;} + d3d9->scene_begun = true; + + e = d3d9->tex->LockRect(0, &rect, NULL, D3DLOCK_DISCARD); + if (e != D3D_OK) {printf("LockRect=%d\n", e); goto except;} + + for (int32_t y = 0; y < 240; y++) + memcpy((uint8_t *) rect.pBits + (y * rect.Pitch), pixels + y * 256, sizeof(uint32_t) * 256); + + e = d3d9->tex->UnlockRect(0); + if (e != D3D_OK) {printf("UnlockRect=%d\n", e); goto except;} + + if (!d3d9->render_target || window_w != d3d9->w || window_h != d3d9->h) { + if (!d3d9->headless) { + D3DPRESENT_PARAMETERS pp = {0}; + pp.BackBufferFormat = d3d9->mode.Format; + pp.BackBufferCount = 1; + pp.SwapEffect = D3DSWAPEFFECT_FLIPEX; + pp.hDeviceWindow = d3d9->hwnd; + pp.Windowed = TRUE; + pp.PresentationInterval = D3DPRESENT_INTERVAL_ONE; + + e = d3d9->device->ResetEx(&pp, NULL); + if (e != D3D_OK) {printf("ResetEx=%d\n", e); goto except;} + } + + if (d3d9->render_target) { + d3d9->render_target->Release(); + d3d9->render_target = NULL; + } + + d3d9->w = window_w; + d3d9->h = window_h; + + e = d3d9->device->CreateRenderTarget(window_w, window_h, D3DFMT_A8R8G8B8, D3DMULTISAMPLE_NONE, + 0, false, &d3d9->render_target, NULL); + if (e != D3D_OK) {printf("CreateRenderTarget=%d\n", e); goto except;} + + } + + d3d9->device->SetRenderTarget(0, d3d9->render_target); + + d3d9_set_viewport(d3d9, window_w, window_h, ASPECT_RATIO(aspect)); + + //vertex/index state + d3d9->device->SetVertexShader(d3d9->vs); + d3d9->device->SetStreamSource(0, d3d9->vb, 0, 4 * sizeof(float)); + d3d9->device->SetVertexDeclaration(d3d9->vd); + d3d9->device->SetIndices(d3d9->ib); + + float texel_offset[4] = {-1.0f / (float) window_w, 1.0f / (float) window_h, 0.0f, 0.0f}; + d3d9->device->SetVertexShaderConstantF(0, texel_offset, 1); + + //pixel shader + d3d9->device->SetPixelShader(d3d9->ps); + d3d9->device->SetTexture(0, d3d9->tex); + + //sampler state + d3d9->device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); + d3d9->device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); + d3d9->device->SetSamplerState(0, D3DSAMP_ADDRESSW, D3DTADDRESS_CLAMP); + + if (d3d9->sampler == SAMPLE_LINEAR) { + d3d9->device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); + d3d9->device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); + + } else { + d3d9->device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT); + d3d9->device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT); + } + + e = d3d9->device->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 4, 0, 2); + if (e != D3D_OK) {printf("DrawIndexedPrimitive=%d\n", e); return;} + + except: + + return; +} + +enum ParsecStatus d3d9_submit_parsec(struct render_mod *mod, ParsecDSO *parsec) +{ + struct d3d9 *d3d9 = (struct d3d9 *) mod; + + return ParsecHostD3D9SubmitFrame(parsec, d3d9->device_og, d3d9->render_target); +} + +void d3d9_present(struct render_mod *mod) +{ + struct d3d9 *d3d9 = (struct d3d9 *) mod; + + if (d3d9->scene_begun) { + d3d9->scene_begun = false; + + int32_t e = d3d9->device->EndScene(); + + if (e == D3D_OK) { + if (!d3d9->headless) { + IDirect3DSurface9 *back_buffer = NULL; + e = d3d9->swap_chain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &back_buffer); + if (e != D3D_OK) {printf("GetBackBuffer=%d\n", e); return;} + + e = d3d9->device->StretchRect(d3d9->render_target, NULL, back_buffer, NULL, D3DTEXF_NONE); + back_buffer->Release(); + if (e != D3D_OK) {printf("StretchRect=%d\n", e); return;} + } + + e = d3d9->device->PresentEx(NULL, NULL, NULL, NULL, 0); + if (e != D3D_OK && e != S_PRESENT_OCCLUDED && e != S_PRESENT_MODE_CHANGED) + {printf("PresentEx=%d\n", e); return;} + + } else { + printf("EndScene=%d\n", e); + } + } +} diff --git a/ui/render/d3d9.h b/ui/render/d3d9.h new file mode 100644 index 0000000..ab41a5f --- /dev/null +++ b/ui/render/d3d9.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include + +#include "parsec-dso.h" + +#include "mod.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int32_t d3d9_init(struct render_mod **mod_out, void *window, bool vsync, + uint32_t width, uint32_t height, enum sampler sampler); +void d3d9_destroy(struct render_mod **mod_out); +void d3d9_get_device(struct render_mod *mod, struct render_device **device, struct render_context **context); +void d3d9_draw(struct render_mod *mod, uint32_t window_w, uint32_t window_h, uint32_t *pixels, uint32_t aspect); +enum ParsecStatus d3d9_submit_parsec(struct render_mod *mod, ParsecDSO *parsec); +void d3d9_present(struct render_mod *mod); +void d3d9_set_sampler(struct render_mod *mod, enum sampler sampler); + +#ifdef __cplusplus +} +#endif diff --git a/ui/render/mod.h b/ui/render/mod.h index ffeee66..0303cc5 100644 --- a/ui/render/mod.h +++ b/ui/render/mod.h @@ -22,8 +22,9 @@ enum render_mode { RENDER_GL = 1, #if defined(_WIN32) - RENDER_D3D11 = 2, - RENDER_D3D12 = 3, + RENDER_D3D9 = 2, + RENDER_D3D11 = 3, + RENDER_D3D12 = 4, #elif defined(__APPLE__) RENDER_METAL = 2, diff --git a/ui/render/render.c b/ui/render/render.c index 295f3b1..4211fd1 100644 --- a/ui/render/render.c +++ b/ui/render/render.c @@ -3,6 +3,7 @@ #include #if defined(_WIN32) + #include "d3d9.h" #include "d3d11.h" #include "d3d12.h" @@ -35,6 +36,7 @@ static struct render_callbacks CBS[] = { [RENDER_GL] = {gl_init, gl_destroy, gl_get_device, gl_draw, gl_submit_parsec, gl_present, gl_set_sampler}, #if defined(_WIN32) + [RENDER_D3D9] = {d3d9_init, d3d9_destroy, d3d9_get_device, d3d9_draw, d3d9_submit_parsec, d3d9_present, d3d9_set_sampler}, [RENDER_D3D11] = {d3d11_init, d3d11_destroy, d3d11_get_device, d3d11_draw, d3d11_submit_parsec, d3d11_present, d3d11_set_sampler}, [RENDER_D3D12] = {d3d12_init, d3d12_destroy, d3d12_get_device, d3d12_draw, d3d12_submit_parsec, d3d12_present, d3d12_set_sampler}, diff --git a/ui/render/shaders/d3d9/makefile b/ui/render/shaders/d3d9/makefile new file mode 100644 index 0000000..4845ac2 --- /dev/null +++ b/ui/render/shaders/d3d9/makefile @@ -0,0 +1,6 @@ +all: clear + fxc /nologo /O3 /Ges /Fh vs.h /T vs_3_0 /Vn VS vs.hlsl + fxc /nologo /O3 /Ges /Fh ps.h /T ps_3_0 /Vn PS ps.hlsl + +clear: + cls diff --git a/ui/render/shaders/d3d9/ps.h b/ui/render/shaders/d3d9/ps.h new file mode 100644 index 0000000..505d595 --- /dev/null +++ b/ui/render/shaders/d3d9/ps.h @@ -0,0 +1,60 @@ +#if 0 +// +// Generated by Microsoft (R) HLSL Shader Compiler 10.1 +// +// Parameters: +// +// Texture2D ss+frame; +// +// +// Registers: +// +// Name Reg Size +// ------------ ----- ---- +// ss+frame s0 1 +// + + ps_3_0 + dcl_texcoord v0.xy + dcl_2d s0 + texld r0, v0, s0 + mov oC0, r0.zyxw + +// approximately 2 instruction slots used (1 texture, 1 arithmetic) +#endif + +const BYTE PS[] = +{ + 0, 3, 255, 255, 254, 255, + 32, 0, 67, 84, 65, 66, + 28, 0, 0, 0, 83, 0, + 0, 0, 0, 3, 255, 255, + 1, 0, 0, 0, 28, 0, + 0, 0, 0, 137, 0, 0, + 76, 0, 0, 0, 48, 0, + 0, 0, 3, 0, 0, 0, + 1, 0, 0, 0, 60, 0, + 0, 0, 0, 0, 0, 0, + 115, 115, 43, 102, 114, 97, + 109, 101, 0, 171, 171, 171, + 4, 0, 7, 0, 1, 0, + 4, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 112, 115, + 95, 51, 95, 48, 0, 77, + 105, 99, 114, 111, 115, 111, + 102, 116, 32, 40, 82, 41, + 32, 72, 76, 83, 76, 32, + 83, 104, 97, 100, 101, 114, + 32, 67, 111, 109, 112, 105, + 108, 101, 114, 32, 49, 48, + 46, 49, 0, 171, 31, 0, + 0, 2, 5, 0, 0, 128, + 0, 0, 3, 144, 31, 0, + 0, 2, 0, 0, 0, 144, + 0, 8, 15, 160, 66, 0, + 0, 3, 0, 0, 15, 128, + 0, 0, 228, 144, 0, 8, + 228, 160, 1, 0, 0, 2, + 0, 8, 15, 128, 0, 0, + 198, 128, 255, 255, 0, 0 +}; diff --git a/ui/render/shaders/d3d9/ps.hlsl b/ui/render/shaders/d3d9/ps.hlsl new file mode 100644 index 0000000..8726bb7 --- /dev/null +++ b/ui/render/shaders/d3d9/ps.hlsl @@ -0,0 +1,15 @@ +struct VS_OUTPUT { + float4 position : SV_POSITION; + float2 texcoord : TEXCOORD; +}; + +SamplerState ss { +}; + +Texture2D frame: register(t0); + +float4 main(VS_OUTPUT input) : SV_TARGET +{ + float4 rgba = frame.Sample(ss, input.texcoord); + return float4(rgba.b, rgba.g, rgba.r, rgba.a); +} diff --git a/ui/render/shaders/d3d9/vs.h b/ui/render/shaders/d3d9/vs.h new file mode 100644 index 0000000..d9f80dd --- /dev/null +++ b/ui/render/shaders/d3d9/vs.h @@ -0,0 +1,75 @@ +#if 0 +// +// Generated by Microsoft (R) HLSL Shader Compiler 10.1 +// +// Parameters: +// +// float2 texel_offset; +// +// +// Registers: +// +// Name Reg Size +// ------------ ----- ---- +// texel_offset c0 1 +// + + vs_3_0 + def c1, 0, 1, 0, 0 + dcl_position v0 + dcl_texcoord v1 + dcl_position o0 + dcl_texcoord o1.xy + add o0.xy, c0, v0 + mov o0.zw, c1.xyxy + mov o1.xy, v1 + +// approximately 3 instruction slots used +#endif + +const BYTE VS[] = +{ + 0, 3, 254, 255, 254, 255, + 33, 0, 67, 84, 65, 66, + 28, 0, 0, 0, 87, 0, + 0, 0, 0, 3, 254, 255, + 1, 0, 0, 0, 28, 0, + 0, 0, 0, 137, 0, 0, + 80, 0, 0, 0, 48, 0, + 0, 0, 2, 0, 0, 0, + 1, 0, 2, 0, 64, 0, + 0, 0, 0, 0, 0, 0, + 116, 101, 120, 101, 108, 95, + 111, 102, 102, 115, 101, 116, + 0, 171, 171, 171, 1, 0, + 3, 0, 1, 0, 2, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 118, 115, 95, 51, + 95, 48, 0, 77, 105, 99, + 114, 111, 115, 111, 102, 116, + 32, 40, 82, 41, 32, 72, + 76, 83, 76, 32, 83, 104, + 97, 100, 101, 114, 32, 67, + 111, 109, 112, 105, 108, 101, + 114, 32, 49, 48, 46, 49, + 0, 171, 81, 0, 0, 5, + 1, 0, 15, 160, 0, 0, + 0, 0, 0, 0, 128, 63, + 0, 0, 0, 0, 0, 0, + 0, 0, 31, 0, 0, 2, + 0, 0, 0, 128, 0, 0, + 15, 144, 31, 0, 0, 2, + 5, 0, 0, 128, 1, 0, + 15, 144, 31, 0, 0, 2, + 0, 0, 0, 128, 0, 0, + 15, 224, 31, 0, 0, 2, + 5, 0, 0, 128, 1, 0, + 3, 224, 2, 0, 0, 3, + 0, 0, 3, 224, 0, 0, + 228, 160, 0, 0, 228, 144, + 1, 0, 0, 2, 0, 0, + 12, 224, 1, 0, 68, 160, + 1, 0, 0, 2, 1, 0, + 3, 224, 1, 0, 228, 144, + 255, 255, 0, 0 +}; diff --git a/ui/render/shaders/d3d9/vs.hlsl b/ui/render/shaders/d3d9/vs.hlsl new file mode 100644 index 0000000..6a51d86 --- /dev/null +++ b/ui/render/shaders/d3d9/vs.hlsl @@ -0,0 +1,18 @@ +struct VS_OUTPUT { + float4 position : SV_POSITION; + float2 texcoord : TEXCOORD; +}; + +uniform float2 texel_offset : register(c0); + +VS_OUTPUT main(float2 position : POSITION, float2 texcoord : TEXCOORD) +{ + VS_OUTPUT output; + + output.position = float4(position.xy, 0, 1); + output.position.xy += texel_offset.xy * output.position.w; + + output.texcoord = texcoord; + + return output; +} diff --git a/ui/render/ui.cpp b/ui/render/ui.cpp index d392c15..a77a3ca 100644 --- a/ui/render/ui.cpp +++ b/ui/render/ui.cpp @@ -8,7 +8,9 @@ #include "imgui/imgui_impl_opengl3.h" #if defined(_WIN32) + #include #include + #include "imgui/imgui_impl_dx9.h" #include "imgui/imgui_impl_dx11.h" #if defined(__x86_64__) #include "ui-d3d12-shim.h" @@ -162,6 +164,9 @@ static void ui_menu(struct ui *ctx, struct ui_props *props) ctx->cbs.mode(RENDER_GL, ctx->opaque); #if defined(_WIN32) + if (ImGui::MenuItem("DirectX 9", "", props->mode == RENDER_D3D9, true)) + ctx->cbs.mode(RENDER_D3D9, ctx->opaque); + if (ImGui::MenuItem("DirectX 11", "", props->mode == RENDER_D3D11, true)) ctx->cbs.mode(RENDER_D3D11, ctx->opaque); @@ -523,6 +528,7 @@ void ui_init(struct ui **ctx_out, SDL_Window *window, struct ui_cbs *cbs, void * switch (mode) { case RENDER_GL: ImGui_ImplOpenGL3_Init("#version 110"); break; #if defined(_WIN32) + case RENDER_D3D9: ImGui_ImplDX9_Init((IDirect3DDevice9 *) device); break; case RENDER_D3D11: ImGui_ImplDX11_Init((ID3D11Device *) device, (ID3D11DeviceContext *) context); break; #if defined(__x86_64__) case RENDER_D3D12: ui_d3d12_init(device, &ctx->d3d12_shim); break; @@ -543,6 +549,7 @@ void ui_destroy(struct ui **ctx_out) switch (ctx->mode) { case RENDER_GL: ImGui_ImplOpenGL3_Shutdown(); break; #if defined(_WIN32) + case RENDER_D3D9: ImGui_ImplDX9_Shutdown(); break; case RENDER_D3D11: ImGui_ImplDX11_Shutdown(); break; #if defined(__x86_64__) case RENDER_D3D12: ui_d3d12_shutdown(&ctx->d3d12_shim); break; @@ -569,6 +576,7 @@ void ui_new_frame(struct ui *ctx, struct render_device *device, struct render_co switch (ctx->mode) { case RENDER_GL: ImGui_ImplOpenGL3_NewFrame(); break; #if defined(_WIN32) + case RENDER_D3D9: ImGui_ImplDX9_NewFrame(); break; case RENDER_D3D11: ImGui_ImplDX11_NewFrame(); break; #if defined(__x86_64__) case RENDER_D3D12: ui_d3d12_new_frame(); break; @@ -606,6 +614,7 @@ void ui_new_frame(struct ui *ctx, struct render_device *device, struct render_co switch (ctx->mode) { case RENDER_GL: ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); break; #if defined(_WIN32) + case RENDER_D3D9: ImGui_ImplDX9_RenderDrawData(ImGui::GetDrawData()); break; case RENDER_D3D11: ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); break; #if defined(__x86_64__) case RENDER_D3D12: ui_d3d12_render_draw_data(ctx->d3d12_shim, context, ImGui::GetDrawData()); break;