Compare commits

..

2 Commits

Author SHA1 Message Date
aae22f5af9 added more constexpr 2026-04-30 05:38:20 +03:00
fa4e2b1d94 update 2026-04-30 05:35:17 +03:00
23 changed files with 24 additions and 1891 deletions

View File

@@ -1,67 +0,0 @@
---
name: karpathy-guidelines
description: Behavioral guidelines to reduce common LLM coding mistakes. Use when writing, reviewing, or refactoring code to avoid overcomplication, make surgical changes, surface assumptions, and define verifiable success criteria.
license: MIT
---
# Karpathy Guidelines
Behavioral guidelines to reduce common LLM coding mistakes, derived from [Andrej Karpathy's observations](https://x.com/karpathy/status/2015883857489522876) on LLM coding pitfalls.
**Tradeoff:** These guidelines bias toward caution over speed. For trivial tasks, use judgment.
## 1. Think Before Coding
**Don't assume. Don't hide confusion. Surface tradeoffs.**
Before implementing:
- State your assumptions explicitly. If uncertain, ask.
- If multiple interpretations exist, present them - don't pick silently.
- If a simpler approach exists, say so. Push back when warranted.
- If something is unclear, stop. Name what's confusing. Ask.
## 2. Simplicity First
**Minimum code that solves the problem. Nothing speculative.**
- No features beyond what was asked.
- No abstractions for single-use code.
- No "flexibility" or "configurability" that wasn't requested.
- No error handling for impossible scenarios.
- If you write 200 lines and it could be 50, rewrite it.
Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify.
## 3. Surgical Changes
**Touch only what you must. Clean up only your own mess.**
When editing existing code:
- Don't "improve" adjacent code, comments, or formatting.
- Don't refactor things that aren't broken.
- Match existing style, even if you'd do it differently.
- If you notice unrelated dead code, mention it - don't delete it.
When your changes create orphans:
- Remove imports/variables/functions that YOUR changes made unused.
- Don't remove pre-existing dead code unless asked.
The test: Every changed line should trace directly to the user's request.
## 4. Goal-Driven Execution
**Define success criteria. Loop until verified.**
Transform tasks into verifiable goals:
- "Add validation" → "Write tests for invalid inputs, then make them pass"
- "Fix the bug" → "Write a test that reproduces it, then make it pass"
- "Refactor X" → "Ensure tests pass before and after"
For multi-step tasks, state a brief plan:
```
1. [Step] → verify: [check]
2. [Step] → verify: [check]
3. [Step] → verify: [check]
```
Strong success criteria let you loop independently. Weak criteria ("make it work") require constant clarification.

1
.idea/editor.xml generated
View File

@@ -139,7 +139,6 @@
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppNotAllPathsReturnValue/@EntryIndexedValue" value="WARNING" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppNotAllPathsReturnValue/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppObjectMemberMightNotBeInitialized/@EntryIndexedValue" value="WARNING" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppObjectMemberMightNotBeInitialized/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppOutParameterMustBeWritten/@EntryIndexedValue" value="WARNING" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppOutParameterMustBeWritten/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppOverrideWithDifferentVisibility/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppParameterMayBeConst/@EntryIndexedValue" value="HINT" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppParameterMayBeConst/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppParameterMayBeConstPtrOrRef/@EntryIndexedValue" value="SUGGESTION" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppParameterMayBeConstPtrOrRef/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppParameterNamesMismatch/@EntryIndexedValue" value="HINT" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppParameterNamesMismatch/@EntryIndexedValue" value="HINT" type="string" />

2
.idea/omath.iml generated Normal file
View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<module classpath="CIDR" type="CPP_MODULE" version="4" />

View File

@@ -1,65 +0,0 @@
# CLAUDE.md
Behavioral guidelines to reduce common LLM coding mistakes. Merge with project-specific instructions as needed.
**Tradeoff:** These guidelines bias toward caution over speed. For trivial tasks, use judgment.
## 1. Think Before Coding
**Don't assume. Don't hide confusion. Surface tradeoffs.**
Before implementing:
- State your assumptions explicitly. If uncertain, ask.
- If multiple interpretations exist, present them - don't pick silently.
- If a simpler approach exists, say so. Push back when warranted.
- If something is unclear, stop. Name what's confusing. Ask.
## 2. Simplicity First
**Minimum code that solves the problem. Nothing speculative.**
- No features beyond what was asked.
- No abstractions for single-use code.
- No "flexibility" or "configurability" that wasn't requested.
- No error handling for impossible scenarios.
- If you write 200 lines and it could be 50, rewrite it.
Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify.
## 3. Surgical Changes
**Touch only what you must. Clean up only your own mess.**
When editing existing code:
- Don't "improve" adjacent code, comments, or formatting.
- Don't refactor things that aren't broken.
- Match existing style, even if you'd do it differently.
- If you notice unrelated dead code, mention it - don't delete it.
When your changes create orphans:
- Remove imports/variables/functions that YOUR changes made unused.
- Don't remove pre-existing dead code unless asked.
The test: Every changed line should trace directly to the user's request.
## 4. Goal-Driven Execution
**Define success criteria. Loop until verified.**
Transform tasks into verifiable goals:
- "Add validation" → "Write tests for invalid inputs, then make them pass"
- "Fix the bug" → "Write a test that reproduces it, then make it pass"
- "Refactor X" → "Ensure tests pass before and after"
For multi-step tasks, state a brief plan:
```
1. [Step] → verify: [check]
2. [Step] → verify: [check]
3. [Step] → verify: [check]
```
Strong success criteria let you loop independently. Weak criteria ("make it work") require constant clarification.
---
**These guidelines are working if:** fewer unnecessary changes in diffs, fewer rewrites due to overcomplication, and clarifying questions come before implementation rather than after mistakes.

View File

@@ -1,4 +1,5 @@
cmake_minimum_required(VERSION 3.26) cmake_minimum_required(VERSION 3.26)
file(READ VERSION OMATH_VERSION) file(READ VERSION OMATH_VERSION)
project(omath VERSION ${OMATH_VERSION} LANGUAGES CXX) project(omath VERSION ${OMATH_VERSION} LANGUAGES CXX)
@@ -30,10 +31,9 @@ option(OMATH_SUPRESS_SAFETY_CHECKS
option(OMATH_ENABLE_COVERAGE "Enable coverage" OFF) option(OMATH_ENABLE_COVERAGE "Enable coverage" OFF)
option(OMATH_ENABLE_FORCE_INLINE option(OMATH_ENABLE_FORCE_INLINE
"Will for compiler to make some functions to be force inlined no matter what" ON) "Will for compiler to make some functions to be force inlined no matter what" ON)
option(OMATH_ENABLE_LUA option(OMATH_ENABLE_LUA
"omath bindings for lua" OFF) "omath bindings for lua" OFF)
option(OMATH_ENABLE_HOOKING "omath will HooksManager that can hook DirectX/OpenGL automatically" OFF)
if(VCPKG_MANIFEST_FEATURES) if(VCPKG_MANIFEST_FEATURES)
foreach(omath_feature IN LISTS VCPKG_MANIFEST_FEATURES) foreach(omath_feature IN LISTS VCPKG_MANIFEST_FEATURES)
if(omath_feature STREQUAL "imgui") if(omath_feature STREQUAL "imgui")
@@ -48,8 +48,6 @@ if(VCPKG_MANIFEST_FEATURES)
set(OMATH_BUILD_EXAMPLES ON) set(OMATH_BUILD_EXAMPLES ON)
elseif(omath_feature STREQUAL "lua") elseif(omath_feature STREQUAL "lua")
set(OMATH_ENABLE_LUA ON) set(OMATH_ENABLE_LUA ON)
elseif(omath_feature STREQUAL "hooking")
set(OMATH_ENABLE_HOOKING ON)
endif() endif()
endforeach() endforeach()
@@ -82,10 +80,6 @@ if(${PROJECT_IS_TOP_LEVEL})
message(STATUS "[${PROJECT_NAME}]: Lua feature status ${OMATH_ENABLE_LUA}") message(STATUS "[${PROJECT_NAME}]: Lua feature status ${OMATH_ENABLE_LUA}")
endif() endif()
if(OMATH_STATIC_MSVC_RUNTIME_LIBRARY)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>" CACHE STRING "" FORCE)
endif()
file(GLOB_RECURSE OMATH_SOURCES CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/source/*.cpp") file(GLOB_RECURSE OMATH_SOURCES CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/source/*.cpp")
file(GLOB_RECURSE OMATH_HEADERS CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/include/*.hpp") file(GLOB_RECURSE OMATH_HEADERS CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/include/*.hpp")
@@ -106,20 +100,6 @@ if (OMATH_ENABLE_LUA)
target_include_directories(${PROJECT_NAME} PRIVATE ${SOL2_INCLUDE_DIRS}) target_include_directories(${PROJECT_NAME} PRIVATE ${SOL2_INCLUDE_DIRS})
endif () endif ()
if (OMATH_ENABLE_HOOKING)
target_compile_definitions(${PROJECT_NAME} PUBLIC OMATH_ENABLE_HOOKING)
find_package(safetyhook CONFIG REQUIRED)
target_link_libraries(${PROJECT_NAME} PRIVATE safetyhook::safetyhook)
if (WIN32)
target_link_libraries(${PROJECT_NAME} PRIVATE d3d9 d3d11 d3d12 dxgi opengl32 gdi32)
elseif (UNIX AND NOT APPLE)
find_package(OpenGL REQUIRED)
target_link_libraries(${PROJECT_NAME} PRIVATE OpenGL::GL ${CMAKE_DL_LIBS})
endif ()
endif ()
add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME})
target_compile_definitions(${PROJECT_NAME} PUBLIC OMATH_VERSION="${PROJECT_VERSION}") target_compile_definitions(${PROJECT_NAME} PUBLIC OMATH_VERSION="${PROJECT_VERSION}")
@@ -167,6 +147,10 @@ set_target_properties(
CXX_STANDARD 23 CXX_STANDARD 23
CXX_STANDARD_REQUIRED ON) CXX_STANDARD_REQUIRED ON)
if(OMATH_STATIC_MSVC_RUNTIME_LIBRARY)
set_target_properties(${PROJECT_NAME} PROPERTIES MSVC_RUNTIME_LIBRARY
"MultiThreaded$<$<CONFIG:Debug>:Debug>")
endif()
if(OMATH_USE_AVX2) if(OMATH_USE_AVX2)
if(MSVC) if(MSVC)

View File

@@ -56,9 +56,7 @@
"hidden": true, "hidden": true,
"inherits": ["windows-base", "vcpkg-base"], "inherits": ["windows-base", "vcpkg-base"],
"cacheVariables": { "cacheVariables": {
"VCPKG_TARGET_TRIPLET": "x64-windows-static", "VCPKG_MANIFEST_FEATURES": "tests;imgui;avx2;examples"
"VCPKG_MANIFEST_FEATURES": "tests;imgui;avx2;examples;hooking",
"OMATH_STATIC_MSVC_RUNTIME_LIBRARY": "ON"
} }
}, },
{ {
@@ -91,10 +89,9 @@
"strategy": "external" "strategy": "external"
}, },
"cacheVariables": { "cacheVariables": {
"VCPKG_TARGET_TRIPLET": "x86-windows-static", "VCPKG_TARGET_TRIPLET": "x86-windows",
"VCPKG_HOST_TRIPLET": "x64-windows", "VCPKG_HOST_TRIPLET": "x64-windows",
"VCPKG_MANIFEST_FEATURES": "tests;imgui;avx2;examples", "VCPKG_MANIFEST_FEATURES": "tests;imgui;avx2;examples"
"OMATH_STATIC_MSVC_RUNTIME_LIBRARY": "ON"
} }
}, },
{ {
@@ -117,10 +114,9 @@
"strategy": "external" "strategy": "external"
}, },
"cacheVariables": { "cacheVariables": {
"VCPKG_TARGET_TRIPLET": "arm64-windows-static", "VCPKG_TARGET_TRIPLET": "arm64-windows",
"VCPKG_HOST_TRIPLET": "arm64-windows-static", "VCPKG_HOST_TRIPLET": "arm64-windows",
"VCPKG_MANIFEST_FEATURES": "tests;imgui;examples", "VCPKG_MANIFEST_FEATURES": "tests;imgui;examples"
"OMATH_STATIC_MSVC_RUNTIME_LIBRARY": "ON"
} }
}, },
{ {

View File

@@ -1 +1 @@
5.2.0 5.0.0

View File

@@ -4,20 +4,6 @@ add_subdirectory(example_proj_mat_builder)
add_subdirectory(example_signature_scan) add_subdirectory(example_signature_scan)
add_subdirectory(example_hud) add_subdirectory(example_hud)
if(OMATH_ENABLE_HOOKING AND WIN32)
# Requires imgui with dx9-binding, dx11-binding, dx12-binding, opengl3-binding, win32-binding.
# Install via: vcpkg install imgui[dx9-binding,dx11-binding,dx12-binding,opengl3-binding,win32-binding]
find_package(imgui CONFIG QUIET)
if(imgui_FOUND)
add_subdirectory(example_dx9_hook)
add_subdirectory(example_dx11_hook)
add_subdirectory(example_dx12_hook)
add_subdirectory(example_opengl_hook)
else()
message(STATUS "[omath] imgui not found - hook examples skipped")
endif()
endif()
if(OMATH_ENABLE_VALGRIND) if(OMATH_ENABLE_VALGRIND)
omath_setup_valgrind(example_projection_matrix_builder) omath_setup_valgrind(example_projection_matrix_builder)
omath_setup_valgrind(example_signature_scan) omath_setup_valgrind(example_signature_scan)

View File

@@ -1,13 +0,0 @@
project(example_dx11_hook)
add_library(${PROJECT_NAME} SHARED dllmain.cpp)
set_target_properties(${PROJECT_NAME} PROPERTIES
CXX_STANDARD 23
MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>"
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}")
find_package(imgui CONFIG REQUIRED)
target_link_libraries(${PROJECT_NAME} PRIVATE omath::omath imgui::imgui d3d11 dxgi)

View File

@@ -1,154 +0,0 @@
#include "omath/hooks/hooks_manager.hpp"
#include <Windows.h>
#include <d3d11.h>
#include <dxgi.h>
#include <imgui.h>
#include <imgui_impl_dx11.h>
#include <imgui_impl_win32.h>
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND, UINT, WPARAM, LPARAM);
namespace
{
bool g_initialized = false;
bool g_init_attempted = false;
ID3D11Device* g_device = nullptr;
ID3D11DeviceContext* g_context = nullptr;
ID3D11RenderTargetView* g_render_target_view = nullptr;
void create_render_target(IDXGISwapChain* swap_chain)
{
ID3D11Texture2D* back_buffer = nullptr;
if (FAILED(swap_chain->GetBuffer(0, IID_PPV_ARGS(&back_buffer))))
return;
g_device->CreateRenderTargetView(back_buffer, nullptr, &g_render_target_view);
back_buffer->Release();
}
void init(IDXGISwapChain* swap_chain)
{
g_init_attempted = true;
if (FAILED(swap_chain->GetDevice(IID_PPV_ARGS(&g_device))))
return;
g_device->GetImmediateContext(&g_context);
DXGI_SWAP_CHAIN_DESC desc{};
swap_chain->GetDesc(&desc);
create_render_target(swap_chain);
ImGui::CreateContext();
ImGui::StyleColorsDark();
ImGui::GetIO().IniFilename = nullptr;
ImGui::GetIO().LogFilename = nullptr;
ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange;
ImGui_ImplWin32_Init(desc.OutputWindow);
ImGui_ImplDX11_Init(g_device, g_context);
auto& mgr = omath::hooks::HooksManager::get();
mgr.set_on_wnd_proc(
[](HWND h, UINT msg, WPARAM wp, LPARAM lp) -> std::optional<LRESULT>
{
if (ImGui_ImplWin32_WndProcHandler(h, msg, wp, lp))
return 0;
return std::nullopt;
});
std::ignore = mgr.hook_wnd_proc(desc.OutputWindow);
g_initialized = true;
}
void on_present(IDXGISwapChain* swap_chain, UINT, UINT)
{
if (!g_initialized)
{
if (!g_init_attempted)
init(swap_chain);
return;
}
if (!g_render_target_view)
create_render_target(swap_chain);
g_context->OMSetRenderTargets(1, &g_render_target_view, nullptr);
ImGui_ImplDX11_NewFrame();
ImGui_ImplWin32_NewFrame();
ImGui::NewFrame();
ImGui::SetNextWindowSize({300.f, 80.f}, ImGuiCond_Once);
ImGui::SetNextWindowPos({10.f, 10.f}, ImGuiCond_Once);
ImGui::Begin("omath | DX11 hook");
ImGui::Text("Hook active");
ImGui::Text("FPS: %.1f", ImGui::GetIO().Framerate);
ImGui::End();
ImGui::Render();
ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
}
void on_resize_buffers(IDXGISwapChain*, UINT, UINT, UINT, DXGI_FORMAT, UINT)
{
if (g_render_target_view)
{
g_render_target_view->Release();
g_render_target_view = nullptr;
}
}
} // namespace
BOOL WINAPI DllMain(HINSTANCE h_instance, DWORD reason, LPVOID)
{
if (reason == DLL_PROCESS_ATTACH)
{
DisableThreadLibraryCalls(h_instance);
CreateThread(
nullptr, 0,
[](LPVOID) -> DWORD
{
while (!GetModuleHandle("d3d11.dll"))
Sleep(100);
auto& mgr = omath::hooks::HooksManager::get();
mgr.set_on_present(on_present);
mgr.set_on_resize_buffers(on_resize_buffers);
mgr.hook_dx11();
return 0;
},
nullptr, 0, nullptr);
}
else if (reason == DLL_PROCESS_DETACH)
{
auto& mgr = omath::hooks::HooksManager::get();
mgr.unhook_wnd_proc();
mgr.unhook_dx11();
if (g_initialized)
{
ImGui_ImplDX11_Shutdown();
ImGui_ImplWin32_Shutdown();
ImGui::DestroyContext();
}
if (g_render_target_view)
{
g_render_target_view->Release();
g_render_target_view = nullptr;
}
if (g_context)
{
g_context->Release();
g_context = nullptr;
}
if (g_device)
{
g_device->Release();
g_device = nullptr;
}
}
return TRUE;
}

View File

@@ -1,13 +0,0 @@
project(example_dx12_hook)
add_library(${PROJECT_NAME} MODULE dllmain.cpp)
set_target_properties(${PROJECT_NAME} PROPERTIES
CXX_STANDARD 23
MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>"
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}")
find_package(imgui CONFIG REQUIRED)
target_link_libraries(${PROJECT_NAME} PRIVATE omath::omath imgui::imgui d3d12 dxgi)

View File

@@ -1,254 +0,0 @@
#include "omath/hooks/hooks_manager.hpp"
#include <Windows.h>
#include <d3d12.h>
#include <dxgi1_4.h>
#include <imgui.h>
#include <imgui_impl_dx12.h>
#include <imgui_impl_win32.h>
#include <tuple>
#include <vector>
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND, UINT, WPARAM, LPARAM);
bool show_menu = true;
namespace
{
struct frame_context
{
ID3D12Resource* render_target = nullptr;
D3D12_CPU_DESCRIPTOR_HANDLE rtv_handle = {};
};
bool g_initialized = false;
bool g_init_attempted = false;
ID3D12Device* g_device = nullptr;
ID3D12CommandQueue* g_command_queue = nullptr;
IDXGISwapChain3* g_swap_chain = nullptr;
ID3D12DescriptorHeap* g_rtv_heap = nullptr;
ID3D12DescriptorHeap* g_srv_heap = nullptr;
ID3D12GraphicsCommandList* g_command_list = nullptr;
ID3D12CommandAllocator* g_command_allocator = nullptr;
std::vector<frame_context> g_frames;
void init(IDXGISwapChain* swap_chain)
{
g_init_attempted = true;
if (FAILED(swap_chain->QueryInterface(IID_PPV_ARGS(&g_swap_chain))))
return;
if (FAILED(swap_chain->GetDevice(IID_PPV_ARGS(&g_device))))
return;
DXGI_SWAP_CHAIN_DESC desc{};
swap_chain->GetDesc(&desc);
const UINT buffer_count = desc.BufferCount;
{
D3D12_DESCRIPTOR_HEAP_DESC heap_desc{};
heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
heap_desc.NumDescriptors = buffer_count;
heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
if (FAILED(g_device->CreateDescriptorHeap(&heap_desc, IID_PPV_ARGS(&g_srv_heap))))
return;
}
{
D3D12_DESCRIPTOR_HEAP_DESC heap_desc{};
heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
heap_desc.NumDescriptors = buffer_count;
heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
heap_desc.NodeMask = 1;
if (FAILED(g_device->CreateDescriptorHeap(&heap_desc, IID_PPV_ARGS(&g_rtv_heap))))
return;
}
if (FAILED(g_device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT,
IID_PPV_ARGS(&g_command_allocator))))
return;
g_frames.resize(buffer_count);
const UINT rtv_size = g_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
D3D12_CPU_DESCRIPTOR_HANDLE rtv_handle = g_rtv_heap->GetCPUDescriptorHandleForHeapStart();
for (UINT i = 0; i < buffer_count; ++i)
{
g_frames[i].rtv_handle = rtv_handle;
if (FAILED(swap_chain->GetBuffer(i, IID_PPV_ARGS(&g_frames[i].render_target))))
return;
g_device->CreateRenderTargetView(g_frames[i].render_target, nullptr, rtv_handle);
rtv_handle.ptr += rtv_size;
}
if (FAILED(g_device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, g_command_allocator, nullptr,
IID_PPV_ARGS(&g_command_list))))
return;
g_command_list->Close();
ImGui::CreateContext();
ImGui::StyleColorsDark();
ImGui::GetIO().IniFilename = nullptr;
ImGui::GetIO().LogFilename = nullptr;
ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
ImGui_ImplWin32_Init(desc.OutputWindow);
ImGui_ImplDX12_Init(g_device, static_cast<int>(buffer_count), desc.BufferDesc.Format, g_srv_heap,
g_srv_heap->GetCPUDescriptorHandleForHeapStart(),
g_srv_heap->GetGPUDescriptorHandleForHeapStart());
ImGui_ImplDX12_CreateDeviceObjects();
auto& mgr = omath::hooks::HooksManager::get();
mgr.set_on_wnd_proc(
[](HWND h, UINT msg, WPARAM wp, LPARAM lp) -> std::optional<LRESULT>
{
if (!show_menu)
return std::nullopt;
ImGui_ImplWin32_WndProcHandler(h, msg, wp, lp);
return true;
});
std::ignore = mgr.hook_wnd_proc(desc.OutputWindow);
g_initialized = true;
}
void on_execute_command_lists(ID3D12CommandQueue* queue, UINT, ID3D12CommandList* const*)
{
if (!g_command_queue)
g_command_queue = queue;
}
void on_present(IDXGISwapChain* swap_chain, UINT, UINT)
{
if (!g_initialized)
{
if (!g_init_attempted && g_command_queue)
init(swap_chain);
return;
}
if (!g_command_queue)
return;
if (GetAsyncKeyState(VK_INSERT) & 1)
show_menu = !show_menu;
if (!show_menu)
return;
ImGui_ImplDX12_NewFrame();
ImGui_ImplWin32_NewFrame();
ImGui::NewFrame();
ImGui::GetIO().MouseDrawCursor = true;
ImGui::ShowDemoWindow();
ImGui::EndFrame();
const UINT buf_idx = g_swap_chain->GetCurrentBackBufferIndex();
auto& fc = g_frames[buf_idx];
g_command_allocator->Reset();
g_command_list->Reset(g_command_allocator, nullptr);
D3D12_RESOURCE_BARRIER barrier{};
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
barrier.Transition.pResource = fc.render_target;
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
g_command_list->ResourceBarrier(1, &barrier);
g_command_list->OMSetRenderTargets(1, &fc.rtv_handle, FALSE, nullptr);
g_command_list->SetDescriptorHeaps(1, &g_srv_heap);
ImGui::Render();
ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData(), g_command_list);
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
g_command_list->ResourceBarrier(1, &barrier);
g_command_list->Close();
ID3D12CommandList* cmd_lists[] = {g_command_list};
g_command_queue->ExecuteCommandLists(1, cmd_lists);
}
void release_dx12_resources()
{
for (auto& fc : g_frames)
{
if (fc.render_target)
{
fc.render_target->Release();
fc.render_target = nullptr;
}
}
g_frames.clear();
if (g_command_allocator)
{
g_command_allocator->Release();
g_command_allocator = nullptr;
}
if (g_command_list)
{
g_command_list->Release();
g_command_list = nullptr;
}
if (g_srv_heap)
{
g_srv_heap->Release();
g_srv_heap = nullptr;
}
if (g_rtv_heap)
{
g_rtv_heap->Release();
g_rtv_heap = nullptr;
}
if (g_swap_chain)
{
g_swap_chain->Release();
g_swap_chain = nullptr;
}
if (g_device)
{
g_device->Release();
g_device = nullptr;
}
}
} // namespace
BOOL WINAPI DllMain(HINSTANCE h_instance, DWORD reason, LPVOID)
{
if (reason == DLL_PROCESS_ATTACH)
{
DisableThreadLibraryCalls(h_instance);
CreateThread(
nullptr, 0,
[](LPVOID) -> DWORD
{
while (!GetModuleHandle("d3d12.dll"))
Sleep(100);
auto& mgr = omath::hooks::HooksManager::get();
mgr.set_on_present(on_present);
mgr.set_on_execute_command_lists(on_execute_command_lists);
std::ignore = mgr.hook_dx12();
return 0;
},
nullptr, 0, nullptr);
}
else if (reason == DLL_PROCESS_DETACH)
{
auto& mgr = omath::hooks::HooksManager::get();
mgr.unhook_wnd_proc();
mgr.unhook_dx12();
if (g_initialized)
{
ImGui_ImplDX12_Shutdown();
ImGui_ImplWin32_Shutdown();
ImGui::DestroyContext();
}
release_dx12_resources();
}
return TRUE;
}

View File

@@ -1,13 +0,0 @@
project(example_dx9_hook)
add_library(${PROJECT_NAME} MODULE dllmain.cpp)
set_target_properties(${PROJECT_NAME} PROPERTIES
CXX_STANDARD 23
MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>"
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}")
find_package(imgui CONFIG REQUIRED)
target_link_libraries(${PROJECT_NAME} PRIVATE omath::omath imgui::imgui d3d9)

View File

@@ -1,107 +0,0 @@
#include <Windows.h>
#include <d3d9.h>
#include <imgui.h>
#include <imgui_impl_dx9.h>
#include <imgui_impl_win32.h>
#include "omath/hooks/hooks_manager.hpp"
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND, UINT, WPARAM, LPARAM);
namespace
{
bool g_initialized = false;
bool g_init_attempted = false;
void init(IDirect3DDevice9* device)
{
g_init_attempted = true;
D3DDEVICE_CREATION_PARAMETERS params{};
if (FAILED(device->GetCreationParameters(&params)))
return;
ImGui::CreateContext();
ImGui::StyleColorsDark();
ImGui::GetIO().IniFilename = nullptr;
ImGui::GetIO().LogFilename = nullptr;
ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange;
ImGui_ImplWin32_Init(params.hFocusWindow);
ImGui_ImplDX9_Init(device);
auto& mgr = omath::hooks::HooksManager::get();
mgr.set_on_wnd_proc([](HWND h, UINT msg, WPARAM wp, LPARAM lp) -> std::optional<LRESULT> {
if (ImGui_ImplWin32_WndProcHandler(h, msg, wp, lp))
return 0;
return std::nullopt;
});
mgr.hook_wnd_proc(params.hFocusWindow);
g_initialized = true;
}
void on_present(IDirect3DDevice9* device, const RECT*, const RECT*, HWND, const RGNDATA*)
{
if (!g_initialized)
{
if (!g_init_attempted)
init(device);
return;
}
ImGui_ImplDX9_NewFrame();
ImGui_ImplWin32_NewFrame();
ImGui::NewFrame();
ImGui::SetNextWindowSize({300.f, 80.f}, ImGuiCond_Once);
ImGui::SetNextWindowPos({10.f, 10.f}, ImGuiCond_Once);
ImGui::Begin("omath | DX9 hook");
ImGui::Text("Hook active");
ImGui::Text("FPS: %.1f", ImGui::GetIO().Framerate);
ImGui::End();
ImGui::EndFrame();
ImGui::Render();
ImGui_ImplDX9_RenderDrawData(ImGui::GetDrawData());
}
void on_reset(IDirect3DDevice9*, D3DPRESENT_PARAMETERS*)
{
if (g_initialized)
ImGui_ImplDX9_InvalidateDeviceObjects();
}
} // namespace
BOOL WINAPI DllMain(HINSTANCE h_instance, DWORD reason, LPVOID)
{
if (reason == DLL_PROCESS_ATTACH)
{
DisableThreadLibraryCalls(h_instance);
CreateThread(nullptr, 0, [](LPVOID) -> DWORD
{
while (!GetModuleHandle("d3d9.dll"))
Sleep(100);
auto& mgr = omath::hooks::HooksManager::get();
mgr.set_on_dx9_present(on_present);
mgr.set_on_dx9_reset(on_reset);
mgr.hook_dx9();
return 0;
}, nullptr, 0, nullptr);
}
else if (reason == DLL_PROCESS_DETACH)
{
auto& mgr = omath::hooks::HooksManager::get();
mgr.unhook_wnd_proc();
mgr.unhook_dx9();
if (g_initialized)
{
ImGui_ImplDX9_Shutdown();
ImGui_ImplWin32_Shutdown();
ImGui::DestroyContext();
}
}
return TRUE;
}

View File

@@ -1,13 +0,0 @@
project(example_opengl_hook)
add_library(${PROJECT_NAME} MODULE dllmain.cpp)
set_target_properties(${PROJECT_NAME} PROPERTIES
CXX_STANDARD 23
MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>"
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}")
find_package(imgui CONFIG REQUIRED)
target_link_libraries(${PROJECT_NAME} PRIVATE omath::omath imgui::imgui opengl32 gdi32)

View File

@@ -1,116 +0,0 @@
#include "omath/hooks/hooks_manager.hpp"
#include <Windows.h>
#include <chrono>
#include <imgui.h>
#include <imgui_impl_opengl3.h>
#include <imgui_impl_win32.h>
#include <optional>
#include <thread>
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND, UINT, WPARAM, LPARAM);
namespace
{
bool g_initialized = false;
bool g_init_attempted = false;
bool g_show_menu = true;
constexpr auto g_module_wait_delay = std::chrono::milliseconds{100};
void init(HDC hdc)
{
g_init_attempted = true;
const HWND hwnd = WindowFromDC(hdc);
if (!hwnd)
return;
ImGui::CreateContext();
ImGui::StyleColorsDark();
ImGui::GetIO().IniFilename = nullptr;
ImGui::GetIO().LogFilename = nullptr;
ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange;
ImGui_ImplWin32_Init(hwnd);
ImGui_ImplOpenGL3_Init();
auto& mgr = omath::hooks::HooksManager::get();
mgr.set_on_wnd_proc(
[](HWND h, UINT msg, WPARAM wp, LPARAM lp) -> std::optional<LRESULT>
{
if (!g_show_menu)
return std::nullopt;
if (ImGui_ImplWin32_WndProcHandler(h, msg, wp, lp))
return 0;
return std::nullopt;
});
(void)mgr.hook_wnd_proc(hwnd);
g_initialized = true;
}
void on_swap_buffers(HDC hdc)
{
if (!g_initialized)
{
if (!g_init_attempted)
init(hdc);
return;
}
if (GetAsyncKeyState(VK_INSERT) & 1)
g_show_menu = !g_show_menu;
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplWin32_NewFrame();
ImGui::NewFrame();
if (g_show_menu)
{
ImGui::SetNextWindowSize({300.f, 100.f}, ImGuiCond_Once);
ImGui::SetNextWindowPos({10.f, 10.f}, ImGuiCond_Once);
ImGui::Begin("omath | OpenGL hook");
ImGui::Text("Hook active");
ImGui::Text("FPS: %.1f", ImGui::GetIO().Framerate);
ImGui::Text("INSERT toggles this window");
ImGui::End();
}
ImGui::Render();
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
}
void hook_when_opengl_is_loaded()
{
while (!GetModuleHandle("opengl32.dll"))
std::this_thread::sleep_for(g_module_wait_delay);
auto& mgr = omath::hooks::HooksManager::get();
mgr.set_on_opengl_swap_buffers(on_swap_buffers);
(void)mgr.hook_opengl();
}
} // namespace
BOOL WINAPI DllMain(HINSTANCE h_instance, DWORD reason, LPVOID)
{
if (reason == DLL_PROCESS_ATTACH)
{
DisableThreadLibraryCalls(h_instance);
std::thread{hook_when_opengl_is_loaded}.detach();
}
else if (reason == DLL_PROCESS_DETACH)
{
auto& mgr = omath::hooks::HooksManager::get();
mgr.unhook_wnd_proc();
mgr.unhook_opengl();
if (g_initialized)
{
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplWin32_Shutdown();
ImGui::DestroyContext();
}
}
return true;
}

View File

@@ -1,214 +0,0 @@
#pragma once
#ifdef OMATH_ENABLE_HOOKING
#include <cstdint>
#include <functional>
#include <memory>
#include <optional>
#include <shared_mutex>
#ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <Windows.h>
#include <d3d12.h>
#include <d3d9.h>
#include <dxgi.h>
#endif // _WIN32
#ifdef __linux__
#include <GL/glx.h>
#endif // __linux__
#include <safetyhook.hpp>
namespace omath::hooks
{
class HooksManager final
{
HooksManager() = default;
public:
#ifdef _WIN32
// IDXGISwapChain callbacks — shared between DX11 and DX12 (same interface, same signature).
using present_callback = std::function<void(IDXGISwapChain*, UINT, UINT)>;
using resize_buffers_callback = std::function<void(IDXGISwapChain*, UINT, UINT, UINT, DXGI_FORMAT, UINT)>;
using execute_command_lists_callback =
std::function<void(ID3D12CommandQueue*, UINT, ID3D12CommandList* const*)>;
// IDirect3DDevice9 callbacks — DX9 only.
using dx9_present_callback =
std::function<void(IDirect3DDevice9*, const RECT*, const RECT*, HWND, const RGNDATA*)>;
using dx9_reset_callback = std::function<void(IDirect3DDevice9*, D3DPRESENT_PARAMETERS*)>;
using dx9_end_scene_callback = std::function<void(IDirect3DDevice9*)>;
// OpenGL callback — Windows. Fires before the hooked buffer swap function calls the original.
using opengl_swap_buffers_callback = std::function<void(HDC)>;
// Return nullopt to pass the message to the original WndProc; return a value to intercept it.
using wnd_proc_callback = std::function<std::optional<LRESULT>(HWND, UINT, WPARAM, LPARAM)>;
#endif // _WIN32
#ifdef __linux__
// OpenGL/GLX callback — Linux. Fires before glXSwapBuffers calls the original.
using opengl_swap_buffers_callback = std::function<void(Display*, GLXDrawable)>;
#endif // __linux__
template<typename Callback>
using callback_ptr = std::shared_ptr<const Callback>;
[[nodiscard]] static HooksManager& get();
HooksManager(const HooksManager&) = delete;
HooksManager& operator=(const HooksManager&) = delete;
~HooksManager();
#ifdef _WIN32
[[nodiscard]] bool hook_dx9();
void unhook_dx9();
void set_on_dx9_present(dx9_present_callback callback);
void set_on_dx9_reset(dx9_reset_callback callback);
void set_on_dx9_end_scene(dx9_end_scene_callback callback);
[[nodiscard]] bool hook_dx11();
void unhook_dx11();
[[nodiscard]] bool hook_dx12();
void unhook_dx12();
#endif // _WIN32
[[nodiscard]] bool hook_opengl();
void unhook_opengl();
void set_on_opengl_swap_buffers(opengl_swap_buffers_callback callback);
#ifdef _WIN32
// Present and ResizeBuffers callbacks fire for whichever of DX11/DX12 is hooked.
void set_on_present(present_callback callback);
void set_on_resize_buffers(resize_buffers_callback callback);
void set_on_execute_command_lists(execute_command_lists_callback callback);
[[nodiscard]] bool hook_wnd_proc(HWND hwnd);
void unhook_wnd_proc();
void set_on_wnd_proc(wnd_proc_callback callback);
#endif // _WIN32
private:
#ifdef _WIN32
[[nodiscard]]
static HRESULT __stdcall dx9_present_detour(IDirect3DDevice9* p_device, const RECT* p_source_rect,
const RECT* p_dest_rect, HWND h_dest_window_override,
const RGNDATA* p_dirty_region);
[[nodiscard]]
static HRESULT __stdcall dx9_reset_detour(IDirect3DDevice9* p_device,
D3DPRESENT_PARAMETERS* p_presentation_parameters);
[[nodiscard]]
static HRESULT __stdcall dx9_end_scene_detour(IDirect3DDevice9* p_device);
[[nodiscard]]
static HRESULT __stdcall dx11_present_detour(IDXGISwapChain* p_swap_chain, UINT sync_interval, UINT flags);
[[nodiscard]]
static HRESULT __stdcall dx11_resize_buffers_detour(IDXGISwapChain* p_swap_chain, UINT buffer_count, UINT width,
UINT height, DXGI_FORMAT new_format, UINT swap_chain_flags);
[[nodiscard]]
static HRESULT __stdcall dx12_present_detour(IDXGISwapChain* p_swap_chain, UINT sync_interval, UINT flags);
[[nodiscard]]
static HRESULT __stdcall dx12_resize_buffers_detour(IDXGISwapChain* p_swap_chain, UINT buffer_count, UINT width,
UINT height, DXGI_FORMAT new_format, UINT swap_chain_flags);
static void __stdcall dx12_execute_command_lists_detour(ID3D12CommandQueue* p_command_queue,
UINT num_command_lists,
ID3D12CommandList* const* pp_command_lists);
[[nodiscard]]
static BOOL __stdcall opengl_wgl_swap_buffers_detour(HDC hdc);
[[nodiscard]]
static BOOL __stdcall opengl_swap_buffers_detour(HDC hdc);
[[nodiscard]]
static LRESULT __stdcall wnd_proc_detour(HWND hwnd, UINT msg, WPARAM w_param, LPARAM l_param);
#endif // _WIN32
#ifdef __linux__
static void opengl_glx_swap_buffers_detour(Display* display, GLXDrawable drawable);
#endif // __linux__
mutable std::shared_mutex m_hook_state_mutex;
#ifdef _WIN32
mutable std::shared_mutex m_dx9_present_mutex;
mutable std::shared_mutex m_dx9_reset_mutex;
mutable std::shared_mutex m_dx9_end_scene_mutex;
mutable std::shared_mutex m_present_mutex;
mutable std::shared_mutex m_resize_buffers_mutex;
mutable std::shared_mutex m_execute_command_lists_mutex;
mutable std::shared_mutex m_wnd_proc_mutex;
#endif // _WIN32
mutable std::shared_mutex m_opengl_swap_buffers_mutex;
#ifdef _WIN32
bool m_is_dx9_hooked = false;
bool m_is_dx11_hooked = false;
bool m_is_dx12_hooked = false;
bool m_is_wnd_proc_hooked = false;
HWND m_hooked_hwnd = nullptr;
WNDPROC m_original_wndproc = nullptr;
safetyhook::InlineHook m_dx9_present_hook;
safetyhook::InlineHook m_dx9_reset_hook;
safetyhook::InlineHook m_dx9_end_scene_hook;
safetyhook::InlineHook m_dx11_present_hook;
safetyhook::InlineHook m_dx11_resize_buffers_hook;
safetyhook::InlineHook m_dx12_present_hook;
safetyhook::InlineHook m_dx12_resize_buffers_hook;
safetyhook::InlineHook m_dx12_execute_command_lists_hook;
safetyhook::InlineHook m_opengl_wgl_swap_buffers_hook;
safetyhook::InlineHook m_opengl_swap_buffers_hook;
#endif // _WIN32
#ifdef __linux__
safetyhook::InlineHook m_opengl_glx_swap_buffers_hook;
#endif // __linux__
bool m_is_opengl_hooked = false;
#ifdef _WIN32
callback_ptr<dx9_present_callback> m_dx9_present_cb;
callback_ptr<dx9_reset_callback> m_dx9_reset_cb;
callback_ptr<dx9_end_scene_callback> m_dx9_end_scene_cb;
callback_ptr<present_callback> m_present_cb;
callback_ptr<resize_buffers_callback> m_resize_buffers_cb;
callback_ptr<execute_command_lists_callback> m_execute_command_lists_cb;
callback_ptr<wnd_proc_callback> m_wnd_proc_cb;
#endif // _WIN32
callback_ptr<opengl_swap_buffers_callback> m_opengl_swap_buffers_cb;
};
} // namespace omath::hooks
#else // !OMATH_ENABLE_HOOKING
namespace omath::hooks
{
class HooksManager final
{
HooksManager() = default;
public:
[[nodiscard]] static HooksManager& get();
HooksManager(const HooksManager&) = delete;
~HooksManager();
};
} // namespace omath::hooks
#endif

View File

@@ -129,8 +129,7 @@ namespace omath
{ {
return x * other.x + y * other.y; return x * other.x + y * other.y;
} }
#ifndef _MSC_VER
[[nodiscard]] constexpr Type length() const noexcept [[nodiscard]] constexpr Type length() const noexcept
{ {
return std::hypot(this->x, this->y); return std::hypot(this->x, this->y);
@@ -141,18 +140,6 @@ namespace omath
const Type len = length(); const Type len = length();
return len > 0.f ? *this / len : *this; return len > 0.f ? *this / len : *this;
} }
#else
[[nodiscard]] Type length() const noexcept
{
return std::hypot(x, y);
}
[[nodiscard]] Vector2 normalized() const noexcept
{
const Type len = length();
return len > static_cast<Type>(0) ? *this / len : *this;
}
#endif
[[nodiscard]] constexpr Type length_sqr() const noexcept [[nodiscard]] constexpr Type length_sqr() const noexcept
{ {
return x * x + y * y; return x * x + y * y;

View File

@@ -129,7 +129,6 @@ namespace omath
return Vector2<Type>::dot(other) + z * other.z; return Vector2<Type>::dot(other) + z * other.z;
} }
#ifndef _MSC_VER
[[nodiscard]] constexpr Type length() const [[nodiscard]] constexpr Type length() const
{ {
return std::hypot(this->x, this->y, z); return std::hypot(this->x, this->y, z);
@@ -149,29 +148,6 @@ namespace omath
return length_value != 0 ? *this / length_value : *this; return length_value != 0 ? *this / length_value : *this;
} }
#else
[[nodiscard]] Type length() const noexcept
{
return std::hypot(this->x, this->y, z);
}
[[nodiscard]] Vector3 normalized() const noexcept
{
const Type len = this->length();
return len != static_cast<Type>(0) ? *this / len : *this;
}
[[nodiscard]] Type length_2d() const noexcept
{
return Vector2<Type>::length();
}
[[nodiscard]] Type distance_to(const Vector3& v_other) const noexcept
{
return (*this - v_other).length();
}
#endif
[[nodiscard]] constexpr Type length_sqr() const noexcept [[nodiscard]] constexpr Type length_sqr() const noexcept
{ {

View File

@@ -70,31 +70,31 @@ namespace omath
} }
[[nodiscard]] [[nodiscard]]
Type sin() const noexcept constexpr Type sin() const noexcept
{ {
return std::sin(as_radians()); return std::sin(as_radians());
} }
[[nodiscard]] [[nodiscard]]
Type cos() const noexcept constexpr Type cos() const noexcept
{ {
return std::cos(as_radians()); return std::cos(as_radians());
} }
[[nodiscard]] [[nodiscard]]
Type tan() const noexcept constexpr Type tan() const noexcept
{ {
return std::tan(as_radians()); return std::tan(as_radians());
} }
[[nodiscard]] [[nodiscard]]
Type atan() const noexcept constexpr Type atan() const noexcept
{ {
return std::atan(as_radians()); return std::atan(as_radians());
} }
[[nodiscard]] [[nodiscard]]
Type cot() const noexcept constexpr Type cot() const noexcept
{ {
return cos() / sin(); return cos() / sin();
} }

View File

@@ -24,7 +24,7 @@ namespace omath::angles
template<class Type> template<class Type>
requires std::is_floating_point_v<Type> requires std::is_floating_point_v<Type>
[[nodiscard]] Type horizontal_fov_to_vertical(const Type& horizontal_fov, const Type& aspect) noexcept [[nodiscard]] constexpr Type horizontal_fov_to_vertical(const Type& horizontal_fov, const Type& aspect) noexcept
{ {
const auto fov_rad = degrees_to_radians(horizontal_fov); const auto fov_rad = degrees_to_radians(horizontal_fov);
@@ -35,7 +35,7 @@ namespace omath::angles
template<class Type> template<class Type>
requires std::is_floating_point_v<Type> requires std::is_floating_point_v<Type>
[[nodiscard]] Type vertical_fov_to_horizontal(const Type& vertical_fov, const Type& aspect) noexcept [[nodiscard]] constexpr Type vertical_fov_to_horizontal(const Type& vertical_fov, const Type& aspect) noexcept
{ {
const auto fov_as_radians = degrees_to_radians(vertical_fov); const auto fov_as_radians = degrees_to_radians(vertical_fov);
@@ -47,7 +47,7 @@ namespace omath::angles
template<class Type> template<class Type>
requires std::is_arithmetic_v<Type> requires std::is_arithmetic_v<Type>
[[nodiscard]] Type wrap_angle(const Type& angle, const Type& min, const Type& max) noexcept [[nodiscard]] constexpr Type wrap_angle(const Type& angle, const Type& min, const Type& max) noexcept
{ {
if (angle <= max && angle >= min) if (angle <= max && angle >= min)
return angle; return angle;

View File

@@ -1,742 +0,0 @@
#include "omath/hooks/hooks_manager.hpp"
#ifdef OMATH_ENABLE_HOOKING
#ifdef _WIN32
#include <d3d11.h>
#endif // _WIN32
#ifdef __linux__
#include <dlfcn.h>
#endif // __linux__
namespace
{
#ifdef _WIN32
thread_local bool g_is_inside_opengl_swap_buffers = false;
class DummyWindow final
{
WNDCLASSEX m_window_class{};
HWND m_window_handle = nullptr;
public:
DummyWindow()
{
m_window_class.cbSize = sizeof(WNDCLASSEX);
m_window_class.style = CS_HREDRAW | CS_VREDRAW;
m_window_class.lpfnWndProc = DefWindowProc;
m_window_class.hInstance = GetModuleHandle(nullptr);
m_window_class.lpszClassName = "OM";
RegisterClassEx(&m_window_class);
m_window_handle = CreateWindow(m_window_class.lpszClassName, "Dummy", WS_OVERLAPPEDWINDOW, 0, 0, 100, 100,
nullptr, nullptr, m_window_class.hInstance, nullptr);
}
~DummyWindow()
{
if (m_window_handle)
DestroyWindow(m_window_handle);
UnregisterClass(m_window_class.lpszClassName, m_window_class.hInstance);
}
[[nodiscard]] HWND handle() const
{
return m_window_handle;
}
[[nodiscard]] bool valid() const
{
return m_window_handle != nullptr;
}
};
void* vtable_fn(void* com_obj, std::size_t index)
{
return (*reinterpret_cast<void***>(com_obj))[index];
}
void* module_proc(const char* module_name, const char* proc_name)
{
const HMODULE module = GetModuleHandle(module_name);
if (!module)
return nullptr;
return reinterpret_cast<void*>(GetProcAddress(module, proc_name));
}
struct dx12_vtable_fns
{
void* present;
void* resize_buffers;
void* execute_command_lists;
};
// RAII wrapper so all early-return paths release COM objects automatically.
struct dx12_com_objects
{
IDXGIFactory* factory = nullptr;
ID3D12Device* device = nullptr;
ID3D12CommandQueue* command_queue = nullptr;
ID3D12CommandAllocator* command_allocator = nullptr;
ID3D12GraphicsCommandList* command_list = nullptr;
IDXGISwapChain* swap_chain = nullptr;
dx12_com_objects() = default;
dx12_com_objects(const dx12_com_objects&) = delete;
dx12_com_objects& operator=(const dx12_com_objects&) = delete;
~dx12_com_objects()
{
if (swap_chain)
swap_chain->Release();
if (command_list)
command_list->Release();
if (command_allocator)
command_allocator->Release();
if (command_queue)
command_queue->Release();
if (device)
device->Release();
if (factory)
factory->Release();
}
};
std::optional<dx12_vtable_fns> read_dx12_vtable_fns(HWND hwnd)
{
using create_dxgi_factory_fn = HRESULT(__stdcall*)(REFIID, void**);
using d3d12_create_device_fn = HRESULT(__stdcall*)(IUnknown*, D3D_FEATURE_LEVEL, REFIID, void**);
const HMODULE d3d12_module = GetModuleHandle("d3d12.dll");
const HMODULE dxgi_module = GetModuleHandle("dxgi.dll");
if (!d3d12_module || !dxgi_module)
return std::nullopt;
const auto create_dxgi_factory =
reinterpret_cast<create_dxgi_factory_fn>(GetProcAddress(dxgi_module, "CreateDXGIFactory"));
const auto d3d12_create_device =
reinterpret_cast<d3d12_create_device_fn>(GetProcAddress(d3d12_module, "D3D12CreateDevice"));
if (!create_dxgi_factory || !d3d12_create_device)
return std::nullopt;
dx12_com_objects objs;
if (FAILED(create_dxgi_factory(__uuidof(IDXGIFactory), reinterpret_cast<void**>(&objs.factory))))
return std::nullopt;
IDXGIAdapter* adapter = nullptr;
if (objs.factory->EnumAdapters(0, &adapter) == DXGI_ERROR_NOT_FOUND)
return std::nullopt;
const HRESULT device_hr = d3d12_create_device(adapter, D3D_FEATURE_LEVEL_11_0, __uuidof(ID3D12Device),
reinterpret_cast<void**>(&objs.device));
adapter->Release();
if (FAILED(device_hr))
return std::nullopt;
D3D12_COMMAND_QUEUE_DESC queue_desc{};
queue_desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
if (FAILED(objs.device->CreateCommandQueue(&queue_desc, __uuidof(ID3D12CommandQueue),
reinterpret_cast<void**>(&objs.command_queue))))
return std::nullopt;
if (FAILED(objs.device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, __uuidof(ID3D12CommandAllocator),
reinterpret_cast<void**>(&objs.command_allocator))))
return std::nullopt;
if (FAILED(objs.device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, objs.command_allocator, nullptr,
__uuidof(ID3D12GraphicsCommandList),
reinterpret_cast<void**>(&objs.command_list))))
return std::nullopt;
DXGI_SWAP_CHAIN_DESC swap_chain_desc{};
swap_chain_desc.BufferDesc.Width = 100;
swap_chain_desc.BufferDesc.Height = 100;
swap_chain_desc.BufferDesc.RefreshRate = {60, 1};
swap_chain_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
swap_chain_desc.SampleDesc = {1, 0};
swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swap_chain_desc.BufferCount = 2;
swap_chain_desc.OutputWindow = hwnd;
swap_chain_desc.Windowed = TRUE;
swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
swap_chain_desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
if (FAILED(objs.factory->CreateSwapChain(objs.command_queue, &swap_chain_desc, &objs.swap_chain)))
return std::nullopt;
// objs destructor releases all COM objects after we capture the addresses.
return dx12_vtable_fns{
vtable_fn(objs.swap_chain, 8), // IDXGISwapChain::Present
vtable_fn(objs.swap_chain, 13), // IDXGISwapChain::ResizeBuffers
vtable_fn(objs.command_queue, 10), // ID3D12CommandQueue::ExecuteCommandLists
};
}
#endif // _WIN32
} // namespace
namespace omath::hooks
{
HooksManager& HooksManager::get()
{
static HooksManager obj;
return obj;
}
HooksManager::~HooksManager()
{
#ifdef _WIN32
unhook_wnd_proc();
unhook_dx9();
unhook_dx11();
unhook_dx12();
#endif // _WIN32
unhook_opengl();
}
#ifdef _WIN32
bool HooksManager::hook_dx9()
{
std::unique_lock lock(m_hook_state_mutex);
if (m_is_dx9_hooked)
return true;
const DummyWindow window;
if (!window.valid())
return false;
const HMODULE d3d9_module = GetModuleHandle("d3d9.dll");
if (!d3d9_module)
return false;
using direct3d_create9_fn = IDirect3D9*(__stdcall*)(UINT);
const auto direct3d_create9 =
reinterpret_cast<direct3d_create9_fn>(GetProcAddress(d3d9_module, "Direct3DCreate9"));
if (!direct3d_create9)
return false;
IDirect3D9* d3d9 = direct3d_create9(D3D_SDK_VERSION);
if (!d3d9)
return false;
D3DPRESENT_PARAMETERS pp{};
pp.SwapEffect = D3DSWAPEFFECT_DISCARD;
pp.hDeviceWindow = window.handle();
pp.Windowed = TRUE;
IDirect3DDevice9* device = nullptr;
if (FAILED(d3d9->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, window.handle(),
D3DCREATE_SOFTWARE_VERTEXPROCESSING, &pp, &device)))
{
d3d9->Release();
return false;
}
// IDirect3DDevice9 vtable indices (from IUnknown base):
// Reset = 16
// Present = 17
// EndScene = 42
m_dx9_present_hook =
safetyhook::create_inline(vtable_fn(device, 17), reinterpret_cast<void*>(&dx9_present_detour));
m_dx9_reset_hook = safetyhook::create_inline(vtable_fn(device, 16), reinterpret_cast<void*>(&dx9_reset_detour));
m_dx9_end_scene_hook =
safetyhook::create_inline(vtable_fn(device, 42), reinterpret_cast<void*>(&dx9_end_scene_detour));
device->Release();
d3d9->Release();
if (!m_dx9_present_hook || !m_dx9_reset_hook || !m_dx9_end_scene_hook)
{
m_dx9_present_hook = {};
m_dx9_reset_hook = {};
m_dx9_end_scene_hook = {};
return false;
}
m_is_dx9_hooked = true;
return true;
}
void HooksManager::unhook_dx9()
{
std::unique_lock lock(m_hook_state_mutex);
m_dx9_present_hook = {};
m_dx9_reset_hook = {};
m_dx9_end_scene_hook = {};
m_is_dx9_hooked = false;
}
void HooksManager::set_on_dx9_present(dx9_present_callback callback)
{
std::unique_lock lock(m_dx9_present_mutex);
m_dx9_present_cb = callback ? std::make_shared<dx9_present_callback>(std::move(callback)) : nullptr;
}
void HooksManager::set_on_dx9_reset(dx9_reset_callback callback)
{
std::unique_lock lock(m_dx9_reset_mutex);
m_dx9_reset_cb = callback ? std::make_shared<dx9_reset_callback>(std::move(callback)) : nullptr;
}
void HooksManager::set_on_dx9_end_scene(dx9_end_scene_callback callback)
{
std::unique_lock lock(m_dx9_end_scene_mutex);
m_dx9_end_scene_cb = callback ? std::make_shared<dx9_end_scene_callback>(std::move(callback)) : nullptr;
}
bool HooksManager::hook_dx11()
{
std::unique_lock lock(m_hook_state_mutex);
if (m_is_dx11_hooked)
return true;
const DummyWindow window;
if (!window.valid())
return false;
const HMODULE d3d11_module = GetModuleHandle("d3d11.dll");
if (!d3d11_module)
return false;
using d3d11_create_device_and_swap_chain_fn =
HRESULT(__stdcall*)(IDXGIAdapter*, D3D_DRIVER_TYPE, HMODULE, UINT, const D3D_FEATURE_LEVEL*, UINT, UINT,
const DXGI_SWAP_CHAIN_DESC*, IDXGISwapChain**, ID3D11Device**, D3D_FEATURE_LEVEL*,
ID3D11DeviceContext**);
const auto create_device_and_swap_chain = reinterpret_cast<d3d11_create_device_and_swap_chain_fn>(
GetProcAddress(d3d11_module, "D3D11CreateDeviceAndSwapChain"));
if (!create_device_and_swap_chain)
return false;
DXGI_SWAP_CHAIN_DESC swap_chain_desc{};
swap_chain_desc.BufferDesc.Width = 100;
swap_chain_desc.BufferDesc.Height = 100;
swap_chain_desc.BufferDesc.RefreshRate = {60, 1};
swap_chain_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
swap_chain_desc.SampleDesc = {1, 0};
swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swap_chain_desc.BufferCount = 1;
swap_chain_desc.OutputWindow = window.handle();
swap_chain_desc.Windowed = TRUE;
swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
constexpr D3D_FEATURE_LEVEL feature_levels[] = {D3D_FEATURE_LEVEL_11_0};
ID3D11Device* device = nullptr;
ID3D11DeviceContext* device_context = nullptr;
IDXGISwapChain* swap_chain = nullptr;
if (FAILED(create_device_and_swap_chain(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, 0, feature_levels, 1,
D3D11_SDK_VERSION, &swap_chain_desc, &swap_chain, &device, nullptr,
&device_context)))
return false;
m_dx11_present_hook = safetyhook::create_inline(vtable_fn(swap_chain, 8), // IDXGISwapChain::Present
reinterpret_cast<void*>(&dx11_present_detour));
m_dx11_resize_buffers_hook =
safetyhook::create_inline(vtable_fn(swap_chain, 13), // IDXGISwapChain::ResizeBuffers
reinterpret_cast<void*>(&dx11_resize_buffers_detour));
swap_chain->Release();
device_context->Release();
device->Release();
if (!m_dx11_present_hook || !m_dx11_resize_buffers_hook)
{
m_dx11_present_hook = {};
m_dx11_resize_buffers_hook = {};
return false;
}
m_is_dx11_hooked = true;
return true;
}
void HooksManager::unhook_dx11()
{
std::unique_lock lock(m_hook_state_mutex);
m_dx11_present_hook = {};
m_dx11_resize_buffers_hook = {};
m_is_dx11_hooked = false;
}
bool HooksManager::hook_dx12()
{
std::unique_lock lock(m_hook_state_mutex);
if (m_is_dx12_hooked)
return true;
const DummyWindow window;
if (!window.valid())
return false;
const auto fns = read_dx12_vtable_fns(window.handle());
if (!fns)
return false;
m_dx12_present_hook = safetyhook::create_inline(fns->present, reinterpret_cast<void*>(&dx12_present_detour));
m_dx12_resize_buffers_hook =
safetyhook::create_inline(fns->resize_buffers, reinterpret_cast<void*>(&dx12_resize_buffers_detour));
m_dx12_execute_command_lists_hook = safetyhook::create_inline(
fns->execute_command_lists, reinterpret_cast<void*>(&dx12_execute_command_lists_detour));
if (!m_dx12_present_hook || !m_dx12_resize_buffers_hook || !m_dx12_execute_command_lists_hook)
{
m_dx12_present_hook = {};
m_dx12_resize_buffers_hook = {};
m_dx12_execute_command_lists_hook = {};
return false;
}
m_is_dx12_hooked = true;
return true;
}
void HooksManager::unhook_dx12()
{
std::unique_lock lock(m_hook_state_mutex);
m_dx12_present_hook = {};
m_dx12_resize_buffers_hook = {};
m_dx12_execute_command_lists_hook = {};
m_is_dx12_hooked = false;
}
bool HooksManager::hook_opengl()
{
std::unique_lock lock(m_hook_state_mutex);
if (m_is_opengl_hooked)
return true;
if (void* wgl_swap_buffers = module_proc("opengl32.dll", "wglSwapBuffers"))
{
m_opengl_wgl_swap_buffers_hook = safetyhook::create_inline(
wgl_swap_buffers, reinterpret_cast<void*>(&opengl_wgl_swap_buffers_detour));
}
if (void* swap_buffers = module_proc("gdi32.dll", "SwapBuffers"))
{
m_opengl_swap_buffers_hook =
safetyhook::create_inline(swap_buffers, reinterpret_cast<void*>(&opengl_swap_buffers_detour));
}
if (!m_opengl_wgl_swap_buffers_hook && !m_opengl_swap_buffers_hook)
{
m_opengl_wgl_swap_buffers_hook = {};
m_opengl_swap_buffers_hook = {};
return false;
}
m_is_opengl_hooked = true;
return true;
}
void HooksManager::unhook_opengl()
{
std::unique_lock lock(m_hook_state_mutex);
m_opengl_wgl_swap_buffers_hook = {};
m_opengl_swap_buffers_hook = {};
m_is_opengl_hooked = false;
}
void HooksManager::set_on_present(present_callback callback)
{
std::unique_lock lock(m_present_mutex);
m_present_cb = callback ? std::make_shared<present_callback>(std::move(callback)) : nullptr;
}
void HooksManager::set_on_resize_buffers(resize_buffers_callback callback)
{
std::unique_lock lock(m_resize_buffers_mutex);
m_resize_buffers_cb = callback ? std::make_shared<resize_buffers_callback>(std::move(callback)) : nullptr;
}
void HooksManager::set_on_execute_command_lists(execute_command_lists_callback callback)
{
std::unique_lock lock(m_execute_command_lists_mutex);
m_execute_command_lists_cb =
callback ? std::make_shared<execute_command_lists_callback>(std::move(callback)) : nullptr;
}
bool HooksManager::hook_wnd_proc(HWND hwnd)
{
std::unique_lock lock(m_hook_state_mutex);
if (m_is_wnd_proc_hooked)
return true;
const auto prev = reinterpret_cast<WNDPROC>(
SetWindowLongPtr(hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(&wnd_proc_detour)));
if (!prev)
return false;
m_hooked_hwnd = hwnd;
m_original_wndproc = prev;
m_is_wnd_proc_hooked = true;
return true;
}
void HooksManager::unhook_wnd_proc()
{
std::unique_lock lock(m_hook_state_mutex);
if (!m_is_wnd_proc_hooked)
return;
SetWindowLongPtr(m_hooked_hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(m_original_wndproc));
m_hooked_hwnd = nullptr;
m_original_wndproc = nullptr;
m_is_wnd_proc_hooked = false;
}
void HooksManager::set_on_wnd_proc(wnd_proc_callback callback)
{
std::unique_lock lock(m_wnd_proc_mutex);
m_wnd_proc_cb = callback ? std::make_shared<wnd_proc_callback>(std::move(callback)) : nullptr;
}
// Detour implementations: copy a shared_ptr to the callback under shared lock, call it unlocked,
// then call original. This avoids copying captured lambda state every frame and still avoids
// a deadlock if the callback itself calls set_on_*().
HRESULT __stdcall HooksManager::dx9_present_detour(IDirect3DDevice9* p_device, const RECT* p_source_rect,
const RECT* p_dest_rect, HWND h_dest_window_override,
const RGNDATA* p_dirty_region)
{
auto& mgr = get();
callback_ptr<dx9_present_callback> cb;
{
std::shared_lock lock(mgr.m_dx9_present_mutex);
cb = mgr.m_dx9_present_cb;
}
if (cb)
(*cb)(p_device, p_source_rect, p_dest_rect, h_dest_window_override, p_dirty_region);
return mgr.m_dx9_present_hook.call<HRESULT>(p_device, p_source_rect, p_dest_rect, h_dest_window_override,
p_dirty_region);
}
HRESULT __stdcall HooksManager::dx9_reset_detour(IDirect3DDevice9* p_device,
D3DPRESENT_PARAMETERS* p_presentation_parameters)
{
auto& mgr = get();
callback_ptr<dx9_reset_callback> cb;
{
std::shared_lock lock(mgr.m_dx9_reset_mutex);
cb = mgr.m_dx9_reset_cb;
}
if (cb)
(*cb)(p_device, p_presentation_parameters);
return mgr.m_dx9_reset_hook.call<HRESULT>(p_device, p_presentation_parameters);
}
HRESULT __stdcall HooksManager::dx9_end_scene_detour(IDirect3DDevice9* p_device)
{
auto& mgr = get();
callback_ptr<dx9_end_scene_callback> cb;
{
std::shared_lock lock(mgr.m_dx9_end_scene_mutex);
cb = mgr.m_dx9_end_scene_cb;
}
if (cb)
(*cb)(p_device);
return mgr.m_dx9_end_scene_hook.call<HRESULT>(p_device);
}
HRESULT __stdcall HooksManager::dx11_present_detour(IDXGISwapChain* p_swap_chain, UINT sync_interval, UINT flags)
{
auto& mgr = get();
callback_ptr<present_callback> cb;
{
std::shared_lock lock(mgr.m_present_mutex);
cb = mgr.m_present_cb;
}
if (cb)
(*cb)(p_swap_chain, sync_interval, flags);
return mgr.m_dx11_present_hook.call<HRESULT>(p_swap_chain, sync_interval, flags);
}
HRESULT __stdcall HooksManager::dx11_resize_buffers_detour(IDXGISwapChain* p_swap_chain, UINT buffer_count,
UINT width, UINT height, DXGI_FORMAT new_format,
UINT swap_chain_flags)
{
auto& mgr = get();
callback_ptr<resize_buffers_callback> cb;
{
std::shared_lock lock(mgr.m_resize_buffers_mutex);
cb = mgr.m_resize_buffers_cb;
}
if (cb)
(*cb)(p_swap_chain, buffer_count, width, height, new_format, swap_chain_flags);
return mgr.m_dx11_resize_buffers_hook.call<HRESULT>(p_swap_chain, buffer_count, width, height, new_format,
swap_chain_flags);
}
HRESULT __stdcall HooksManager::dx12_present_detour(IDXGISwapChain* p_swap_chain, UINT sync_interval, UINT flags)
{
auto& mgr = get();
callback_ptr<present_callback> cb;
{
std::shared_lock lock(mgr.m_present_mutex);
cb = mgr.m_present_cb;
}
if (cb)
(*cb)(p_swap_chain, sync_interval, flags);
return mgr.m_dx12_present_hook.call<HRESULT>(p_swap_chain, sync_interval, flags);
}
HRESULT __stdcall HooksManager::dx12_resize_buffers_detour(IDXGISwapChain* p_swap_chain, UINT buffer_count,
UINT width, UINT height, DXGI_FORMAT new_format,
UINT swap_chain_flags)
{
auto& mgr = get();
callback_ptr<resize_buffers_callback> cb;
{
std::shared_lock lock(mgr.m_resize_buffers_mutex);
cb = mgr.m_resize_buffers_cb;
}
if (cb)
(*cb)(p_swap_chain, buffer_count, width, height, new_format, swap_chain_flags);
return mgr.m_dx12_resize_buffers_hook.call<HRESULT>(p_swap_chain, buffer_count, width, height, new_format,
swap_chain_flags);
}
void __stdcall HooksManager::dx12_execute_command_lists_detour(ID3D12CommandQueue* p_command_queue,
UINT num_command_lists,
ID3D12CommandList* const* pp_command_lists)
{
auto& mgr = get();
callback_ptr<execute_command_lists_callback> cb;
{
std::shared_lock lock(mgr.m_execute_command_lists_mutex);
cb = mgr.m_execute_command_lists_cb;
}
if (cb)
(*cb)(p_command_queue, num_command_lists, pp_command_lists);
mgr.m_dx12_execute_command_lists_hook.call<void>(p_command_queue, num_command_lists, pp_command_lists);
}
BOOL __stdcall HooksManager::opengl_wgl_swap_buffers_detour(HDC hdc)
{
auto& mgr = get();
if (!g_is_inside_opengl_swap_buffers)
return mgr.m_opengl_wgl_swap_buffers_hook.call<BOOL>(hdc);
g_is_inside_opengl_swap_buffers = true;
callback_ptr<opengl_swap_buffers_callback> cb;
{
std::shared_lock lock(mgr.m_opengl_swap_buffers_mutex);
cb = mgr.m_opengl_swap_buffers_cb;
}
if (cb)
(*cb)(hdc);
const BOOL result = mgr.m_opengl_wgl_swap_buffers_hook.call<BOOL>(hdc);
g_is_inside_opengl_swap_buffers = false;
return result;
}
BOOL __stdcall HooksManager::opengl_swap_buffers_detour(HDC hdc)
{
auto& mgr = get();
if (g_is_inside_opengl_swap_buffers)
return mgr.m_opengl_swap_buffers_hook.call<BOOL>(hdc);
g_is_inside_opengl_swap_buffers = true;
callback_ptr<opengl_swap_buffers_callback> cb;
{
std::shared_lock lock(mgr.m_opengl_swap_buffers_mutex);
cb = mgr.m_opengl_swap_buffers_cb;
}
if (cb)
(*cb)(hdc);
const BOOL result = mgr.m_opengl_swap_buffers_hook.call<BOOL>(hdc);
g_is_inside_opengl_swap_buffers = false;
return result;
}
LRESULT __stdcall HooksManager::wnd_proc_detour(HWND hwnd, UINT msg, WPARAM w_param, LPARAM l_param)
{
auto& mgr = get();
callback_ptr<wnd_proc_callback> cb;
WNDPROC original;
{
std::shared_lock lock(mgr.m_wnd_proc_mutex);
cb = mgr.m_wnd_proc_cb;
original = mgr.m_original_wndproc;
}
if (cb)
{
if (const auto result = (*cb)(hwnd, msg, w_param, l_param))
return *result;
}
return CallWindowProc(original, hwnd, msg, w_param, l_param);
}
#endif // _WIN32
#ifdef __linux__
bool HooksManager::hook_opengl()
{
std::unique_lock lock(m_hook_state_mutex);
if (m_is_opengl_hooked)
return true;
void* glx_swap_buffers = dlsym(RTLD_DEFAULT, "glXSwapBuffers");
if (!glx_swap_buffers)
return false;
m_opengl_glx_swap_buffers_hook = safetyhook::create_inline(
glx_swap_buffers, reinterpret_cast<void*>(&opengl_glx_swap_buffers_detour));
if (!m_opengl_glx_swap_buffers_hook)
return false;
m_is_opengl_hooked = true;
return true;
}
void HooksManager::unhook_opengl()
{
std::unique_lock lock(m_hook_state_mutex);
m_opengl_glx_swap_buffers_hook = {};
m_is_opengl_hooked = false;
}
void HooksManager::opengl_glx_swap_buffers_detour(Display* display, GLXDrawable drawable)
{
auto& mgr = get();
callback_ptr<opengl_swap_buffers_callback> cb;
{
std::shared_lock lock(mgr.m_opengl_swap_buffers_mutex);
cb = mgr.m_opengl_swap_buffers_cb;
}
if (cb)
(*cb)(display, drawable);
mgr.m_opengl_glx_swap_buffers_hook.call<void>(display, drawable);
}
#endif // __linux__
void HooksManager::set_on_opengl_swap_buffers(opengl_swap_buffers_callback callback)
{
std::unique_lock lock(m_opengl_swap_buffers_mutex);
m_opengl_swap_buffers_cb =
callback ? std::make_shared<opengl_swap_buffers_callback>(std::move(callback)) : nullptr;
}
} // namespace omath::hooks
#else // !OMATH_ENABLE_HOOKING
namespace omath::hooks
{
HooksManager& HooksManager::get()
{
static HooksManager obj;
return obj;
}
HooksManager::~HooksManager() = default;
} // namespace omath::hooks
#endif

View File

@@ -16,15 +16,6 @@
} }
], ],
"features": { "features": {
"all": {
"description": "Enable all additional features",
"dependencies": [
{
"name": "omath",
"features": ["imgui", "lua", "hooking"]
}
]
},
"avx2": { "avx2": {
"description": "omath will use AVX2 to boost performance", "description": "omath will use AVX2 to boost performance",
"supports": "!arm" "supports": "!arm"
@@ -35,32 +26,15 @@
"benchmark" "benchmark"
] ]
}, },
"hooking": {
"description": "Add interface for automatic hooking of DirectX",
"dependencies": [
"safetyhook"
],
"supports": "(windows | linux) & !arm & !uwp"
},
"examples": { "examples": {
"description": "Build examples", "description": "Build examples",
"dependencies": [ "dependencies": [
"glfw3", "glfw3",
"glew", "glew",
"opengl", "opengl",
{
"name": "omath",
"features": ["hooking"],
"platform": "windows & !arm & !uwp"
},
{ {
"name": "imgui", "name": "imgui",
"features": ["glfw-binding", "opengl3-binding"] "features": ["glfw-binding", "opengl3-binding"]
},
{
"name": "imgui",
"features": ["dx9-binding", "dx11-binding", "dx12-binding", "win32-binding"],
"platform": "windows & !arm & !uwp"
} }
] ]
}, },