From e53dd431b4d39f30cf20521811f472569c9ec06b Mon Sep 17 00:00:00 2001 From: Themaister Date: Fri, 26 Oct 2012 21:09:30 +0200 Subject: [PATCH] Begin merging D3D9 driver. --- Makefile.win | 14 + config.def.h | 3 + driver.c | 3 + driver.h | 1 + gfx/d3d9/config_file.hpp | 149 ++++++ gfx/d3d9/d3d9.cpp | 915 ++++++++++++++++++++++++++++++++ gfx/d3d9/d3d9.hpp | 100 ++++ gfx/d3d9/render_chain.cpp | 1039 +++++++++++++++++++++++++++++++++++++ gfx/d3d9/render_chain.hpp | 160 ++++++ gfx/gl.c | 8 +- gfx/gl_common.h | 3 + settings.c | 2 + 12 files changed, 2393 insertions(+), 4 deletions(-) create mode 100644 gfx/d3d9/config_file.hpp create mode 100644 gfx/d3d9/d3d9.cpp create mode 100644 gfx/d3d9/d3d9.hpp create mode 100644 gfx/d3d9/render_chain.cpp create mode 100644 gfx/d3d9/render_chain.hpp 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 +#include +#include + +struct Vertex +{ + float x, y, z; + float u, v; + float lut_u, lut_v; +}; + +struct LinkInfo +{ + enum ScaleType { Relative, Absolute, Viewport }; + + unsigned tex_w, tex_h; + + float scale_x, scale_y; + unsigned abs_x, abs_y; + bool filter_linear; + ScaleType scale_type_x, scale_type_y; + + std::string shader_path; +}; + +class RenderChain +{ + public: + enum PixelFormat { RGB565, ARGB }; + + RenderChain(const video_info_t &video_info, + IDirect3DDevice9 *dev, + CGcontext cgCtx, + const LinkInfo &info, + PixelFormat fmt, + const D3DVIEWPORT9 &final_viewport); + + void add_pass(const LinkInfo &info); + void add_lut(const std::string &id, const std::string &path, bool smooth); + void add_state_tracker(const std::string &program, + const std::string &py_class, + const std::vector &uniforms); + + bool render(const void *data, + unsigned width, unsigned height, unsigned pitch, unsigned rotation); + + static void convert_geometry(const LinkInfo &info, + unsigned &out_width, unsigned &out_height, + unsigned width, unsigned height, + const D3DVIEWPORT9 &final_viewport); + + void clear(); + ~RenderChain(); + + private: + + IDirect3DDevice9 *dev; + CGcontext cgCtx; + unsigned pixel_size; + + const video_info_t &video_info; + + //std::unique_ptr tracker; + + enum { Textures = 8, TexturesMask = Textures - 1 }; + struct + { + IDirect3DTexture9 *tex[Textures]; + IDirect3DVertexBuffer9 *vertex_buf[Textures]; + unsigned ptr; + unsigned last_width[Textures]; + unsigned last_height[Textures]; + } prev; + + struct Pass + { + LinkInfo info; + IDirect3DTexture9 *tex; + IDirect3DVertexBuffer9 *vertex_buf; + CGprogram vPrg, fPrg; + unsigned last_width, last_height; + + IDirect3DVertexDeclaration9 *vertex_decl; + std::vector attrib_map; + }; + std::vector passes; + + struct lut_info + { + IDirect3DTexture9 *tex; + std::string id; + bool smooth; + }; + std::vector luts; + + D3DVIEWPORT9 final_viewport; + unsigned frame_count; + + void create_first_pass(const LinkInfo &info, PixelFormat fmt); + void compile_shaders(Pass &pass, const std::string &shader); + + void set_vertices(Pass &pass, + unsigned width, unsigned height, + unsigned out_width, unsigned out_height, + unsigned vp_width, unsigned vp_height, + unsigned rotation); + void set_viewport(const D3DVIEWPORT9 &vp); + + void set_shaders(Pass &pass); + void set_cg_mvp(Pass &pass, const D3DXMATRIX &matrix); + void set_cg_params(Pass &pass, + unsigned input_w, unsigned input_h, + unsigned tex_w, unsigned tex_h, + unsigned vp_w, unsigned vp_h); + + void clear_texture(Pass &pass); + + void blit_to_texture(const void *data, + unsigned width, unsigned height, + unsigned pitch); + + void render_pass(Pass &pass, unsigned pass_index); + void log_info(const LinkInfo &info); + + void start_render(); + void end_render(); + + std::vector bound_tex; + std::vector bound_vert; + void bind_luts(Pass &pass); + void bind_orig(Pass &pass); + void bind_prev(Pass &pass); + void bind_pass(Pass &pass, unsigned pass_index); + void bind_tracker(Pass &pass); + void unbind_all(); + + void init_fvf(Pass &pass); +}; + +#endif + diff --git a/gfx/gl.c b/gfx/gl.c index ed6dacad11..991eae49c7 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -444,25 +444,25 @@ static void gl_compute_fbo_geometry(gl_t *gl, unsigned width, unsigned height, break; } - if (gl->fbo_rect[i].img_width > max_size) + if (gl->fbo_rect[i].img_width > (unsigned)max_size) { size_modified = true; gl->fbo_rect[i].img_width = max_size; } - if (gl->fbo_rect[i].img_height > max_size) + if (gl->fbo_rect[i].img_height > (unsigned)max_size) { size_modified = true; gl->fbo_rect[i].img_height = max_size; } - if (gl->fbo_rect[i].max_img_width > max_size) + if (gl->fbo_rect[i].max_img_width > (unsigned)max_size) { size_modified = true; gl->fbo_rect[i].max_img_width = max_size; } - if (gl->fbo_rect[i].max_img_height > max_size) + if (gl->fbo_rect[i].max_img_height > (unsigned)max_size) { size_modified = true; gl->fbo_rect[i].max_img_height = max_size; diff --git a/gfx/gl_common.h b/gfx/gl_common.h index cb90dcec3c..9f5af902b2 100644 --- a/gfx/gl_common.h +++ b/gfx/gl_common.h @@ -294,6 +294,9 @@ extern PFNGLACTIVETEXTUREPROC pglActiveTexture; #undef GL_UNPACK_ROW_LENGTH #endif +extern const GLfloat vertexes_flipped[]; +extern const GLfloat white_color[]; + void gl_shader_use(unsigned index); void gl_set_projection(gl_t *gl, struct gl_ortho *ortho, bool allow_rotate); void gl_set_viewport(gl_t *gl, unsigned width, unsigned height, bool force_full, bool allow_rotate); diff --git a/settings.c b/settings.c index 54301bfebf..aada7efae0 100644 --- a/settings.c +++ b/settings.c @@ -88,6 +88,8 @@ const char *config_get_default_video(void) return "xenon360"; case VIDEO_XDK_D3D: return "xdk_d3d"; + case VIDEO_D3D9: + return "d3d9"; case VIDEO_XVIDEO: return "xvideo"; case VIDEO_SDL: