/* * 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) struct vkd3d_pipeline_blob { uint32_t version; uint32_t vendor_id; uint32_t device_id; uint64_t vkd3d_build; 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 (blob->vkd3d_build != vkd3d_build || 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; blob->vkd3d_build = vkd3d_build; 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; uint64_t vkd3d_build; 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; header->vkd3d_build = vkd3d_build; 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 (header->vkd3d_build != vkd3d_build || 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; }