Add inter-process communcation to shader cache
authorJari Komppa <jari.komppa@siru.fi>
Mon, 16 May 2022 09:28:54 +0000 (12:28 +0300)
committerMatthew Netsch <quic_mnetsch@quicinc.com>
Thu, 2 Jun 2022 22:40:16 +0000 (22:40 +0000)
This change allows running several instances of CTS which share a single
shader cache.

std::map which was used to hold the cache index was replaced by a simple
binary search tree implementation in order to play nice in a
pre-allocated buffer. Benchmarking gives approximately same performance
or better in random cases. Worst case behavior is terrible, but did not
occur while testing after integrating with CTS.

By default the size of the index is 1M entries, taking 16M of memory.
This memory pool is not resized dynamically, and will assert if over 1M
unique shaders are inserted to the cache.

Additional small improvement to the shader cache behavior avoids
re-hashing the cache key at cache miss.

For platforms that do not support IPC, the IPC code can be compiled out
using a predefined macro DISABLE_SHADERCACHE_IPC.

The IPC behavior is disabled by default, leading to old behavior, except
for the std::map to bst change and no re-hashing.

Affects:
*

VK-GL-CTS issue: 3565

Change-Id: I4f2433256968b76de26c9bd50981454bb4accae0

external/vulkancts/README.md
external/vulkancts/framework/vulkan/vkIPC.inl [new file with mode: 0644]
external/vulkancts/framework/vulkan/vkPrograms.cpp
framework/common/tcuCommandLine.cpp
framework/common/tcuCommandLine.hpp

index 33e0501..e585c48 100644 (file)
@@ -605,6 +605,21 @@ targets.
 Do not truncate the shader cache file at startup. No shader compilation will
 occur on repeated runs of the CTS.
 
+       --deqp-shadercache-ipc=enable
+
+Enables the use of inter-process communication primitives to allow several
+instances of CTS to share a single cache file. All of the instances must
+use the same shader cache filename.
+
+Note that if one instance should crash while holding the cache file lock,
+the other instances will hang. The lock is only held while reading or
+writing to the cache, so crashes are unlikely.
+
+In case of a crash outside the cache file lock, the named shared memory
+and shared semaphore may be left behind. These will be re-used by CTS on
+subsequent runs, so additional memory leak will not occur. Shader cache
+truncate may not work in this case. On Windows, when all instances of
+CTS have terminated the shared resources get automatically cleaned up.
 
 RenderDoc
 ---------
diff --git a/external/vulkancts/framework/vulkan/vkIPC.inl b/external/vulkancts/framework/vulkan/vkIPC.inl
new file mode 100644 (file)
index 0000000..3ec4fab
--- /dev/null
@@ -0,0 +1,248 @@
+#if defined(_WIN32)
+typedef void *HANDLE;
+#else
+#include <semaphore.h>
+#endif
+
+typedef struct ipc_sharedmemory_
+{
+    char*                      name;
+    unsigned char*     data;
+    size_t                     size;
+#if defined(_WIN32)
+    HANDLE                     handle;
+#else
+    int                                fd;
+#endif
+} ipc_sharedmemory;
+
+typedef struct ipc_sharedsemaphore_
+{
+    char*                      name;
+#if defined(_WIN32)
+    HANDLE                     handle;
+#else
+    sem_t*                     semaphore;
+#endif
+} ipc_sharedsemaphore;
+
+#if defined(_WIN32)
+#include <windows.h>
+#else // !_WIN32
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#endif // !_WIN32
+
+static char* ipc_strdup (char* src)
+{
+       int             i;
+       int             len = 0;
+    char*      dst = NULL;
+    while (src[len]) len++;
+#if !defined(_WIN32)
+    len++;
+#endif
+    dst = (char*)malloc(len + 1);
+    if (!dst) return NULL;
+    dst[len] = 0;
+
+#if defined(_WIN32)
+    for (i = 0; i < len; i++)
+        dst[i] = src[i];
+#else
+    dst[0] = '/';
+    for (i = 0; i < len - 1; i++)
+        dst[i + 1] = src[i];
+#endif
+    return dst;
+}
+
+void ipc_mem_init (ipc_sharedmemory* mem, char* name, size_t size)
+{
+    mem->name = ipc_strdup(name);
+
+    mem->size = size;
+    mem->data = NULL;
+#if defined(_WIN32)
+    mem->handle = 0;
+#else
+    mem->fd = -1;
+#endif
+}
+
+unsigned char* ipc_mem_access (ipc_sharedmemory* mem)
+{
+    return mem->data;
+}
+
+void ipc_sem_init (ipc_sharedsemaphore* sem, char* name)
+{
+    sem->name = ipc_strdup(name);
+#if defined(_WIN32)
+    sem->handle = 0;
+#else
+    sem->semaphore = NULL;
+#endif
+}
+
+#if defined(_WIN32)
+
+int ipc_mem_open_existing (ipc_sharedmemory* mem)
+{
+    mem->handle = OpenFileMappingA(FILE_MAP_ALL_ACCESS, FALSE, mem->name);
+
+    if (!mem->handle)
+        return -1;
+
+    mem->data = (unsigned char*)MapViewOfFile(mem->handle, FILE_MAP_ALL_ACCESS, 0, 0, mem->size);
+
+    if (!mem->data)
+        return -1;
+    return 0;
+}
+
+int ipc_mem_create (ipc_sharedmemory* mem)
+{
+    mem->handle = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, (DWORD)mem->size, mem->name);
+
+    if (!mem->handle)
+        return -1;
+
+    mem->data = (unsigned char*)MapViewOfFile(mem->handle, FILE_MAP_ALL_ACCESS, 0, 0, mem->size);
+
+    if (!mem->data)
+        return -1;
+
+    return 0;
+}
+
+void ipc_mem_close (ipc_sharedmemory* mem)
+{
+    if (mem->data != NULL)
+    {
+        UnmapViewOfFile(mem->data);
+        mem->data = NULL;
+    }
+    free(mem->name);
+    mem->name = NULL;
+    mem->size = 0;
+}
+
+int ipc_sem_create (ipc_sharedsemaphore* sem, int initialvalue)
+{
+    sem->handle = CreateSemaphoreA(NULL, initialvalue, 0x7fffffff, sem->name);
+    if (!sem->handle)
+        return -1;
+    return 0;
+}
+
+void ipc_sem_close (ipc_sharedsemaphore* sem)
+{
+    CloseHandle(sem->handle);
+    free(sem->name);
+    sem->handle = 0;
+}
+
+void ipc_sem_increment (ipc_sharedsemaphore* sem)
+{
+    ReleaseSemaphore(sem->handle, 1, NULL);
+}
+
+void ipc_sem_decrement (ipc_sharedsemaphore* sem)
+{
+    WaitForSingleObject(sem->handle, INFINITE);
+}
+
+int ipc_sem_try_decrement (ipc_sharedsemaphore* sem)
+{
+    DWORD ret = WaitForSingleObject(sem->handle, 0);
+    if (ret == WAIT_OBJECT_0)
+        return 1;
+    return 0;
+}
+
+#else // !defined(_WIN32)
+
+int ipc_mem_open_existing (ipc_sharedmemory* mem)
+{
+    mem->fd = shm_open(mem->name, O_RDWR, 0755);
+    if (mem->fd < 0)
+        return -1;
+
+    mem->data = (unsigned char *)mmap(NULL, mem->size, PROT_READ | PROT_WRITE, MAP_SHARED, mem->fd, 0);
+    if (!mem->data)
+        return -1;
+
+    return 0;
+}
+
+int ipc_mem_create (ipc_sharedmemory* mem)
+{
+    int ret;
+    ret = shm_unlink(mem->name);
+    if (ret < 0 && errno != ENOENT)
+        return -1;
+
+    mem->fd = shm_open(mem->name, O_CREAT | O_RDWR, 0755);
+    if (mem->fd < 0)
+        return -1;
+
+    ftruncate(mem->fd, mem->size);
+
+    mem->data = (unsigned char *)mmap(NULL, mem->size, PROT_READ | PROT_WRITE, MAP_SHARED, mem->fd, 0);
+    if (!mem->data)
+        return -1;
+
+    return 0;
+}
+
+void ipc_mem_close (ipc_sharedmemory* mem)
+{
+    if (mem->data != NULL)
+    {
+        munmap(mem->data, mem->size);
+        close(mem->fd);
+        shm_unlink(mem->name);
+    }
+    free(mem->name);
+    mem->name = NULL;
+    mem->size = 0;
+}
+
+int ipc_sem_create (ipc_sharedsemaphore* sem, int initialvalue)
+{
+    sem->semaphore = sem_open(sem->name, O_CREAT, 0700, initialvalue);
+    if (sem->semaphore == SEM_FAILED)
+        return -1;
+    return 0;
+}
+
+void ipc_sem_close (ipc_sharedsemaphore* sem)
+{
+    sem_close(sem->semaphore);
+    sem_unlink(sem->name);
+    free(sem->name);
+}
+
+void ipc_sem_increment (ipc_sharedsemaphore* sem)
+{
+    sem_post(sem->semaphore);
+}
+
+void ipc_sem_decrement (ipc_sharedsemaphore* sem)
+{
+    sem_wait(sem->semaphore);
+}
+
+int ipc_sem_try_decrement(ipc_sharedsemaphore* sem)
+{
+    int res = sem_trywait(sem->semaphore);
+    if (res == 0)
+        return 1;
+    return 0;
+}
+
+#endif // !_WIN32
index 77e839f..2b06e79 100644 (file)
@@ -177,28 +177,218 @@ void validateCompiledBinary(const vector<deUint32>& binary, SpirVProgramInfo* bu
        }
 }
 
-de::Mutex                                                      cacheFileMutex;
-map<deUint32, vector<deUint32> >       cacheFileIndex;
-bool                                                           cacheFileFirstRun = true;
+// IPC functions
+#ifndef DISABLE_SHADERCACHE_IPC
+#include "vkIPC.inl"
+#endif
+
+// Overridable wrapper for de::Mutex
+class cacheMutex
+{
+public:
+       cacheMutex () {}
+       virtual ~cacheMutex () {}
+       virtual void lock () { localMutex.lock(); }
+       virtual void unlock () { localMutex.unlock(); }
+private:
+       de::Mutex localMutex;
+};
+
+#ifndef DISABLE_SHADERCACHE_IPC
+// Overriden cacheMutex that uses IPC instead
+class cacheMutexIPC : public cacheMutex
+{
+public:
+       cacheMutexIPC ()
+       {
+               ipc_sem_init(&guard, "cts_shadercache_ipc_guard");
+               ipc_sem_create(&guard, 1);
+       }
+       virtual ~cacheMutexIPC ()
+       {
+               ipc_sem_close(&guard);
+       }
+       virtual void lock () { ipc_sem_decrement(&guard); }
+       virtual void unlock () { ipc_sem_increment(&guard); }
+private:
+       ipc_sharedsemaphore guard;
+};
+#endif
+
+// Each cache node takes 4 * 4 = 16 bytes; 1M items takes 16M memory.
+const deUint32                                         cacheMaxItems           = 1024 * 1024;
+cacheMutex*                                                    cacheFileMutex          = 0;
+bool                                                           cacheFileFirstRun       = true;
+deUint32*                                                      cacheMempool            = 0;
+#ifndef DISABLE_SHADERCACHE_IPC
+ipc_sharedmemory                                       cacheIPCMemory;
+#endif
+
+struct cacheNode
+{
+       deUint32 key;
+       deUint32 data;
+       deUint32 right_child;
+       deUint32 left_child;
+};
+
+cacheNode* cacheSearch (deUint32 key)
+{
+       cacheNode*              r = (cacheNode*)(cacheMempool + 1);
+       unsigned int    p = 0;
+
+       while (1)
+       {
+               if (r[p].key == key)
+                       return &r[p];
+
+               if (key > r[p].key)
+                       p = r[p].right_child;
+               else
+                       p = r[p].left_child;
+
+               if (p == 0)
+                       return 0;
+       }
+}
+
+void cacheInsert (deUint32 key, deUint32 data)
+{
+       cacheNode*      r               = (cacheNode*)(cacheMempool + 1);
+       int*            tail    = (int*)cacheMempool;
+       int                     newnode = *tail;
+
+       DE_ASSERT(newnode < cacheMaxItems);
+
+       // If we run out of cache space, reset the cache index.
+       if (newnode >= cacheMaxItems)
+       {
+               *tail = 0;
+               newnode = 0;
+       }
+
+       r[*tail].data = data;
+       r[*tail].key = key;
+       r[*tail].left_child = 0;
+       r[*tail].right_child = 0;
+
+       (*tail)++;
+
+       if (newnode == 0)
+       {
+               // first
+               return;
+       }
+
+       int p = 0;
+       while (1)
+       {
+               if (r[p].key == key)
+               {
+                       // collision; use the latest data
+                       r[p].data = data;
+                       (*tail)--;
+                       return;
+               }
+
+               if (key > r[p].key)
+               {
+                       if (r[p].right_child != 0)
+                       {
+                               p = r[p].right_child;
+                       }
+                       else
+                       {
+                               r[p].right_child = newnode;
+                               return;
+                       }
+               }
+               else
+               {
+                       if (r[p].left_child != 0)
+                       {
+                               p = r[p].left_child;
+                       }
+                       else
+                       {
+                               r[p].left_child = newnode;
+                               return;
+                       }
+               }
+       }
+}
+
+// Called via atexit()
+void shaderCacheClean ()
+{
+       delete cacheFileMutex;
+       delete[] cacheMempool;
+}
+
+#ifndef DISABLE_SHADERCACHE_IPC
+// Called via atexit()
+void shaderCacheCleanIPC ()
+{
+       delete cacheFileMutex;
+       ipc_mem_close(&cacheIPCMemory);
+}
+#endif
 
-void shaderCacheFirstRunCheck (const char* shaderCacheFile, bool truncate)
+void shaderCacheFirstRunCheck (const tcu::CommandLine& commandLine)
 {
-       cacheFileMutex.lock();
-       if (cacheFileFirstRun)
+       bool first = true;
+
+       if (!cacheFileFirstRun)
+               return;
+
+       cacheFileFirstRun = false;
+
+#ifndef DISABLE_SHADERCACHE_IPC
+       if (commandLine.isShaderCacheIPCEnabled())
+       {
+               // IPC path, allocate shared mutex and shared memory
+               cacheFileMutex = new cacheMutexIPC;
+               cacheFileMutex->lock();
+               ipc_mem_init(&cacheIPCMemory, "cts_shadercache_memory", sizeof(deUint32) * (cacheMaxItems * 4 + 1));
+               if (ipc_mem_open_existing(&cacheIPCMemory) != 0)
+               {
+                       ipc_mem_create(&cacheIPCMemory);
+                       cacheMempool = (deUint32*)ipc_mem_access(&cacheIPCMemory);
+                       cacheMempool[0] = 0;
+               }
+               else
+               {
+                       cacheMempool = (deUint32*)ipc_mem_access(&cacheIPCMemory);
+                       first = false;
+               }
+               atexit(shaderCacheCleanIPC);
+       }
+       else
+#endif
+       {
+               // Non-IPC path, allocate local mutex and memory
+               cacheFileMutex = new cacheMutex;
+               cacheFileMutex->lock();
+               cacheMempool = new deUint32[cacheMaxItems * 4 + 1];
+               cacheMempool[0] = 0;
+
+               atexit(shaderCacheClean);
+       }
+
+       if (first)
        {
-               cacheFileFirstRun = false;
-               if (truncate)
+               if (commandLine.isShaderCacheTruncateEnabled())
                {
                        // Open file with "w" access to truncate it
-                       FILE* f = fopen(shaderCacheFile, "wb");
+                       FILE* f = fopen(commandLine.getShaderCacheFilename(), "wb");
                        if (f)
                                fclose(f);
                }
                else
                {
                        // Parse chunked shader cache file for hashes and offsets
-                       FILE* file = fopen(shaderCacheFile, "rb");
-                       int count = 0;
+                       FILE* file      = fopen(commandLine.getShaderCacheFilename(), "rb");
+                       int count       = 0;
                        if (file)
                        {
                                deUint32 chunksize      = 0;
@@ -210,7 +400,7 @@ void shaderCacheFirstRunCheck (const char* shaderCacheFile, bool truncate)
                                        offset = (deUint32)ftell(file);
                                        if (ok) ok = fread(&chunksize, 1, 4, file)                              == 4;
                                        if (ok) ok = fread(&hash, 1, 4, file)                                   == 4;
-                                       if (ok) cacheFileIndex[hash].push_back(offset);
+                                       if (ok) cacheInsert(hash, offset);
                                        if (ok) ok = fseek(file, offset + chunksize, SEEK_SET)  == 0;
                                        count++;
                                }
@@ -218,7 +408,7 @@ void shaderCacheFirstRunCheck (const char* shaderCacheFile, bool truncate)
                        }
                }
        }
-       cacheFileMutex.unlock();
+       cacheFileMutex->unlock();
 }
 
 std::string intToString (deUint32 integer)
@@ -243,122 +433,117 @@ deUint32 shadercacheHash (const char* str)
        return hash;
 }
 
-vk::ProgramBinary* shadercacheLoad (const std::string& shaderstring, const char* shaderCacheFilename)
+vk::ProgramBinary* shadercacheLoad (const std::string& shaderstring, const char* shaderCacheFilename, deUint32 hash)
 {
-       deUint32                hash            = shadercacheHash(shaderstring.c_str());
        deInt32                 format;
        deInt32                 length;
        deInt32                 sourcelength;
-       deUint32                i;
        deUint32                temp;
        deUint8*                bin                     = 0;
        char*                   source          = 0;
        deBool                  ok                      = true;
        deBool                  diff            = true;
-       cacheFileMutex.lock();
+       cacheNode*              node            = 0;
+       cacheFileMutex->lock();
 
-       if (cacheFileIndex.count(hash) == 0)
+       node = cacheSearch(hash);
+       if (node == 0)
        {
-               cacheFileMutex.unlock();
+               cacheFileMutex->unlock();
                return 0;
        }
        FILE*                   file            = fopen(shaderCacheFilename, "rb");
-       ok                              = file                                                                                  != 0;
-
-       for (i = 0; i < cacheFileIndex[hash].size(); i++)
+       ok                              = file                                                                          != 0;
+
+       if (ok) ok = fseek(file, node->data, SEEK_SET)                          == 0;
+       if (ok) ok = fread(&temp, 1, 4, file)                                           == 4; // Chunk size (skip)
+       if (ok) ok = fread(&temp, 1, 4, file)                                           == 4; // Stored hash
+       if (ok) ok = temp                                                                                       == hash; // Double check
+       if (ok) ok = fread(&format, 1, 4, file)                                         == 4;
+       if (ok) ok = fread(&length, 1, 4, file)                                         == 4;
+       if (ok) ok = length                                                                                     > 0; // sanity check
+       if (ok) bin = new deUint8[length];
+       if (ok) ok = fread(bin, 1, length, file)                                        == (size_t)length;
+       if (ok) ok = fread(&sourcelength, 1, 4, file)                           == 4;
+       if (ok && sourcelength > 0)
        {
-               if (ok) ok = fseek(file, cacheFileIndex[hash][i], SEEK_SET)     == 0;
-               if (ok) ok = fread(&temp, 1, 4, file)                                           == 4; // Chunk size (skip)
-               if (ok) ok = fread(&temp, 1, 4, file)                                           == 4; // Stored hash
-               if (ok) ok = temp                                                                                       == hash; // Double check
-               if (ok) ok = fread(&format, 1, 4, file)                                         == 4;
-               if (ok) ok = fread(&length, 1, 4, file)                                         == 4;
-               if (ok) ok = length                                                                                     > 0; // sanity check
-               if (ok) bin = new deUint8[length];
-               if (ok) ok = fread(bin, 1, length, file)                                        == (size_t)length;
-               if (ok) ok = fread(&sourcelength, 1, 4, file)                           == 4;
-               if (ok && sourcelength > 0)
-               {
-                       source = new char[sourcelength + 1];
-                       ok = fread(source, 1, sourcelength, file)                               == (size_t)sourcelength;
-                       source[sourcelength] = 0;
-                       diff = shaderstring != std::string(source);
-               }
-               if (!ok || diff)
-               {
-                       // Mismatch, but may still exist in cache if there were hash collisions
-                       delete[] source;
-                       delete[] bin;
-               }
-               else
-               {
-                       delete[] source;
-                       if (file) fclose(file);
-                       cacheFileMutex.unlock();
-                       vk::ProgramBinary* res = new vk::ProgramBinary((vk::ProgramFormat)format, length, bin);
-                       delete[] bin;
-                       return res;
-               }
+               source = new char[sourcelength + 1];
+               ok = fread(source, 1, sourcelength, file)                               == (size_t)sourcelength;
+               source[sourcelength] = 0;
+               diff = shaderstring != std::string(source);
+       }
+       if (!ok || diff)
+       {
+               // Mismatch
+               delete[] source;
+               delete[] bin;
+       }
+       else
+       {
+               delete[] source;
+               if (file) fclose(file);
+               cacheFileMutex->unlock();
+               vk::ProgramBinary* res = new vk::ProgramBinary((vk::ProgramFormat)format, length, bin);
+               delete[] bin;
+               return res;
        }
        if (file) fclose(file);
-       cacheFileMutex.unlock();
+       cacheFileMutex->unlock();
        return 0;
 }
 
-void shadercacheSave (const vk::ProgramBinary* binary, const std::string& shaderstring, const char* shaderCacheFilename)
+void shadercacheSave (const vk::ProgramBinary* binary, const std::string& shaderstring, const char* shaderCacheFilename, deUint32 hash)
 {
        if (binary == 0)
                return;
-       deUint32                        hash            = shadercacheHash(shaderstring.c_str());
        deInt32                         format          = binary->getFormat();
        deUint32                        length          = (deUint32)binary->getSize();
        deUint32                        chunksize;
        deUint32                        offset;
        const deUint8*          bin                     = binary->getBinary();
        const de::FilePath      filePath        (shaderCacheFilename);
+       cacheNode*                      node            = 0;
 
-       cacheFileMutex.lock();
+       cacheFileMutex->lock();
 
-       if (cacheFileIndex[hash].size())
+       node = cacheSearch(hash);
+
+       if (node)
        {
                FILE*                   file            = fopen(shaderCacheFilename, "rb");
                deBool                  ok                      = (file != 0);
                deBool                  diff            = DE_TRUE;
                deInt32                 sourcelength;
-               deUint32                i;
                deUint32                temp;
 
-               for (i = 0; i < cacheFileIndex[hash].size(); i++)
+               deUint32        cachedLength    = 0;
+
+               if (ok) ok = fseek(file, node->data, SEEK_SET)                          == 0;
+               if (ok) ok = fread(&temp, 1, 4, file)                                           == 4; // Chunk size (skip)
+               if (ok) ok = fread(&temp, 1, 4, file)                                           == 4; // Stored hash
+               if (ok) ok = temp                                                                                       == hash; // Double check
+               if (ok) ok = fread(&temp, 1, 4, file)                                           == 4;
+               if (ok) ok = fread(&cachedLength, 1, 4, file)                           == 4;
+               if (ok) ok = cachedLength                                                                       > 0; // sanity check
+               if (ok) fseek(file, cachedLength, SEEK_CUR); // skip binary
+               if (ok) ok = fread(&sourcelength, 1, 4, file)                           == 4;
+
+               if (ok && sourcelength > 0)
                {
-                       deUint32        cachedLength    = 0;
-
-                       if (ok) ok = fseek(file, cacheFileIndex[hash][i], SEEK_SET)     == 0;
-                       if (ok) ok = fread(&temp, 1, 4, file)                                           == 4; // Chunk size (skip)
-                       if (ok) ok = fread(&temp, 1, 4, file)                                           == 4; // Stored hash
-                       if (ok) ok = temp                                                                                       == hash; // Double check
-                       if (ok) ok = fread(&temp, 1, 4, file)                                           == 4;
-                       if (ok) ok = fread(&cachedLength, 1, 4, file)                           == 4;
-                       if (ok) ok = cachedLength                                                                       > 0; // sanity check
-                       if (ok) fseek(file, cachedLength, SEEK_CUR); // skip binary
-                       if (ok) ok = fread(&sourcelength, 1, 4, file)                           == 4;
-
-                       if (ok && sourcelength > 0)
-                       {
-                               char* source;
-                               source  = new char[sourcelength + 1];
-                               ok              = fread(source, 1, sourcelength, file)                  == (size_t)sourcelength;
-                               source[sourcelength] = 0;
-                               diff    = shaderstring != std::string(source);
-                               delete[] source;
-                       }
+                       char* source;
+                       source  = new char[sourcelength + 1];
+                       ok              = fread(source, 1, sourcelength, file)                  == (size_t)sourcelength;
+                       source[sourcelength] = 0;
+                       diff    = shaderstring != std::string(source);
+                       delete[] source;
+               }
 
-                       if (ok && !diff)
-                       {
-                               // Already in cache (written by another thread, probably)
-                               fclose(file);
-                               cacheFileMutex.unlock();
-                               return;
-                       }
+               if (ok && !diff)
+               {
+                       // Already in cache (written by another thread, probably)
+                       fclose(file);
+                       cacheFileMutex->unlock();
+                       return;
                }
                fclose(file);
        }
@@ -369,7 +554,7 @@ void shadercacheSave (const vk::ProgramBinary* binary, const std::string& shader
        FILE*                           file            = fopen(shaderCacheFilename, "ab");
        if (!file)
        {
-               cacheFileMutex.unlock();
+               cacheFileMutex->unlock();
                return;
        }
        // Append mode starts writing from the end of the file,
@@ -386,9 +571,9 @@ void shadercacheSave (const vk::ProgramBinary* binary, const std::string& shader
        fwrite(&length, 1, 4, file);
        fwrite(shaderstring.c_str(), 1, length, file);
        fclose(file);
-       cacheFileIndex[hash].push_back(offset);
+       cacheInsert(hash, offset);
 
-       cacheFileMutex.unlock();
+       cacheFileMutex->unlock();
 }
 
 // Insert any information that may affect compilation into the shader string.
@@ -430,10 +615,11 @@ ProgramBinary* buildProgram (const GlslSource& program, glu::ShaderProgramInfo*
        std::string                     shaderstring;
        vk::ProgramBinary*      res                                     = 0;
        const int                       optimizationRecipe      = commandLine.getOptimizationRecipe();
+       deUint32                        hash                            = 0;
 
        if (commandLine.isShadercacheEnabled())
        {
-               shaderCacheFirstRunCheck(commandLine.getShaderCacheFilename(), commandLine.isShaderCacheTruncateEnabled());
+               shaderCacheFirstRunCheck(commandLine);
                getCompileEnvironment(cachekey);
                getBuildOptions(cachekey, program.buildOptions, optimizationRecipe);
 
@@ -450,7 +636,9 @@ ProgramBinary* buildProgram (const GlslSource& program, glu::ShaderProgramInfo*
 
                cachekey = cachekey + shaderstring;
 
-               res = shadercacheLoad(cachekey, commandLine.getShaderCacheFilename());
+               hash = shadercacheHash(cachekey.c_str());
+
+               res = shadercacheLoad(cachekey, commandLine.getShaderCacheFilename(), hash);
 
                if (res)
                {
@@ -501,7 +689,7 @@ ProgramBinary* buildProgram (const GlslSource& program, glu::ShaderProgramInfo*
 
                res = createProgramBinaryFromSpirV(binary);
                if (commandLine.isShadercacheEnabled())
-                       shadercacheSave(res, cachekey, commandLine.getShaderCacheFilename());
+                       shadercacheSave(res, cachekey, commandLine.getShaderCacheFilename(), hash);
        }
        return res;
 }
@@ -515,10 +703,11 @@ ProgramBinary* buildProgram (const HlslSource& program, glu::ShaderProgramInfo*
        std::string                     shaderstring;
        vk::ProgramBinary*      res                                     = 0;
        const int                       optimizationRecipe      = commandLine.getOptimizationRecipe();
+       deInt32                         hash                            = 0;
 
        if (commandLine.isShadercacheEnabled())
        {
-               shaderCacheFirstRunCheck(commandLine.getShaderCacheFilename(), commandLine.isShaderCacheTruncateEnabled());
+               shaderCacheFirstRunCheck(commandLine);
                getCompileEnvironment(cachekey);
                getBuildOptions(cachekey, program.buildOptions, optimizationRecipe);
 
@@ -535,7 +724,9 @@ ProgramBinary* buildProgram (const HlslSource& program, glu::ShaderProgramInfo*
 
                cachekey = cachekey + shaderstring;
 
-               res = shadercacheLoad(cachekey, commandLine.getShaderCacheFilename());
+               hash = shadercacheHash(cachekey.c_str());
+
+               res = shadercacheLoad(cachekey, commandLine.getShaderCacheFilename(), hash);
 
                if (res)
                {
@@ -586,7 +777,9 @@ ProgramBinary* buildProgram (const HlslSource& program, glu::ShaderProgramInfo*
 
                res = createProgramBinaryFromSpirV(binary);
                if (commandLine.isShadercacheEnabled())
-                       shadercacheSave(res, cachekey, commandLine.getShaderCacheFilename());
+               {
+                       shadercacheSave(res, cachekey, commandLine.getShaderCacheFilename(), hash);
+               }
        }
        return res;
 }
@@ -599,10 +792,11 @@ ProgramBinary* assembleProgram (const SpirVAsmSource& program, SpirVProgramInfo*
        vk::ProgramBinary*      res                                     = 0;
        std::string                     cachekey;
        const int                       optimizationRecipe      = commandLine.isSpirvOptimizationEnabled() ? commandLine.getOptimizationRecipe() : 0;
+       deUint32                        hash                            = 0;
 
        if (commandLine.isShadercacheEnabled())
        {
-               shaderCacheFirstRunCheck(commandLine.getShaderCacheFilename(), commandLine.isShaderCacheTruncateEnabled());
+               shaderCacheFirstRunCheck(commandLine);
                getCompileEnvironment(cachekey);
                cachekey += "Target Spir-V ";
                cachekey += getSpirvVersionName(spirvVersion);
@@ -616,7 +810,9 @@ ProgramBinary* assembleProgram (const SpirVAsmSource& program, SpirVProgramInfo*
 
                cachekey += program.source;
 
-               res = shadercacheLoad(cachekey, commandLine.getShaderCacheFilename());
+               hash = shadercacheHash(cachekey.c_str());
+
+               res = shadercacheLoad(cachekey, commandLine.getShaderCacheFilename(), hash);
 
                if (res)
                {
@@ -646,7 +842,9 @@ ProgramBinary* assembleProgram (const SpirVAsmSource& program, SpirVProgramInfo*
 
                res = createProgramBinaryFromSpirV(binary);
                if (commandLine.isShadercacheEnabled())
-                       shadercacheSave(res, cachekey, commandLine.getShaderCacheFilename());
+               {
+                       shadercacheSave(res, cachekey, commandLine.getShaderCacheFilename(), hash);
+               }
        }
        return res;
 }
index 0fff299..600aa9e 100644 (file)
@@ -99,6 +99,7 @@ DE_DECLARE_COMMAND_LINE_OPT(ShaderCacheFilename,              std::string);
 DE_DECLARE_COMMAND_LINE_OPT(Optimization,                              int);
 DE_DECLARE_COMMAND_LINE_OPT(OptimizeSpirv,                             bool);
 DE_DECLARE_COMMAND_LINE_OPT(ShaderCacheTruncate,               bool);
+DE_DECLARE_COMMAND_LINE_OPT(ShaderCacheIPC,                            bool);
 DE_DECLARE_COMMAND_LINE_OPT(RenderDoc,                                 bool);
 DE_DECLARE_COMMAND_LINE_OPT(CaseFraction,                              std::vector<int>);
 DE_DECLARE_COMMAND_LINE_OPT(CaseFractionMandatoryTests,        std::string);
@@ -226,6 +227,7 @@ void registerOptions (de::cmdline::Parser& parser)
                << Option<ShaderCache>                                  (DE_NULL,       "deqp-shadercache",                                                     "Enable or disable shader cache",                                       s_enableNames,          "enable")
                << Option<ShaderCacheFilename>                  (DE_NULL,       "deqp-shadercache-filename",                            "Write shader cache to given file",                                                                             "shadercache.bin")
                << Option<ShaderCacheTruncate>                  (DE_NULL,       "deqp-shadercache-truncate",                            "Truncate shader cache before running tests",           s_enableNames,          "enable")
+               << Option<ShaderCacheIPC>                               (DE_NULL,       "deqp-shadercache-ipc",                                         "Should shader cache use inter process comms",          s_enableNames,          "disable")
                << Option<RenderDoc>                                    (DE_NULL,       "deqp-renderdoc",                                                       "Enable RenderDoc frame markers",                                       s_enableNames,          "disable")
                << Option<CaseFraction>                                 (DE_NULL,       "deqp-fraction",                                                        "Run a fraction of the test cases (e.g. N,M means run group%M==N)",     parseIntList,   "")
                << Option<CaseFractionMandatoryTests>   (DE_NULL,       "deqp-fraction-mandatory-caselist-file",        "Case list file that must be run for each fraction",                                    "")
@@ -964,6 +966,7 @@ bool                                        CommandLine::isOutOfMemoryTestEnabled           (void) const    { return m_cmdLine.
 bool                                   CommandLine::isShadercacheEnabled                       (void) const    { return m_cmdLine.getOption<opt::ShaderCache>();                                                       }
 const char*                            CommandLine::getShaderCacheFilename                     (void) const    { return m_cmdLine.getOption<opt::ShaderCacheFilename>().c_str();                       }
 bool                                   CommandLine::isShaderCacheTruncateEnabled       (void) const    { return m_cmdLine.getOption<opt::ShaderCacheTruncate>();                                       }
+bool                                   CommandLine::isShaderCacheIPCEnabled            (void) const    { return m_cmdLine.getOption<opt::ShaderCacheIPC>();                                            }
 int                                            CommandLine::getOptimizationRecipe                      (void) const    { return m_cmdLine.getOption<opt::Optimization>();                                                      }
 bool                                   CommandLine::isSpirvOptimizationEnabled         (void) const    { return m_cmdLine.getOption<opt::OptimizeSpirv>();                                                     }
 bool                                   CommandLine::isRenderDocEnabled                         (void) const    { return m_cmdLine.getOption<opt::RenderDoc>();                                                         }
index f845789..1701c39 100644 (file)
@@ -247,6 +247,9 @@ public:
        //! Should the shader cache be truncated before run (--deqp-shadercache-truncate)
        bool                                                    isShaderCacheTruncateEnabled    (void) const;
 
+       //! Should the shader cache use inter process communication (IPC) (--deqp-shadercache-ipc)
+       bool                                                    isShaderCacheIPCEnabled (void) const;
+
        //! Get shader optimization recipe (--deqp-optimization-recipe)
        int                                                             getOptimizationRecipe           (void) const;