/* * Copyright 2016-2017 Józef Kucia for CodeWeavers * Copyright 2020-2021 Philip Rebohle for Valve Corporation * Copyright 2020-2021 Joshua Ashton for Valve Corporation * Copyright 2020-2021 Hans-Kristian Arntzen 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 "d3d12_crosstest.h" void test_get_cached_blob(void) { D3D12_COMPUTE_PIPELINE_STATE_DESC compute_desc; D3D12_ROOT_SIGNATURE_DESC root_signature_desc; ID3D12RootSignature *root_signature_alt; ID3D12RootSignature *root_signature; struct test_context context; ID3D12PipelineState *state; ID3D12Device *device; ID3DBlob *blob; HRESULT hr; #if 0 [numthreads(1,1,1)] void main() { } #endif static const DWORD cs_dxbc[] = { 0x43425844, 0x1acc3ad0, 0x71c7b057, 0xc72c4306, 0xf432cb57, 0x00000001, 0x00000074, 0x00000003, 0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f, 0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x00000020, 0x00050050, 0x00000008, 0x0100086a, 0x0400009b, 0x00000001, 0x00000001, 0x00000001, 0x0100003e, }; #if 0 [numthreads(2,1,1)] void main() { } #endif static const DWORD cs_dxbc_2[] = { 0x43425844, 0xcdd3f1fb, 0x7e892d91, 0xe5a2ea15, 0xab4fc56d, 0x00000001, 0x00000074, 0x00000003, 0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f, 0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x00000020, 0x00050050, 0x00000008, 0x0100086a, 0x0400009b, 0x00000002, 0x00000001, 0x00000001, 0x0100003e, }; if (!init_test_context(&context, NULL)) return; device = context.device; memset(&root_signature_desc, 0, sizeof(root_signature_desc)); hr = create_root_signature(device, &root_signature_desc, &root_signature); ok(hr == S_OK, "Failed to create root signature, hr %#x.\n", hr); root_signature_desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT; hr = create_root_signature(device, &root_signature_desc, &root_signature_alt); ok(hr == S_OK, "Failed to create root signature, hr %#x.\n", hr); memset(&compute_desc, 0, sizeof(compute_desc)); compute_desc.pRootSignature = root_signature; compute_desc.CS.pShaderBytecode = cs_dxbc; compute_desc.CS.BytecodeLength = sizeof(cs_dxbc); hr = ID3D12Device_CreateComputePipelineState(device, &compute_desc, &IID_ID3D12PipelineState, (void**)&state); ok(hr == S_OK, "Failed to create compute pipeline, hr %#x.\n", hr); hr = ID3D12PipelineState_GetCachedBlob(state, &blob); ok(hr == S_OK, "Failed to get cached blob, hr %#x.\n", hr); ok(ID3D10Blob_GetBufferSize(blob) > 0, "Cached blob is empty.\n"); ID3D12PipelineState_Release(state); compute_desc.CachedPSO.pCachedBlob = ID3D10Blob_GetBufferPointer(blob); compute_desc.CachedPSO.CachedBlobSizeInBytes = ID3D10Blob_GetBufferSize(blob); hr = ID3D12Device_CreateComputePipelineState(device, &compute_desc, &IID_ID3D12PipelineState, (void**)&state); ok(hr == S_OK, "Failed to create compute pipeline, hr %#x.\n", hr); ID3D12PipelineState_Release(state); /* Using mismatched shader code must fail. */ compute_desc.CS.pShaderBytecode = cs_dxbc_2; compute_desc.CS.BytecodeLength = sizeof(cs_dxbc_2); hr = ID3D12Device_CreateComputePipelineState(device, &compute_desc, &IID_ID3D12PipelineState, (void**)&state); ok(hr == E_INVALIDARG, "Unexpected hr %#x.\n", hr); /* Using mismatched root signature must fail. */ compute_desc.CS.pShaderBytecode = cs_dxbc; compute_desc.CS.BytecodeLength = sizeof(cs_dxbc); compute_desc.pRootSignature = root_signature_alt; hr = ID3D12Device_CreateComputePipelineState(device, &compute_desc, &IID_ID3D12PipelineState, (void**)&state); ok(hr == E_INVALIDARG, "Unexpected hr %#x.\n", hr); ID3D12RootSignature_Release(root_signature); ID3D12RootSignature_Release(root_signature_alt); ID3D10Blob_Release(blob); destroy_test_context(&context); } void test_pipeline_library(void) { D3D12_GRAPHICS_PIPELINE_STATE_DESC graphics_desc; D3D12_COMPUTE_PIPELINE_STATE_DESC compute_desc; D3D12_ROOT_SIGNATURE_DESC root_signature_desc; ID3D12PipelineLibrary *pipeline_library; ID3D12RootSignature *root_signature; struct test_context context; ID3D12PipelineState *state3; ID3D12PipelineState *state2; ID3D12PipelineState *state; ULONG reference_refcount; size_t serialized_size; ID3D12Device1 *device1; void *serialized_data; ID3D12Device *device; ID3D12Fence *fence; HRESULT hr; #if 0 [numthreads(1,1,1)] void main() { } #endif static const DWORD cs_dxbc[] = { 0x43425844, 0x1acc3ad0, 0x71c7b057, 0xc72c4306, 0xf432cb57, 0x00000001, 0x00000074, 0x00000003, 0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f, 0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x00000020, 0x00050050, 0x00000008, 0x0100086a, 0x0400009b, 0x00000001, 0x00000001, 0x00000001, 0x0100003e, }; #if 0 float4 main() : SV_POSITION { return float4(0.0f, 0.0f, 0.0f, 0.0f); } #endif static const DWORD vs_dxbc[] = { 0x43425844, 0xae39b246, 0xddd05b5a, 0x5057a6a2, 0x034461ee, 0x00000001, 0x000000b8, 0x00000003, 0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x505f5653, 0x5449534f, 0x004e4f49, 0x58454853, 0x00000040, 0x00010050, 0x00000010, 0x0100086a, 0x04000067, 0x001020f2, 0x00000000, 0x00000001, 0x08000036, 0x001020f2, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0100003e, }; #if 0 float4 main() : SV_TARGET { return float4(1.0f, 1.0f, 1.0f, 1.0f); } #endif static const DWORD ps_dxbc[] = { 0x43425844, 0x29b14cf3, 0xb991cf90, 0x9e455ffc, 0x4675b046, 0x00000001, 0x000000b4, 0x00000003, 0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x0000000f, 0x545f5653, 0x45475241, 0xabab0054, 0x58454853, 0x0000003c, 0x00000050, 0x0000000f, 0x0100086a, 0x03000065, 0x001020f2, 0x00000000, 0x08000036, 0x001020f2, 0x00000000, 0x00004002, 0x3f800000, 0x3f800000, 0x3f800000, 0x3f800000, 0x0100003e, }; const WCHAR *graphics_name = u"GRAPHICS"; const WCHAR *compute_name = u"COMPUTE"; if (!init_test_context(&context, NULL)) return; device = context.device; if (FAILED(hr = ID3D12Device_QueryInterface(device, &IID_ID3D12Device1, (void**)&device1))) { skip("ID3D12Device1 not available.\n"); return; } /* Test adding pipelines to an empty pipeline library */ hr = ID3D12Device1_CreatePipelineLibrary(device1, NULL, 0, &IID_ID3D12PipelineLibrary, (void**)&pipeline_library); ok(hr == S_OK, "Failed to create pipeline library, hr %#x.\n", hr); /* ppData == NULL means a query */ hr = ID3D12Device1_CreatePipelineLibrary(device1, NULL, 0, NULL, NULL); ok(hr == S_FALSE, "Failed to query pipeline library, hr %#x.\n", hr); memset(&root_signature_desc, 0, sizeof(root_signature_desc)); hr = create_root_signature(device, &root_signature_desc, &root_signature); ok(hr == S_OK, "Failed to create root signature, hr %#x.\n", hr); memset(&compute_desc, 0, sizeof(compute_desc)); compute_desc.pRootSignature = root_signature; compute_desc.CS.pShaderBytecode = cs_dxbc; compute_desc.CS.BytecodeLength = sizeof(cs_dxbc); hr = ID3D12PipelineLibrary_LoadComputePipeline(pipeline_library, compute_name, &compute_desc, &IID_ID3D12PipelineState, (void**)&state); ok(hr == E_INVALIDARG, "Unexpected hr %#x.\n", hr); hr = ID3D12Device_CreateComputePipelineState(device, &compute_desc, &IID_ID3D12PipelineState, (void**)&state); ok(hr == S_OK, "Failed to create compute pipeline, hr %#x.\n", hr); hr = ID3D12PipelineLibrary_StorePipeline(pipeline_library, compute_name, state); ok(hr == S_OK, "Failed to store compute pipeline, hr %x.\n", hr); ID3D12PipelineState_Release(state); memset(&graphics_desc, 0, sizeof(graphics_desc)); graphics_desc.pRootSignature = root_signature; graphics_desc.VS.pShaderBytecode = vs_dxbc; graphics_desc.VS.BytecodeLength = sizeof(vs_dxbc); graphics_desc.PS.pShaderBytecode = ps_dxbc; graphics_desc.PS.BytecodeLength = sizeof(ps_dxbc); graphics_desc.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID; graphics_desc.RasterizerState.CullMode = D3D12_CULL_MODE_BACK; graphics_desc.RasterizerState.FrontCounterClockwise = true; graphics_desc.SampleMask = 0x1; graphics_desc.SampleDesc.Count = 1; graphics_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; graphics_desc.NumRenderTargets = 1; graphics_desc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM; hr = ID3D12PipelineLibrary_LoadGraphicsPipeline(pipeline_library, graphics_name, &graphics_desc, &IID_ID3D12PipelineState, (void**)&state); ok(hr == E_INVALIDARG, "Unexpected hr %#x.\n", hr); hr = ID3D12Device_CreateGraphicsPipelineState(device, &graphics_desc, &IID_ID3D12PipelineState, (void**)&state); ok(hr == S_OK, "Failed to create graphics pipeline, hr %#x.\n", hr); hr = ID3D12PipelineLibrary_StorePipeline(pipeline_library, graphics_name, state); ok(hr == S_OK, "Failed to store graphics pipeline, hr %x.\n", hr); /* Try to load PSO after a Store. Verify that we have a ref-count. */ hr = ID3D12PipelineLibrary_LoadGraphicsPipeline(pipeline_library, graphics_name, &graphics_desc, &IID_ID3D12PipelineState, (void**)&state2); ok(hr == S_OK, "Failed to load graphics pipeline, hr %x.\n", hr); ok(state == state2, "Resulting PSOs must point to same object.\n"); ok(get_refcount(state2) == 2, "Refcount %u != 2.\n", get_refcount(state2)); hr = ID3D12PipelineLibrary_StorePipeline(pipeline_library, compute_name, state); ok(hr == E_INVALIDARG, "Storing pipeline with already existing name succeeded, hr %x.\n", hr); ID3D12PipelineState_Release(state); ID3D12PipelineState_Release(state2); /* Test looking up pipelines in a new pipeline library */ hr = ID3D12PipelineLibrary_LoadComputePipeline(pipeline_library, compute_name, &compute_desc, &IID_ID3D12PipelineState, (void**)&state); ok(hr == S_OK, "Failed to load compute pipeline from pipeline library, hr %#x.\n", hr); ID3D12PipelineState_Release(state); hr = ID3D12PipelineLibrary_LoadGraphicsPipeline(pipeline_library, graphics_name, &graphics_desc, &IID_ID3D12PipelineState, (void**)&state); ok(hr == S_OK, "Failed to load graphics pipeline from pipeline library, hr %#x.\n", hr); ID3D12PipelineState_Release(state); /* Verify that modifying a PSO description must be invalidated by runtime. */ graphics_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE; hr = ID3D12PipelineLibrary_LoadGraphicsPipeline(pipeline_library, graphics_name, &graphics_desc, &IID_ID3D12PipelineState, (void**)&state); ok(hr == E_INVALIDARG, "Unexpected result, hr %#x.\n", hr); graphics_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; serialized_size = ID3D12PipelineLibrary_GetSerializedSize(pipeline_library); ok(serialized_size > 0, "Serialized size for pipeline library is 0.\n"); serialized_data = malloc(serialized_size); hr = ID3D12PipelineLibrary_Serialize(pipeline_library, serialized_data, serialized_size - 1); ok(hr == E_INVALIDARG, "Unexpected hr %#x.\n", hr); hr = ID3D12PipelineLibrary_Serialize(pipeline_library, serialized_data, serialized_size); ok(hr == S_OK, "Failed to serialize pipeline library, hr %#x.\n", hr); ID3D12PipelineLibrary_Release(pipeline_library); /* Test deserializing a pipeline library */ hr = ID3D12Device1_CreatePipelineLibrary(device1, serialized_data, serialized_size, &IID_ID3D12PipelineLibrary, (void**)&pipeline_library); ok(hr == S_OK, "Failed to create pipeline library, hr %#x.\n"); /* Verify that PSO library must internally ref-count a unique PSO. */ hr = ID3D12PipelineLibrary_LoadGraphicsPipeline(pipeline_library, graphics_name, &graphics_desc, &IID_ID3D12PipelineState, (void**)&state); ok(hr == S_OK, "Failed to load graphics pipeline from pipeline library, hr %#x.\n", hr); hr = ID3D12PipelineLibrary_LoadGraphicsPipeline(pipeline_library, graphics_name, &graphics_desc, &IID_ID3D12PipelineState, (void**)&state2); ok(hr == S_OK, "Failed to load graphics pipeline from pipeline library, hr %#x.\n", hr); hr = ID3D12PipelineLibrary_LoadGraphicsPipeline(pipeline_library, graphics_name, &graphics_desc, &IID_ID3D12PipelineState, (void**)&state3); ok(hr == S_OK, "Failed to load graphics pipeline from pipeline library, hr %#x.\n", hr); ok(state == state2 && state == state3, "Resulting PSOs must point to same object.\n"); ok(get_refcount(state) == 3, "Refcount %u != 3.\n", get_refcount(state)); ok(get_refcount(state2) == 3, "Refcount %u != 3.\n", get_refcount(state2)); ok(get_refcount(state3) == 3, "Refcount %u != 3.\n", get_refcount(state3)); ID3D12PipelineState_Release(state); ID3D12PipelineState_Release(state2); ID3D12PipelineState_Release(state3); reference_refcount = get_refcount(context.device); /* Verify that PSO library must internally ref-count a unique PSO. */ hr = ID3D12PipelineLibrary_LoadComputePipeline(pipeline_library, compute_name, &compute_desc, &IID_ID3D12PipelineState, (void**)&state); ok(hr == S_OK, "Failed to load compute pipeline from pipeline library, hr %#x.\n", hr); hr = ID3D12PipelineLibrary_LoadComputePipeline(pipeline_library, compute_name, &compute_desc, &IID_ID3D12PipelineState, (void**)&state2); ok(hr == S_OK, "Failed to load compute pipeline from pipeline library, hr %#x.\n", hr); hr = ID3D12PipelineLibrary_LoadComputePipeline(pipeline_library, compute_name, &compute_desc, &IID_ID3D12PipelineState, (void**)&state3); ok(hr == S_OK, "Failed to load compute pipeline from pipeline library, hr %#x.\n", hr); ok(get_refcount(context.device) == reference_refcount + 1, "Refcount %u != %u\n", get_refcount(context.device), reference_refcount + 1); ID3D12Device_CreateFence(context.device, 0, D3D12_FENCE_FLAG_NONE, &IID_ID3D12Fence, (void**)&fence); ok(get_refcount(context.device) == reference_refcount + 2, "Refcount %u != %u\n", get_refcount(context.device), reference_refcount + 2); ID3D12PipelineState_SetPrivateDataInterface(state, &IID_ID3D12Fence, (const IUnknown *)fence); ok(get_refcount(fence) == 2, "Refcount %u != 2.\n", get_refcount(fence)); ok(state == state2 && state == state3, "Resulting PSOs must point to same object.\n"); ok(state && get_refcount(state) == 3, "Refcount %u != 3.\n", get_refcount(state)); ok(state2 && get_refcount(state2) == 3, "Refcount %u != 3.\n", get_refcount(state2)); ok(state3 && get_refcount(state3) == 3, "Refcount %u != 3.\n", get_refcount(state3)); ID3D12PipelineState_Release(state); ID3D12PipelineState_Release(state2); ok(get_refcount(fence) == 2, "Refcount %u != 2.\n", get_refcount(fence)); ok(get_refcount(context.device) == reference_refcount + 2, "Refcount %u != %u\n", get_refcount(context.device), reference_refcount + 2); ok(ID3D12PipelineState_Release(state3) == 0, "Refcount did not hit 0.\n"); /* Releasing the last public reference does not release private data. */ ok(get_refcount(fence) == 2, "Refcount %u != 2.\n", get_refcount(fence)); /* Device ref count does release however ... */ ok(get_refcount(context.device) == reference_refcount + 1, "Refcount %u != %u\n", get_refcount(context.device), reference_refcount + 1); hr = ID3D12PipelineLibrary_LoadComputePipeline(pipeline_library, compute_name, &compute_desc, &IID_ID3D12PipelineState, (void**)&state2); /* Device ref count increases here again. */ ok(get_refcount(context.device) == reference_refcount + 2, "Refcount %u != %u\n", get_refcount(context.device), reference_refcount + 2); ok(hr == S_OK, "Failed to load compute pipeline from pipeline library, hr %#x.\n", hr); ok(state == state2, "Reloading dead PSO must point to same object.\n"); ID3D12PipelineState_Release(state2); hr = ID3D12PipelineLibrary_LoadComputePipeline(pipeline_library, graphics_name, &compute_desc, &IID_ID3D12PipelineState, (void**)&state); ok(hr == E_INVALIDARG, "Unexpected hr %#x.\n", hr); if (SUCCEEDED(hr)) ID3D12PipelineState_Release(state); ID3D12PipelineLibrary_Release(pipeline_library); /* This should release the fence reference. */ ok(get_refcount(fence) == 1, "Refcount %u != 1.\n", get_refcount(fence)); ID3D12Fence_Release(fence); free(serialized_data); ID3D12RootSignature_Release(root_signature); ID3D12Device1_Release(device1); destroy_test_context(&context); }