added r_gpuIndex and /gpulist

This commit is contained in:
myT 2024-01-21 02:27:18 +01:00
parent 0cae0a9545
commit 9a66155d14
7 changed files with 155 additions and 2 deletions

View File

@ -37,6 +37,12 @@ add: r_gpuPreference <0 to 2> (default: 0) sets the GPU selection preference
1 - low power (integrated graphics)
2 - none
add: r_gpuIndex <0 to GPU count> (default: 0) sets the specific GPU to use
0 - default (falls back to r_gpuPreference)
I - uses the GPU at index I from /gpulist
add: /gpulist lists available GPUs, the numbers can be used to set r_gpuIndex
add: r_sleepThreshold <2000 to 4000> (default: 2500) is the frame sleep time cushion, in microseconds
it's a trade-off between frame time consistency and CPU usage
set to 2000 if you have a struggling old/low-power CPU

View File

@ -634,6 +634,12 @@ namespace RHI
ResourceStates::Flags newState = ResourceStates::Common;
};
struct GPU
{
char name[256];
LUID uniqueId;
};
struct RHIPrivate
{
bool initialized;
@ -733,6 +739,8 @@ namespace RHI
PIX pix;
int64_t beforeInputSamplingUS;
int64_t beforeRenderingUS;
GPU gpus[16];
uint32_t gpuCount;
// immediate-mode barrier API
TextureBarrier textureBarriers[64];
@ -1453,6 +1461,52 @@ namespace RHI
return true;
}
static void CreateAdapterList()
{
IDXGIAdapter1* adapter = NULL;
UINT enumIndex = 0;
rhi.gpuCount = 0;
while(rhi.gpuCount < ARRAY_LEN(rhi.gpus) &&
SUCCEEDED(rhi.factory->EnumAdapters1(enumIndex++, &adapter)))
{
DXGI_ADAPTER_DESC1 desc;
if(IsSuitableAdapter(adapter) && SUCCEEDED(adapter->GetDesc1(&desc)))
{
GPU& gpu = rhi.gpus[rhi.gpuCount++];
gpu.uniqueId = desc.AdapterLuid;
Q_strncpyz(gpu.name, GetUTF8String(desc.Description, "???"), sizeof(gpu.name));
}
COM_RELEASE(adapter);
}
}
static IDXGIAdapter1* GetAdapterAtIndex(int gpuIndex)
{
if(gpuIndex < 0 || gpuIndex >= ARRAY_LEN(rhi.gpus))
{
ri.Printf(PRINT_WARNING, "GPU index %d is invalid", gpuIndex + 1);
return NULL;
}
const LUID uniqueId = rhi.gpus[gpuIndex].uniqueId;
IDXGIAdapter1* adapter = NULL;
UINT enumIndex = 0;
while(SUCCEEDED(rhi.factory->EnumAdapters1(enumIndex++, &adapter)))
{
DXGI_ADAPTER_DESC1 desc;
if(SUCCEEDED(adapter->GetDesc1(&desc)) &&
desc.AdapterLuid.LowPart == uniqueId.LowPart &&
desc.AdapterLuid.HighPart == uniqueId.HighPart)
{
return adapter;
}
COM_RELEASE(adapter);
}
ri.Printf(PRINT_WARNING, "GPU at index %d (%s) is no longer available", gpuIndex + 1, rhi.gpus[gpuIndex].name);
return NULL;
}
static IDXGIAdapter1* FindMostSuitableAdapter(IDXGIFactory1* factory, int enginePreference)
{
IDXGIAdapter1* adapter = NULL;
@ -3145,7 +3199,15 @@ namespace RHI
D3D(CreateDXGIFactory1(IID_PPV_ARGS(&rhi.factory)));
#endif
rhi.adapter = FindMostSuitableAdapter(rhi.factory, r_gpuPreference->integer);
CreateAdapterList();
if(r_gpuIndex->integer > 0)
{
rhi.adapter = GetAdapterAtIndex(r_gpuIndex->integer - 1);
}
if(rhi.adapter == NULL)
{
rhi.adapter = FindMostSuitableAdapter(rhi.factory, r_gpuPreference->integer);
}
{
char adapterName[256];
const char* adapterNamePtr = "unknown";
@ -3158,6 +3220,24 @@ namespace RHI
ri.Printf(PRINT_ALL, "Selected graphics adapter: %s\n", adapterNamePtr);
Q_strncpyz(rhi.adapterName, adapterNamePtr, sizeof(rhi.adapterName));
}
{
Cvar_SetRange(r_gpuIndex->name, r_gpuIndex->type, "0", va("%d", rhi.gpuCount));
char values[256];
StringList stringList;
stringList.Init(values, sizeof(values));
stringList.Append("0");
stringList.Append("Default GPU");
stringList.Append("");
for(uint32_t i = 0; i < rhi.gpuCount; ++i)
{
stringList.Append(va("%d", (int)i + 1));
stringList.Append(rhi.gpus[i].name);
stringList.Append("");
}
stringList.Terminate();
Cvar_SetMenuData(r_gpuIndex->name, CVARCAT_DISPLAY | CVARCAT_PERFORMANCE, "GPU selection", "Choose the GPU to use", "", values);
}
D3D(D3D12CreateDevice(rhi.adapter, FeatureLevel, IID_PPV_ARGS(&rhi.device)));
@ -3475,6 +3555,13 @@ namespace RHI
destroyWindow = true;
}
if(!destroyWindow &&
r_gpuIndex->latchedString != NULL &&
Q_stricmp(r_gpuIndex->latchedString, r_gpuIndex->string) != 0)
{
destroyWindow = true;
}
if(rhi.frameBegun)
{
backEnd.renderFrame = qfalse;
@ -5257,6 +5344,17 @@ namespace RHI
barrier.UAV.pResource = NULL;
rhi.commandList->ResourceBarrier(1, &barrier);
}
void PrintGPUList()
{
CreateAdapterList();
ri.Printf(PRINT_ALL, "%s0^7. Default\n", S_COLOR_VAL);
for(uint32_t i = 0; i < rhi.gpuCount; ++i)
{
ri.Printf(PRINT_ALL, "%s%d^7. %s\n", S_COLOR_VAL, (int)i + 1, rhi.gpus[i].name);
}
}
}
void R_WaitBeforeInputSampling()

View File

@ -1102,4 +1102,35 @@ namespace RHI
uint32_t size = 0;
uint32_t offset = 0;
};
struct StringList
{
void Init(char* buffer_, size_t byteCount)
{
buffer = buffer_;
written = 0;
remaining = byteCount;
}
void Append(const char* string)
{
const size_t l = strlen(string);
if(remaining >= l + 2)
{
memcpy(buffer + written, string, l + 1);
written += l + 1;
remaining -= l + 1;
}
}
void Terminate()
{
buffer[written++] = '\0';
remaining--;
}
char* buffer = NULL;
size_t written = 0;
size_t remaining = 0;
};
}

View File

@ -51,4 +51,6 @@ namespace RHI
uint32_t srcRowByteCount;
uint32_t dstRowByteCount;
};
void PrintGPUList();
}

View File

@ -128,6 +128,15 @@ S_COLOR_VAL " 0 " S_COLOR_HELP "= High performance\n" \
S_COLOR_VAL " 1 " S_COLOR_HELP "= Low power\n" \
S_COLOR_VAL " 2 " S_COLOR_HELP "= None"
#define help_r_gpuIndex \
"sets the index of the GPU to use\n" \
S_COLOR_VAL " 0 " S_COLOR_HELP "= Default\n" \
"Use " S_COLOR_CMD "gpulist " S_COLOR_HELP "to see the numbered list of GPUs."
#define help_gpulist \
"prints usable GPUs\n" \
"The numbers can be used to set " S_COLOR_CVAR "r_gpuIndex" S_COLOR_HELP "."
#define help_r_colorMipLevels \
"colorizes textures based on their mip level"

View File

@ -69,6 +69,7 @@ cvar_t *r_dither;
cvar_t *r_rtColorFormat;
cvar_t *r_depthClamp;
cvar_t *r_gpuPreference;
cvar_t *r_gpuIndex;
cvar_t *r_mipGenFilter;
cvar_t *r_mipGenGamma;
@ -316,6 +317,7 @@ static const cmdTableItem_t r_cmds[] =
{ "shadermixeduse", R_ShaderMixedUse_f, NULL, "prints all mixed use issues" },
{ "skinlist", R_SkinList_f, NULL, "prints loaded skins" },
{ "modellist", R_Modellist_f, NULL, "prints loaded models" },
{ "gpulist", RHI::PrintGPUList, NULL, help_gpulist },
{ "screenshot", R_ScreenShotTGA_f, NULL, "takes a TARGA (.tga) screenshot" },
{ "screenshotJPEG", R_ScreenShotJPG_f, NULL, "takes a JPEG (.jpg) screenshot" },
{ "screenshotnc", R_ScreenShotNoConTGA_f, NULL, "takes a TARGA screenshot w/o the console" },
@ -439,11 +441,15 @@ static const cvarTableItem_t r_cvars[] =
},
{
&r_gpuPreference, "r_gpuPreference", "0", CVAR_ARCHIVE | CVAR_LATCH, CVART_INTEGER, "0", XSTRING(GPUPREF_MAX), help_r_gpuPreference,
"GPU selection", CVARCAT_DISPLAY | CVARCAT_PERFORMANCE, "Choose between low-power and high-performance devices", "",
"GPU preference", CVARCAT_DISPLAY | CVARCAT_PERFORMANCE, "Choose between low-power and high-performance devices", "",
CVAR_GUI_VALUE("0", "High performance", "")
CVAR_GUI_VALUE("1", "Low power", "")
CVAR_GUI_VALUE("2", "None", "")
},
{
// GUI settings and data range are set by the RHI during init
&r_gpuIndex, "r_gpuIndex", "0", CVAR_ARCHIVE | CVAR_LATCH, CVART_INTEGER, "0", NULL, help_r_gpuIndex
},
{
&r_vsync, "r_vsync", "0", CVAR_ARCHIVE | CVAR_LATCH, CVART_BOOL, NULL, NULL, "enables v-sync",
"V-Sync", CVARCAT_DISPLAY | CVARCAT_PERFORMANCE, "Enabling locks the framerate to the monitor's refresh rate", "",

View File

@ -1060,6 +1060,7 @@ extern cvar_t *r_dither; // enables dithering
extern cvar_t *r_rtColorFormat; // color render target format, see RTCF_*
extern cvar_t *r_depthClamp; // disables clipping vertices against the near and far clip planes
extern cvar_t *r_gpuPreference; // shall we use high-performance or low-power devices?
extern cvar_t *r_gpuIndex; // the index of the specific device to use
extern cvar_t *r_mipGenFilter; // if the string is invalid, Lanczos 4 is used
extern cvar_t *r_mipGenGamma; // what gamma-space do we consider the textures to be in