658 lines
16 KiB
C++
658 lines
16 KiB
C++
#include <windows.h>
|
|
#include <GL/gl.h>
|
|
#include <dxgi.h>
|
|
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
|
|
#include "TSFuncs.hpp"
|
|
|
|
// Identifies the graphics adapter and only shrinks the shadow map when that
|
|
// adapter cannot back the size the quality setting asked for. Shader source
|
|
// is left alone: patching it in memory disabled the NVIDIA shader compile.
|
|
|
|
static char gDescription[160] = "unknown";
|
|
static const char *gVendor = "unknown";
|
|
static const char *gDescriptionPtr = gDescription;
|
|
static int gDedicatedMB = 0;
|
|
static int gShadowCap = 8192;
|
|
static bool gHaveProfile = false;
|
|
static bool gScriptsReady = false;
|
|
static bool gPublished = false;
|
|
static bool gLogged = false;
|
|
static int *gShadowSize = 0;
|
|
static int *gShadowCount = 0;
|
|
static int gUserSize = 0;
|
|
static int gUserCount = 0;
|
|
static int gAppliedSize = -1;
|
|
static int gAppliedCount = -1;
|
|
static int gLimitSize = 0;
|
|
static int gLimitCount = 0;
|
|
static int gFrameRate = 0;
|
|
static int gInRegen = 0;
|
|
static bool gSwapHooked = false;
|
|
static bool gScaleActive = false;
|
|
static bool gWasPaused = false;
|
|
|
|
static const double kFpsLow = 50.0;
|
|
static const double kFpsHigh = 70.0;
|
|
static const double kFpsUpMargin = 15.0;
|
|
static const double kLowHold = 1.0;
|
|
static const double kHighHold = 8.0;
|
|
static const double kCooldown = 2.5;
|
|
static const double kGainSettle = 2.5;
|
|
static const double kStepUpWatch = 1.0;
|
|
static const double kStepUpBan = 45.0;
|
|
|
|
// The FPS gained by lowering shadows is not spare headroom. Raising the map
|
|
// again would give that cost back and the counter would jump.
|
|
static double gClock = 0.0;
|
|
static double gFpsBeforeStep = 0.0;
|
|
static double gStepGain = 0.0;
|
|
static double gGainReadyAt = 0.0;
|
|
static double gStepUpBanUntil = 0.0;
|
|
static double gStepUpWatchAt = 0.0;
|
|
static bool gHaveGain = false;
|
|
static bool gWatchingStepUp = false;
|
|
|
|
typedef BOOL (WINAPI *SwapBuffersFn)(HDC);
|
|
static SwapBuffersFn wglSwapBuffersOriginal = 0;
|
|
|
|
static const unsigned kVendorNvidia = 0x10DE;
|
|
static const unsigned kVendorAmd = 0x1002;
|
|
static const unsigned kVendorIntel = 0x8086;
|
|
|
|
static int powerOfTwoFloor(int value)
|
|
{
|
|
int pot = 256;
|
|
while (pot <= 32768 / 2 && pot * 2 <= value)
|
|
pot *= 2;
|
|
return pot;
|
|
}
|
|
|
|
// regenerateShadowMapFBOs allocates one GL_TEXTURE_2D_ARRAY of
|
|
// GL_DEPTH_COMPONENT16: width * height * cascades * 2 bytes. 16384x4 is
|
|
// exactly 2 GiB. This 32-bit process was given that size and the driver
|
|
// returned out of memory, so the cap stays one byte under 2 GiB. 8192x4
|
|
// is 512 MB. Also stay under a quarter of dedicated memory and inside
|
|
// GL_MAX_TEXTURE_SIZE.
|
|
static int maxShadowSize(int dedicatedMB, int layers, int glMax)
|
|
{
|
|
int glCap = powerOfTwoFloor(glMax);
|
|
if (glCap < 1024)
|
|
glCap = 8192;
|
|
if (layers < 1)
|
|
layers = 4;
|
|
|
|
long long budget = (1ll << 31) - 1;
|
|
if (dedicatedMB > 0) {
|
|
const long long quarter = (long long)dedicatedMB * 1024ll * 1024ll / 4ll;
|
|
if (quarter < budget)
|
|
budget = quarter;
|
|
}
|
|
|
|
int size = 1024;
|
|
int best = 1024;
|
|
while (size <= glCap && size <= 32768) {
|
|
const long long bytes = (long long)size * (long long)size * (long long)layers * 2ll;
|
|
if (bytes > budget)
|
|
break;
|
|
best = size;
|
|
if (size > 32768 / 2)
|
|
break;
|
|
size *= 2;
|
|
}
|
|
return best;
|
|
}
|
|
|
|
static bool textHas(const char *text, const char *needle)
|
|
{
|
|
return text != 0 && needle != 0 && std::strstr(text, needle) != 0;
|
|
}
|
|
|
|
static void applyProfile(unsigned vendorId, const char *description, int dedicatedMB)
|
|
{
|
|
const char *vendor = "unknown";
|
|
int cap = 8192;
|
|
|
|
if (dedicatedMB > 0)
|
|
gDedicatedMB = dedicatedMB;
|
|
if (description != 0 && description[0] != '\0' && std::strcmp(description, "unknown") != 0)
|
|
std::snprintf(gDescription, sizeof gDescription, "%s", description);
|
|
|
|
if (vendorId == kVendorNvidia || textHas(description, "NVIDIA")) {
|
|
vendor = "NVIDIA";
|
|
cap = 8192;
|
|
}
|
|
else if (vendorId == kVendorAmd || textHas(description, "AMD") || textHas(description, "Radeon") || textHas(description, "ATI")) {
|
|
vendor = "AMD";
|
|
cap = 4096;
|
|
}
|
|
else if (vendorId == kVendorIntel || textHas(description, "Intel")) {
|
|
vendor = "Intel";
|
|
cap = gDedicatedMB >= 4096 ? 4096 : 1024;
|
|
if (gDedicatedMB >= 1536 && gDedicatedMB < 4096)
|
|
cap = 2048;
|
|
}
|
|
|
|
if (gDedicatedMB > 0 && gDedicatedMB < 512)
|
|
cap = 1024;
|
|
else if (gDedicatedMB > 0 && gDedicatedMB < 1536 && cap > 2048)
|
|
cap = 2048;
|
|
|
|
gVendor = vendor;
|
|
gShadowCap = cap;
|
|
gHaveProfile = vendorId != 0 || textHas(gDescription, "NVIDIA") || textHas(gDescription, "AMD") || textHas(gDescription, "Radeon") || textHas(gDescription, "Intel");
|
|
}
|
|
|
|
static void detectAdapter()
|
|
{
|
|
IDXGIFactory *factory = 0;
|
|
if (FAILED(CreateDXGIFactory(IID_IDXGIFactory, (void **)&factory)) || factory == 0)
|
|
return;
|
|
|
|
IDXGIAdapter *best = 0;
|
|
DXGI_ADAPTER_DESC bestDesc;
|
|
std::memset(&bestDesc, 0, sizeof bestDesc);
|
|
SIZE_T bestMemory = 0;
|
|
|
|
for (UINT index = 0; index < 8; ++index) {
|
|
IDXGIAdapter *adapter = 0;
|
|
if (FAILED(factory->EnumAdapters(index, &adapter)) || adapter == 0)
|
|
break;
|
|
|
|
DXGI_ADAPTER_DESC desc;
|
|
std::memset(&desc, 0, sizeof desc);
|
|
if (SUCCEEDED(adapter->GetDesc(&desc)) && desc.DedicatedVideoMemory >= bestMemory) {
|
|
if (best != 0)
|
|
best->Release();
|
|
best = adapter;
|
|
bestDesc = desc;
|
|
bestMemory = desc.DedicatedVideoMemory;
|
|
}
|
|
else
|
|
adapter->Release();
|
|
}
|
|
|
|
if (best != 0) {
|
|
char name[128];
|
|
WideCharToMultiByte(CP_UTF8, 0, bestDesc.Description, -1, name, sizeof name, 0, 0);
|
|
int dedicatedMB = (int)(bestDesc.DedicatedVideoMemory / (1024u * 1024u));
|
|
applyProfile(bestDesc.VendorId, name, dedicatedMB);
|
|
best->Release();
|
|
}
|
|
|
|
factory->Release();
|
|
}
|
|
|
|
static void refineFromGl()
|
|
{
|
|
if (!gHaveProfile)
|
|
detectAdapter();
|
|
if (wglGetCurrentContext() == 0)
|
|
return;
|
|
|
|
const char *vendor = (const char *)glGetString(GL_VENDOR);
|
|
const char *renderer = (const char *)glGetString(GL_RENDERER);
|
|
if (vendor != 0 || renderer != 0) {
|
|
const char *who = renderer != 0 ? renderer : vendor;
|
|
unsigned id = 0;
|
|
if (textHas(vendor, "NVIDIA") || textHas(renderer, "NVIDIA"))
|
|
id = kVendorNvidia;
|
|
else if (textHas(vendor, "AMD") || textHas(vendor, "ATI") || textHas(renderer, "Radeon"))
|
|
id = kVendorAmd;
|
|
else if (textHas(vendor, "Intel") || textHas(renderer, "Intel"))
|
|
id = kVendorIntel;
|
|
if (id != 0)
|
|
applyProfile(id, who, gDedicatedMB);
|
|
}
|
|
|
|
for (int i = 0; i < 8; ++i) {
|
|
if (glGetError() == GL_NO_ERROR)
|
|
break;
|
|
}
|
|
|
|
GLint maxTex = 0;
|
|
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTex);
|
|
|
|
GLint dedicatedKB = 0;
|
|
glGetIntegerv(0x9047, &dedicatedKB);
|
|
if (glGetError() != GL_NO_ERROR)
|
|
dedicatedKB = 0;
|
|
if (dedicatedKB > 0) {
|
|
int nvMB = dedicatedKB / 1024;
|
|
if (nvMB > gDedicatedMB)
|
|
gDedicatedMB = nvMB;
|
|
}
|
|
|
|
int layers = gUserCount > 0 ? gUserCount : 4;
|
|
int cap = maxShadowSize(gDedicatedMB, layers, (int)maxTex);
|
|
if (cap >= 1024)
|
|
gShadowCap = cap;
|
|
}
|
|
|
|
static void publishVars()
|
|
{
|
|
if (gPublished || !gScriptsReady)
|
|
return;
|
|
|
|
tsf_AddVar("$ShaderFit::Vendor", &gVendor);
|
|
tsf_AddVar("$ShaderFit::Renderer", &gDescriptionPtr);
|
|
tsf_AddVar("$ShaderFit::DedicatedMB", &gDedicatedMB);
|
|
tsf_AddVar("$ShaderFit::ShadowCap", &gShadowCap);
|
|
tsf_AddVar("$ShaderFit::FrameRate", &gFrameRate);
|
|
gPublished = true;
|
|
}
|
|
|
|
static bool varIsOff(const char *value)
|
|
{
|
|
return value != 0 && (value[0] == '0' || std::strcmp(value, "false") == 0);
|
|
}
|
|
|
|
// Off and Minimum both leave the shadow map at size 0. Minimum also keeps the
|
|
// shader name "minimum" while Enabled stays on.
|
|
static bool shaderModeUsesShadows()
|
|
{
|
|
if (gScriptsReady) {
|
|
if (varIsOff(tsf_GetVar("$Shader::Enabled")))
|
|
return false;
|
|
const char *name = tsf_GetVar("$Shader::ShaderName");
|
|
if (name != 0 && std::strcmp(name, "minimum") == 0)
|
|
return false;
|
|
}
|
|
if (gShadowSize == 0 || gShadowCount == 0)
|
|
return false;
|
|
return *gShadowSize > 0 && *gShadowCount > 0;
|
|
}
|
|
|
|
static void resetStepMemory()
|
|
{
|
|
gFpsBeforeStep = 0.0;
|
|
gStepGain = 0.0;
|
|
gGainReadyAt = 0.0;
|
|
gStepUpBanUntil = 0.0;
|
|
gStepUpWatchAt = 0.0;
|
|
gHaveGain = false;
|
|
gWatchingStepUp = false;
|
|
}
|
|
|
|
static void pauseShadowScaling()
|
|
{
|
|
gLimitSize = 0;
|
|
gLimitCount = 0;
|
|
gScaleActive = false;
|
|
resetStepMemory();
|
|
BlPrintf("%s: shadow scaling paused", PROJECT_NAME);
|
|
}
|
|
|
|
static void rememberUserChoice()
|
|
{
|
|
if (gShadowSize == 0 || gShadowCount == 0)
|
|
return;
|
|
|
|
int size = *gShadowSize;
|
|
int count = *gShadowCount;
|
|
if (size == gAppliedSize && count == gAppliedCount)
|
|
return;
|
|
gUserSize = size;
|
|
gUserCount = count;
|
|
gLimitSize = 0;
|
|
gLimitCount = 0;
|
|
if (size <= 0) {
|
|
gLimitSize = 0;
|
|
gLimitCount = 0;
|
|
gAppliedSize = size;
|
|
gAppliedCount = count;
|
|
}
|
|
}
|
|
|
|
static void applyShadowLevel()
|
|
{
|
|
refineFromGl();
|
|
publishVars();
|
|
if (!gLogged) {
|
|
BlPrintf("%s: %s, %s, %d MB, shadow cap %d",
|
|
PROJECT_NAME, gVendor, gDescription, gDedicatedMB, gShadowCap);
|
|
gLogged = true;
|
|
}
|
|
if (gShadowSize == 0 || gShadowCount == 0)
|
|
return;
|
|
|
|
rememberUserChoice();
|
|
if (gUserSize <= 0)
|
|
return;
|
|
|
|
int wantSize = gUserSize;
|
|
int wantCount = gUserCount > 0 ? gUserCount : *gShadowCount;
|
|
if (gLimitSize > 0)
|
|
wantSize = gLimitSize;
|
|
if (gLimitCount > 0)
|
|
wantCount = gLimitCount;
|
|
if (wantSize > gShadowCap && wantSize > gUserSize)
|
|
wantSize = gShadowCap;
|
|
if (wantCount < 1)
|
|
wantCount = 1;
|
|
|
|
if (*gShadowSize != wantSize)
|
|
*gShadowSize = wantSize;
|
|
if (*gShadowCount != wantCount)
|
|
*gShadowCount = wantCount;
|
|
gAppliedSize = *gShadowSize;
|
|
gAppliedCount = *gShadowCount;
|
|
}
|
|
|
|
static bool stepShadowsDown()
|
|
{
|
|
int size = gLimitSize > 0 ? gLimitSize : gUserSize;
|
|
int count = gLimitCount > 0 ? gLimitCount : gUserCount;
|
|
if (size > 1024) {
|
|
int next = size / 2;
|
|
if (next < 1024)
|
|
next = 1024;
|
|
gLimitSize = next;
|
|
gLimitCount = count;
|
|
return true;
|
|
}
|
|
if (count > 1) {
|
|
gLimitSize = size;
|
|
gLimitCount = count - 1;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool stepShadowsUp()
|
|
{
|
|
int size = gLimitSize > 0 ? gLimitSize : gUserSize;
|
|
int count = gLimitCount > 0 ? gLimitCount : gUserCount;
|
|
if (count < gUserCount)
|
|
count += 1;
|
|
else if (size < gShadowCap) {
|
|
int next = size * 2;
|
|
if (next > gShadowCap)
|
|
next = gShadowCap;
|
|
if (next <= size)
|
|
return false;
|
|
size = next;
|
|
}
|
|
else
|
|
return false;
|
|
|
|
if (size == gUserSize && count == gUserCount) {
|
|
gLimitSize = 0;
|
|
gLimitCount = 0;
|
|
}
|
|
else {
|
|
gLimitSize = size;
|
|
gLimitCount = count;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static void rebuildShadows();
|
|
static void regenerateFitted();
|
|
|
|
static void sampleFrameRate()
|
|
{
|
|
static LARGE_INTEGER freq;
|
|
static LARGE_INTEGER last;
|
|
static bool haveLast = false;
|
|
static double avg = 60.0;
|
|
static double warm = 0.0;
|
|
static double lowHold = 0.0;
|
|
static double highHold = 0.0;
|
|
static double cooldown = 0.0;
|
|
|
|
if (!shaderModeUsesShadows()) {
|
|
if (gScaleActive) {
|
|
pauseShadowScaling();
|
|
gWasPaused = true;
|
|
}
|
|
haveLast = false;
|
|
warm = 0.0;
|
|
lowHold = 0.0;
|
|
highHold = 0.0;
|
|
cooldown = 0.0;
|
|
avg = 60.0;
|
|
return;
|
|
}
|
|
if (!gScaleActive) {
|
|
gScaleActive = true;
|
|
haveLast = false;
|
|
warm = 0.0;
|
|
lowHold = 0.0;
|
|
highHold = 0.0;
|
|
cooldown = 0.0;
|
|
avg = 60.0;
|
|
if (gWasPaused) {
|
|
gWasPaused = false;
|
|
BlPrintf("%s: shadow scaling resumed", PROJECT_NAME);
|
|
}
|
|
}
|
|
|
|
if (freq.QuadPart == 0)
|
|
QueryPerformanceFrequency(&freq);
|
|
|
|
LARGE_INTEGER now;
|
|
QueryPerformanceCounter(&now);
|
|
if (!haveLast) {
|
|
last = now;
|
|
haveLast = true;
|
|
return;
|
|
}
|
|
|
|
double dt = (double)(now.QuadPart - last.QuadPart) / (double)freq.QuadPart;
|
|
last = now;
|
|
if (dt <= 0.0 || dt > 0.25)
|
|
return;
|
|
|
|
warm += dt;
|
|
gClock += dt;
|
|
if (cooldown > 0.0)
|
|
cooldown -= dt;
|
|
avg += ((1.0 / dt) - avg) * 0.1;
|
|
gFrameRate = (int)(avg + 0.5);
|
|
if (gGainReadyAt > 0.0 && gClock >= gGainReadyAt) {
|
|
gStepGain = avg - gFpsBeforeStep;
|
|
if (gStepGain < 0.0)
|
|
gStepGain = 0.0;
|
|
gHaveGain = true;
|
|
gGainReadyAt = 0.0;
|
|
}
|
|
if (warm < 1.0 || cooldown > 0.0 || gUserSize <= 0 || gInRegen != 0)
|
|
return;
|
|
|
|
if (gWatchingStepUp && gClock >= gStepUpWatchAt) {
|
|
if (avg < kFpsLow) {
|
|
gWatchingStepUp = false;
|
|
gStepUpBanUntil = gClock + kStepUpBan;
|
|
gStepGain = gFpsBeforeStep - avg;
|
|
if (gStepGain < kFpsUpMargin)
|
|
gStepGain = kFpsUpMargin;
|
|
gHaveGain = true;
|
|
gGainReadyAt = 0.0;
|
|
if (stepShadowsDown()) {
|
|
BlPrintf("%s: keeping this shadow level, raising it fell to %d fps", PROJECT_NAME, gFrameRate);
|
|
lowHold = 0.0;
|
|
highHold = 0.0;
|
|
cooldown = kCooldown;
|
|
rebuildShadows();
|
|
}
|
|
return;
|
|
}
|
|
if (gClock >= gStepUpWatchAt + 3.0) {
|
|
gWatchingStepUp = false;
|
|
gHaveGain = true;
|
|
gStepGain = 0.0;
|
|
}
|
|
}
|
|
|
|
if (avg < kFpsLow) {
|
|
lowHold += dt;
|
|
highHold = 0.0;
|
|
}
|
|
else if (avg > kFpsHigh) {
|
|
highHold += dt;
|
|
lowHold = 0.0;
|
|
}
|
|
else {
|
|
lowHold = 0.0;
|
|
highHold = 0.0;
|
|
}
|
|
|
|
bool changed = false;
|
|
bool steppedDown = false;
|
|
if (lowHold >= kLowHold) {
|
|
changed = stepShadowsDown();
|
|
steppedDown = changed;
|
|
}
|
|
else if (highHold >= kHighHold && gGainReadyAt == 0.0 && gClock >= gStepUpBanUntil
|
|
&& (!gHaveGain || (avg - gStepGain) >= (kFpsLow + kFpsUpMargin))) {
|
|
changed = stepShadowsUp();
|
|
}
|
|
if (!changed)
|
|
return;
|
|
|
|
if (steppedDown) {
|
|
gFpsBeforeStep = avg;
|
|
gHaveGain = false;
|
|
gGainReadyAt = gClock + kGainSettle;
|
|
gWatchingStepUp = false;
|
|
}
|
|
else {
|
|
gFpsBeforeStep = avg;
|
|
gWatchingStepUp = true;
|
|
gStepUpWatchAt = gClock + kStepUpWatch;
|
|
gHaveGain = false;
|
|
gGainReadyAt = 0.0;
|
|
}
|
|
|
|
lowHold = 0.0;
|
|
highHold = 0.0;
|
|
cooldown = kCooldown;
|
|
rebuildShadows();
|
|
}
|
|
|
|
static BOOL WINAPI wglSwapBuffersHook(HDC dc)
|
|
{
|
|
BOOL ok = wglSwapBuffersOriginal(dc);
|
|
sampleFrameRate();
|
|
return ok;
|
|
}
|
|
|
|
static void installSwapHook()
|
|
{
|
|
if (gSwapHooked)
|
|
return;
|
|
|
|
HMODULE gl = GetModuleHandleA("opengl32.dll");
|
|
void *target = gl == 0 ? 0 : (void *)GetProcAddress(gl, "wglSwapBuffers");
|
|
if (target == 0)
|
|
return;
|
|
if (MH_CreateHook(target, (void *)wglSwapBuffersHook, (void **)&wglSwapBuffersOriginal) != MH_OK)
|
|
return;
|
|
if (MH_EnableHook(target) != MH_OK)
|
|
return;
|
|
gSwapHooked = true;
|
|
}
|
|
|
|
BlFunctionDef(void, , regenerateShadowMapFBOs);
|
|
BlFunctionHookDef(regenerateShadowMapFBOs);
|
|
|
|
static unsigned char *shaderEnabledFlag()
|
|
{
|
|
unsigned char *base = reinterpret_cast<unsigned char *>(GetModuleHandle(NULL));
|
|
return base + (0x7C2142 - 0x400000);
|
|
}
|
|
|
|
static void regenerateFitted()
|
|
{
|
|
unsigned char *enabled = shaderEnabledFlag();
|
|
unsigned char wasEnabled = *enabled;
|
|
int tried = gShadowSize != 0 ? *gShadowSize : 0;
|
|
regenerateShadowMapFBOsOriginal();
|
|
if (wasEnabled != 0 && *enabled == 0 && tried > 8192 && gShadowSize != 0) {
|
|
BlPrintf("%s: %d did not fit, keeping 8192", PROJECT_NAME, tried);
|
|
gShadowCap = 8192;
|
|
gLimitSize = 8192;
|
|
*gShadowSize = 8192;
|
|
gAppliedSize = 8192;
|
|
*enabled = 1;
|
|
regenerateShadowMapFBOsOriginal();
|
|
}
|
|
}
|
|
|
|
void regenerateShadowMapFBOsHook()
|
|
{
|
|
if (gInRegen != 0) {
|
|
regenerateShadowMapFBOsOriginal();
|
|
return;
|
|
}
|
|
gInRegen += 1;
|
|
installSwapHook();
|
|
applyShadowLevel();
|
|
regenerateFitted();
|
|
gInRegen -= 1;
|
|
}
|
|
|
|
static void rebuildShadows()
|
|
{
|
|
if (regenerateShadowMapFBOsOriginal == 0 || gInRegen != 0)
|
|
return;
|
|
gInRegen += 1;
|
|
applyShadowLevel();
|
|
int size = gShadowSize != 0 ? *gShadowSize : 0;
|
|
int count = gShadowCount != 0 ? *gShadowCount : 0;
|
|
BlPrintf("%s: %d fps, shadows %dx%d", PROJECT_NAME, gFrameRate, size, count);
|
|
regenerateFitted();
|
|
gInRegen -= 1;
|
|
}
|
|
|
|
bool init()
|
|
{
|
|
if (!tsh_InitInternal())
|
|
return true;
|
|
|
|
gScriptsReady = tsf_InitInternal();
|
|
|
|
ADDR shadowFn = tsh_ScanFunctionHex((char *)"83 EC 08 56 8B 35 ? ? ? ? FF 15");
|
|
if (shadowFn != 0) {
|
|
gShadowSize = *(int **)(shadowFn + 6);
|
|
gShadowCount = gShadowSize - 1;
|
|
regenerateShadowMapFBOs = (tsh_regenerateShadowMapFBOsFnT)shadowFn;
|
|
BlCreateHook(regenerateShadowMapFBOs);
|
|
BlTestEnableHook(regenerateShadowMapFBOs);
|
|
}
|
|
|
|
BlPrintf("%s (v%s): loaded, waiting for the graphics device", PROJECT_NAME, PROJECT_VERSION);
|
|
return true;
|
|
}
|
|
|
|
bool deinit()
|
|
{
|
|
if (regenerateShadowMapFBOs != 0)
|
|
MH_DisableHook((void *)regenerateShadowMapFBOs);
|
|
if (gSwapHooked) {
|
|
void *target = (void *)GetProcAddress(GetModuleHandleA("opengl32.dll"), "wglSwapBuffers");
|
|
if (target != 0)
|
|
MH_DisableHook(target);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool __stdcall DllMain(HINSTANCE hinstance, unsigned int reason, void *reserved)
|
|
{
|
|
(void)hinstance;
|
|
(void)reserved;
|
|
switch (reason) {
|
|
case DLL_PROCESS_ATTACH:
|
|
return init();
|
|
case DLL_PROCESS_DETACH:
|
|
return deinit();
|
|
default:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
extern "C" void __declspec(dllexport) __cdecl PROJECT_EXPORT() {}
|