diff --git a/Makefile.win b/Makefile.win
index f7b675cbad..66ad39a44d 100644
--- a/Makefile.win
+++ b/Makefile.win
@@ -109,6 +109,15 @@ ifeq ($(HAVE_CG), 1)
OBJ += gfx/shader_cg.o
LIBS += -lcg -lcgGL
DEFINES += -DHAVE_CG
+else
+ HAVE_D3D9 = 0
+endif
+
+ifeq ($(HAVE_D3D9), 1)
+ CXX_BUILD = 1
+ OBJ += gfx/d3d9/d3d9.o gfx/d3d9/render_chain.o
+ DEFINES += -DHAVE_WIN32_D3D9
+ LIBS += -ld3d9 -lcg -lcgD3D9 -ld3dx9 -ldxguid
endif
ifeq ($(HAVE_XAUDIO), 1)
@@ -227,6 +236,10 @@ $(TARGET): $(OBJ)
$(Q)$(CC) $(CFLAGS) $(DEFINES) -c -o $@ $<
@$(if $(Q), $(shell echo echo CC $<),)
+%.o: %.cpp
+ $(Q)$(CXX) $(CFLAGS) $(DEFINES) -c -o $@ $<
+ @$(if $(Q), $(shell echo echo CXX $<),)
+
%.o: %.rc
$(Q)$(WINDRES) -o $@ $<
@$(if $(Q), $(shell echo echo WINDRES $<),)
@@ -247,6 +260,7 @@ clean:
rm -f conf/*.o
rm -f gfx/scaler/*.o
rm -f gfx/*.o
+ rm -f gfx/d3d9/*.o
rm -f gfx/context/*.o
rm -f gfx/math/*.o
rm -f gfx/fonts/*.o
diff --git a/config.def.h b/config.def.h
index 85b8d6b395..fb5eb59eae 100644
--- a/config.def.h
+++ b/config.def.h
@@ -37,6 +37,7 @@ enum
VIDEO_WII,
VIDEO_XENON360,
VIDEO_XDK_D3D,
+ VIDEO_D3D9,
VIDEO_VG,
VIDEO_NULL,
@@ -81,6 +82,8 @@ enum
#define VIDEO_DEFAULT_DRIVER VIDEO_XENON360
#elif (defined(_XBOX1) || defined(_XBOX360)) && (defined(HAVE_D3D8) || defined(HAVE_D3D9))
#define VIDEO_DEFAULT_DRIVER VIDEO_XDK_D3D
+#elif defined(HAVE_WIN32_D3D9)
+#define VIDEO_DEFAULT_DRIVER VIDEO_D3D9
#elif defined(HAVE_VG)
#define VIDEO_DEFAULT_DRIVER VIDEO_VG
#elif defined(HAVE_XVIDEO)
diff --git a/driver.c b/driver.c
index 2add0b8efd..b8883e1563 100644
--- a/driver.c
+++ b/driver.c
@@ -95,6 +95,9 @@ static const video_driver_t *video_drivers[] = {
#if defined(_XBOX) && (defined(HAVE_D3D8) || defined(HAVE_D3D9))
&video_xdk_d3d,
#endif
+#if defined(HAVE_WIN32_D3D9)
+ &video_d3d9,
+#endif
#ifdef HAVE_SDL
&video_sdl,
#endif
diff --git a/driver.h b/driver.h
index 61242ed48d..20029c5d8b 100644
--- a/driver.h
+++ b/driver.h
@@ -277,6 +277,7 @@ extern const audio_driver_t audio_ps3;
extern const audio_driver_t audio_gx;
extern const audio_driver_t audio_null;
extern const video_driver_t video_gl;
+extern const video_driver_t video_d3d9;
extern const video_driver_t video_gx;
extern const video_driver_t video_xenon360;
extern const video_driver_t video_xvideo;
diff --git a/gfx/d3d9/config_file.hpp b/gfx/d3d9/config_file.hpp
new file mode 100644
index 0000000000..5765fb00eb
--- /dev/null
+++ b/gfx/d3d9/config_file.hpp
@@ -0,0 +1,149 @@
+/* RetroArch - A frontend for libretro.
+ * Copyright (C) 2010-2012 - Hans-Kristian Arntzen
+ *
+ * RetroArch is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with RetroArch.
+ * If not, see .
+ */
+
+#ifndef __CONFIG_FILE_HPP
+#define __CONFIG_FILE_HPP
+
+#include "../../conf/config_file.h"
+#include
+#include
+
+class ConfigFile
+{
+ public:
+ ConfigFile(const std::string& _path = "") : path(_path)
+ {
+ conf = config_file_new(path.c_str());
+ if (!conf)
+ conf = config_file_new(nullptr);
+ }
+
+ ConfigFile(const ConfigFile&) = delete;
+ void operator=(const ConfigFile&) = delete;
+
+ operator bool() { return conf; }
+
+ ConfigFile& operator=(ConfigFile&& _in)
+ {
+ if (conf)
+ {
+ if (path[0])
+ config_file_write(conf, path.c_str());
+ config_file_free(conf);
+ conf = _in.conf;
+ _in.conf = nullptr;
+ path = _in.path;
+ }
+ return *this;
+ }
+
+ bool get(const std::string& key, int& out)
+ {
+ if (!conf) return false;
+ int val;
+ if (config_get_int(conf, key.c_str(), &val))
+ {
+ out = val;
+ return true;
+ }
+ return false;
+ }
+
+ bool get(const std::string& key, char& out)
+ {
+ if (!conf) return false;
+ char val;
+ if (config_get_char(conf, key.c_str(), &val))
+ {
+ out = val;
+ return true;
+ }
+ return false;
+ }
+
+ bool get(const std::string& key, bool& out)
+ {
+ if (!conf) return false;
+ bool val;
+ if (config_get_bool(conf, key.c_str(), &val))
+ {
+ out = val;
+ return true;
+ }
+ return false;
+ }
+
+ bool get(const std::string& key, std::string& out)
+ {
+ if (!conf) return false;
+ char *val;
+ if (config_get_string(conf, key.c_str(), &val))
+ {
+ out = val;
+ std::free(val);
+ return out.length() > 0;
+ }
+ return false;
+ }
+
+ bool get(const std::string& key, double& out)
+ {
+ if (!conf) return false;
+ double val;
+ if (config_get_double(conf, key.c_str(), &val))
+ {
+ out = val;
+ return true;
+ }
+ return false;
+ }
+
+ void set(const std::string& key, int val)
+ {
+ if (conf) config_set_int(conf, key.c_str(), val);
+ }
+
+ void set(const std::string& key, double val)
+ {
+ if (conf) config_set_double(conf, key.c_str(), val);
+ }
+
+ void set(const std::string& key, const std::string& val)
+ {
+ if (conf) config_set_string(conf, key.c_str(), val.c_str());
+ }
+
+ void set(const std::string& key, char val)
+ {
+ if (conf) config_set_char(conf, key.c_str(), val);
+ }
+
+ void set(const std::string& key, bool val)
+ {
+ if (conf) config_set_bool(conf, key.c_str(), val);
+ }
+
+ void write() { if (conf && path[0]) config_file_write(conf, path.c_str()); }
+
+ ConfigFile(ConfigFile&& _in) { *this = std::move(_in); }
+
+ ~ConfigFile() { if (conf) config_file_free(conf); }
+
+ private:
+ config_file_t *conf;
+ std::string path;
+};
+
+#endif
diff --git a/gfx/d3d9/d3d9.cpp b/gfx/d3d9/d3d9.cpp
new file mode 100644
index 0000000000..34a50f6d6d
--- /dev/null
+++ b/gfx/d3d9/d3d9.cpp
@@ -0,0 +1,915 @@
+/* RetroArch - A frontend for libretro.
+ * Copyright (C) 2010-2012 - Hans-Kristian Arntzen
+ *
+ * RetroArch is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with RetroArch.
+ * If not, see .
+ */
+
+// This driver is merged from the external RetroArch-D3D9 driver.
+// It is written in C++11 (should be compat with MSVC 2010).
+// Might get rewritten in C99 if I have lots of time to burn.
+//
+// TODO: Integrate state tracker again.
+// TODO: Multi-monitor.
+// TODO: Window resize?
+
+#include "d3d9.hpp"
+#include "render_chain.hpp"
+#include "config_file.hpp"
+#include "../gfx_common.h"
+#include "../../compat/posix_string.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define IDI_ICON 1
+
+namespace Callback
+{
+ static bool quit = false;
+
+ LRESULT CALLBACK WindowProc(HWND hWnd, UINT message,
+ WPARAM wParam, LPARAM lParam)
+ {
+ switch (message)
+ {
+ case WM_SYSKEYDOWN:
+ switch (wParam)
+ {
+ case VK_F10:
+ case VK_RSHIFT:
+ return 0;
+ }
+ break;
+
+ case WM_DESTROY:
+ quit = true;
+ return 0;
+
+ default:
+ return DefWindowProc(hWnd, message, wParam, lParam);
+ }
+ return DefWindowProc(hWnd, message, wParam, lParam);
+ }
+}
+
+void D3DVideo::init_base(const video_info_t &info)
+{
+ D3DPRESENT_PARAMETERS d3dpp;
+ make_d3dpp(info, d3dpp);
+
+ g_pD3D = Direct3DCreate9(D3D_SDK_VERSION);
+ if (!g_pD3D)
+ throw std::runtime_error("Failed to create D3D9 interface!");
+
+ if (FAILED(g_pD3D->CreateDevice(
+ D3DADAPTER_DEFAULT,
+ D3DDEVTYPE_HAL,
+ hWnd,
+ D3DCREATE_HARDWARE_VERTEXPROCESSING,
+ &d3dpp,
+ &dev)))
+ {
+ throw std::runtime_error("Failed to init device");
+ }
+}
+
+void D3DVideo::make_d3dpp(const video_info_t &info, D3DPRESENT_PARAMETERS &d3dpp)
+{
+ std::memset(&d3dpp, 0, sizeof(d3dpp));
+
+ d3dpp.Windowed = true;
+
+ d3dpp.PresentationInterval = info.vsync ? D3DPRESENT_INTERVAL_ONE : D3DPRESENT_INTERVAL_IMMEDIATE;
+ d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
+ d3dpp.hDeviceWindow = hWnd;
+ d3dpp.BackBufferCount = 2;
+ d3dpp.BackBufferFormat = info.fullscreen ? D3DFMT_X8R8G8B8 : D3DFMT_UNKNOWN;
+
+ if (info.fullscreen)
+ {
+ d3dpp.BackBufferWidth = screen_width;
+ d3dpp.BackBufferHeight = screen_height;
+ }
+}
+
+void D3DVideo::init(const video_info_t &info)
+{
+ if (!g_pD3D)
+ init_base(info);
+ else if (needs_restore)
+ {
+ D3DPRESENT_PARAMETERS d3dpp;
+ make_d3dpp(info, d3dpp);
+ if (dev->Reset(&d3dpp) != D3D_OK)
+ throw std::runtime_error("Failed to reset device ...");
+ }
+
+ calculate_rect(screen_width, screen_height, info.force_aspect, g_settings.video.aspect_ratio);
+
+ if (!init_cg())
+ throw std::runtime_error("Failed to init Cg");
+ if (!init_chain(info))
+ throw std::runtime_error("Failed to init render chain");
+ if (!init_font())
+ throw std::runtime_error("Failed to init Font");
+}
+
+void D3DVideo::set_viewport(unsigned x, unsigned y, unsigned width, unsigned height)
+{
+ D3DVIEWPORT9 viewport;
+ viewport.X = x;
+ viewport.Y = y;
+ viewport.Width = width;
+ viewport.Height = height;
+ viewport.MinZ = 0.0f;
+ viewport.MaxZ = 1.0f;
+
+ font_rect.left = x + width * g_settings.video.msg_pos_x;
+ font_rect.right = x + width;
+ font_rect.top = y + (1.0f - g_settings.video.msg_pos_y) * height - g_settings.video.font_size;
+ font_rect.bottom = height;
+
+ font_rect_shifted = font_rect;
+ font_rect_shifted.left -= 2;
+ font_rect_shifted.right -= 2;
+ font_rect_shifted.top += 2;
+ font_rect_shifted.bottom += 2;
+
+ final_viewport = viewport;
+}
+
+void D3DVideo::set_rotation(unsigned rot)
+{
+ rotation = rot;
+}
+
+void D3DVideo::viewport_size(unsigned &width, unsigned &height)
+{
+ width = final_viewport.Width;
+ height = final_viewport.Height;
+}
+
+bool D3DVideo::read_viewport(uint8_t *buffer)
+{
+ bool ret = true;
+ IDirect3DSurface9 *target = nullptr;
+ IDirect3DSurface9 *dest = nullptr;
+
+ if (FAILED(dev->GetRenderTarget(0, &target)))
+ {
+ ret = false;
+ goto end;
+ }
+
+ if (FAILED(dev->CreateOffscreenPlainSurface(screen_width, screen_height,
+ D3DFMT_X8R8G8B8, D3DPOOL_SYSTEMMEM,
+ &dest, nullptr)))
+ {
+ ret = false;
+ goto end;
+ }
+
+ if (FAILED(dev->GetRenderTargetData(target, dest)))
+ {
+ ret = false;
+ goto end;
+ }
+
+ D3DLOCKED_RECT rect;
+ if (SUCCEEDED(dest->LockRect(&rect, nullptr, D3DLOCK_READONLY)))
+ {
+ unsigned pitchpix = rect.Pitch / 4;
+ const uint32_t *pixels = (const uint32_t*)rect.pBits;
+ pixels += final_viewport.X;
+ pixels += (final_viewport.Height - 1) * pitchpix;
+ pixels -= final_viewport.Y * pitchpix;
+
+ for (unsigned y = 0; y < final_viewport.Height; y++, pixels -= pitchpix)
+ {
+ for (unsigned x = 0; x < final_viewport.Width; x++)
+ {
+ *buffer++ = (pixels[x] >> 0) & 0xff;
+ *buffer++ = (pixels[x] >> 8) & 0xff;
+ *buffer++ = (pixels[x] >> 16) & 0xff;
+ }
+ }
+
+ dest->UnlockRect();
+ }
+ else
+ {
+ ret = false;
+ goto end;
+ }
+
+end:
+ if (target)
+ target->Release();
+ if (dest)
+ dest->Release();
+ return ret;
+}
+
+void D3DVideo::calculate_rect(unsigned width, unsigned height,
+ bool keep, float desired_aspect)
+{
+ if (!keep)
+ set_viewport(0, 0, width, height);
+ else
+ {
+ float device_aspect = static_cast(width) / static_cast(height);
+ if (fabs(device_aspect - desired_aspect) < 0.001)
+ set_viewport(0, 0, width, height);
+ else if (device_aspect > desired_aspect)
+ {
+ float delta = (desired_aspect / device_aspect - 1.0) / 2.0 + 0.5;
+ set_viewport(width * (0.5 - delta), 0, 2.0 * width * delta, height);
+ }
+ else
+ {
+ float delta = (device_aspect / desired_aspect - 1.0) / 2.0 + 0.5;
+ set_viewport(0, height * (0.5 - delta), width, 2.0 * height * delta);
+ }
+ }
+}
+
+static void show_cursor(bool show)
+{
+ if (show)
+ while (ShowCursor(TRUE) < 0);
+ else
+ while (ShowCursor(FALSE) >= 0);
+}
+
+D3DVideo::D3DVideo(const video_info_t *info) :
+ g_pD3D(nullptr), dev(nullptr), rotation(0), needs_restore(false)
+{
+ gfx_set_dwm();
+
+ std::memset(&windowClass, 0, sizeof(windowClass));
+ windowClass.cbSize = sizeof(windowClass);
+ windowClass.style = CS_HREDRAW | CS_VREDRAW;
+ windowClass.lpfnWndProc = Callback::WindowProc;
+ windowClass.hInstance = nullptr;
+ windowClass.hCursor = LoadCursor(nullptr, IDC_ARROW);
+ windowClass.lpszClassName = "RetroArch";
+ windowClass.hIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON));
+ windowClass.hIconSm = (HICON)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON), IMAGE_ICON, 16, 16, 0);
+ if (!info->fullscreen)
+ windowClass.hbrBackground = (HBRUSH)COLOR_WINDOW;
+
+ RegisterClassEx(&windowClass);
+
+ // TODO: Multi-monitor stuff.
+ RECT rect;
+ GetClientRect(GetDesktopWindow(), &rect);
+ unsigned full_x = info->width == 0 ? (rect.right - rect.left) : info->width;
+ unsigned full_y = info->height == 0 ? (rect.bottom - rect.top) : info->height;
+ RARCH_LOG("[D3D9]: Desktop size: %dx%d.\n", (int)rect.right, (int)rect.bottom);
+
+ screen_width = info->fullscreen ? full_x : info->width;
+ screen_height = info->fullscreen ? full_y : info->height;
+
+ unsigned win_width = screen_width;
+ unsigned win_height = screen_height;
+
+ if (!info->fullscreen)
+ {
+ RECT rect = {0};
+ rect.right = screen_width;
+ rect.bottom = screen_height;
+ AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW & ~(WS_MAXIMIZEBOX | WS_THICKFRAME), FALSE);
+ win_width = rect.right - rect.left;
+ win_height = rect.bottom - rect.top;
+ }
+
+ gfx_window_title_reset();
+ char buffer[128];
+ gfx_window_title(buffer, sizeof(buffer));
+ std::string title = buffer;
+ title += " || Direct3D9";
+
+ hWnd = CreateWindowEx(0, "RetroArch", title.c_str(),
+ info->fullscreen ?
+ (WS_EX_TOPMOST | WS_POPUP) : WS_OVERLAPPEDWINDOW & ~(WS_MAXIMIZEBOX | WS_THICKFRAME),
+ info->fullscreen ? 0 : CW_USEDEFAULT,
+ info->fullscreen ? 0 : CW_USEDEFAULT, win_width, win_height,
+ nullptr, nullptr, nullptr, nullptr);
+
+ driver.display_type = RARCH_DISPLAY_WIN32;
+ driver.video_display = 0;
+ driver.video_window = (uintptr_t)hWnd;
+
+ show_cursor(!info->fullscreen);
+ Callback::quit = false;
+
+ ShowWindow(hWnd, SW_RESTORE);
+ UpdateWindow(hWnd);
+ SetForegroundWindow(hWnd);
+ SetFocus(hWnd);
+
+ video_info = *info;
+ init(video_info);
+
+ RARCH_LOG("[D3D9]: Init complete.\n");
+}
+
+void D3DVideo::deinit()
+{
+ deinit_font();
+ deinit_chain();
+ deinit_cg();
+
+ needs_restore = false;
+}
+
+D3DVideo::~D3DVideo()
+{
+ deinit();
+ if (dev)
+ dev->Release();
+ if (g_pD3D)
+ g_pD3D->Release();
+
+ DestroyWindow(hWnd);
+ UnregisterClass("RetroArch", GetModuleHandle(nullptr));
+}
+
+bool D3DVideo::restore()
+{
+ deinit();
+ try
+ {
+ needs_restore = true;
+ init(video_info);
+ needs_restore = false;
+ }
+ catch (const std::exception &e)
+ {
+ RARCH_ERR("[D3D9]: Restore error: (%s).\n", e.what());
+ needs_restore = true;
+ }
+
+ return !needs_restore;
+}
+
+bool D3DVideo::frame(const void *frame,
+ unsigned width, unsigned height, unsigned pitch,
+ const char *msg)
+{
+ if (needs_restore && !restore())
+ {
+ RARCH_ERR("[D3D9]: Restore failed.\n");
+ return false;
+ }
+
+ if (!chain->render(frame, width, height, pitch, rotation))
+ return RARCH_FALSE;
+
+ if (msg && SUCCEEDED(dev->BeginScene()))
+ {
+ font->DrawTextA(nullptr,
+ msg,
+ -1,
+ &font_rect_shifted,
+ DT_LEFT,
+ ((font_color >> 2) & 0x3f3f3f) | 0xff000000);
+
+ font->DrawTextA(nullptr,
+ msg,
+ -1,
+ &font_rect,
+ DT_LEFT,
+ font_color | 0xff000000);
+
+ dev->EndScene();
+ }
+
+ if (dev->Present(nullptr, nullptr, nullptr, nullptr) != D3D_OK)
+ {
+ needs_restore = true;
+ return true;
+ }
+
+ update_title();
+ return true;
+}
+
+void D3DVideo::set_nonblock_state(bool state)
+{
+ video_info.vsync = !state;
+ restore();
+}
+
+bool D3DVideo::alive()
+{
+ process();
+ return !Callback::quit;
+}
+
+bool D3DVideo::focus() const
+{
+ return GetFocus() == hWnd;
+}
+
+void D3DVideo::process()
+{
+ MSG msg;
+ while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+}
+
+bool D3DVideo::init_cg()
+{
+ cgCtx = cgCreateContext();
+ if (cgCtx == nullptr)
+ return false;
+
+ RARCH_LOG("[D3D9 Cg]: Created context.\n");
+
+ HRESULT ret = cgD3D9SetDevice(dev);
+ if (FAILED(ret))
+ return false;
+
+ return true;
+}
+
+void D3DVideo::deinit_cg()
+{
+ if (cgCtx)
+ {
+ cgD3D9UnloadAllPrograms();
+ cgD3D9SetDevice(nullptr);
+ cgDestroyContext(cgCtx);
+ cgCtx = nullptr;
+ }
+}
+
+void D3DVideo::init_chain_singlepass(const video_info_t &video_info)
+{
+ LinkInfo info = {0};
+
+ auto shader_type = g_settings.video.shader_type;
+ if ((shader_type == RARCH_SHADER_CG || shader_type == RARCH_SHADER_AUTO) && *g_settings.video.cg_shader_path)
+ cg_shader = info.shader_path = g_settings.video.cg_shader_path;
+
+ info.scale_x = info.scale_y = 1.0f;
+ info.filter_linear = video_info.smooth;
+ info.tex_w = info.tex_h = RARCH_SCALE_BASE * video_info.input_scale;
+ info.scale_type_x = info.scale_type_y = LinkInfo::Viewport;
+
+ chain = std::unique_ptr(new RenderChain(
+ video_info,
+ dev, cgCtx,
+ info,
+ video_info.rgb32 ? RenderChain::ARGB : RenderChain::RGB565,
+ final_viewport));
+}
+
+static std::vector tokenize(const std::string &str)
+{
+ std::vector list;
+ char *elem = strdup(str.c_str());
+
+ char *save;
+ const char *tex = strtok_r(elem, ";", &save);
+ while (tex)
+ {
+ list.push_back(tex);
+ tex = strtok_r(nullptr, ";", &save);
+ }
+ free(elem);
+
+ return list;
+}
+
+void D3DVideo::init_imports(ConfigFile &conf, const std::string &basedir)
+{
+ std::string imports;
+ if (!conf.get("imports", imports))
+ return;
+
+ std::vector list = tokenize(imports);
+
+ std::string path;
+ if (!conf.get("import_script", path))
+ throw std::runtime_error("Didn't find import_script!");
+
+ std::string py_class;
+ if (!conf.get("import_script_class", py_class))
+ throw std::runtime_error("Didn't find import_script_class!");
+
+ chain->add_state_tracker(basedir + path, py_class, list);
+}
+
+void D3DVideo::init_luts(ConfigFile &conf, const std::string &basedir)
+{
+ std::string textures;
+ if (!conf.get("textures", textures))
+ return;
+
+ std::vector list = tokenize(textures);
+
+ for (unsigned i = 0; i < list.size(); i++)
+ {
+ const std::string &id = list[i];
+
+ bool smooth = true;
+ conf.get(id + "_filter", smooth);
+
+ std::string path;
+ if (!conf.get(id, path))
+ throw std::runtime_error("Failed to get LUT texture path!");
+
+ chain->add_lut(id, basedir + path, smooth);
+ }
+}
+
+void D3DVideo::init_chain_multipass(const video_info_t &info)
+{
+ ConfigFile conf(cg_shader);
+
+ int shaders = 0;
+ if (!conf.get("shaders", shaders))
+ throw std::runtime_error("Couldn't find \"shaders\" in meta-shader");
+
+ if (shaders < 1)
+ throw std::runtime_error("Must have at least one shader!");
+
+ RARCH_LOG("[D3D9 Meta-Cg] Found %d shaders.\n", shaders);
+
+ std::string basedir = cg_shader;
+ size_t pos = basedir.rfind('/');
+ if (pos == std::string::npos)
+ pos = basedir.rfind('\\');
+ if (pos != std::string::npos)
+ basedir.replace(basedir.begin() + pos + 1, basedir.end(), "");
+
+ bool use_extra_pass = false;
+ bool use_first_pass_only = false;
+
+ std::vector shader_paths;
+ std::vector scale_types_x;
+ std::vector scale_types_y;
+ std::vector scales_x;
+ std::vector scales_y;
+ std::vector abses_x;
+ std::vector abses_y;
+ std::vector filters;
+
+ // Shader paths.
+ for (int i = 0; i < shaders; i++)
+ {
+ char buf[256];
+ snprintf(buf, sizeof(buf), "shader%d", i);
+
+ std::string relpath;
+ if (!conf.get(buf, relpath))
+ throw std::runtime_error("Couldn't locate shader path in meta-shader");
+
+ shader_paths.push_back(basedir);
+ shader_paths.back() += relpath;
+ }
+
+ // Dimensions.
+ for (int i = 0; i < shaders; i++)
+ {
+ char attr_type[64];
+ char attr_type_x[64];
+ char attr_type_y[64];
+ char attr_scale[64];
+ char attr_scale_x[64];
+ char attr_scale_y[64];
+ int abs_x = RARCH_SCALE_BASE * info.input_scale;
+ int abs_y = RARCH_SCALE_BASE * info.input_scale;
+ double scale_x = 1.0f;
+ double scale_y = 1.0f;
+
+ std::string attr = "source";
+ std::string attr_x = "source";
+ std::string attr_y = "source";
+ snprintf(attr_type, sizeof(attr_type), "scale_type%d", i);
+ snprintf(attr_type_x, sizeof(attr_type_x), "scale_type_x%d", i);
+ snprintf(attr_type_y, sizeof(attr_type_x), "scale_type_y%d", i);
+ snprintf(attr_scale, sizeof(attr_scale), "scale%d", i);
+ snprintf(attr_scale_x, sizeof(attr_scale_x), "scale_x%d", i);
+ snprintf(attr_scale_y, sizeof(attr_scale_y), "scale_y%d", i);
+
+ bool has_scale = false;
+
+ if (conf.get(attr_type, attr))
+ {
+ attr_x = attr_y = attr;
+ has_scale = true;
+ }
+ else
+ {
+ if (conf.get(attr_type_x, attr))
+ has_scale = true;
+ if (conf.get(attr_type_y, attr))
+ has_scale = true;
+ }
+
+ if (attr_x == "source")
+ scale_types_x.push_back(LinkInfo::Relative);
+ else if (attr_x == "viewport")
+ scale_types_x.push_back(LinkInfo::Viewport);
+ else if (attr_x == "absolute")
+ scale_types_x.push_back(LinkInfo::Absolute);
+ else
+ throw std::runtime_error("Invalid scale_type_x!");
+
+ if (attr_y == "source")
+ scale_types_y.push_back(LinkInfo::Relative);
+ else if (attr_y == "viewport")
+ scale_types_y.push_back(LinkInfo::Viewport);
+ else if (attr_y == "absolute")
+ scale_types_y.push_back(LinkInfo::Absolute);
+ else
+ throw std::runtime_error("Invalid scale_type_y!");
+
+ double scale = 0.0;
+ if (conf.get(attr_scale, scale))
+ scale_x = scale_y = scale;
+ else
+ {
+ conf.get(attr_scale_x, scale_x);
+ conf.get(attr_scale_y, scale_y);
+ }
+
+ int absolute = 0;
+ if (conf.get(attr_scale, absolute))
+ abs_x = abs_y = absolute;
+ else
+ {
+ conf.get(attr_scale_x, abs_x);
+ conf.get(attr_scale_y, abs_y);
+ }
+
+ scales_x.push_back(scale_x);
+ scales_y.push_back(scale_y);
+ abses_x.push_back(abs_x);
+ abses_y.push_back(abs_y);
+
+ if (has_scale && i == shaders - 1)
+ use_extra_pass = true;
+ else if (!has_scale && i == 0)
+ use_first_pass_only = true;
+ else if (i > 0)
+ use_first_pass_only = false;
+ }
+
+ // Filter options.
+ for (int i = 0; i < shaders; i++)
+ {
+ char attr_filter[64];
+ snprintf(attr_filter, sizeof(attr_filter), "filter_linear%d", i);
+ bool filter = info.smooth;
+ conf.get(attr_filter, filter);
+ filters.push_back(filter);
+ }
+
+ // Setup information for first pass.
+ LinkInfo link_info = {0};
+ link_info.shader_path = shader_paths[0];
+
+ if (use_first_pass_only)
+ {
+ link_info.scale_x = link_info.scale_y = 1.0f;
+ link_info.scale_type_x = link_info.scale_type_y = LinkInfo::Viewport;
+ }
+ else
+ {
+ link_info.scale_x = scales_x[0];
+ link_info.scale_y = scales_y[0];
+ link_info.abs_x = abses_x[0];
+ link_info.abs_y = abses_y[0];
+ link_info.scale_type_x = scale_types_x[0];
+ link_info.scale_type_y = scale_types_y[0];
+ }
+
+ link_info.filter_linear = filters[0];
+ link_info.tex_w = link_info.tex_h = info.input_scale * RARCH_SCALE_BASE;
+
+ chain = std::unique_ptr(
+ new RenderChain(
+ video_info,
+ dev, cgCtx,
+ link_info,
+ info.rgb32 ? RenderChain::ARGB : RenderChain::RGB565,
+ final_viewport));
+
+ unsigned current_width = link_info.tex_w;
+ unsigned current_height = link_info.tex_h;
+ unsigned out_width = 0;
+ unsigned out_height = 0;
+
+ for (int i = 1; i < shaders; i++)
+ {
+ RenderChain::convert_geometry(link_info,
+ out_width, out_height,
+ current_width, current_height, final_viewport);
+
+ link_info.scale_x = scales_x[i];
+ link_info.scale_y = scales_y[i];
+ link_info.tex_w = next_pow2(out_width);
+ link_info.tex_h = next_pow2(out_height);
+ link_info.scale_type_x = scale_types_x[i];
+ link_info.scale_type_y = scale_types_y[i];
+ link_info.filter_linear = filters[i];
+ link_info.shader_path = shader_paths[i];
+
+ current_width = out_width;
+ current_height = out_height;
+
+ if (i == shaders - 1 && !use_extra_pass)
+ {
+ link_info.scale_x = link_info.scale_y = 1.0f;
+ link_info.scale_type_x = link_info.scale_type_y = LinkInfo::Viewport;
+ }
+
+ chain->add_pass(link_info);
+ }
+
+ if (use_extra_pass)
+ {
+ RenderChain::convert_geometry(link_info,
+ out_width, out_height,
+ current_width, current_height, final_viewport);
+
+ link_info.scale_x = link_info.scale_y = 1.0f;
+ link_info.scale_type_x = link_info.scale_type_y = LinkInfo::Viewport;
+ link_info.filter_linear = info.smooth;
+ link_info.tex_w = next_pow2(out_width);
+ link_info.tex_h = next_pow2(out_height);
+ link_info.shader_path = "";
+ chain->add_pass(link_info);
+ }
+
+ init_luts(conf, basedir);
+ init_imports(conf, basedir);
+}
+
+bool D3DVideo::init_chain(const video_info_t &video_info)
+{
+ try
+ {
+ if (cg_shader.find(".cgp") != std::string::npos)
+ init_chain_multipass(video_info);
+ else
+ init_chain_singlepass(video_info);
+ }
+ catch (const std::exception &e)
+ {
+ RARCH_ERR("[D3D9]: Render chain error: (%s)\n", e.what());
+ return false;
+ }
+
+ return true;
+}
+
+void D3DVideo::deinit_chain()
+{
+ chain.reset();
+}
+
+bool D3DVideo::init_font()
+{
+ D3DXFONT_DESC desc = {
+ static_cast(g_settings.video.font_size), 0, 400, 0,
+ false, DEFAULT_CHARSET,
+ OUT_TT_PRECIS,
+ CLIP_DEFAULT_PRECIS,
+ DEFAULT_PITCH,
+ "Verdana" // Hardcode ftl :(
+ };
+
+ uint32_t r = static_cast(g_settings.video.msg_color_r * 255) & 0xff;
+ uint32_t g = static_cast(g_settings.video.msg_color_g * 255) & 0xff;
+ uint32_t b = static_cast(g_settings.video.msg_color_b * 255) & 0xff;
+ font_color = D3DCOLOR_XRGB(r, g, b);
+
+ return SUCCEEDED(D3DXCreateFontIndirect(dev, &desc, &font));
+}
+
+void D3DVideo::deinit_font()
+{
+ if (font)
+ font->Release();
+}
+
+void D3DVideo::update_title()
+{
+ char buffer[128];
+ if (gfx_window_title(buffer, sizeof(buffer)))
+ {
+ std::string title = buffer;
+ title += " || Direct3D9";
+ SetWindowText(hWnd, title.c_str());
+ }
+}
+
+static void *d3d9_init(const video_info_t *info, const input_driver_t **input,
+ void **input_data)
+{
+ try
+ {
+ D3DVideo *vid = new D3DVideo(info);
+ if (!vid)
+ return nullptr;
+
+ void *dinput = input_dinput.init();
+ *input = dinput ? &input_dinput : nullptr;
+ *input_data = dinput;
+
+ return vid;
+ }
+ catch (const std::exception &e)
+ {
+ RARCH_ERR("[D3D9]: Failed to init D3D9 (%s).\n", e.what());
+ return nullptr;
+ }
+}
+
+static bool d3d9_frame(void *data, const void *frame,
+ unsigned width, unsigned height, unsigned pitch,
+ const char *msg)
+{
+ return reinterpret_cast(data)->frame(frame,
+ width, height, pitch, msg);
+}
+
+static void d3d9_set_nonblock_state(void *data, bool state)
+{
+ reinterpret_cast(data)->set_nonblock_state(state);
+}
+
+static bool d3d9_alive(void *data)
+{
+ return reinterpret_cast(data)->alive();
+}
+
+static bool d3d9_focus(void *data)
+{
+ return reinterpret_cast(data)->focus();
+}
+
+static void d3d9_set_rotation(void *data, unsigned rot)
+{
+ reinterpret_cast(data)->set_rotation(rot);
+}
+
+static void d3d9_free(void *data)
+{
+ delete reinterpret_cast(data);
+}
+
+static void d3d9_viewport_size(void *data, unsigned *width, unsigned *height)
+{
+ reinterpret_cast(data)->viewport_size(*width, *height);
+}
+
+static bool d3d9_read_viewport(void *data, uint8_t *buffer)
+{
+ return reinterpret_cast(data)->read_viewport(buffer);
+}
+
+const video_driver_t video_d3d9 = {
+ d3d9_init,
+ d3d9_frame,
+ d3d9_set_nonblock_state,
+ d3d9_alive,
+ d3d9_focus,
+
+#if 0
+ d3d9_set_shader,
+#else
+ NULL,
+#endif
+
+ d3d9_free,
+ "d3d9",
+
+ d3d9_set_rotation,
+ d3d9_viewport_size,
+ d3d9_read_viewport,
+};
+
diff --git a/gfx/d3d9/d3d9.hpp b/gfx/d3d9/d3d9.hpp
new file mode 100644
index 0000000000..b075e8446c
--- /dev/null
+++ b/gfx/d3d9/d3d9.hpp
@@ -0,0 +1,100 @@
+/* RetroArch - A frontend for libretro.
+ * Copyright (C) 2010-2012 - Hans-Kristian Arntzen
+ *
+ * RetroArch is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with RetroArch.
+ * If not, see .
+ */
+
+#ifndef D3DVIDEO_HPP__
+#define D3DVIDEO_HPP__
+
+#include "../../general.h"
+#include "../../driver.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+class ConfigFile;
+class RenderChain;
+
+class D3DVideo
+{
+ public:
+ D3DVideo(const video_info_t* info);
+ bool frame(const void* frame,
+ unsigned width, unsigned height, unsigned pitch,
+ const char *msg);
+ ~D3DVideo();
+
+ bool alive();
+ bool focus() const;
+ void set_nonblock_state(bool state);
+ void set_rotation(unsigned rot);
+ void viewport_size(unsigned &width, unsigned &height);
+ bool read_viewport(uint8_t *buffer);
+
+ private:
+
+ WNDCLASSEX windowClass;
+ HWND hWnd;
+ IDirect3D9 *g_pD3D;
+ IDirect3DDevice9 *dev;
+ LPD3DXFONT font;
+
+ void calculate_rect(unsigned width, unsigned height, bool keep, float aspect);
+ void set_viewport(unsigned x, unsigned y, unsigned width, unsigned height);
+ unsigned screen_width;
+ unsigned screen_height;
+ unsigned rotation;
+ D3DVIEWPORT9 final_viewport;
+
+ std::string cg_shader;
+
+ void process();
+
+ void init(const video_info_t &info);
+ void init_base(const video_info_t &info);
+ void make_d3dpp(const video_info_t &info, D3DPRESENT_PARAMETERS &d3dpp);
+ void deinit();
+ video_info_t video_info;
+
+ bool needs_restore;
+ bool restore();
+
+ CGcontext cgCtx;
+ bool init_cg();
+ void deinit_cg();
+
+ void init_imports(ConfigFile &conf, const std::string &basedir);
+ void init_luts(ConfigFile &conf, const std::string &basedir);
+ void init_chain_singlepass(const video_info_t &video_info);
+ void init_chain_multipass(const video_info_t &video_info);
+ bool init_chain(const video_info_t &video_info);
+ std::unique_ptr chain;
+ void deinit_chain();
+
+ bool init_font();
+ void deinit_font();
+ RECT font_rect;
+ RECT font_rect_shifted;
+ uint32_t font_color;
+
+ void update_title();
+};
+
+#endif
+
diff --git a/gfx/d3d9/render_chain.cpp b/gfx/d3d9/render_chain.cpp
new file mode 100644
index 0000000000..03653a51aa
--- /dev/null
+++ b/gfx/d3d9/render_chain.cpp
@@ -0,0 +1,1039 @@
+/* RetroArch - A frontend for libretro.
+ * Copyright (C) 2010-2012 - Hans-Kristian Arntzen
+ *
+ * RetroArch is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with RetroArch.
+ * If not, see .
+ */
+
+#include "render_chain.hpp"
+#include
+
+#include
+#include
+#include
+#include
+
+namespace Global
+{
+ static const char *stock_program =
+ "void main_vertex(out float4 oPos : POSITION, float4 pos : POSITION,\n"
+ " out float2 oTex : TEXCOORD0, float2 tex : TEXCOORD0,\n"
+ " uniform float4x4 modelViewProj)\n"
+ "{\n"
+ " oPos = mul(modelViewProj, pos);\n"
+ " oTex = tex;\n"
+ "}\n"
+
+ "float4 main_fragment(uniform sampler2D samp, float2 tex : TEXCOORD0) : COLOR\n"
+ "{\n"
+ " return tex2D(samp, tex);\n"
+ "}";
+}
+
+#define FVF 0
+
+RenderChain::~RenderChain()
+{
+ clear();
+}
+
+RenderChain::RenderChain(const video_info_t &video_info,
+ IDirect3DDevice9 *dev_,
+ CGcontext cgCtx_,
+ const LinkInfo &info, PixelFormat fmt,
+ const D3DVIEWPORT9 &final_viewport_)
+ : dev(dev_), cgCtx(cgCtx_), video_info(video_info), final_viewport(final_viewport_), frame_count(0)
+{
+ pixel_size = fmt == RGB565 ? 2 : 4;
+ create_first_pass(info, fmt);
+ log_info(info);
+}
+
+void RenderChain::clear()
+{
+ for (unsigned i = 0; i < Textures; i++)
+ {
+ if (prev.tex[i])
+ prev.tex[i]->Release();
+ if (prev.vertex_buf[i])
+ prev.vertex_buf[i]->Release();
+ }
+
+ if (passes[0].vertex_decl)
+ passes[0].vertex_decl->Release();
+ for (unsigned i = 1; i < passes.size(); i++)
+ {
+ if (passes[i].tex)
+ passes[i].tex->Release();
+ if (passes[i].vertex_buf)
+ passes[i].vertex_buf->Release();
+ if (passes[i].vertex_decl)
+ passes[i].vertex_decl->Release();
+ }
+
+ for (unsigned i = 0; i < luts.size(); i++)
+ {
+ if (luts[i].tex)
+ luts[i].tex->Release();
+ }
+
+ passes.clear();
+ luts.clear();
+}
+
+void RenderChain::add_pass(const LinkInfo &info)
+{
+ Pass pass;
+ pass.info = info;
+ pass.last_width = 0;
+ pass.last_height = 0;
+
+ compile_shaders(pass, info.shader_path);
+ init_fvf(pass);
+
+ if (FAILED(dev->CreateVertexBuffer(
+ 4 * sizeof(Vertex),
+ 0,
+ FVF,
+ D3DPOOL_DEFAULT,
+ &pass.vertex_buf,
+ nullptr)))
+ {
+ throw std::runtime_error("Failed to create Vertex buf ...");
+ }
+
+ if (FAILED(dev->CreateTexture(info.tex_w, info.tex_h, 1,
+ D3DUSAGE_RENDERTARGET,
+ D3DFMT_X8R8G8B8,
+ D3DPOOL_DEFAULT,
+ &pass.tex, nullptr)))
+ {
+ throw std::runtime_error("Failed to create texture ...");
+ }
+
+ dev->SetTexture(0, pass.tex);
+ dev->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER);
+ dev->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER);
+ dev->SetTexture(0, nullptr);
+
+ passes.push_back(pass);
+
+ log_info(info);
+}
+
+void RenderChain::add_lut(const std::string &id,
+ const std::string &path,
+ bool smooth)
+{
+ IDirect3DTexture9 *lut;
+
+ RARCH_LOG("[D3D9]: Loading LUT texture: %s.\n", path.c_str());
+
+ if (FAILED(D3DXCreateTextureFromFileExA(
+ dev,
+ path.c_str(),
+ D3DX_DEFAULT_NONPOW2,
+ D3DX_DEFAULT_NONPOW2,
+ 0,
+ 0,
+ D3DFMT_FROM_FILE,
+ D3DPOOL_MANAGED,
+ smooth ? D3DX_FILTER_LINEAR : D3DX_FILTER_POINT,
+ 0,
+ 0,
+ nullptr,
+ nullptr,
+ &lut)))
+ {
+ throw std::runtime_error("Failed to load LUT!");
+ }
+
+ dev->SetTexture(0, lut);
+ dev->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER);
+ dev->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER);
+ dev->SetTexture(0, nullptr);
+
+ lut_info info = { lut, id, smooth };
+ luts.push_back(info);
+}
+
+void RenderChain::add_state_tracker(const std::string &program,
+ const std::string &py_class,
+ const std::vector &uniforms)
+{
+ //tracker = std::unique_ptr(new StateTracker(
+ // program, py_class, uniforms, video_info));
+}
+
+void RenderChain::start_render()
+{
+ passes[0].tex = prev.tex[prev.ptr];
+ passes[0].vertex_buf = prev.vertex_buf[prev.ptr];
+ passes[0].last_width = prev.last_width[prev.ptr];
+ passes[0].last_height = prev.last_height[prev.ptr];
+}
+
+void RenderChain::end_render()
+{
+ prev.last_width[prev.ptr] = passes[0].last_width;
+ prev.last_height[prev.ptr] = passes[0].last_height;
+ prev.ptr = (prev.ptr + 1) & TexturesMask;
+}
+
+bool RenderChain::render(const void *data,
+ unsigned width, unsigned height, unsigned pitch, unsigned rotation)
+{
+ start_render();
+
+ unsigned current_width = width, current_height = height;
+ unsigned out_width = 0, out_height = 0;
+ convert_geometry(passes[0].info, out_width, out_height,
+ current_width, current_height, final_viewport);
+
+ blit_to_texture(data, width, height, pitch);
+
+ // Grab back buffer.
+ IDirect3DSurface9 *back_buffer;
+ dev->GetRenderTarget(0, &back_buffer);
+
+ // In-between render target passes.
+ for (unsigned i = 0; i < passes.size() - 1; i++)
+ {
+ Pass &from_pass = passes[i];
+ Pass &to_pass = passes[i + 1];
+
+ IDirect3DSurface9 *target;
+ to_pass.tex->GetSurfaceLevel(0, &target);
+ dev->SetRenderTarget(0, target);
+
+ convert_geometry(from_pass.info,
+ out_width, out_height,
+ current_width, current_height, final_viewport);
+
+ D3DVIEWPORT9 viewport = {0};
+ viewport.X = 0;
+ viewport.Y = 0;
+ viewport.Width = out_width;
+ viewport.Height = out_height;
+ viewport.MinZ = 0.0f;
+ viewport.MaxZ = 1.0f;
+ set_viewport(viewport);
+
+ set_vertices(from_pass,
+ current_width, current_height,
+ out_width, out_height,
+ out_width, out_height, 0);
+
+ render_pass(from_pass, i + 1);
+
+ current_width = out_width;
+ current_height = out_height;
+ target->Release();
+ }
+
+ // Final pass
+ dev->SetRenderTarget(0, back_buffer);
+ Pass &last_pass = passes.back();
+
+ convert_geometry(last_pass.info,
+ out_width, out_height,
+ current_width, current_height, final_viewport);
+ set_viewport(final_viewport);
+ set_vertices(last_pass,
+ current_width, current_height,
+ out_width, out_height,
+ final_viewport.Width, final_viewport.Height,
+ rotation);
+ render_pass(last_pass, passes.size());
+
+ frame_count++;
+
+ back_buffer->Release();
+
+ end_render();
+ return true;
+}
+
+void RenderChain::create_first_pass(const LinkInfo &info, PixelFormat fmt)
+{
+ D3DXMATRIX ident;
+ D3DXMatrixIdentity(&ident);
+ dev->SetTransform(D3DTS_WORLD, &ident);
+ dev->SetTransform(D3DTS_VIEW, &ident);
+
+ Pass pass;
+ pass.info = info;
+ pass.last_width = 0;
+ pass.last_height = 0;
+
+ prev.ptr = 0;
+ for (unsigned i = 0; i < Textures; i++)
+ {
+ prev.last_width[i] = 0;
+ prev.last_height[i] = 0;
+
+ if (FAILED(dev->CreateVertexBuffer(
+ 4 * sizeof(Vertex),
+ 0,
+ FVF,
+ D3DPOOL_DEFAULT,
+ &prev.vertex_buf[i],
+ nullptr)))
+ {
+ throw std::runtime_error("Failed to create Vertex buf ...");
+ }
+
+ if (FAILED(dev->CreateTexture(info.tex_w, info.tex_h, 1, 0,
+ fmt == RGB565 ? D3DFMT_R5G6B5 : D3DFMT_X8R8G8B8,
+ D3DPOOL_MANAGED,
+ &prev.tex[i], nullptr)))
+ {
+ throw std::runtime_error("Failed to create texture ...");
+ }
+
+ dev->SetTexture(0, prev.tex[i]);
+ dev->SetSamplerState(0, D3DSAMP_MINFILTER,
+ info.filter_linear ? D3DTEXF_LINEAR : D3DTEXF_POINT);
+ dev->SetSamplerState(0, D3DSAMP_MAGFILTER,
+ info.filter_linear ? D3DTEXF_LINEAR : D3DTEXF_POINT);
+ dev->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER);
+ dev->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER);
+ dev->SetTexture(0, nullptr);
+ }
+
+ compile_shaders(pass, info.shader_path);
+ init_fvf(pass);
+ passes.push_back(pass);
+}
+
+void RenderChain::compile_shaders(Pass &pass, const std::string &shader)
+{
+ CGprofile fragment_profile = cgD3D9GetLatestPixelProfile();
+ CGprofile vertex_profile = cgD3D9GetLatestVertexProfile();
+ const char **fragment_opts = cgD3D9GetOptimalOptions(fragment_profile);
+ const char **vertex_opts = cgD3D9GetOptimalOptions(vertex_profile);
+
+ if (shader.length() > 0)
+ {
+ RARCH_LOG("[D3D9 Cg]: Compiling shader: %s.\n", shader.c_str());
+ pass.fPrg = cgCreateProgramFromFile(cgCtx, CG_SOURCE,
+ shader.c_str(), fragment_profile, "main_fragment", fragment_opts);
+
+ if (cgGetLastListing(cgCtx))
+ RARCH_ERR("[D3D9 Cg]: Fragment error:\n%s\n", cgGetLastListing(cgCtx));
+
+ pass.vPrg = cgCreateProgramFromFile(cgCtx, CG_SOURCE,
+ shader.c_str(), vertex_profile, "main_vertex", vertex_opts);
+
+ if (cgGetLastListing(cgCtx))
+ RARCH_ERR("[D3D9 Cg]: Vertex error:\n%s\n", cgGetLastListing(cgCtx));
+ }
+ else
+ {
+ RARCH_LOG("[D3D9 Cg]: Compiling stock shader.\n");
+
+ pass.fPrg = cgCreateProgram(cgCtx, CG_SOURCE, Global::stock_program,
+ fragment_profile, "main_fragment", fragment_opts);
+
+ if (cgGetLastListing(cgCtx))
+ RARCH_ERR("[D3D9 Cg]: Fragment error:\n%s\n", cgGetLastListing(cgCtx));
+
+ pass.vPrg = cgCreateProgram(cgCtx, CG_SOURCE, Global::stock_program,
+ vertex_profile, "main_vertex", vertex_opts);
+
+ if (cgGetLastListing(cgCtx))
+ RARCH_ERR("[D3D9 Cg]: Vertex error:\n%s\n", cgGetLastListing(cgCtx));
+ }
+
+ if (!pass.fPrg || !pass.vPrg)
+ throw std::runtime_error("Failed to compile shaders!");
+
+ cgD3D9LoadProgram(pass.fPrg, true, 0);
+ cgD3D9LoadProgram(pass.vPrg, true, 0);
+}
+
+void RenderChain::set_shaders(Pass &pass)
+{
+ cgD3D9BindProgram(pass.fPrg);
+ cgD3D9BindProgram(pass.vPrg);
+}
+
+void RenderChain::set_vertices(Pass &pass,
+ unsigned width, unsigned height,
+ unsigned out_width, unsigned out_height,
+ unsigned vp_width, unsigned vp_height,
+ unsigned rotation)
+{
+ const LinkInfo &info = pass.info;
+
+ if (pass.last_width != width || pass.last_height != height)
+ {
+ pass.last_width = width;
+ pass.last_height = height;
+
+ float _u = static_cast(width) / info.tex_w;
+ float _v = static_cast(height) / info.tex_h;
+ Vertex vert[4];
+ for (unsigned i = 0; i < 4; i++)
+ vert[i].z = 0.5f;
+
+ vert[0].x = 0.0f;
+ vert[1].x = out_width;
+ vert[2].x = 0.0f;
+ vert[3].x = out_width;
+ vert[0].y = out_height;
+ vert[1].y = out_height;
+ vert[2].y = 0.0f;
+ vert[3].y = 0.0f;
+
+ vert[0].u = 0.0f;
+ vert[1].u = _u;
+ vert[2].u = 0.0f;
+ vert[3].u = _u;
+ vert[0].v = 0.0f;
+ vert[1].v = 0.0f;
+ vert[2].v = _v;
+ vert[3].v = _v;
+
+ vert[0].lut_u = 0.0f;
+ vert[1].lut_u = 1.0f;
+ vert[2].lut_u = 0.0f;
+ vert[3].lut_u = 1.0f;
+ vert[0].lut_v = 0.0f;
+ vert[1].lut_v = 0.0f;
+ vert[2].lut_v = 1.0f;
+ vert[3].lut_v = 1.0f;
+
+ // Align texels and vertices.
+ for (unsigned i = 0; i < 4; i++)
+ {
+ vert[i].x -= 0.5f;
+ vert[i].y += 0.5f;
+ }
+
+ void *verts;
+ pass.vertex_buf->Lock(0, sizeof(vert), &verts, 0);
+ std::memcpy(verts, vert, sizeof(vert));
+ pass.vertex_buf->Unlock();
+ }
+
+ D3DXMATRIX proj, ortho, rot;
+ D3DXMatrixOrthoOffCenterLH(&ortho, 0, vp_width, 0, vp_height, 0, 1);
+
+ if (rotation)
+ D3DXMatrixRotationZ(&rot, rotation * (M_PI / 2.0));
+ else
+ D3DXMatrixIdentity(&rot);
+
+ D3DXMatrixMultiply(&proj, &ortho, &rot);
+
+ set_cg_mvp(pass, proj);
+
+ set_cg_params(pass,
+ width, height,
+ info.tex_w, info.tex_h,
+ vp_width, vp_height);
+}
+
+void RenderChain::set_viewport(const D3DVIEWPORT9 &vp)
+{
+ dev->SetViewport(&vp);
+}
+
+void RenderChain::set_cg_mvp(Pass &pass, const D3DXMATRIX &matrix)
+{
+ D3DXMATRIX tmp;
+ D3DXMatrixTranspose(&tmp, &matrix);
+ CGparameter cgpModelViewProj = cgGetNamedParameter(pass.vPrg, "modelViewProj");
+ if (cgpModelViewProj)
+ cgD3D9SetUniformMatrix(cgpModelViewProj, &tmp);
+}
+
+template
+static void set_cg_param(CGprogram prog, const char *param,
+ const T& val)
+{
+ CGparameter cgp = cgGetNamedParameter(prog, param);
+ if (cgp)
+ cgD3D9SetUniform(cgp, &val);
+}
+
+void RenderChain::set_cg_params(Pass &pass,
+ unsigned video_w, unsigned video_h,
+ unsigned tex_w, unsigned tex_h,
+ unsigned viewport_w, unsigned viewport_h)
+{
+ D3DXVECTOR2 video_size;
+ video_size.x = video_w;
+ video_size.y = video_h;
+ D3DXVECTOR2 texture_size;
+ texture_size.x = tex_w;
+ texture_size.y = tex_h;
+ D3DXVECTOR2 output_size;
+ output_size.x = viewport_w;
+ output_size.y = viewport_h;
+
+ set_cg_param(pass.vPrg, "IN.video_size", video_size);
+ set_cg_param(pass.fPrg, "IN.video_size", video_size);
+ set_cg_param(pass.vPrg, "IN.texture_size", texture_size);
+ set_cg_param(pass.fPrg, "IN.texture_size", texture_size);
+ set_cg_param(pass.vPrg, "IN.output_size", output_size);
+ set_cg_param(pass.fPrg, "IN.output_size", output_size);
+
+ float frame_cnt = frame_count;
+ set_cg_param(pass.fPrg, "IN.frame_count", frame_cnt);
+ set_cg_param(pass.vPrg, "IN.frame_count", frame_cnt);
+}
+
+void RenderChain::clear_texture(Pass &pass)
+{
+ D3DLOCKED_RECT d3dlr;
+ if (SUCCEEDED(pass.tex->LockRect(0, &d3dlr, nullptr, D3DLOCK_NOSYSLOCK)))
+ {
+ std::memset(d3dlr.pBits, 0, pass.info.tex_h * d3dlr.Pitch);
+ pass.tex->UnlockRect(0);
+ }
+}
+
+void RenderChain::convert_geometry(const LinkInfo &info,
+ unsigned &out_width, unsigned &out_height,
+ unsigned width, unsigned height,
+ const D3DVIEWPORT9 &final_viewport)
+{
+ switch (info.scale_type_x)
+ {
+ case LinkInfo::Viewport:
+ out_width = info.scale_x * final_viewport.Width;
+ break;
+
+ case LinkInfo::Absolute:
+ out_width = info.abs_x;
+ break;
+
+ case LinkInfo::Relative:
+ out_width = info.scale_x * width;
+ break;
+ }
+
+ switch (info.scale_type_y)
+ {
+ case LinkInfo::Viewport:
+ out_height = info.scale_y * final_viewport.Height;
+ break;
+
+ case LinkInfo::Absolute:
+ out_height = info.abs_y;
+ break;
+
+ case LinkInfo::Relative:
+ out_height = info.scale_y * height;
+ break;
+ }
+}
+
+void RenderChain::blit_to_texture(const void *frame,
+ unsigned width, unsigned height,
+ unsigned pitch)
+{
+ Pass &first = passes[0];
+ if (first.last_width != width || first.last_height != height)
+ clear_texture(first);
+
+ D3DLOCKED_RECT d3dlr;
+ if (SUCCEEDED(first.tex->LockRect(0, &d3dlr, nullptr, D3DLOCK_NOSYSLOCK)))
+ {
+ for (unsigned y = 0; y < height; y++)
+ {
+ const uint8_t *in = reinterpret_cast(frame) + y * pitch;
+ uint8_t *out = reinterpret_cast(d3dlr.pBits) + y * d3dlr.Pitch;
+ std::memcpy(out, in, width * pixel_size);
+ }
+
+ first.tex->UnlockRect(0);
+ }
+}
+
+void RenderChain::render_pass(Pass &pass, unsigned pass_index)
+{
+ set_shaders(pass);
+ dev->SetTexture(0, pass.tex);
+ dev->SetSamplerState(0, D3DSAMP_MINFILTER,
+ pass.info.filter_linear ? D3DTEXF_LINEAR : D3DTEXF_POINT);
+ dev->SetSamplerState(0, D3DSAMP_MAGFILTER,
+ pass.info.filter_linear ? D3DTEXF_LINEAR : D3DTEXF_POINT);
+
+ dev->SetVertexDeclaration(pass.vertex_decl);
+ for (unsigned i = 0; i < 4; i++)
+ dev->SetStreamSource(i, pass.vertex_buf, 0, sizeof(Vertex));
+
+ bind_orig(pass);
+ bind_prev(pass);
+ bind_pass(pass, pass_index);
+ bind_luts(pass);
+ bind_tracker(pass);
+
+ dev->Clear(0, 0, D3DCLEAR_TARGET, 0, 1, 0);
+ if (SUCCEEDED(dev->BeginScene()))
+ {
+ dev->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
+ dev->EndScene();
+ }
+
+ // So we don't render with linear filter into render targets,
+ // which apparently looked odd (too blurry).
+ dev->SetSamplerState(0, D3DSAMP_MINFILTER,
+ D3DTEXF_POINT);
+ dev->SetSamplerState(0, D3DSAMP_MAGFILTER,
+ D3DTEXF_POINT);
+
+ unbind_all();
+}
+
+void RenderChain::log_info(const LinkInfo &info)
+{
+ RARCH_LOG("[D3D9 Cg]: Render pass info:\n");
+ RARCH_LOG("\tTexture width: %u\n", info.tex_w);
+ RARCH_LOG("\tTexture height: %u\n", info.tex_h);
+
+ RARCH_LOG("\tScale type (X): ");
+ switch (info.scale_type_x)
+ {
+ case LinkInfo::Relative:
+ RARCH_LOG("Relative @ %fx\n", info.scale_x);
+ break;
+
+ case LinkInfo::Viewport:
+ RARCH_LOG("Viewport @ %fx\n", info.scale_x);
+ break;
+
+ case LinkInfo::Absolute:
+ RARCH_LOG("Absolute @ %d px\n", info.abs_x);
+ break;
+ }
+
+ RARCH_LOG("\tScale type (Y): ");
+ switch (info.scale_type_y)
+ {
+ case LinkInfo::Relative:
+ RARCH_LOG("Relative @ %fx\n", info.scale_y);
+ break;
+
+ case LinkInfo::Viewport:
+ RARCH_LOG("Viewport @ %fx\n", info.scale_y);
+ break;
+
+ case LinkInfo::Absolute:
+ RARCH_LOG("Absolute @ %d px\n", info.abs_y);
+ break;
+ }
+
+ RARCH_LOG("\tBilinear filter: %s\n", info.filter_linear ? "true" : "false");
+}
+
+void RenderChain::bind_orig(Pass &pass)
+{
+ D3DXVECTOR2 video_size;
+ video_size.x = passes[0].last_width;
+ video_size.y = passes[0].last_height;
+
+ D3DXVECTOR2 texture_size;
+ texture_size.x = passes[0].info.tex_w;
+ texture_size.y = passes[0].info.tex_h;
+
+ set_cg_param(pass.vPrg, "ORIG.video_size", video_size);
+ set_cg_param(pass.fPrg, "ORIG.video_size", video_size);
+ set_cg_param(pass.vPrg, "ORIG.texture_size", texture_size);
+ set_cg_param(pass.fPrg, "ORIG.texture_size", texture_size);
+
+ CGparameter param = cgGetNamedParameter(pass.fPrg, "ORIG.texture");
+ if (param)
+ {
+ unsigned index = cgGetParameterResourceIndex(param);
+ dev->SetTexture(index, passes[0].tex);
+ dev->SetSamplerState(index, D3DSAMP_MAGFILTER,
+ passes[0].info.filter_linear ? D3DTEXF_LINEAR : D3DTEXF_POINT);
+ dev->SetSamplerState(index, D3DSAMP_MINFILTER,
+ passes[0].info.filter_linear ? D3DTEXF_LINEAR : D3DTEXF_POINT);
+ dev->SetSamplerState(index, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER);
+ dev->SetSamplerState(index, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER);
+ bound_tex.push_back(index);
+ }
+
+ param = cgGetNamedParameter(pass.vPrg, "ORIG.tex_coord");
+ if (param)
+ {
+ unsigned index = pass.attrib_map[cgGetParameterResourceIndex(param)];
+ dev->SetStreamSource(index, passes[0].vertex_buf, 0, sizeof(Vertex));
+ bound_vert.push_back(index);
+ }
+}
+
+void RenderChain::bind_prev(Pass &pass)
+{
+ static const char *prev_names[] = {
+ "PREV",
+ "PREV1",
+ "PREV2",
+ "PREV3",
+ "PREV4",
+ "PREV5",
+ "PREV6",
+ };
+
+ char attr_texture[64];
+ char attr_input_size[64];
+ char attr_tex_size[64];
+ char attr_coord[64];
+
+ D3DXVECTOR2 texture_size;
+ texture_size.x = passes[0].info.tex_w;
+ texture_size.y = passes[0].info.tex_h;
+
+ for (unsigned i = 0; i < Textures - 1; i++)
+ {
+ std::snprintf(attr_texture, sizeof(attr_texture), "%s.texture", prev_names[i]);
+ std::snprintf(attr_input_size, sizeof(attr_input_size), "%s.video_size", prev_names[i]);
+ std::snprintf(attr_tex_size, sizeof(attr_tex_size), "%s.texture_size", prev_names[i]);
+ std::snprintf(attr_coord, sizeof(attr_coord), "%s.tex_coord", prev_names[i]);
+
+ D3DXVECTOR2 video_size;
+ video_size.x = prev.last_width[(prev.ptr - (i + 1)) & TexturesMask];
+ video_size.y = prev.last_height[(prev.ptr - (i + 1)) & TexturesMask];
+
+ set_cg_param(pass.vPrg, attr_input_size, video_size);
+ set_cg_param(pass.fPrg, attr_input_size, video_size);
+ set_cg_param(pass.vPrg, attr_tex_size, texture_size);
+ set_cg_param(pass.fPrg, attr_tex_size, texture_size);
+
+ CGparameter param = cgGetNamedParameter(pass.fPrg, attr_texture);
+ if (param)
+ {
+ unsigned index = cgGetParameterResourceIndex(param);
+
+ IDirect3DTexture9 *tex = prev.tex[(prev.ptr - (i + 1)) & TexturesMask];
+ dev->SetTexture(index, tex);
+ bound_tex.push_back(index);
+
+ dev->SetSamplerState(index, D3DSAMP_MAGFILTER,
+ passes[0].info.filter_linear ? D3DTEXF_LINEAR : D3DTEXF_POINT);
+ dev->SetSamplerState(index, D3DSAMP_MINFILTER,
+ passes[0].info.filter_linear ? D3DTEXF_LINEAR : D3DTEXF_POINT);
+ dev->SetSamplerState(index, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER);
+ dev->SetSamplerState(index, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER);
+ }
+
+ param = cgGetNamedParameter(pass.vPrg, attr_coord);
+ if (param)
+ {
+ unsigned index = pass.attrib_map[cgGetParameterResourceIndex(param)];
+ IDirect3DVertexBuffer9 *vert_buf = prev.vertex_buf[(prev.ptr - (i + 1)) & TexturesMask];
+ bound_vert.push_back(index);
+
+ dev->SetStreamSource(index, vert_buf, 0, sizeof(Vertex));
+ }
+ }
+}
+
+void RenderChain::bind_pass(Pass &pass, unsigned pass_index)
+{
+ // We only bother binding passes which are two indices behind.
+ if (pass_index < 3)
+ return;
+
+ for (unsigned i = 1; i < pass_index - 1; i++)
+ {
+ char pass_base[64];
+ snprintf(pass_base, sizeof(pass_base), "PASS%u.", i);
+
+ std::string attr_texture = pass_base;
+ attr_texture += "texture";
+ std::string attr_video_size = pass_base;
+ attr_video_size += "video_size";
+ std::string attr_texture_size = pass_base;
+ attr_texture_size += "texture_size";
+ std::string attr_tex_coord = pass_base;
+ attr_tex_coord += "tex_coord";
+
+ D3DXVECTOR2 video_size;
+ video_size.x = passes[i].last_width;
+ video_size.y = passes[i].last_height;
+
+ D3DXVECTOR2 texture_size;
+ texture_size.x = passes[i].info.tex_w;
+ texture_size.y = passes[i].info.tex_h;
+
+ set_cg_param(pass.vPrg, attr_video_size.c_str(), video_size);
+ set_cg_param(pass.fPrg, attr_video_size.c_str(), video_size);
+ set_cg_param(pass.vPrg, attr_texture_size.c_str(), texture_size);
+ set_cg_param(pass.fPrg, attr_texture_size.c_str(), texture_size);
+
+ CGparameter param = cgGetNamedParameter(pass.fPrg, attr_texture.c_str());
+ if (param)
+ {
+ unsigned index = cgGetParameterResourceIndex(param);
+ bound_tex.push_back(index);
+
+ dev->SetTexture(index, passes[i].tex);
+ dev->SetSamplerState(index, D3DSAMP_MAGFILTER,
+ passes[i].info.filter_linear ? D3DTEXF_LINEAR : D3DTEXF_POINT);
+ dev->SetSamplerState(index, D3DSAMP_MINFILTER,
+ passes[i].info.filter_linear ? D3DTEXF_LINEAR : D3DTEXF_POINT);
+ dev->SetSamplerState(index, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER);
+ dev->SetSamplerState(index, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER);
+ }
+
+ param = cgGetNamedParameter(pass.vPrg, attr_tex_coord.c_str());
+ if (param)
+ {
+ unsigned index = pass.attrib_map[cgGetParameterResourceIndex(param)];
+ dev->SetStreamSource(index, passes[i].vertex_buf, 0, sizeof(Vertex));
+ bound_vert.push_back(index);
+ }
+ }
+}
+
+void RenderChain::bind_luts(Pass &pass)
+{
+ for (unsigned i = 0; i < luts.size(); i++)
+ {
+ CGparameter param = cgGetNamedParameter(pass.fPrg, luts[i].id.c_str());
+ if (param)
+ {
+ unsigned index = cgGetParameterResourceIndex(param);
+ dev->SetTexture(index, luts[i].tex);
+ dev->SetSamplerState(index, D3DSAMP_MAGFILTER,
+ luts[i].smooth ? D3DTEXF_LINEAR : D3DTEXF_POINT);
+ dev->SetSamplerState(index, D3DSAMP_MINFILTER,
+ luts[i].smooth ? D3DTEXF_LINEAR : D3DTEXF_POINT);
+ dev->SetSamplerState(index, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER);
+ dev->SetSamplerState(index, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER);
+ bound_tex.push_back(index);
+ }
+ }
+}
+
+void RenderChain::unbind_all()
+{
+ // Have to be a bit anal about it.
+ // Render targets hate it when they have filters apparently.
+ for (unsigned i = 0; i < bound_tex.size(); i++)
+ {
+ dev->SetSamplerState(bound_tex[i], D3DSAMP_MAGFILTER,
+ D3DTEXF_POINT);
+ dev->SetSamplerState(bound_tex[i], D3DSAMP_MINFILTER,
+ D3DTEXF_POINT);
+ dev->SetTexture(bound_tex[i], nullptr);
+ }
+
+ for (unsigned i = 0; i < bound_vert.size(); i++)
+ dev->SetStreamSource(bound_vert[i], 0, 0, 0);
+
+ bound_tex.clear();
+ bound_vert.clear();
+}
+
+static inline bool validate_param_name(const char *name)
+{
+ static const char *illegal[] = {
+ "PREV.",
+ "PREV1.",
+ "PREV2.",
+ "PREV3.",
+ "PREV4.",
+ "PREV5.",
+ "PREV6.",
+ "ORIG.",
+ "IN.",
+ "PASS",
+ };
+
+ for (unsigned i = 0; i < sizeof(illegal) / sizeof(illegal[0]); i++)
+ if (std::strstr(name, illegal[i]) == name)
+ return false;
+
+ return true;
+}
+
+static inline CGparameter find_param_from_semantic(CGparameter param, const std::string &sem)
+{
+ while (param)
+ {
+ if (cgGetParameterType(param) == CG_STRUCT)
+ {
+ CGparameter ret = find_param_from_semantic(cgGetFirstStructParameter(param), sem);
+ if (ret)
+ return ret;
+ }
+ else
+ {
+ if (cgGetParameterSemantic(param) &&
+ sem == cgGetParameterSemantic(param) &&
+ cgGetParameterDirection(param) == CG_IN &&
+ cgGetParameterVariability(param) == CG_VARYING &&
+ validate_param_name(cgGetParameterName(param)))
+ {
+ return param;
+ }
+ }
+ param = cgGetNextParameter(param);
+ }
+
+ return nullptr;
+}
+
+static inline CGparameter find_param_from_semantic(CGprogram prog, const std::string &sem)
+{
+ return find_param_from_semantic(cgGetFirstParameter(prog, CG_PROGRAM), sem);
+}
+
+#define DECL_FVF_POSITION(stream) \
+ { (WORD)(stream), 0 * sizeof(float), D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, \
+ D3DDECLUSAGE_POSITION, 0 }
+#define DECL_FVF_TEXCOORD(stream, offset, index) \
+ { (WORD)(stream), (WORD)(offset * sizeof(float)), D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, \
+ D3DDECLUSAGE_TEXCOORD, (BYTE)(index) }
+#define DECL_FVF_COLOR(stream, offset, index) \
+ { (WORD)(stream), (WORD)(offset * sizeof(float)), D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, \
+ D3DDECLUSAGE_COLOR, (BYTE)(index) } \
+
+void RenderChain::init_fvf(Pass &pass)
+{
+ static const D3DVERTEXELEMENT9 decl_end = D3DDECL_END();
+ static const D3DVERTEXELEMENT9 position_decl = DECL_FVF_POSITION(0);
+ static const D3DVERTEXELEMENT9 tex_coord0 = DECL_FVF_TEXCOORD(1, 3, 0);
+ static const D3DVERTEXELEMENT9 tex_coord1 = DECL_FVF_TEXCOORD(2, 5, 1);
+ static const D3DVERTEXELEMENT9 color = DECL_FVF_COLOR(3, 3, 0);
+
+ D3DVERTEXELEMENT9 decl[MAXD3DDECLLENGTH] = {{0}};
+ if (cgD3D9GetVertexDeclaration(pass.vPrg, decl) == CG_FALSE)
+ throw std::runtime_error("Failed to get VertexDeclaration!");
+
+ unsigned count;
+ for (count = 0; count < MAXD3DDECLLENGTH; count++)
+ {
+ if (std::memcmp(&decl_end, &decl[count], sizeof(decl_end)) == 0)
+ break;
+ }
+
+ // This is completely insane.
+ // We do not have a good and easy way of setting up our
+ // attribute streams, so we have to do it ourselves, yay!
+ // Stream 0 => POSITION
+ // Stream 1 => TEXCOORD0
+ // Stream 2 => TEXCOORD1
+ // Stream 3 => COLOR // Not really used for anything.
+ // Stream {4..N} => Texture coord streams for varying resources which have no semantics.
+
+ std::vector indices(count);
+ bool texcoord0_taken = false;
+ bool texcoord1_taken = false;
+ bool stream_taken[4] = {false};
+
+ CGparameter param = find_param_from_semantic(pass.vPrg, "POSITION");
+ if (!param)
+ param = find_param_from_semantic(pass.vPrg, "POSITION0");
+ if (param)
+ {
+ stream_taken[0] = true;
+ RARCH_LOG("[FVF]: POSITION semantic found.\n");
+ unsigned index = cgGetParameterResourceIndex(param);
+ decl[index] = position_decl;
+ indices[index] = true;
+ }
+
+ param = find_param_from_semantic(pass.vPrg, "TEXCOORD");
+ if (!param)
+ param = find_param_from_semantic(pass.vPrg, "TEXCOORD0");
+ if (param)
+ {
+ stream_taken[1] = true;
+ texcoord0_taken = true;
+ RARCH_LOG("[FVF]: TEXCOORD0 semantic found.\n");
+ unsigned index = cgGetParameterResourceIndex(param);
+ decl[index] = tex_coord0;
+ indices[index] = true;
+ }
+
+ param = find_param_from_semantic(pass.vPrg, "TEXCOORD1");
+ if (param)
+ {
+ stream_taken[2] = true;
+ texcoord1_taken = true;
+ RARCH_LOG("[FVF]: TEXCOORD1 semantic found.\n");
+ unsigned index = cgGetParameterResourceIndex(param);
+ decl[index] = tex_coord1;
+ indices[index] = true;
+ }
+
+ param = find_param_from_semantic(pass.vPrg, "COLOR");
+ if (!param)
+ param = find_param_from_semantic(pass.vPrg, "COLOR0");
+ if (param)
+ {
+ stream_taken[3] = true;
+ RARCH_LOG("[FVF]: COLOR0 semantic found.\n");
+ unsigned index = cgGetParameterResourceIndex(param);
+ decl[index] = color;
+ indices[index] = true;
+ }
+
+ // Stream {0, 1, 2, 3} might be already taken. Find first vacant stream.
+ unsigned index;
+ for (index = 0; index < 4 && stream_taken[index]; index++);
+
+ // Find first vacant texcoord declaration.
+ unsigned tex_index = 0;
+ if (texcoord0_taken && texcoord1_taken)
+ tex_index = 2;
+ else if (texcoord1_taken && !texcoord0_taken)
+ tex_index = 0;
+ else if (texcoord0_taken && !texcoord1_taken)
+ tex_index = 1;
+
+ for (unsigned i = 0; i < count; i++)
+ {
+ if (indices[i])
+ pass.attrib_map.push_back(0);
+ else
+ {
+ pass.attrib_map.push_back(index);
+ decl[i] = DECL_FVF_TEXCOORD(index, 3, tex_index);
+
+ // Find next vacant stream.
+ index++;
+ while (index < 4 && stream_taken[index]) index++;
+
+ // Find next vacant texcoord declaration.
+ tex_index++;
+ if (tex_index == 1 && texcoord1_taken)
+ tex_index++;
+ }
+ }
+
+ if (FAILED(dev->CreateVertexDeclaration(decl, &pass.vertex_decl)))
+ throw std::runtime_error("Failed to set up FVF!");
+}
+
+void RenderChain::bind_tracker(Pass &pass)
+{
+#if 0
+ if (!tracker)
+ return;
+
+ auto res = tracker->get_uniforms(frame_count);
+ for (unsigned i = 0; i < res.size(); i++)
+ {
+ set_cg_param(pass.fPrg, res[i].first.c_str(), res[i].second);
+ set_cg_param(pass.vPrg, res[i].first.c_str(), res[i].second);
+ }
+#endif
+}
+
diff --git a/gfx/d3d9/render_chain.hpp b/gfx/d3d9/render_chain.hpp
new file mode 100644
index 0000000000..448ce444b3
--- /dev/null
+++ b/gfx/d3d9/render_chain.hpp
@@ -0,0 +1,160 @@
+/* RetroArch - A frontend for libretro.
+ * Copyright (C) 2010-2012 - Hans-Kristian Arntzen
+ *
+ * RetroArch is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with RetroArch.
+ * If not, see .
+ */
+
+#ifndef RENDER_CHAIN_HPP__
+#define RENDER_CHAIN_HPP__
+
+#include "d3d9.hpp"
+#include