Changeset View
Changeset View
Standalone View
Standalone View
source/graphics/TextureConverter.cpp
Show First 20 Lines • Show All 278 Lines • ▼ Show 20 Lines | CTextureConverter::CTextureConverter(PIVFS vfs, bool highQuality) : | ||||
m_VFS(vfs), m_HighQuality(highQuality), m_Shutdown(false) | m_VFS(vfs), m_HighQuality(highQuality), m_Shutdown(false) | ||||
{ | { | ||||
// Verify that we are running with at least the version we were compiled with, | // Verify that we are running with at least the version we were compiled with, | ||||
// to avoid bugs caused by ABI changes | // to avoid bugs caused by ABI changes | ||||
#if CONFIG2_NVTT | #if CONFIG2_NVTT | ||||
ENSURE(nvtt::version() >= NVTT_VERSION); | ENSURE(nvtt::version() >= NVTT_VERSION); | ||||
#endif | #endif | ||||
// Set up the worker thread: | |||||
// Use SDL semaphores since OS X doesn't implement sem_init | |||||
m_WorkerSem = SDL_CreateSemaphore(0); | |||||
ENSURE(m_WorkerSem); | |||||
m_WorkerThread = std::thread(RunThread, this); | m_WorkerThread = std::thread(RunThread, this); | ||||
// Maybe we should share some centralised pool of worker threads? | // Maybe we should share some centralised pool of worker threads? | ||||
// For now we'll just stick with a single thread for this specific use. | // For now we'll just stick with a single thread for this specific use. | ||||
} | } | ||||
CTextureConverter::~CTextureConverter() | CTextureConverter::~CTextureConverter() | ||||
{ | { | ||||
// Tell the thread to shut down | // Tell the thread to shut down | ||||
{ | { | ||||
std::lock_guard<std::mutex> lock(m_WorkerMutex); | std::lock_guard<std::mutex> lock(m_WorkerMutex); | ||||
m_Shutdown = true; | m_Shutdown = true; | ||||
} | } | ||||
// Wake it up so it sees the notification | while (true) | ||||
SDL_SemPost(m_WorkerSem); | { | ||||
// Wake the thread up so that it shutdowns. | |||||
// If we send the message once, there is a chance it will be missed, | |||||
// so keep sending until shtudown becomes false again, indicating that the thread has shut down. | |||||
std::lock_guard<std::mutex> lock(m_WorkerMutex); | |||||
m_WorkerCV.notify_all(); | |||||
if (!m_Shutdown) | |||||
break; | |||||
} | |||||
// Wait for it to shut down cleanly | // Wait for it to shut down cleanly | ||||
m_WorkerThread.join(); | m_WorkerThread.join(); | ||||
// Clean up resources | |||||
SDL_DestroySemaphore(m_WorkerSem); | |||||
} | } | ||||
bool CTextureConverter::ConvertTexture(const CTexturePtr& texture, const VfsPath& src, const VfsPath& dest, const Settings& settings) | bool CTextureConverter::ConvertTexture(const CTexturePtr& texture, const VfsPath& src, const VfsPath& dest, const Settings& settings) | ||||
{ | { | ||||
shared_ptr<u8> file; | shared_ptr<u8> file; | ||||
size_t fileSize; | size_t fileSize; | ||||
if (m_VFS->LoadFile(src, file, fileSize) < 0) | if (m_VFS->LoadFile(src, file, fileSize) < 0) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 135 Lines • ▼ Show 20 Lines | #if CONFIG2_NVTT | ||||
} | } | ||||
{ | { | ||||
std::lock_guard<std::mutex> lock(m_WorkerMutex); | std::lock_guard<std::mutex> lock(m_WorkerMutex); | ||||
m_RequestQueue.push_back(request); | m_RequestQueue.push_back(request); | ||||
} | } | ||||
// Wake up the worker thread | // Wake up the worker thread | ||||
SDL_SemPost(m_WorkerSem); | m_WorkerCV.notify_all(); | ||||
return true; | return true; | ||||
#else | #else | ||||
LOGERROR("Failed to convert texture \"%s\" (NVTT not available)", src.string8()); | LOGERROR("Failed to convert texture \"%s\" (NVTT not available)", src.string8()); | ||||
return false; | return false; | ||||
#endif | #endif | ||||
} | } | ||||
▲ Show 20 Lines • Show All 47 Lines • ▼ Show 20 Lines | |||||
#else // #if CONFIG2_NVTT | #else // #if CONFIG2_NVTT | ||||
return false; | return false; | ||||
#endif | #endif | ||||
} | } | ||||
bool CTextureConverter::IsBusy() | bool CTextureConverter::IsBusy() | ||||
{ | { | ||||
std::lock_guard<std::mutex> lock(m_WorkerMutex); | std::lock_guard<std::mutex> lock(m_WorkerMutex); | ||||
bool busy = !m_RequestQueue.empty(); | return !m_RequestQueue.empty(); | ||||
return busy; | |||||
} | } | ||||
Stan: Why the need for the scope and busy variable here ? | |||||
Done Inline Actionsprobably un-necessary, I ran into an issue but it wasn't related here. wraitii: probably un-necessary, I ran into an issue but it wasn't related here. | |||||
void CTextureConverter::RunThread(CTextureConverter* textureConverter) | void CTextureConverter::RunThread(CTextureConverter* textureConverter) | ||||
{ | { | ||||
debug_SetThreadName("TextureConverter"); | debug_SetThreadName("TextureConverter"); | ||||
g_Profiler2.RegisterCurrentThread("texconv"); | g_Profiler2.RegisterCurrentThread("texconv"); | ||||
#if CONFIG2_NVTT | #if CONFIG2_NVTT | ||||
// Wait until the main thread wakes us up | // Wait until the main thread wakes us up | ||||
while (SDL_SemWait(textureConverter->m_WorkerSem) == 0) | while (true) | ||||
{ | { | ||||
// We may have several textures in the incoming queue, process them all before going back to sleep. | |||||
if (!textureConverter->IsBusy()) { | |||||
std::unique_lock<std::mutex> wait_lock(textureConverter->m_WorkerMutex); | |||||
// Use the no-condition variant because spurious wake-ups don't matter that much here. | |||||
textureConverter->m_WorkerCV.wait(wait_lock); | |||||
} | |||||
g_Profiler2.RecordSyncMarker(); | g_Profiler2.RecordSyncMarker(); | ||||
PROFILE2_EVENT("wakeup"); | PROFILE2_EVENT("wakeup"); | ||||
shared_ptr<ConversionRequest> request; | shared_ptr<ConversionRequest> request; | ||||
{ | { | ||||
std::lock_guard<std::mutex> lock(textureConverter->m_WorkerMutex); | std::lock_guard<std::mutex> wait_lock(textureConverter->m_WorkerMutex); | ||||
if (textureConverter->m_Shutdown) | if (textureConverter->m_Shutdown) | ||||
break; | break; | ||||
// If we weren't woken up for shutdown, we must have been woken up for | // If we weren't woken up for shutdown, we must have been woken up for | ||||
// a new request, so grab it from the queue | // a new request, so grab it from the queue | ||||
request = textureConverter->m_RequestQueue.front(); | request = textureConverter->m_RequestQueue.front(); | ||||
textureConverter->m_RequestQueue.pop_front(); | textureConverter->m_RequestQueue.pop_front(); | ||||
} | } | ||||
Show All 23 Lines | // TIMER(L"TextureConverter compress"); | ||||
if (request->isDXT1a && result->ret && result->output.buffer.size() > 80) | if (request->isDXT1a && result->ret && result->output.buffer.size() > 80) | ||||
result->output.buffer[80] |= 1; // DDPF_ALPHAPIXELS in DDS_PIXELFORMAT.dwFlags | result->output.buffer[80] |= 1; // DDPF_ALPHAPIXELS in DDS_PIXELFORMAT.dwFlags | ||||
// Ugly hack: NVTT always sets DDPF_RGB, even if we're trying to output 8-bit | // Ugly hack: NVTT always sets DDPF_RGB, even if we're trying to output 8-bit | ||||
// alpha-only DDS with no RGB components. Unset that flag. | // alpha-only DDS with no RGB components. Unset that flag. | ||||
if (request->is8bpp) | if (request->is8bpp) | ||||
result->output.buffer[80] &= ~0x40; // DDPF_RGB in DDS_PIXELFORMAT.dwFlags | result->output.buffer[80] &= ~0x40; // DDPF_RGB in DDS_PIXELFORMAT.dwFlags | ||||
// Push the result onto the queue | // Push the result onto the queue | ||||
std::lock_guard<std::mutex> lock(textureConverter->m_WorkerMutex); | std::lock_guard<std::mutex> wait_lock(textureConverter->m_WorkerMutex); | ||||
textureConverter->m_ResultQueue.push_back(result); | textureConverter->m_ResultQueue.push_back(result); | ||||
} | } | ||||
std::lock_guard<std::mutex> wait_lock(textureConverter->m_WorkerMutex); | |||||
textureConverter->m_Shutdown = false; | |||||
#endif | #endif | ||||
} | } |
Wildfire Games · Phabricator
Why the need for the scope and busy variable here ?