737 lines
26 KiB
C
737 lines
26 KiB
C
/*
|
|
* Copyright 2020 Philip Rebohle for Valve Corporation
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#define VKD3D_DBG_CHANNEL VKD3D_DBG_CHANNEL_API
|
|
|
|
#include "vkd3d_private.h"
|
|
|
|
static VkResult vkd3d_create_pipeline_cache(struct d3d12_device *device,
|
|
size_t size, const void *data, VkPipelineCache *cache)
|
|
{
|
|
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
|
|
|
|
VkPipelineCacheCreateInfo info;
|
|
info.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
|
|
info.pNext = NULL;
|
|
info.flags = 0;
|
|
info.initialDataSize = size;
|
|
info.pInitialData = data;
|
|
|
|
return VK_CALL(vkCreatePipelineCache(device->vk_device, &info, NULL, cache));
|
|
}
|
|
|
|
#define VKD3D_CACHE_BLOB_VERSION MAKE_MAGIC('V','K','B',1)
|
|
#define VKD3D_CACHE_BUILD_SIZE 8
|
|
|
|
struct vkd3d_pipeline_blob
|
|
{
|
|
uint32_t version;
|
|
uint32_t vendor_id;
|
|
uint32_t device_id;
|
|
char vkd3d_build[VKD3D_CACHE_BUILD_SIZE];
|
|
uint8_t cache_uuid[VK_UUID_SIZE];
|
|
uint8_t vk_blob[];
|
|
};
|
|
|
|
HRESULT vkd3d_create_pipeline_cache_from_d3d12_desc(struct d3d12_device *device,
|
|
const D3D12_CACHED_PIPELINE_STATE *state, VkPipelineCache *cache)
|
|
{
|
|
const VkPhysicalDeviceProperties *device_properties = &device->device_info.properties2.properties;
|
|
const struct vkd3d_pipeline_blob *blob = state->pCachedBlob;
|
|
VkResult vr;
|
|
|
|
if (!state->CachedBlobSizeInBytes)
|
|
{
|
|
vr = vkd3d_create_pipeline_cache(device, 0, NULL, cache);
|
|
return hresult_from_vk_result(vr);
|
|
}
|
|
|
|
/* Avoid E_INVALIDARG with an invalid header size, since that may confuse some games */
|
|
if (state->CachedBlobSizeInBytes < sizeof(*blob) || blob->version != VKD3D_CACHE_BLOB_VERSION)
|
|
return D3D12_ERROR_DRIVER_VERSION_MISMATCH;
|
|
|
|
/* Indicate that the cached data is not useful if we're running on a different device or driver */
|
|
if (blob->vendor_id != device_properties->vendorID || blob->device_id != device_properties->deviceID)
|
|
return D3D12_ERROR_ADAPTER_NOT_FOUND;
|
|
|
|
/* Check the vkd3d build since the shader compiler itself may change,
|
|
* and the driver since that will affect the generated pipeline cache */
|
|
if (strncmp(blob->vkd3d_build, vkd3d_build, VKD3D_CACHE_BUILD_SIZE) ||
|
|
memcmp(blob->cache_uuid, device_properties->pipelineCacheUUID, VK_UUID_SIZE))
|
|
return D3D12_ERROR_DRIVER_VERSION_MISMATCH;
|
|
|
|
vr = vkd3d_create_pipeline_cache(device, state->CachedBlobSizeInBytes - sizeof(*blob), blob->vk_blob, cache);
|
|
return hresult_from_vk_result(vr);
|
|
}
|
|
|
|
VkResult vkd3d_serialize_pipeline_state(const struct d3d12_pipeline_state *state, size_t *size, void *data)
|
|
{
|
|
const VkPhysicalDeviceProperties *device_properties = &state->device->device_info.properties2.properties;
|
|
const struct vkd3d_vk_device_procs *vk_procs = &state->device->vk_procs;
|
|
struct vkd3d_pipeline_blob *blob = data;
|
|
size_t total_size = sizeof(*blob);
|
|
size_t vk_blob_size = 0;
|
|
VkResult vr;
|
|
|
|
if (state->vk_pso_cache)
|
|
{
|
|
if ((vr = VK_CALL(vkGetPipelineCacheData(state->device->vk_device, state->vk_pso_cache, &vk_blob_size, NULL))))
|
|
{
|
|
ERR("Failed to retrieve pipeline cache size, vr %d.\n", vr);
|
|
return vr;
|
|
}
|
|
}
|
|
|
|
total_size += vk_blob_size;
|
|
|
|
if (blob && *size < total_size)
|
|
return VK_INCOMPLETE;
|
|
|
|
if (blob)
|
|
{
|
|
blob->version = VKD3D_CACHE_BLOB_VERSION;
|
|
blob->vendor_id = device_properties->vendorID;
|
|
blob->device_id = device_properties->deviceID;
|
|
strncpy(blob->vkd3d_build, vkd3d_build, VKD3D_CACHE_BUILD_SIZE);
|
|
memcpy(blob->cache_uuid, device_properties->pipelineCacheUUID, VK_UUID_SIZE);
|
|
|
|
if (state->vk_pso_cache)
|
|
{
|
|
if ((vr = VK_CALL(vkGetPipelineCacheData(state->device->vk_device, state->vk_pso_cache, &vk_blob_size, blob->vk_blob))))
|
|
return vr;
|
|
}
|
|
}
|
|
|
|
*size = total_size;
|
|
return VK_SUCCESS;
|
|
}
|
|
|
|
struct vkd3d_cached_pipeline_key
|
|
{
|
|
size_t name_length;
|
|
const void *name;
|
|
};
|
|
|
|
struct vkd3d_cached_pipeline_data
|
|
{
|
|
size_t blob_length;
|
|
const void *blob;
|
|
bool is_new;
|
|
};
|
|
|
|
struct vkd3d_cached_pipeline_entry
|
|
{
|
|
struct hash_map_entry entry;
|
|
struct vkd3d_cached_pipeline_key key;
|
|
struct vkd3d_cached_pipeline_data data;
|
|
};
|
|
|
|
static uint32_t vkd3d_cached_pipeline_hash(const void *key)
|
|
{
|
|
const struct vkd3d_cached_pipeline_key *k = key;
|
|
uint32_t hash = 0;
|
|
size_t i;
|
|
|
|
for (i = 0; i < k->name_length; i += 4)
|
|
{
|
|
uint32_t accum = 0;
|
|
memcpy(&accum, (const char*)k->name + i,
|
|
min(k->name_length - i, sizeof(accum)));
|
|
hash = hash_combine(hash, accum);
|
|
}
|
|
|
|
return hash;
|
|
}
|
|
|
|
static bool vkd3d_cached_pipeline_compare(const void *key, const struct hash_map_entry *entry)
|
|
{
|
|
const struct vkd3d_cached_pipeline_entry *e = (const struct vkd3d_cached_pipeline_entry*)entry;
|
|
const struct vkd3d_cached_pipeline_key *k = key;
|
|
|
|
return k->name_length == e->key.name_length &&
|
|
!memcmp(k->name, e->key.name, k->name_length);
|
|
}
|
|
|
|
struct vkd3d_serialized_pipeline
|
|
{
|
|
uint32_t name_length;
|
|
uint32_t blob_length;
|
|
uint8_t data[];
|
|
};
|
|
|
|
#define VKD3D_PIPELINE_LIBRARY_VERSION MAKE_MAGIC('V','K','L',1)
|
|
|
|
struct vkd3d_serialized_pipeline_library
|
|
{
|
|
uint32_t version;
|
|
uint32_t vendor_id;
|
|
uint32_t device_id;
|
|
uint32_t pipeline_count;
|
|
char vkd3d_build[VKD3D_CACHE_BUILD_SIZE];
|
|
uint8_t cache_uuid[VK_UUID_SIZE];
|
|
uint8_t data[];
|
|
};
|
|
|
|
/* ID3D12PipelineLibrary */
|
|
static inline struct d3d12_pipeline_library *impl_from_ID3D12PipelineLibrary(d3d12_pipeline_library_iface *iface)
|
|
{
|
|
return CONTAINING_RECORD(iface, struct d3d12_pipeline_library, ID3D12PipelineLibrary_iface);
|
|
}
|
|
|
|
static bool d3d12_pipeline_library_serialize_entry(struct d3d12_pipeline_library *pipeline_library,
|
|
const struct vkd3d_cached_pipeline_entry *entry, size_t *size, void *data)
|
|
{
|
|
struct vkd3d_serialized_pipeline *header = data;
|
|
size_t total_size;
|
|
|
|
total_size = sizeof(header) + entry->key.name_length + entry->data.blob_length;
|
|
|
|
if (header)
|
|
{
|
|
if (*size < total_size)
|
|
{
|
|
ERR("Not enough memory provided to store pipeline blob.\n");
|
|
return false;
|
|
}
|
|
|
|
header->name_length = entry->key.name_length;
|
|
header->blob_length = entry->data.blob_length;
|
|
memcpy(header->data, entry->key.name, entry->key.name_length);
|
|
memcpy(header->data + entry->key.name_length, entry->data.blob, entry->data.blob_length);
|
|
}
|
|
|
|
*size = total_size;
|
|
return true;
|
|
}
|
|
|
|
static void d3d12_pipeline_library_cleanup(struct d3d12_pipeline_library *pipeline_library, struct d3d12_device *device)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < pipeline_library->map.entry_count; i++)
|
|
{
|
|
struct vkd3d_cached_pipeline_entry *e = (struct vkd3d_cached_pipeline_entry*)hash_map_get_entry(&pipeline_library->map, i);
|
|
|
|
if ((e->entry.flags & HASH_MAP_ENTRY_OCCUPIED) && e->data.is_new)
|
|
{
|
|
vkd3d_free((void*)e->key.name);
|
|
vkd3d_free((void*)e->data.blob);
|
|
}
|
|
}
|
|
|
|
hash_map_clear(&pipeline_library->map);
|
|
|
|
vkd3d_private_store_destroy(&pipeline_library->private_store);
|
|
pthread_mutex_destroy(&pipeline_library->mutex);
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE d3d12_pipeline_library_QueryInterface(d3d12_pipeline_library_iface *iface,
|
|
REFIID riid, void **object)
|
|
{
|
|
TRACE("iface %p, riid %s, object %p.\n", iface, debugstr_guid(riid), object);
|
|
|
|
if (IsEqualGUID(riid, &IID_ID3D12PipelineLibrary)
|
|
|| IsEqualGUID(riid, &IID_ID3D12PipelineLibrary1)
|
|
|| IsEqualGUID(riid, &IID_ID3D12DeviceChild)
|
|
|| IsEqualGUID(riid, &IID_ID3D12Object)
|
|
|| IsEqualGUID(riid, &IID_IUnknown))
|
|
{
|
|
ID3D12PipelineLibrary_AddRef(iface);
|
|
*object = iface;
|
|
return S_OK;
|
|
}
|
|
|
|
WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(riid));
|
|
|
|
*object = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
static ULONG STDMETHODCALLTYPE d3d12_pipeline_library_AddRef(d3d12_pipeline_library_iface *iface)
|
|
{
|
|
struct d3d12_pipeline_library *pipeline_library = impl_from_ID3D12PipelineLibrary(iface);
|
|
ULONG refcount = InterlockedIncrement(&pipeline_library->refcount);
|
|
|
|
TRACE("%p increasing refcount to %u.\n", pipeline_library, refcount);
|
|
|
|
return refcount;
|
|
}
|
|
|
|
static ULONG STDMETHODCALLTYPE d3d12_pipeline_library_Release(d3d12_pipeline_library_iface *iface)
|
|
{
|
|
struct d3d12_pipeline_library *pipeline_library = impl_from_ID3D12PipelineLibrary(iface);
|
|
ULONG refcount = InterlockedDecrement(&pipeline_library->refcount);
|
|
|
|
TRACE("%p decreasing refcount to %u.\n", pipeline_library, refcount);
|
|
|
|
if (!refcount)
|
|
{
|
|
d3d12_pipeline_library_cleanup(pipeline_library, pipeline_library->device);
|
|
vkd3d_free(pipeline_library);
|
|
d3d12_device_release(pipeline_library->device);
|
|
}
|
|
|
|
return refcount;
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE d3d12_pipeline_library_GetPrivateData(d3d12_pipeline_library_iface *iface,
|
|
REFGUID guid, UINT *data_size, void *data)
|
|
{
|
|
struct d3d12_pipeline_library *pipeline_library = impl_from_ID3D12PipelineLibrary(iface);
|
|
|
|
TRACE("iface %p, guid %s, data_size %p, data %p.\n", iface, debugstr_guid(guid), data_size, data);
|
|
|
|
return vkd3d_get_private_data(&pipeline_library->private_store, guid, data_size, data);
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE d3d12_pipeline_library_SetPrivateData(d3d12_pipeline_library_iface *iface,
|
|
REFGUID guid, UINT data_size, const void *data)
|
|
{
|
|
struct d3d12_pipeline_library *pipeline_library = impl_from_ID3D12PipelineLibrary(iface);
|
|
|
|
TRACE("iface %p, guid %s, data_size %u, data %p.\n", iface, debugstr_guid(guid), data_size, data);
|
|
|
|
return vkd3d_set_private_data(&pipeline_library->private_store, guid, data_size, data);
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE d3d12_pipeline_library_SetPrivateDataInterface(d3d12_pipeline_library_iface *iface,
|
|
REFGUID guid, const IUnknown *data)
|
|
{
|
|
struct d3d12_pipeline_library *pipeline_library = impl_from_ID3D12PipelineLibrary(iface);
|
|
|
|
TRACE("iface %p, guid %s, data %p.\n", iface, debugstr_guid(guid), data);
|
|
|
|
return vkd3d_set_private_data_interface(&pipeline_library->private_store, guid, data);
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE d3d12_pipeline_library_SetName(d3d12_pipeline_library_iface *iface, const WCHAR *name)
|
|
{
|
|
struct d3d12_pipeline_library *pipeline_library = impl_from_ID3D12PipelineLibrary(iface);
|
|
|
|
TRACE("iface %p, name %s.\n", iface, debugstr_w(name, pipeline_library->device->wchar_size));
|
|
|
|
return name ? S_OK : E_INVALIDARG;
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE d3d12_pipeline_library_GetDevice(d3d12_pipeline_library_iface *iface,
|
|
REFIID iid, void **device)
|
|
{
|
|
struct d3d12_pipeline_library *pipeline_library = impl_from_ID3D12PipelineLibrary(iface);
|
|
|
|
TRACE("iface %p, iid %s, device %p.\n", iface, debugstr_guid(iid), device);
|
|
|
|
return d3d12_device_query_interface(pipeline_library->device, iid, device);
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE d3d12_pipeline_library_StorePipeline(d3d12_pipeline_library_iface *iface,
|
|
LPCWSTR name, ID3D12PipelineState *pipeline)
|
|
{
|
|
struct d3d12_pipeline_library *pipeline_library = impl_from_ID3D12PipelineLibrary(iface);
|
|
struct d3d12_pipeline_state *pipeline_state = unsafe_impl_from_ID3D12PipelineState(pipeline);
|
|
size_t wchar_size = pipeline_library->device->wchar_size;
|
|
struct vkd3d_cached_pipeline_entry entry;
|
|
void *new_name, *new_blob;
|
|
VkResult vr;
|
|
int rc;
|
|
|
|
TRACE("iface %p, name %s, pipeline %p.\n", iface, debugstr_w(name, pipeline_library->device->wchar_size), pipeline);
|
|
|
|
if ((rc = pthread_mutex_lock(&pipeline_library->mutex)))
|
|
{
|
|
ERR("Failed to lock mutex, rc %d.\n", rc);
|
|
return hresult_from_errno(rc);
|
|
}
|
|
|
|
entry.key.name_length = vkd3d_wcslen(name, wchar_size) * wchar_size;
|
|
entry.key.name = name;
|
|
|
|
if (hash_map_find(&pipeline_library->map, &entry.key))
|
|
{
|
|
WARN("Pipeline %s already exists.\n", debugstr_w(name, pipeline_library->device->wchar_size));
|
|
pthread_mutex_unlock(&pipeline_library->mutex);
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
/* We need to allocate persistent storage for the name */
|
|
if (!(new_name = malloc(entry.key.name_length)))
|
|
{
|
|
pthread_mutex_unlock(&pipeline_library->mutex);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
memcpy(new_name, name, entry.key.name_length);
|
|
entry.key.name = new_name;
|
|
|
|
if (FAILED(vr = vkd3d_serialize_pipeline_state(pipeline_state, &entry.data.blob_length, NULL)))
|
|
{
|
|
vkd3d_free(new_name);
|
|
pthread_mutex_unlock(&pipeline_library->mutex);
|
|
return hresult_from_vk_result(vr);
|
|
}
|
|
|
|
if (!(new_blob = malloc(entry.data.blob_length)))
|
|
{
|
|
vkd3d_free(new_name);
|
|
pthread_mutex_unlock(&pipeline_library->mutex);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
if (FAILED(vr = vkd3d_serialize_pipeline_state(pipeline_state, &entry.data.blob_length, new_blob)))
|
|
{
|
|
vkd3d_free(new_name);
|
|
vkd3d_free(new_blob);
|
|
pthread_mutex_unlock(&pipeline_library->mutex);
|
|
return hresult_from_vk_result(vr);
|
|
}
|
|
|
|
entry.data.blob = new_blob;
|
|
entry.data.is_new = true;
|
|
|
|
if (!hash_map_insert(&pipeline_library->map, &entry.key, &entry.entry))
|
|
{
|
|
vkd3d_free(new_name);
|
|
vkd3d_free(new_blob);
|
|
pthread_mutex_unlock(&pipeline_library->mutex);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
pthread_mutex_unlock(&pipeline_library->mutex);
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT d3d12_pipeline_library_load_pipeline(struct d3d12_pipeline_library *pipeline_library, LPCWSTR name,
|
|
VkPipelineBindPoint bind_point, struct d3d12_pipeline_state_desc *desc, struct d3d12_pipeline_state **state)
|
|
{
|
|
size_t wchar_size = pipeline_library->device->wchar_size;
|
|
const struct vkd3d_cached_pipeline_entry *e;
|
|
struct vkd3d_cached_pipeline_key key;
|
|
int rc;
|
|
|
|
if ((rc = pthread_mutex_lock(&pipeline_library->mutex)))
|
|
{
|
|
ERR("Failed to lock mutex, rc %d.\n", rc);
|
|
return hresult_from_errno(rc);
|
|
}
|
|
|
|
key.name_length = vkd3d_wcslen(name, wchar_size) * wchar_size;
|
|
key.name = name;
|
|
|
|
if (!(e = (const struct vkd3d_cached_pipeline_entry*)hash_map_find(&pipeline_library->map, &key)))
|
|
{
|
|
WARN("Pipeline %s does not exist.\n", debugstr_w(name, pipeline_library->device->wchar_size));
|
|
pthread_mutex_unlock(&pipeline_library->mutex);
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
desc->cached_pso.CachedBlobSizeInBytes = e->data.blob_length;
|
|
desc->cached_pso.pCachedBlob = e->data.blob;
|
|
pthread_mutex_unlock(&pipeline_library->mutex);
|
|
|
|
return d3d12_pipeline_state_create(pipeline_library->device, bind_point, desc, state);
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE d3d12_pipeline_library_LoadGraphicsPipeline(d3d12_pipeline_library_iface *iface,
|
|
LPCWSTR name, const D3D12_GRAPHICS_PIPELINE_STATE_DESC *desc, REFIID iid, void **pipeline_state)
|
|
{
|
|
struct d3d12_pipeline_library *pipeline_library = impl_from_ID3D12PipelineLibrary(iface);
|
|
struct d3d12_pipeline_state_desc pipeline_desc;
|
|
struct d3d12_pipeline_state *object;
|
|
HRESULT hr;
|
|
|
|
TRACE("iface %p, name %s, desc %p, iid %s, pipeline_state %p.\n", iface,
|
|
debugstr_w(name, pipeline_library->device->wchar_size),
|
|
desc, debugstr_guid(iid), pipeline_state);
|
|
|
|
if (FAILED(hr = vkd3d_pipeline_state_desc_from_d3d12_graphics_desc(&pipeline_desc, desc)))
|
|
return hr;
|
|
|
|
if (FAILED(hr = d3d12_pipeline_library_load_pipeline(pipeline_library,
|
|
name, VK_PIPELINE_BIND_POINT_GRAPHICS, &pipeline_desc, &object)))
|
|
return hr;
|
|
|
|
return return_interface(&object->ID3D12PipelineState_iface,
|
|
&IID_ID3D12PipelineState, iid, pipeline_state);
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE d3d12_pipeline_library_LoadComputePipeline(d3d12_pipeline_library_iface *iface,
|
|
LPCWSTR name, const D3D12_COMPUTE_PIPELINE_STATE_DESC *desc, REFIID iid, void **pipeline_state)
|
|
{
|
|
struct d3d12_pipeline_library *pipeline_library = impl_from_ID3D12PipelineLibrary(iface);
|
|
struct d3d12_pipeline_state_desc pipeline_desc;
|
|
struct d3d12_pipeline_state *object;
|
|
HRESULT hr;
|
|
|
|
TRACE("iface %p, name %s, desc %p, iid %s, pipeline_state %p.\n", iface,
|
|
debugstr_w(name, pipeline_library->device->wchar_size),
|
|
desc, debugstr_guid(iid), pipeline_state);
|
|
|
|
if (FAILED(hr = vkd3d_pipeline_state_desc_from_d3d12_compute_desc(&pipeline_desc, desc)))
|
|
return hr;
|
|
|
|
if (FAILED(hr = d3d12_pipeline_library_load_pipeline(pipeline_library,
|
|
name, VK_PIPELINE_BIND_POINT_COMPUTE, &pipeline_desc, &object)))
|
|
return hr;
|
|
|
|
return return_interface(&object->ID3D12PipelineState_iface,
|
|
&IID_ID3D12PipelineState, iid, pipeline_state);
|
|
}
|
|
|
|
static SIZE_T STDMETHODCALLTYPE d3d12_pipeline_library_GetSerializedSize(d3d12_pipeline_library_iface *iface)
|
|
{
|
|
struct d3d12_pipeline_library *pipeline_library = impl_from_ID3D12PipelineLibrary(iface);
|
|
size_t total_size = sizeof(struct vkd3d_serialized_pipeline_library);
|
|
uint32_t i;
|
|
int rc;
|
|
|
|
TRACE("iface %p.\n", iface);
|
|
|
|
if ((rc = pthread_mutex_lock(&pipeline_library->mutex)))
|
|
{
|
|
ERR("Failed to lock mutex, rc %d.\n", rc);
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0; i < pipeline_library->map.entry_count; i++)
|
|
{
|
|
struct vkd3d_cached_pipeline_entry *e = (struct vkd3d_cached_pipeline_entry*)hash_map_get_entry(&pipeline_library->map, i);
|
|
|
|
if (e->entry.flags & HASH_MAP_ENTRY_OCCUPIED)
|
|
{
|
|
size_t pipeline_size = 0;
|
|
|
|
if (!d3d12_pipeline_library_serialize_entry(pipeline_library, e, &pipeline_size, NULL))
|
|
return 0;
|
|
|
|
total_size += pipeline_size;
|
|
}
|
|
}
|
|
|
|
pthread_mutex_unlock(&pipeline_library->mutex);
|
|
return total_size;
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE d3d12_pipeline_library_Serialize(d3d12_pipeline_library_iface *iface,
|
|
void *data, SIZE_T data_size)
|
|
{
|
|
struct d3d12_pipeline_library *pipeline_library = impl_from_ID3D12PipelineLibrary(iface);
|
|
const VkPhysicalDeviceProperties *device_properties = &pipeline_library->device->device_info.properties2.properties;
|
|
struct vkd3d_serialized_pipeline_library *header = data;
|
|
size_t serialized_size = data_size - sizeof(*header);
|
|
uint8_t *serialized_data = header->data;
|
|
uint32_t i;
|
|
int rc;
|
|
|
|
TRACE("iface %p.\n", iface);
|
|
|
|
if (data_size < sizeof(*header))
|
|
return E_INVALIDARG;
|
|
|
|
if ((rc = pthread_mutex_lock(&pipeline_library->mutex)))
|
|
{
|
|
ERR("Failed to lock mutex, rc %d.\n", rc);
|
|
return 0;
|
|
}
|
|
|
|
header->version = VKD3D_PIPELINE_LIBRARY_VERSION;
|
|
header->vendor_id = device_properties->vendorID;
|
|
header->device_id = device_properties->deviceID;
|
|
header->pipeline_count = pipeline_library->map.used_count;
|
|
strncpy(header->vkd3d_build, vkd3d_build, VKD3D_CACHE_BUILD_SIZE);
|
|
memcpy(header->cache_uuid, device_properties->pipelineCacheUUID, VK_UUID_SIZE);
|
|
|
|
for (i = 0; i < pipeline_library->map.entry_count; i++)
|
|
{
|
|
struct vkd3d_cached_pipeline_entry *e = (struct vkd3d_cached_pipeline_entry*)hash_map_get_entry(&pipeline_library->map, i);
|
|
|
|
if (e->entry.flags & HASH_MAP_ENTRY_OCCUPIED)
|
|
{
|
|
size_t pipeline_size = serialized_size;
|
|
|
|
/* Fails if the provided buffer is too small to fit the pipeline */
|
|
if (!d3d12_pipeline_library_serialize_entry(pipeline_library, e, &pipeline_size, serialized_data))
|
|
{
|
|
pthread_mutex_unlock(&pipeline_library->mutex);
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
serialized_data += pipeline_size;
|
|
serialized_size -= pipeline_size;
|
|
}
|
|
}
|
|
|
|
pthread_mutex_unlock(&pipeline_library->mutex);
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE d3d12_pipeline_library_LoadPipeline(d3d12_pipeline_library_iface *iface,
|
|
LPCWSTR name, const D3D12_PIPELINE_STATE_STREAM_DESC *desc, REFIID iid, void **pipeline_state)
|
|
{
|
|
struct d3d12_pipeline_library *pipeline_library = impl_from_ID3D12PipelineLibrary(iface);
|
|
struct d3d12_pipeline_state_desc pipeline_desc;
|
|
struct d3d12_pipeline_state *object;
|
|
VkPipelineBindPoint pipeline_type;
|
|
HRESULT hr;
|
|
|
|
TRACE("iface %p, name %s, desc %p, iid %s, pipeline_state %p.\n", iface,
|
|
debugstr_w(name, pipeline_library->device->wchar_size),
|
|
desc, debugstr_guid(iid), pipeline_state);
|
|
|
|
if (FAILED(hr = vkd3d_pipeline_state_desc_from_d3d12_stream_desc(&pipeline_desc, desc, &pipeline_type)))
|
|
return hr;
|
|
|
|
if (FAILED(hr = d3d12_pipeline_library_load_pipeline(pipeline_library,
|
|
name, pipeline_type, &pipeline_desc, &object)))
|
|
return hr;
|
|
|
|
return return_interface(&object->ID3D12PipelineState_iface,
|
|
&IID_ID3D12PipelineState, iid, pipeline_state);
|
|
}
|
|
|
|
static CONST_VTBL struct ID3D12PipelineLibrary1Vtbl d3d12_pipeline_library_vtbl =
|
|
{
|
|
/* IUnknown methods */
|
|
d3d12_pipeline_library_QueryInterface,
|
|
d3d12_pipeline_library_AddRef,
|
|
d3d12_pipeline_library_Release,
|
|
/* ID3D12Object methods */
|
|
d3d12_pipeline_library_GetPrivateData,
|
|
d3d12_pipeline_library_SetPrivateData,
|
|
d3d12_pipeline_library_SetPrivateDataInterface,
|
|
d3d12_pipeline_library_SetName,
|
|
/* ID3D12DeviceChild methods */
|
|
d3d12_pipeline_library_GetDevice,
|
|
/* ID3D12PipelineLibrary methods */
|
|
d3d12_pipeline_library_StorePipeline,
|
|
d3d12_pipeline_library_LoadGraphicsPipeline,
|
|
d3d12_pipeline_library_LoadComputePipeline,
|
|
d3d12_pipeline_library_GetSerializedSize,
|
|
d3d12_pipeline_library_Serialize,
|
|
/* ID3D12PipelineLibrary1 methods */
|
|
d3d12_pipeline_library_LoadPipeline,
|
|
};
|
|
|
|
static HRESULT d3d12_pipeline_library_read_blob(struct d3d12_pipeline_library *pipeline_library,
|
|
struct d3d12_device *device, const void *blob, size_t blob_length)
|
|
{
|
|
const VkPhysicalDeviceProperties *device_properties = &device->device_info.properties2.properties;
|
|
const struct vkd3d_serialized_pipeline_library *header = blob;
|
|
const uint8_t *end = header->data + blob_length - sizeof(*header);
|
|
const uint8_t *cur = header->data;
|
|
uint32_t i;
|
|
|
|
/* Same logic as for pipeline blobs, indicate that the app needs
|
|
* to rebuild the pipeline library in case vkd3d itself or the
|
|
* underlying device/driver changed */
|
|
if (blob_length < sizeof(*header) || header->version != VKD3D_PIPELINE_LIBRARY_VERSION)
|
|
return D3D12_ERROR_DRIVER_VERSION_MISMATCH;
|
|
|
|
if (header->device_id != device_properties->deviceID || header->vendor_id != device_properties->vendorID)
|
|
return D3D12_ERROR_ADAPTER_NOT_FOUND;
|
|
|
|
if (strncmp(header->vkd3d_build, vkd3d_build, VKD3D_CACHE_BUILD_SIZE) ||
|
|
memcmp(header->cache_uuid, device_properties->pipelineCacheUUID, VK_UUID_SIZE))
|
|
return D3D12_ERROR_DRIVER_VERSION_MISMATCH;
|
|
|
|
/* The application is not allowed to free the blob, so we
|
|
* can safely use pointers without copying the data first. */
|
|
for (i = 0; i < header->pipeline_count; i++)
|
|
{
|
|
const struct vkd3d_serialized_pipeline *pipeline = (const struct vkd3d_serialized_pipeline*)cur;
|
|
struct vkd3d_cached_pipeline_entry entry;
|
|
|
|
if (cur + sizeof(*pipeline) > end)
|
|
return E_INVALIDARG;
|
|
|
|
cur += sizeof(*pipeline) + pipeline->name_length + pipeline->blob_length;
|
|
|
|
if (cur > end)
|
|
return E_INVALIDARG;
|
|
|
|
entry.key.name_length = pipeline->name_length;
|
|
entry.key.name = pipeline->data;
|
|
|
|
entry.data.blob_length = pipeline->blob_length;
|
|
entry.data.blob = pipeline->data + pipeline->name_length;
|
|
entry.data.is_new = false;
|
|
|
|
if (!hash_map_insert(&pipeline_library->map, &entry.key, &entry.entry))
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT d3d12_pipeline_library_init(struct d3d12_pipeline_library *pipeline_library,
|
|
struct d3d12_device *device, const void *blob, size_t blob_length)
|
|
{
|
|
HRESULT hr;
|
|
int rc;
|
|
|
|
memset(pipeline_library, 0, sizeof(*pipeline_library));
|
|
pipeline_library->ID3D12PipelineLibrary_iface.lpVtbl = &d3d12_pipeline_library_vtbl;
|
|
pipeline_library->refcount = 1;
|
|
|
|
if (!blob_length && blob)
|
|
return E_INVALIDARG;
|
|
|
|
if ((rc = pthread_mutex_init(&pipeline_library->mutex, NULL)))
|
|
return hresult_from_errno(rc);
|
|
|
|
hash_map_init(&pipeline_library->map, &vkd3d_cached_pipeline_hash,
|
|
&vkd3d_cached_pipeline_compare, sizeof(struct vkd3d_cached_pipeline_entry));
|
|
|
|
if (blob_length)
|
|
{
|
|
if (FAILED(hr = d3d12_pipeline_library_read_blob(pipeline_library, device, blob, blob_length)))
|
|
goto cleanup_hash_map;
|
|
}
|
|
|
|
if (FAILED(hr = vkd3d_private_store_init(&pipeline_library->private_store)))
|
|
goto cleanup_mutex;
|
|
|
|
d3d12_device_add_ref(pipeline_library->device = device);
|
|
return hr;
|
|
|
|
cleanup_hash_map:
|
|
hash_map_clear(&pipeline_library->map);
|
|
cleanup_mutex:
|
|
pthread_mutex_destroy(&pipeline_library->mutex);
|
|
return hr;
|
|
}
|
|
|
|
HRESULT d3d12_pipeline_library_create(struct d3d12_device *device, const void *blob,
|
|
size_t blob_length, struct d3d12_pipeline_library **pipeline_library)
|
|
{
|
|
struct d3d12_pipeline_library *object;
|
|
HRESULT hr;
|
|
|
|
if (!(object = vkd3d_malloc(sizeof(*object))))
|
|
return E_OUTOFMEMORY;
|
|
|
|
if (FAILED(hr = d3d12_pipeline_library_init(object, device, blob, blob_length)))
|
|
{
|
|
vkd3d_free(object);
|
|
return hr;
|
|
}
|
|
|
|
TRACE("Created pipeline library %p.\n", object);
|
|
|
|
*pipeline_library = object;
|
|
return S_OK;
|
|
}
|