Skip to content

Commit a69dd6b

Browse files
committed
- Threaded screenshot capture so it doesnt freeze game
1 parent a18c2e5 commit a69dd6b

3 files changed

Lines changed: 110 additions & 80 deletions

File tree

GeneralsMD/Code/GameEngine/Include/GameNetwork/GeneralsOnline/OnlineServices_Init.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,7 @@ class NGMP_OnlineServicesManager
398398
void StartDownloadUpdate(std::function<void(void)> cb);
399399
void ContinueUpdate();
400400

401-
static std::vector<unsigned char> CaptureScreenshot(bool bResizeForTransmit);
401+
static void CaptureScreenshot(bool bResizeForTransmit, std::function<void(std::vector<unsigned char>)> cbOnDataAvailable);
402402
static void CaptureScreenshotToDisk();
403403

404404
/*

GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/OnlineServices_Init.cpp

Lines changed: 93 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -81,36 +81,40 @@ void NGMP_OnlineServicesManager::GetAndParseServiceConfig(std::function<void(voi
8181

8282
void NGMP_OnlineServicesManager::CaptureScreenshotToDisk()
8383
{
84-
std::vector<unsigned char> vecBuffer = NGMP_OnlineServicesManager::CaptureScreenshot(false);
84+
// create dirs
85+
std::string strScreenshotsDir = std::format("{}/GeneralsOnlineScreenshots/", TheGlobalData->getPath_UserData().str());
8586

86-
if (!vecBuffer.empty())
87+
if (!std::filesystem::exists(strScreenshotsDir))
8788
{
88-
char GameDir[MAX_PATH + 1] = {};
89-
::GetCurrentDirectoryA(MAX_PATH + 1u, GameDir);
90-
std::string strScreenshotsDir = std::format("{}/GeneralsOnlineScreenshots/", TheGlobalData->getPath_UserData().str());
91-
92-
if (!std::filesystem::exists(strScreenshotsDir))
93-
{
94-
std::filesystem::create_directory(strScreenshotsDir);
95-
}
96-
97-
// write to disk
98-
auto now = std::chrono::system_clock::now();
99-
auto in_time_t = std::chrono::system_clock::to_time_t(now);
100-
std::stringstream ss;
101-
ss << std::put_time(std::localtime(&in_time_t), "GeneralsOnline_Screenshot_%Y-%m-%d-%H-%M-%S.jpg");
89+
std::filesystem::create_directory(strScreenshotsDir);
90+
}
10291

103-
std::string strFilePath = std::format("{}/{}", strScreenshotsDir.c_str(), ss.str().c_str());
92+
// calculate path
93+
auto now = std::chrono::system_clock::now();
94+
auto in_time_t = std::chrono::system_clock::to_time_t(now);
95+
std::stringstream ss;
96+
ss << std::put_time(std::localtime(&in_time_t), "GeneralsOnline_Screenshot_%Y-%m-%d-%H-%M-%S.jpg");
10497

105-
FILE* pFile = fopen(strFilePath.c_str(), "wb");
106-
fwrite(vecBuffer.data(), sizeof(uint8_t), vecBuffer.size(), pFile);
107-
fclose(pFile);
98+
std::string strFilePath = std::format("{}/{}", strScreenshotsDir.c_str(), ss.str().c_str());
10899

109-
// UI output (if ingame)
100+
// do UI output immediately on mainthread (if ingame)
101+
if (TheInGameUI != nullptr)
102+
{
110103
UnicodeString ufileName;
111104
ufileName.translate(AsciiString(strFilePath.c_str()));
112105
TheInGameUI->message(TheGameText->fetch("GUI:ScreenCapture"), ufileName.str());
113106
}
107+
108+
NGMP_OnlineServicesManager::CaptureScreenshot(false, [strFilePath](std::vector<unsigned char> vecBuffer)
109+
{
110+
if (!vecBuffer.empty())
111+
{
112+
// write to disk
113+
FILE* pFile = fopen(strFilePath.c_str(), "wb");
114+
fwrite(vecBuffer.data(), sizeof(uint8_t), vecBuffer.size(), pFile);
115+
fclose(pFile);
116+
}
117+
});
114118
}
115119

116120
NGMP_OnlineServicesManager* NGMP_OnlineServicesManager::m_pOnlineServicesManager = nullptr;
@@ -341,9 +345,13 @@ void NGMP_OnlineServicesManager::ContinueUpdate()
341345
}
342346

343347

344-
std::vector<unsigned char> NGMP_OnlineServicesManager::CaptureScreenshot(bool bResizeForTransmit)
348+
void NGMP_OnlineServicesManager::CaptureScreenshot(bool bResizeForTransmit, std::function<void(std::vector<unsigned char>)> cbOnDataAvailable)
345349
{
346-
std::vector<unsigned char> vecData;
350+
// no callback, nothing to do
351+
if (cbOnDataAvailable == nullptr)
352+
{
353+
return;
354+
}
347355

348356
SurfaceClass* surface = DX8Wrapper::_Get_DX8_Back_Buffer();
349357

@@ -357,68 +365,89 @@ std::vector<unsigned char> NGMP_OnlineServicesManager::CaptureScreenshot(bool bR
357365

358366
D3DDISPLAYMODE mode;
359367
if (FAILED(hr = DX8Wrapper::_Get_D3D_Device8()->GetDisplayMode(&mode)))
360-
return vecData;
368+
{
369+
cbOnDataAvailable(std::vector<unsigned char>());
370+
return;
371+
}
361372

362373
LPDIRECT3DSURFACE8 surf;
363374
if (FAILED(hr = DX8Wrapper::_Get_D3D_Device8()->CreateImageSurface(mode.Width, mode.Height,
364375
D3DFMT_A8R8G8B8, &surf)))
365-
return vecData;
376+
{
377+
cbOnDataAvailable(std::vector<unsigned char>());
378+
return;
379+
}
366380

367381
if (FAILED(hr = DX8Wrapper::_Get_D3D_Device8()->GetFrontBuffer(surf))) {
368382
surf->Release();
369-
return vecData;
383+
{
384+
cbOnDataAvailable(std::vector<unsigned char>());
385+
return;
386+
}
370387
}
371388

389+
// gather all our data
372390
int pitch = 0;
373391
void* pBits = surfaceCopy->Lock(&pitch);
374392

375393
unsigned char* rgbData = new unsigned char[surfaceDesc.Width * surfaceDesc.Height * 3];
376394

377395
int width = surfaceDesc.Width;
378396
int height = surfaceDesc.Height;
379-
for (int y = 0; y < height; ++y) {
380-
uint8_t* row = (uint8_t*)pBits + y * pitch;
381-
for (int x = 0; x < width; ++x) {
382-
int srcIndex = x * 4;
383-
int dstIndex = (y * width + x) * 3;
384-
385-
rgbData[dstIndex + 0] = row[srcIndex + 2]; // R
386-
rgbData[dstIndex + 1] = row[srcIndex + 1]; // G
387-
rgbData[dstIndex + 2] = row[srcIndex + 0]; // B
388-
}
389-
}
390397

391-
// resize
392-
unsigned char* pBufferToWrite = rgbData;
393-
if (bResizeForTransmit)
394-
{
395-
int new_width = 844;
396-
int new_height = 506;
397-
int channels = 3;
398-
unsigned char* resized = new unsigned char[new_width * new_height * channels];
398+
// release the image surface
399+
surf->Release();
399400

400-
stbir_resize_uint8(rgbData, surfaceDesc.Width, surfaceDesc.Height, 0,
401-
resized, new_width, new_height, 0,
402-
channels
403-
);
401+
// process on thread
402+
std::thread* pNewThread = new std::thread([cbOnDataAvailable, width, height, pBits, pitch, rgbData, bResizeForTransmit]()
403+
{
404+
std::vector<unsigned char> vecData;
404405

405-
// update data
406-
width = new_width;
407-
height = new_height;
408-
pBufferToWrite = resized;
409-
}
410-
// end resize
406+
int finalWidth = width;
407+
int finalHeight = height;
411408

412-
stbi_write_jpg_to_func([](void* context, void* data, int size)
413-
{
414-
std::vector<unsigned char>* buffer = static_cast<std::vector<unsigned char>*>(context);
415-
buffer->insert(buffer->end(), (unsigned char*)data, (unsigned char*)data + size);
416-
}, &vecData, width, height, 3, pBufferToWrite, bResizeForTransmit ? 0 : 50);
417-
418-
// release the image surface
419-
surf->Release();
409+
for (int y = 0; y < height; ++y) {
410+
uint8_t* row = (uint8_t*)pBits + y * pitch;
411+
for (int x = 0; x < width; ++x) {
412+
int srcIndex = x * 4;
413+
int dstIndex = (y * width + x) * 3;
414+
415+
rgbData[dstIndex + 0] = row[srcIndex + 2]; // R
416+
rgbData[dstIndex + 1] = row[srcIndex + 1]; // G
417+
rgbData[dstIndex + 2] = row[srcIndex + 0]; // B
418+
}
419+
}
420+
421+
// resize
422+
unsigned char* pBufferToWrite = rgbData;
423+
if (bResizeForTransmit)
424+
{
425+
int new_width = 844;
426+
int new_height = 506;
427+
int channels = 3;
428+
unsigned char* resized = new unsigned char[new_width * new_height * channels];
429+
430+
stbir_resize_uint8(rgbData, width, height, 0,
431+
resized, new_width, new_height, 0,
432+
channels
433+
);
434+
435+
// update data
436+
finalWidth = new_width;
437+
finalHeight = new_height;
438+
pBufferToWrite = resized;
439+
}
440+
// end resize
420441

421-
return vecData;
442+
stbi_write_jpg_to_func([](void* context, void* data, int size)
443+
{
444+
std::vector<unsigned char>* buffer = static_cast<std::vector<unsigned char>*>(context);
445+
buffer->insert(buffer->end(), (unsigned char*)data, (unsigned char*)data + size);
446+
}, &vecData, finalWidth, finalHeight, 3, pBufferToWrite, bResizeForTransmit ? 0 : 90);
447+
448+
cbOnDataAvailable(vecData);
449+
});
450+
SetThreadDescription(static_cast<HANDLE>(pNewThread->native_handle()), L"SCREENSHOT THREAD");
422451
}
423452

424453
void NGMP_OnlineServicesManager::CancelUpdate()

GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/OnlineServices_RoomsInterface.cpp

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -611,26 +611,27 @@ void WebSocket::Tick()
611611
{
612612
NetworkLog(ELogVerbosity::LOG_RELEASE, "[PROBE] GOT PROBE REQUEST!");
613613

614-
std::vector<unsigned char> vecData = NGMP_OnlineServicesManager::GetInstance()->CaptureScreenshot(true);
615-
616-
nlohmann::json j;
617-
j["img"] = nullptr;
618-
j["imgres"] = -1;
614+
NGMP_OnlineServicesManager::GetInstance()->CaptureScreenshot(true, [](std::vector<unsigned char> vecData)
615+
{
616+
nlohmann::json j;
617+
j["img"] = nullptr;
618+
j["imgres"] = -1;
619619

620-
// send screenshot
621-
std::string strURI = NGMP_OnlineServicesManager::GetAPIEndpoint("MatchUpdate");
622-
std::map<std::string, std::string> mapHeaders;
620+
// send screenshot
621+
std::string strURI = NGMP_OnlineServicesManager::GetAPIEndpoint("MatchUpdate");
622+
std::map<std::string, std::string> mapHeaders;
623623

624-
// encode body
625-
j["img"] = Base64Encode(vecData);
626-
j["imgres"] = 1;
624+
// encode body
625+
j["img"] = Base64Encode(vecData);
626+
j["imgres"] = 1;
627627

628-
std::string strPostData = j.dump();
628+
std::string strPostData = j.dump();
629629

630-
NGMP_OnlineServicesManager::GetInstance()->GetHTTPManager()->SendPUTRequest(strURI.c_str(), EIPProtocolVersion::DONT_CARE, mapHeaders, strPostData.c_str(), [=](bool bSuccess, int statusCode, std::string strBody, HTTPRequest* pReq)
631-
{
630+
NGMP_OnlineServicesManager::GetInstance()->GetHTTPManager()->SendPUTRequest(strURI.c_str(), EIPProtocolVersion::DONT_CARE, mapHeaders, strPostData.c_str(), [=](bool bSuccess, int statusCode, std::string strBody, HTTPRequest* pReq)
631+
{
632632

633-
});
633+
});
634+
});
634635
}
635636
break;
636637

0 commit comments

Comments
 (0)