// d3d12-device.cpp #include "d3d12-device.h" #include "../nvapi/nvapi-util.h" #include "d3d12-buffer.h" #include "d3d12-fence.h" #include "d3d12-framebuffer.h" #include "d3d12-helper-functions.h" #include "d3d12-pipeline-state.h" #include "d3d12-query.h" #include "d3d12-render-pass.h" #include "d3d12-resource-views.h" #include "d3d12-sampler.h" #include "d3d12-shader-object.h" #include "d3d12-shader-program.h" #include "d3d12-shader-table.h" #include "d3d12-swap-chain.h" #include "d3d12-vertex-layout.h" #ifdef GFX_NVAPI #include "../nvapi/nvapi-include.h" #endif #ifdef GFX_NV_AFTERMATH #include "GFSDK_Aftermath.h" #include "GFSDK_Aftermath_Defines.h" #include "GFSDK_Aftermath_GpuCrashDump.h" #endif namespace gfx { namespace d3d12 { using namespace Slang; static const uint32_t D3D_FEATURE_LEVEL_12_2 = 0xc200; #if GFX_NV_AFTERMATH /* static */ const bool DeviceImpl::g_isAftermathEnabled = true; #else /* static */ const bool DeviceImpl::g_isAftermathEnabled = false; #endif struct ShaderModelInfo { D3D_SHADER_MODEL shaderModel; SlangCompileTarget compileTarget; const char* profileName; }; // List of shader models. Do not change oldest to newest order. static ShaderModelInfo kKnownShaderModels[] = { #define SHADER_MODEL_INFO_DXBC(major, minor) \ { \ D3D_SHADER_MODEL_##major##_##minor, SLANG_DXBC, "sm_" #major "_" #minor \ } SHADER_MODEL_INFO_DXBC(5, 1), #undef SHADER_MODEL_INFO_DXBC #define SHADER_MODEL_INFO_DXIL(major, minor) \ { \ (D3D_SHADER_MODEL)0x##major##minor, SLANG_DXIL, "sm_" #major "_" #minor \ } SHADER_MODEL_INFO_DXIL(6, 0), SHADER_MODEL_INFO_DXIL(6, 1), SHADER_MODEL_INFO_DXIL(6, 2), SHADER_MODEL_INFO_DXIL(6, 3), SHADER_MODEL_INFO_DXIL(6, 4), SHADER_MODEL_INFO_DXIL(6, 5), SHADER_MODEL_INFO_DXIL(6, 6), SHADER_MODEL_INFO_DXIL(6, 7), SHADER_MODEL_INFO_DXIL(6, 8), SHADER_MODEL_INFO_DXIL(6, 9) #undef SHADER_MODEL_INFO_DXIL }; Result DeviceImpl::createBuffer( const D3D12_RESOURCE_DESC& resourceDesc, const void* srcData, Size srcDataSize, D3D12_RESOURCE_STATES finalState, D3D12Resource& resourceOut, bool isShared, MemoryType memoryType) { const Size bufferSize = Size(resourceDesc.Width); D3D12_HEAP_PROPERTIES heapProps; heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; heapProps.CreationNodeMask = 1; heapProps.VisibleNodeMask = 1; D3D12_HEAP_FLAGS flags = D3D12_HEAP_FLAG_NONE; if (isShared) flags |= D3D12_HEAP_FLAG_SHARED; D3D12_RESOURCE_DESC desc = resourceDesc; D3D12_RESOURCE_STATES initialState = finalState; switch (memoryType) { case MemoryType::ReadBack: assert(!srcData); heapProps.Type = D3D12_HEAP_TYPE_READBACK; desc.Flags = D3D12_RESOURCE_FLAG_NONE; initialState |= D3D12_RESOURCE_STATE_COPY_DEST; break; case MemoryType::Upload: heapProps.Type = D3D12_HEAP_TYPE_UPLOAD; desc.Flags = D3D12_RESOURCE_FLAG_NONE; initialState |= D3D12_RESOURCE_STATE_GENERIC_READ; break; case MemoryType::DeviceLocal: heapProps.Type = D3D12_HEAP_TYPE_DEFAULT; if (initialState != D3D12_RESOURCE_STATE_RAYTRACING_ACCELERATION_STRUCTURE) initialState = D3D12_RESOURCE_STATE_COMMON; break; default: return SLANG_FAIL; } // Create the resource. SLANG_RETURN_ON_FAIL( resourceOut.initCommitted(m_device, heapProps, flags, desc, initialState, nullptr)); if (srcData) { D3D12Resource uploadResource; if (memoryType == MemoryType::DeviceLocal) { // If the buffer is on the default heap, create upload buffer. D3D12_RESOURCE_DESC uploadDesc(resourceDesc); uploadDesc.Flags = D3D12_RESOURCE_FLAG_NONE; heapProps.Type = D3D12_HEAP_TYPE_UPLOAD; SLANG_RETURN_ON_FAIL(uploadResource.initCommitted( m_device, heapProps, D3D12_HEAP_FLAG_NONE, uploadDesc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr)); } // Be careful not to actually copy a resource here. D3D12Resource& uploadResourceRef = (memoryType == MemoryType::DeviceLocal) ? uploadResource : resourceOut; // Copy data to the intermediate upload heap and then schedule a copy // from the upload heap to the vertex buffer. UINT8* dstData; D3D12_RANGE readRange = {}; // We do not intend to read from this resource on the CPU. ID3D12Resource* dxUploadResource = uploadResourceRef.getResource(); SLANG_RETURN_ON_FAIL( dxUploadResource->Map(0, &readRange, reinterpret_cast(&dstData))); ::memcpy(dstData, srcData, srcDataSize); dxUploadResource->Unmap(0, nullptr); if (memoryType == MemoryType::DeviceLocal) { auto encodeInfo = encodeResourceCommands(); encodeInfo.d3dCommandList ->CopyBufferRegion(resourceOut, 0, uploadResourceRef, 0, bufferSize); submitResourceCommandsAndWait(encodeInfo); } } return SLANG_OK; } Result DeviceImpl::captureTextureToSurface( TextureResourceImpl* resourceImpl, ResourceState state, ISlangBlob** outBlob, Size* outRowPitch, Size* outPixelSize) { auto& resource = resourceImpl->m_resource; const D3D12_RESOURCE_STATES initialState = D3DUtil::getResourceState(state); const ITextureResource::Desc& gfxDesc = *resourceImpl->getDesc(); const D3D12_RESOURCE_DESC desc = resource.getResource()->GetDesc(); // Don't bother supporting MSAA for right now if (desc.SampleDesc.Count > 1) { fprintf(stderr, "ERROR: cannot capture multi-sample texture\n"); return SLANG_FAIL; } FormatInfo formatInfo; gfxGetFormatInfo(gfxDesc.format, &formatInfo); Size bytesPerPixel = formatInfo.blockSizeInBytes / formatInfo.pixelsPerBlock; Size rowPitch = int(desc.Width) * bytesPerPixel; static const Size align = 256; // D3D requires minimum 256 byte alignment for texture data. rowPitch = (rowPitch + align - 1) & ~(align - 1); // Bit trick for rounding up Size bufferSize = rowPitch * int(desc.Height) * int(desc.DepthOrArraySize); if (outRowPitch) *outRowPitch = rowPitch; if (outPixelSize) *outPixelSize = bytesPerPixel; D3D12Resource stagingResource; { D3D12_RESOURCE_DESC stagingDesc; initBufferResourceDesc(bufferSize, stagingDesc); D3D12_HEAP_PROPERTIES heapProps; heapProps.Type = D3D12_HEAP_TYPE_READBACK; heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; heapProps.CreationNodeMask = 1; heapProps.VisibleNodeMask = 1; SLANG_RETURN_ON_FAIL(stagingResource.initCommitted( m_device, heapProps, D3D12_HEAP_FLAG_NONE, stagingDesc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr)); } auto encodeInfo = encodeResourceCommands(); auto currentState = D3DUtil::getResourceState(state); { D3D12BarrierSubmitter submitter(encodeInfo.d3dCommandList); resource.transition(currentState, D3D12_RESOURCE_STATE_COPY_SOURCE, submitter); } // Do the copy { D3D12_TEXTURE_COPY_LOCATION srcLoc; srcLoc.pResource = resource; srcLoc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; srcLoc.SubresourceIndex = 0; D3D12_TEXTURE_COPY_LOCATION dstLoc; dstLoc.pResource = stagingResource; dstLoc.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; dstLoc.PlacedFootprint.Offset = 0; dstLoc.PlacedFootprint.Footprint.Format = desc.Format; dstLoc.PlacedFootprint.Footprint.Width = UINT(desc.Width); dstLoc.PlacedFootprint.Footprint.Height = UINT(desc.Height); dstLoc.PlacedFootprint.Footprint.Depth = UINT(desc.DepthOrArraySize); dstLoc.PlacedFootprint.Footprint.RowPitch = UINT(rowPitch); encodeInfo.d3dCommandList->CopyTextureRegion(&dstLoc, 0, 0, 0, &srcLoc, nullptr); } { D3D12BarrierSubmitter submitter(encodeInfo.d3dCommandList); resource.transition(D3D12_RESOURCE_STATE_COPY_SOURCE, currentState, submitter); } // Submit the copy, and wait for copy to complete submitResourceCommandsAndWait(encodeInfo); { ID3D12Resource* dxResource = stagingResource; UINT8* data; D3D12_RANGE readRange = {0, bufferSize}; SLANG_RETURN_ON_FAIL(dxResource->Map(0, &readRange, reinterpret_cast(&data))); List blobData; blobData.setCount(bufferSize); memcpy(blobData.getBuffer(), data, bufferSize); dxResource->Unmap(0, nullptr); auto resultBlob = Slang::ListBlob::moveCreate(blobData); returnComPtr(outBlob, resultBlob); return SLANG_OK; } } Result DeviceImpl::getNativeDeviceHandles(InteropHandles* outHandles) { outHandles->handles[0].handleValue = (uint64_t)m_device; outHandles->handles[0].api = InteropHandleAPI::D3D12; return SLANG_OK; } Result DeviceImpl::_createDevice( DeviceCheckFlags deviceCheckFlags, const AdapterLUID* adapterLUID, D3D_FEATURE_LEVEL featureLevel, D3D12DeviceInfo& outDeviceInfo) { if (m_dxDebug && (deviceCheckFlags & DeviceCheckFlag::UseDebug) && !g_isAftermathEnabled) { m_dxDebug->EnableDebugLayer(); } outDeviceInfo.clear(); ComPtr dxgiFactory; SLANG_RETURN_ON_FAIL(D3DUtil::createFactory(deviceCheckFlags, dxgiFactory)); List> dxgiAdapters; SLANG_RETURN_ON_FAIL( D3DUtil::findAdapters(deviceCheckFlags, adapterLUID, dxgiFactory, dxgiAdapters)); ComPtr device; ComPtr adapter; for (Index i = 0; i < dxgiAdapters.getCount(); ++i) { IDXGIAdapter* dxgiAdapter = dxgiAdapters[i]; if (SLANG_SUCCEEDED( m_D3D12CreateDevice(dxgiAdapter, featureLevel, IID_PPV_ARGS(device.writeRef())))) { adapter = dxgiAdapter; break; } } if (!device) { return SLANG_FAIL; } if (m_dxDebug && (deviceCheckFlags & DeviceCheckFlag::UseDebug) && !g_isAftermathEnabled) { ComPtr infoQueue; if (SLANG_SUCCEEDED(device->QueryInterface(infoQueue.writeRef()))) { // Make break infoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, true); if (m_extendedDesc.debugBreakOnD3D12Error) { infoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, true); } D3D12_MESSAGE_ID hideMessages[] = { D3D12_MESSAGE_ID_CLEARRENDERTARGETVIEW_MISMATCHINGCLEARVALUE, D3D12_MESSAGE_ID_CLEARDEPTHSTENCILVIEW_MISMATCHINGCLEARVALUE, }; D3D12_INFO_QUEUE_FILTER f = {}; f.DenyList.NumIDs = (UINT)SLANG_COUNT_OF(hideMessages); f.DenyList.pIDList = hideMessages; infoQueue->AddStorageFilterEntries(&f); // Apparently there is a problem with sm 6.3 with spurious errors, with debug layer // enabled D3D12_FEATURE_DATA_SHADER_MODEL featureShaderModel; featureShaderModel.HighestShaderModel = D3D_SHADER_MODEL_6_3; SLANG_SUCCEEDED(device->CheckFeatureSupport( D3D12_FEATURE_SHADER_MODEL, &featureShaderModel, sizeof(featureShaderModel))); if (featureShaderModel.HighestShaderModel >= D3D_SHADER_MODEL_6_3) { // Filter out any messages that cause issues // TODO: Remove this when the debug layers work properly D3D12_MESSAGE_ID messageIds[] = { // When the debug layer is enabled this error is triggered sometimes after a // CopyDescriptorsSimple call The failed check validates that the source and // destination ranges of the copy do not overlap. The check assumes descriptor // handles are pointers to memory, but this is not always the case and the check // fails (even though everything is okay). D3D12_MESSAGE_ID_COPY_DESCRIPTORS_INVALID_RANGES, }; // We filter INFO messages because they are way too many D3D12_MESSAGE_SEVERITY severities[] = {D3D12_MESSAGE_SEVERITY_INFO}; D3D12_INFO_QUEUE_FILTER infoQueueFilter = {}; infoQueueFilter.DenyList.NumSeverities = SLANG_COUNT_OF(severities); infoQueueFilter.DenyList.pSeverityList = severities; infoQueueFilter.DenyList.NumIDs = SLANG_COUNT_OF(messageIds); infoQueueFilter.DenyList.pIDList = messageIds; infoQueue->PushStorageFilter(&infoQueueFilter); } } } #ifdef GFX_NV_AFTERMATH { if ((deviceCheckFlags & DeviceCheckFlag::UseDebug) && g_isAftermathEnabled) { // Initialize Nsight Aftermath for this device. // This combination of flags is not necessarily appropraite for real world usage const uint32_t aftermathFlags = GFSDK_Aftermath_FeatureFlags_EnableMarkers | // Enable event marker tracking. GFSDK_Aftermath_FeatureFlags_CallStackCapturing | // Enable automatic call stack // event markers. GFSDK_Aftermath_FeatureFlags_EnableResourceTracking | // Enable tracking of // resources. GFSDK_Aftermath_FeatureFlags_GenerateShaderDebugInfo | // Generate debug information // for shaders. GFSDK_Aftermath_FeatureFlags_EnableShaderErrorReporting; // Enable additional // runtime shader error // reporting. auto initResult = GFSDK_Aftermath_DX12_Initialize( GFSDK_Aftermath_Version_API, aftermathFlags, device); if (initResult != GFSDK_Aftermath_Result_Success) { SLANG_ASSERT_FAILURE("Unable to initialize aftermath"); // Unable to initialize return SLANG_FAIL; } } } #endif // Get the descs { adapter->GetDesc(&outDeviceInfo.m_desc); // Look up GetDesc1 info ComPtr adapter1; if (SLANG_SUCCEEDED(adapter->QueryInterface(adapter1.writeRef()))) { adapter1->GetDesc1(&outDeviceInfo.m_desc1); } } // Save other info outDeviceInfo.m_device = device; outDeviceInfo.m_dxgiFactory = dxgiFactory; outDeviceInfo.m_adapter = adapter; outDeviceInfo.m_isWarp = D3DUtil::isWarp(dxgiFactory, adapter); const UINT kMicrosoftVendorId = 5140; outDeviceInfo.m_isSoftware = outDeviceInfo.m_isWarp || ((outDeviceInfo.m_desc1.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) != 0) || outDeviceInfo.m_desc.VendorId == kMicrosoftVendorId; return SLANG_OK; } Result DeviceImpl::initialize(const Desc& desc) { SLANG_RETURN_ON_FAIL(RendererBase::initialize(desc)); // Rather than statically link against D3D, we load it dynamically. SharedLibrary::Handle d3dModule; #if SLANG_WINDOWS_FAMILY const char* libName = "d3d12"; #else const char* libName = "vkd3d-proton-d3d12"; #endif if (SLANG_FAILED(SharedLibrary::load(libName, d3dModule))) { getDebugCallback()->handleMessage( DebugMessageType::Error, DebugMessageSource::Layer, "error: failed load 'd3d12.dll'\n"); return SLANG_FAIL; } // Find extended desc. for (GfxIndex i = 0; i < desc.extendedDescCount; i++) { StructType stype; memcpy(&stype, desc.extendedDescs[i], sizeof(stype)); switch (stype) { case StructType::D3D12DeviceExtendedDesc: memcpy(&m_extendedDesc, desc.extendedDescs[i], sizeof(m_extendedDesc)); break; case StructType::D3D12ExperimentalFeaturesDesc: processExperimentalFeaturesDesc(d3dModule, desc.extendedDescs[i]); break; } } // Initialize queue index allocator. // Support max 32 queues. m_queueIndexAllocator.initPool(32); // Initialize DeviceInfo { m_info.deviceType = DeviceType::DirectX12; m_info.bindingStyle = BindingStyle::DirectX; m_info.projectionStyle = ProjectionStyle::DirectX; m_info.apiName = "Direct3D 12"; static const float kIdentity[] = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}; ::memcpy(m_info.identityProjectionMatrix, kIdentity, sizeof(kIdentity)); } // Get all the dll entry points m_D3D12SerializeRootSignature = (PFN_D3D12_SERIALIZE_ROOT_SIGNATURE)loadProc(d3dModule, "D3D12SerializeRootSignature"); if (!m_D3D12SerializeRootSignature) { return SLANG_FAIL; } m_D3D12SerializeVersionedRootSignature = (PFN_D3D12_SERIALIZE_VERSIONED_ROOT_SIGNATURE)loadProc( d3dModule, "D3D12SerializeVersionedRootSignature"); if (!m_D3D12SerializeVersionedRootSignature) { return SLANG_FAIL; } #if SLANG_ENABLE_PIX HMODULE pixModule = LoadLibraryW(L"WinPixEventRuntime.dll"); if (pixModule) { m_BeginEventOnCommandList = (PFN_BeginEventOnCommandList)GetProcAddress(pixModule, "PIXBeginEventOnCommandList"); m_EndEventOnCommandList = (PFN_EndEventOnCommandList)GetProcAddress(pixModule, "PIXEndEventOnCommandList"); } #endif // If Aftermath is enabled, we can't enable the D3D12 debug layer as well if (isGfxDebugLayerEnabled() && !g_isAftermathEnabled) { m_D3D12GetDebugInterface = (PFN_D3D12_GET_DEBUG_INTERFACE)loadProc(d3dModule, "D3D12GetDebugInterface"); if (m_D3D12GetDebugInterface) { if (SLANG_SUCCEEDED(m_D3D12GetDebugInterface(IID_PPV_ARGS(m_dxDebug.writeRef())))) { #if 0 // Can enable for extra validation. NOTE! That d3d12 warns if you do.... // D3D12 MESSAGE : Device Debug Layer Startup Options : GPU - Based Validation is enabled(disabled by default). // This results in new validation not possible during API calls on the CPU, by creating patched shaders that have validation // added directly to the shader. However, it can slow things down a lot, especially for applications with numerous // PSOs.Time to see the first render frame may take several minutes. // [INITIALIZATION MESSAGE #1016: CREATEDEVICE_DEBUG_LAYER_STARTUP_OPTIONS] ComPtr debug1; if (SLANG_SUCCEEDED(m_dxDebug->QueryInterface(debug1.writeRef()))) { debug1->SetEnableGPUBasedValidation(true); } #endif } } } m_D3D12CreateDevice = (PFN_D3D12_CREATE_DEVICE)loadProc(d3dModule, "D3D12CreateDevice"); if (!m_D3D12CreateDevice) { return SLANG_FAIL; } if (desc.existingDeviceHandles.handles[0].handleValue == 0) { FlagCombiner combiner; if (isGfxDebugLayerEnabled()) { combiner.add( DeviceCheckFlag::UseDebug, ChangeType::OnOff); ///< First try debug then non debug } else { combiner.add(DeviceCheckFlag::UseDebug, ChangeType::Off); ///< Don't bother with debug } combiner.add( DeviceCheckFlag::UseHardwareDevice, ChangeType::OnOff); ///< First try hardware, then reference const D3D_FEATURE_LEVEL featureLevels[] = { (D3D_FEATURE_LEVEL)D3D_FEATURE_LEVEL_12_2, D3D_FEATURE_LEVEL_12_1, D3D_FEATURE_LEVEL_12_0, D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_9_3, D3D_FEATURE_LEVEL_9_2, D3D_FEATURE_LEVEL_9_1}; for (auto featureLevel : featureLevels) { const int numCombinations = combiner.getNumCombinations(); for (int i = 0; i < numCombinations; ++i) { if (SLANG_SUCCEEDED(_createDevice( combiner.getCombination(i), desc.adapterLUID, featureLevel, m_deviceInfo))) { goto succ; } } } succ: if (!m_deviceInfo.m_adapter) { // Couldn't find an adapter return SLANG_FAIL; } } else { // Store the existing device handle in desc in m_deviceInfo m_deviceInfo.m_device = (ID3D12Device*)desc.existingDeviceHandles.handles[0].handleValue; } // Set the device m_device = m_deviceInfo.m_device; if (m_deviceInfo.m_isSoftware) { m_features.add("software-device"); } else { m_features.add("hardware-device"); } // NVAPI if (desc.nvapiExtnSlot >= 0) { if (SLANG_FAILED(NVAPIUtil::initialize())) { return SLANG_E_NOT_AVAILABLE; } #ifdef GFX_NVAPI // From DOCS: Applications are expected to bind null UAV to this slot. // NOTE! We don't currently do this, but doesn't seem to be a problem. const NvAPI_Status status = NvAPI_D3D12_SetNvShaderExtnSlotSpace(m_device, NvU32(desc.nvapiExtnSlot), NvU32(0)); if (status != NVAPI_OK) { return SLANG_E_NOT_AVAILABLE; } if (isSupportedNVAPIOp(m_device, NV_EXTN_OP_UINT64_ATOMIC)) { m_features.add("atomic-int64"); } if (isSupportedNVAPIOp(m_device, NV_EXTN_OP_FP32_ATOMIC)) { m_features.add("atomic-float"); } // If we have NVAPI well assume we have realtime clock { m_features.add("realtime-clock"); } m_nvapi = true; #endif } D3D12_FEATURE_DATA_SHADER_MODEL shaderModelData = {}; // Find what features are supported { // Check this is how this is laid out... SLANG_COMPILE_TIME_ASSERT(D3D_SHADER_MODEL_6_0 == 0x60); { // CheckFeatureSupport(D3D12_FEATURE_SHADER_MODEL) can fail if the runtime/driver does // not yet know the specified highest shader model. Therefore we assemble a list of // shader models to check and walk it from highest to lowest to find the supported // shader model. Slang::ShortList shaderModels; if (m_extendedDesc.highestShaderModel != 0) shaderModels.add((D3D_SHADER_MODEL)m_extendedDesc.highestShaderModel); for (int i = SLANG_COUNT_OF(kKnownShaderModels) - 1; i >= 0; --i) shaderModels.add(kKnownShaderModels[i].shaderModel); for (D3D_SHADER_MODEL shaderModel : shaderModels) { shaderModelData.HighestShaderModel = shaderModel; if (SLANG_SUCCEEDED(m_device->CheckFeatureSupport( D3D12_FEATURE_SHADER_MODEL, &shaderModelData, sizeof(shaderModelData)))) break; } // TODO: Currently warp causes a crash when using half, so disable for now if (m_deviceInfo.m_isWarp == false && shaderModelData.HighestShaderModel >= D3D_SHADER_MODEL_6_2) { // With sm_6_2 we have half m_features.add("half"); } } { D3D12_FEATURE_DATA_D3D12_OPTIONS options; if (SLANG_SUCCEEDED(m_device->CheckFeatureSupport( D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(options)))) { // Check double precision support if (options.DoublePrecisionFloatShaderOps) m_features.add("double"); // Check conservative-rasterization support auto conservativeRasterTier = options.ConservativeRasterizationTier; if (conservativeRasterTier == D3D12_CONSERVATIVE_RASTERIZATION_TIER_3) { m_features.add("conservative-rasterization-3"); m_features.add("conservative-rasterization-2"); m_features.add("conservative-rasterization-1"); } else if (conservativeRasterTier == D3D12_CONSERVATIVE_RASTERIZATION_TIER_2) { m_features.add("conservative-rasterization-2"); m_features.add("conservative-rasterization-1"); } else if (conservativeRasterTier == D3D12_CONSERVATIVE_RASTERIZATION_TIER_1) { m_features.add("conservative-rasterization-1"); } // Check rasterizer ordered views support if (options.ROVsSupported) { m_features.add("rasterizer-ordered-views"); } } } { D3D12_FEATURE_DATA_D3D12_OPTIONS1 options; if (SLANG_SUCCEEDED(m_device->CheckFeatureSupport( D3D12_FEATURE_D3D12_OPTIONS1, &options, sizeof(options)))) { // Check wave operations support if (options.WaveOps) m_features.add("wave-ops"); } } { D3D12_FEATURE_DATA_D3D12_OPTIONS2 options; if (SLANG_SUCCEEDED(m_device->CheckFeatureSupport( D3D12_FEATURE_D3D12_OPTIONS2, &options, sizeof(options)))) { // Check programmable sample positions support switch (options.ProgrammableSamplePositionsTier) { case D3D12_PROGRAMMABLE_SAMPLE_POSITIONS_TIER_2: m_features.add("programmable-sample-positions-2"); m_features.add("programmable-sample-positions-1"); break; case D3D12_PROGRAMMABLE_SAMPLE_POSITIONS_TIER_1: m_features.add("programmable-sample-positions-1"); break; default: break; } } } { D3D12_FEATURE_DATA_D3D12_OPTIONS3 options; if (SLANG_SUCCEEDED(m_device->CheckFeatureSupport( D3D12_FEATURE_D3D12_OPTIONS3, &options, sizeof(options)))) { // Check barycentrics support if (options.BarycentricsSupported) { m_features.add("barycentrics"); } } } // Check ray tracing support { D3D12_FEATURE_DATA_D3D12_OPTIONS5 options; if (SLANG_SUCCEEDED(m_device->CheckFeatureSupport( D3D12_FEATURE_D3D12_OPTIONS5, &options, sizeof(options)))) { if (options.RaytracingTier != D3D12_RAYTRACING_TIER_NOT_SUPPORTED) { m_features.add("ray-tracing"); } if (options.RaytracingTier >= D3D12_RAYTRACING_TIER_1_1) { m_features.add("ray-query"); } } } // Check mesh shader support { D3D12_FEATURE_DATA_D3D12_OPTIONS7 options; if (SLANG_SUCCEEDED(m_device->CheckFeatureSupport( D3D12_FEATURE_D3D12_OPTIONS7, &options, sizeof(options)))) { if (options.MeshShaderTier >= D3D12_MESH_SHADER_TIER_1) { m_features.add("mesh-shader"); } } } } m_desc = desc; // Create a command queue for internal resource transfer operations. SLANG_RETURN_ON_FAIL(createCommandQueueImpl(m_resourceCommandQueue.writeRef())); // `CommandQueueImpl` holds a back reference to `D3D12Device`, make it a weak reference here // since this object is already owned by `D3D12Device`. m_resourceCommandQueue->breakStrongReferenceToDevice(); // Retrieve timestamp frequency. m_resourceCommandQueue->m_d3dQueue->GetTimestampFrequency(&m_info.timestampFrequency); // Get device limits. { DeviceLimits limits = {}; limits.maxTextureDimension1D = D3D12_REQ_TEXTURE1D_U_DIMENSION; limits.maxTextureDimension2D = D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION; limits.maxTextureDimension3D = D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION; limits.maxTextureDimensionCube = D3D12_REQ_TEXTURECUBE_DIMENSION; limits.maxTextureArrayLayers = D3D12_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION; limits.maxVertexInputElements = D3D12_IA_VERTEX_INPUT_STRUCTURE_ELEMENT_COUNT; limits.maxVertexInputElementOffset = 256; // TODO limits.maxVertexStreams = D3D12_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT; limits.maxVertexStreamStride = D3D12_REQ_MULTI_ELEMENT_STRUCTURE_SIZE_IN_BYTES; limits.maxComputeThreadsPerGroup = D3D12_CS_THREAD_GROUP_MAX_THREADS_PER_GROUP; limits.maxComputeThreadGroupSize[0] = D3D12_CS_THREAD_GROUP_MAX_X; limits.maxComputeThreadGroupSize[1] = D3D12_CS_THREAD_GROUP_MAX_Y; limits.maxComputeThreadGroupSize[2] = D3D12_CS_THREAD_GROUP_MAX_Z; limits.maxComputeDispatchThreadGroups[0] = D3D12_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION; limits.maxComputeDispatchThreadGroups[1] = D3D12_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION; limits.maxComputeDispatchThreadGroups[2] = D3D12_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION; limits.maxViewports = D3D12_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE; limits.maxViewportDimensions[0] = D3D12_VIEWPORT_BOUNDS_MAX; limits.maxViewportDimensions[1] = D3D12_VIEWPORT_BOUNDS_MAX; limits.maxFramebufferDimensions[0] = D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION; limits.maxFramebufferDimensions[1] = D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION; limits.maxFramebufferDimensions[2] = 1; limits.maxShaderVisibleSamplers = D3D12_MAX_SHADER_VISIBLE_SAMPLER_HEAP_SIZE; m_info.limits = limits; } SLANG_RETURN_ON_FAIL(createTransientResourceHeapImpl( ITransientResourceHeap::Flags::AllowResizing, 0, 8, 4, m_resourceCommandTransientHeap.writeRef())); // `TransientResourceHeap` holds a back reference to `D3D12Device`, make it a weak reference // here since this object is already owned by `D3D12Device`. m_resourceCommandTransientHeap->breakStrongReferenceToDevice(); m_cpuViewHeap = new D3D12GeneralExpandingDescriptorHeap(); SLANG_RETURN_ON_FAIL(m_cpuViewHeap->init( m_device, 1024 * 1024, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE)); m_cpuSamplerHeap = new D3D12GeneralExpandingDescriptorHeap(); SLANG_RETURN_ON_FAIL(m_cpuSamplerHeap->init( m_device, 2048, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, D3D12_DESCRIPTOR_HEAP_FLAG_NONE)); m_rtvAllocator = new D3D12GeneralExpandingDescriptorHeap(); SLANG_RETURN_ON_FAIL(m_rtvAllocator->init( m_device, 16 * 1024, D3D12_DESCRIPTOR_HEAP_TYPE_RTV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE)); m_dsvAllocator = new D3D12GeneralExpandingDescriptorHeap(); SLANG_RETURN_ON_FAIL(m_dsvAllocator->init( m_device, 1024, D3D12_DESCRIPTOR_HEAP_TYPE_DSV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE)); ComPtr dxgiDevice; if (m_deviceInfo.m_adapter) { DXGI_ADAPTER_DESC adapterDesc; m_deviceInfo.m_adapter->GetDesc(&adapterDesc); m_adapterName = String::fromWString(adapterDesc.Description); m_info.adapterName = m_adapterName.begin(); } // Initialize DXR interface. #if SLANG_GFX_HAS_DXR_SUPPORT m_device->QueryInterface(m_deviceInfo.m_device5.writeRef()); m_device5 = m_deviceInfo.m_device5.get(); #endif // Check shader model version. SlangCompileTarget compileTarget = SLANG_DXBC; const char* profileName = "sm_5_1"; for (auto& sm : kKnownShaderModels) { if (sm.shaderModel <= shaderModelData.HighestShaderModel) { m_features.add(sm.profileName); profileName = sm.profileName; compileTarget = sm.compileTarget; } else { break; } } // If user specified a higher shader model than what the system supports, return failure. int userSpecifiedShaderModel = D3DUtil::getShaderModelFromProfileName(desc.slang.targetProfile); if (userSpecifiedShaderModel > shaderModelData.HighestShaderModel) { getDebugCallback()->handleMessage( gfx::DebugMessageType::Error, gfx::DebugMessageSource::Layer, "The requested shader model is not supported by the system."); return SLANG_E_NOT_AVAILABLE; } SLANG_RETURN_ON_FAIL(slangContext.initialize( desc.slang, desc.extendedDescCount, desc.extendedDescs, compileTarget, profileName, makeArray(slang::PreprocessorMacroDesc{"__D3D12__", "1"}).getView())); // Allocate a D3D12 "command signature" object that matches the behavior // of a D3D11-style `DrawInstancedIndirect` operation. { D3D12_INDIRECT_ARGUMENT_DESC args; args.Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW; D3D12_COMMAND_SIGNATURE_DESC desc; desc.ByteStride = sizeof(D3D12_DRAW_ARGUMENTS); desc.NumArgumentDescs = 1; desc.pArgumentDescs = &args; desc.NodeMask = 0; SLANG_RETURN_ON_FAIL(m_device->CreateCommandSignature( &desc, nullptr, IID_PPV_ARGS(drawIndirectCmdSignature.writeRef()))); } // Allocate a D3D12 "command signature" object that matches the behavior // of a D3D11-style `DrawIndexedInstancedIndirect` operation. { D3D12_INDIRECT_ARGUMENT_DESC args; args.Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW_INDEXED; D3D12_COMMAND_SIGNATURE_DESC desc; desc.ByteStride = sizeof(D3D12_DRAW_INDEXED_ARGUMENTS); desc.NumArgumentDescs = 1; desc.pArgumentDescs = &args; desc.NodeMask = 0; SLANG_RETURN_ON_FAIL(m_device->CreateCommandSignature( &desc, nullptr, IID_PPV_ARGS(drawIndexedIndirectCmdSignature.writeRef()))); } // Allocate a D3D12 "command signature" object that matches the behavior // of a D3D11-style `Dispatch` operation. { D3D12_INDIRECT_ARGUMENT_DESC args; args.Type = D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH; D3D12_COMMAND_SIGNATURE_DESC desc; desc.ByteStride = sizeof(D3D12_DISPATCH_ARGUMENTS); desc.NumArgumentDescs = 1; desc.pArgumentDescs = &args; desc.NodeMask = 0; SLANG_RETURN_ON_FAIL(m_device->CreateCommandSignature( &desc, nullptr, IID_PPV_ARGS(dispatchIndirectCmdSignature.writeRef()))); } m_isInitialized = true; return SLANG_OK; } Result DeviceImpl::createTransientResourceHeap( const ITransientResourceHeap::Desc& desc, ITransientResourceHeap** outHeap) { RefPtr heap; SLANG_RETURN_ON_FAIL(createTransientResourceHeapImpl( desc.flags, desc.constantBufferSize, getViewDescriptorCount(desc), Math::Max(1024, desc.samplerDescriptorCount), heap.writeRef())); returnComPtr(outHeap, heap); return SLANG_OK; } Result DeviceImpl::createCommandQueue(const ICommandQueue::Desc& desc, ICommandQueue** outQueue) { RefPtr queue; SLANG_RETURN_ON_FAIL(createCommandQueueImpl(queue.writeRef())); returnComPtr(outQueue, queue); return SLANG_OK; } Result DeviceImpl::createSwapchain( const ISwapchain::Desc& desc, WindowHandle window, ISwapchain** outSwapchain) { RefPtr swapchain = new SwapchainImpl(); SLANG_RETURN_ON_FAIL(swapchain->init(this, desc, window)); returnComPtr(outSwapchain, swapchain); return SLANG_OK; } SlangResult DeviceImpl::readTextureResource( ITextureResource* resource, ResourceState state, ISlangBlob** outBlob, Size* outRowPitch, Size* outPixelSize) { return captureTextureToSurface( static_cast(resource), state, outBlob, outRowPitch, outPixelSize); } Result DeviceImpl::getTextureAllocationInfo( const ITextureResource::Desc& desc, Size* outSize, Size* outAlignment) { TextureResource::Desc srcDesc = fixupTextureDesc(desc); D3D12_RESOURCE_DESC resourceDesc = {}; initTextureResourceDesc(resourceDesc, srcDesc); auto allocInfo = m_device->GetResourceAllocationInfo(0, 1, &resourceDesc); *outSize = (Size)allocInfo.SizeInBytes; *outAlignment = (Size)allocInfo.Alignment; return SLANG_OK; } Result DeviceImpl::getTextureRowAlignment(Size* outAlignment) { *outAlignment = D3D12_TEXTURE_DATA_PITCH_ALIGNMENT; return SLANG_OK; } Result DeviceImpl::createTextureResource( const ITextureResource::Desc& descIn, const ITextureResource::SubresourceData* initData, ITextureResource** outResource) { // Description of uploading on Dx12 // https://msdn.microsoft.com/en-us/library/windows/desktop/dn899215%28v=vs.85%29.aspx TextureResource::Desc srcDesc = fixupTextureDesc(descIn); D3D12_RESOURCE_DESC resourceDesc = {}; initTextureResourceDesc(resourceDesc, srcDesc); const int arraySize = calcEffectiveArraySize(srcDesc); const int numMipMaps = srcDesc.numMipLevels; RefPtr texture(new TextureResourceImpl(srcDesc)); // Create the target resource { D3D12_HEAP_PROPERTIES heapProps; heapProps.Type = D3D12_HEAP_TYPE_DEFAULT; heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; heapProps.CreationNodeMask = 1; heapProps.VisibleNodeMask = 1; D3D12_HEAP_FLAGS flags = D3D12_HEAP_FLAG_NONE; if (descIn.isShared) flags |= D3D12_HEAP_FLAG_SHARED; D3D12_CLEAR_VALUE clearValue; D3D12_CLEAR_VALUE* clearValuePtr = nullptr; clearValue.Format = resourceDesc.Format; if (descIn.optimalClearValue) { memcpy(clearValue.Color, &descIn.optimalClearValue->color, sizeof(clearValue.Color)); clearValue.DepthStencil.Depth = descIn.optimalClearValue->depthStencil.depth; clearValue.DepthStencil.Stencil = descIn.optimalClearValue->depthStencil.stencil; clearValuePtr = &clearValue; } if ((resourceDesc.Flags & (D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)) == 0) { clearValuePtr = nullptr; } if (isTypelessDepthFormat(resourceDesc.Format)) { clearValuePtr = nullptr; } SLANG_RETURN_ON_FAIL(texture->m_resource.initCommitted( m_device, heapProps, flags, resourceDesc, D3D12_RESOURCE_STATE_COPY_DEST, clearValuePtr)); texture->m_resource.setDebugName(L"Texture"); } // Calculate the layout List layouts; layouts.setCount(numMipMaps); List mipRowSizeInBytes; mipRowSizeInBytes.setCount(srcDesc.numMipLevels); List mipNumRows; mipNumRows.setCount(numMipMaps); // NOTE! This is just the size for one array upload -> not for the whole texture UInt64 requiredSize = 0; m_device->GetCopyableFootprints( &resourceDesc, 0, srcDesc.numMipLevels, 0, layouts.begin(), mipNumRows.begin(), mipRowSizeInBytes.begin(), &requiredSize); // Sub resource indexing // https://msdn.microsoft.com/en-us/library/windows/desktop/dn705766(v=vs.85).aspx#subresource_indexing if (initData) { // Create the upload texture D3D12Resource uploadTexture; { D3D12_HEAP_PROPERTIES heapProps; heapProps.Type = D3D12_HEAP_TYPE_UPLOAD; heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; heapProps.CreationNodeMask = 1; heapProps.VisibleNodeMask = 1; D3D12_RESOURCE_DESC uploadResourceDesc; uploadResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; uploadResourceDesc.Format = DXGI_FORMAT_UNKNOWN; uploadResourceDesc.Width = requiredSize; uploadResourceDesc.Height = 1; uploadResourceDesc.DepthOrArraySize = 1; uploadResourceDesc.MipLevels = 1; uploadResourceDesc.SampleDesc.Count = 1; uploadResourceDesc.SampleDesc.Quality = 0; uploadResourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE; uploadResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; uploadResourceDesc.Alignment = 0; SLANG_RETURN_ON_FAIL(uploadTexture.initCommitted( m_device, heapProps, D3D12_HEAP_FLAG_NONE, uploadResourceDesc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr)); uploadTexture.setDebugName(L"TextureUpload"); } // Get the pointer to the upload resource ID3D12Resource* uploadResource = uploadTexture; int subResourceIndex = 0; for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { uint8_t* p; uploadResource->Map(0, nullptr, reinterpret_cast(&p)); for (int j = 0; j < numMipMaps; ++j) { auto srcSubresource = initData[subResourceIndex + j]; const D3D12_PLACED_SUBRESOURCE_FOOTPRINT& layout = layouts[j]; const D3D12_SUBRESOURCE_FOOTPRINT& footprint = layout.Footprint; TextureResource::Extents mipSize = calcMipSize(srcDesc.size, j); if (gfxIsCompressedFormat(descIn.format)) { mipSize.width = int(D3DUtil::calcAligned(mipSize.width, 4)); mipSize.height = int(D3DUtil::calcAligned(mipSize.height, 4)); } assert( footprint.Width == mipSize.width && footprint.Height == mipSize.height && footprint.Depth == mipSize.depth); auto mipRowSize = mipRowSizeInBytes[j]; const ptrdiff_t dstMipRowPitch = ptrdiff_t(footprint.RowPitch); const ptrdiff_t srcMipRowPitch = ptrdiff_t(srcSubresource.strideY); const ptrdiff_t dstMipLayerPitch = ptrdiff_t(footprint.RowPitch * footprint.Height); const ptrdiff_t srcMipLayerPitch = ptrdiff_t(srcSubresource.strideZ); // Our outer loop will copy the depth layers one at a time. // const uint8_t* srcLayer = (const uint8_t*)srcSubresource.data; uint8_t* dstLayer = p + layouts[j].Offset; for (int l = 0; l < mipSize.depth; l++) { // Our inner loop will copy the rows one at a time. // const uint8_t* srcRow = srcLayer; uint8_t* dstRow = dstLayer; int j = gfxIsCompressedFormat(descIn.format) ? 4 : 1; // BC compressed formats are organized into 4x4 blocks for (int k = 0; k < mipSize.height; k += j) { ::memcpy(dstRow, srcRow, (Size)mipRowSize); srcRow += srcMipRowPitch; dstRow += dstMipRowPitch; } srcLayer += srcMipLayerPitch; dstLayer += dstMipLayerPitch; } // assert(srcRow == (const uint8_t*)(srcMip.getBuffer() + srcMip.getCount())); } uploadResource->Unmap(0, nullptr); auto encodeInfo = encodeResourceCommands(); for (int mipIndex = 0; mipIndex < numMipMaps; ++mipIndex) { // https://msdn.microsoft.com/en-us/library/windows/desktop/dn903862(v=vs.85).aspx D3D12_TEXTURE_COPY_LOCATION src; src.pResource = uploadTexture; src.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; src.PlacedFootprint = layouts[mipIndex]; D3D12_TEXTURE_COPY_LOCATION dst; dst.pResource = texture->m_resource; dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; dst.SubresourceIndex = subResourceIndex; encodeInfo.d3dCommandList->CopyTextureRegion(&dst, 0, 0, 0, &src, nullptr); subResourceIndex++; } // Block - waiting for copy to complete (so can drop upload texture) submitResourceCommandsAndWait(encodeInfo); } } { auto encodeInfo = encodeResourceCommands(); { D3D12BarrierSubmitter submitter(encodeInfo.d3dCommandList); texture->m_resource.transition( D3D12_RESOURCE_STATE_COPY_DEST, texture->m_defaultState, submitter); } submitResourceCommandsAndWait(encodeInfo); } returnComPtr(outResource, texture); return SLANG_OK; } Result DeviceImpl::createTextureFromNativeHandle( InteropHandle handle, const ITextureResource::Desc& srcDesc, ITextureResource** outResource) { RefPtr texture(new TextureResourceImpl(srcDesc)); if (handle.api == InteropHandleAPI::D3D12) { texture->m_resource.setResource((ID3D12Resource*)handle.handleValue); } else { return SLANG_FAIL; } returnComPtr(outResource, texture); return SLANG_OK; } Result DeviceImpl::createBufferResource( const IBufferResource::Desc& descIn, const void* initData, IBufferResource** outResource) { BufferResource::Desc srcDesc = fixupBufferDesc(descIn); RefPtr buffer(new BufferResourceImpl(srcDesc)); D3D12_RESOURCE_DESC bufferDesc; initBufferResourceDesc(descIn.sizeInBytes, bufferDesc); bufferDesc.Flags |= calcResourceFlags(srcDesc.allowedStates); const D3D12_RESOURCE_STATES initialState = buffer->m_defaultState; SLANG_RETURN_ON_FAIL(createBuffer( bufferDesc, initData, srcDesc.sizeInBytes, initialState, buffer->m_resource, descIn.isShared, descIn.memoryType)); returnComPtr(outResource, buffer); return SLANG_OK; } Result DeviceImpl::createBufferFromNativeHandle( InteropHandle handle, const IBufferResource::Desc& srcDesc, IBufferResource** outResource) { RefPtr buffer(new BufferResourceImpl(srcDesc)); if (handle.api == InteropHandleAPI::D3D12) { buffer->m_resource.setResource((ID3D12Resource*)handle.handleValue); } else { return SLANG_FAIL; } returnComPtr(outResource, buffer); return SLANG_OK; } Result DeviceImpl::createSamplerState(ISamplerState::Desc const& desc, ISamplerState** outSampler) { D3D12_FILTER_REDUCTION_TYPE dxReduction = translateFilterReduction(desc.reductionOp); D3D12_FILTER dxFilter; if (desc.maxAnisotropy > 1) { dxFilter = D3D12_ENCODE_ANISOTROPIC_FILTER(dxReduction); } else { D3D12_FILTER_TYPE dxMin = translateFilterMode(desc.minFilter); D3D12_FILTER_TYPE dxMag = translateFilterMode(desc.magFilter); D3D12_FILTER_TYPE dxMip = translateFilterMode(desc.mipFilter); dxFilter = D3D12_ENCODE_BASIC_FILTER(dxMin, dxMag, dxMip, dxReduction); } D3D12_SAMPLER_DESC dxDesc = {}; dxDesc.Filter = dxFilter; dxDesc.AddressU = translateAddressingMode(desc.addressU); dxDesc.AddressV = translateAddressingMode(desc.addressV); dxDesc.AddressW = translateAddressingMode(desc.addressW); dxDesc.MipLODBias = desc.mipLODBias; dxDesc.MaxAnisotropy = desc.maxAnisotropy; dxDesc.ComparisonFunc = translateComparisonFunc(desc.comparisonFunc); for (int ii = 0; ii < 4; ++ii) dxDesc.BorderColor[ii] = desc.borderColor[ii]; dxDesc.MinLOD = desc.minLOD; dxDesc.MaxLOD = desc.maxLOD; auto& samplerHeap = m_cpuSamplerHeap; D3D12Descriptor cpuDescriptor; samplerHeap->allocate(&cpuDescriptor); m_device->CreateSampler(&dxDesc, cpuDescriptor.cpuHandle); // TODO: We really ought to have a free-list of sampler-heap // entries that we check before we go to the heap, and then // when we are done with a sampler we simply add it to the free list. // RefPtr samplerImpl = new SamplerStateImpl(); samplerImpl->m_allocator = samplerHeap; samplerImpl->m_descriptor = cpuDescriptor; returnComPtr(outSampler, samplerImpl); return SLANG_OK; } Result DeviceImpl::createTextureView( ITextureResource* texture, IResourceView::Desc const& desc, IResourceView** outView) { auto resourceImpl = (TextureResourceImpl*)texture; RefPtr viewImpl = new ResourceViewImpl(); viewImpl->m_resource = resourceImpl; viewImpl->m_desc = desc; bool isArray = resourceImpl ? resourceImpl->getDesc()->arraySize > 1 : false; bool isMultiSample = resourceImpl ? resourceImpl->getDesc()->sampleDesc.numSamples > 1 : false; switch (desc.type) { default: return SLANG_FAIL; case IResourceView::Type::RenderTarget: { SLANG_RETURN_ON_FAIL(m_rtvAllocator->allocate(&viewImpl->m_descriptor)); viewImpl->m_allocator = m_rtvAllocator; D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {}; rtvDesc.Format = D3DUtil::getMapFormat(desc.format); switch (desc.renderTarget.shape) { case IResource::Type::Texture1D: rtvDesc.ViewDimension = isArray ? D3D12_RTV_DIMENSION_TEXTURE1DARRAY : D3D12_RTV_DIMENSION_TEXTURE1D; if (isArray) { rtvDesc.Texture1DArray.MipSlice = desc.subresourceRange.mipLevel; rtvDesc.Texture1DArray.FirstArraySlice = desc.subresourceRange.baseArrayLayer; rtvDesc.Texture1DArray.ArraySize = desc.subresourceRange.layerCount; } else { rtvDesc.Texture1D.MipSlice = desc.subresourceRange.mipLevel; } break; case IResource::Type::Texture2D: if (isMultiSample) { rtvDesc.ViewDimension = isArray ? D3D12_RTV_DIMENSION_TEXTURE2DMSARRAY : D3D12_RTV_DIMENSION_TEXTURE2DMS; rtvDesc.Texture2DMSArray.ArraySize = desc.subresourceRange.layerCount; rtvDesc.Texture2DMSArray.FirstArraySlice = desc.subresourceRange.baseArrayLayer; } else { rtvDesc.ViewDimension = isArray ? D3D12_RTV_DIMENSION_TEXTURE2DARRAY : D3D12_RTV_DIMENSION_TEXTURE2D; if (isArray) { rtvDesc.Texture2DArray.MipSlice = desc.subresourceRange.mipLevel; rtvDesc.Texture2DArray.ArraySize = desc.subresourceRange.layerCount; rtvDesc.Texture2DArray.FirstArraySlice = desc.subresourceRange.baseArrayLayer; rtvDesc.Texture2DArray.PlaneSlice = resourceImpl ? D3DUtil::getPlaneSlice( D3DUtil::getMapFormat(resourceImpl->getDesc()->format), desc.subresourceRange.aspectMask) : 0; } else { rtvDesc.Texture2D.MipSlice = desc.subresourceRange.mipLevel; rtvDesc.Texture2D.PlaneSlice = resourceImpl ? D3DUtil::getPlaneSlice( D3DUtil::getMapFormat(resourceImpl->getDesc()->format), desc.subresourceRange.aspectMask) : 0; } } break; case IResource::Type::TextureCube: rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY; rtvDesc.Texture2DArray.MipSlice = desc.subresourceRange.mipLevel; rtvDesc.Texture2DArray.ArraySize = desc.subresourceRange.layerCount; rtvDesc.Texture2DArray.FirstArraySlice = desc.subresourceRange.baseArrayLayer; rtvDesc.Texture2DArray.PlaneSlice = resourceImpl ? D3DUtil::getPlaneSlice( D3DUtil::getMapFormat(resourceImpl->getDesc()->format), desc.subresourceRange.aspectMask) : 0; break; case IResource::Type::Texture3D: rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE3D; rtvDesc.Texture3D.MipSlice = desc.subresourceRange.mipLevel; rtvDesc.Texture3D.FirstWSlice = desc.subresourceRange.baseArrayLayer; rtvDesc.Texture3D.WSize = (desc.subresourceRange.layerCount == 0) ? -1 : desc.subresourceRange.layerCount; break; case IResource::Type::Buffer: rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_BUFFER; break; default: return SLANG_FAIL; } m_device->CreateRenderTargetView( resourceImpl ? resourceImpl->m_resource.getResource() : nullptr, &rtvDesc, viewImpl->m_descriptor.cpuHandle); } break; case IResourceView::Type::DepthStencil: { SLANG_RETURN_ON_FAIL(m_dsvAllocator->allocate(&viewImpl->m_descriptor)); viewImpl->m_allocator = m_dsvAllocator; D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc = {}; dsvDesc.Format = D3DUtil::getMapFormat(desc.format); switch (desc.renderTarget.shape) { case IResource::Type::Texture1D: dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE1D; dsvDesc.Texture1D.MipSlice = desc.subresourceRange.mipLevel; break; case IResource::Type::Texture2D: if (isMultiSample) { dsvDesc.ViewDimension = isArray ? D3D12_DSV_DIMENSION_TEXTURE2DMSARRAY : D3D12_DSV_DIMENSION_TEXTURE2DMS; dsvDesc.Texture2DMSArray.ArraySize = desc.subresourceRange.layerCount; dsvDesc.Texture2DMSArray.FirstArraySlice = desc.subresourceRange.baseArrayLayer; } else { dsvDesc.ViewDimension = isArray ? D3D12_DSV_DIMENSION_TEXTURE2DARRAY : D3D12_DSV_DIMENSION_TEXTURE2D; dsvDesc.Texture2DArray.MipSlice = desc.subresourceRange.mipLevel; dsvDesc.Texture2DArray.ArraySize = desc.subresourceRange.layerCount; dsvDesc.Texture2DArray.FirstArraySlice = desc.subresourceRange.baseArrayLayer; } break; case IResource::Type::TextureCube: dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DARRAY; dsvDesc.Texture2DArray.MipSlice = desc.subresourceRange.mipLevel; dsvDesc.Texture2DArray.ArraySize = desc.subresourceRange.layerCount; dsvDesc.Texture2DArray.FirstArraySlice = desc.subresourceRange.baseArrayLayer; break; default: return SLANG_FAIL; } m_device->CreateDepthStencilView( resourceImpl ? resourceImpl->m_resource.getResource() : nullptr, &dsvDesc, viewImpl->m_descriptor.cpuHandle); } break; case IResourceView::Type::UnorderedAccess: { // TODO: need to support the separate "counter resource" for the case // of append/consume buffers with attached counters. SLANG_RETURN_ON_FAIL(m_cpuViewHeap->allocate(&viewImpl->m_descriptor)); viewImpl->m_allocator = m_cpuViewHeap; D3D12_UNORDERED_ACCESS_VIEW_DESC d3d12desc = {}; auto& resourceDesc = *resourceImpl->getDesc(); d3d12desc.Format = gfxIsTypelessFormat(texture->getDesc()->format) ? D3DUtil::getMapFormat(desc.format) : D3DUtil::getMapFormat(texture->getDesc()->format); switch (resourceImpl->getDesc()->type) { case IResource::Type::Texture1D: d3d12desc.ViewDimension = isArray ? D3D12_UAV_DIMENSION_TEXTURE1DARRAY : D3D12_UAV_DIMENSION_TEXTURE1D; if (isArray) { d3d12desc.Texture1DArray.MipSlice = desc.subresourceRange.mipLevel; d3d12desc.Texture1DArray.ArraySize = desc.subresourceRange.layerCount == 0 ? resourceDesc.arraySize : desc.subresourceRange.layerCount; d3d12desc.Texture1DArray.FirstArraySlice = desc.subresourceRange.baseArrayLayer; } else { d3d12desc.Texture1D.MipSlice = desc.subresourceRange.mipLevel; } break; case IResource::Type::Texture2D: d3d12desc.ViewDimension = isArray ? D3D12_UAV_DIMENSION_TEXTURE2DARRAY : D3D12_UAV_DIMENSION_TEXTURE2D; if (isArray) { d3d12desc.Texture2DArray.MipSlice = desc.subresourceRange.mipLevel; d3d12desc.Texture2DArray.ArraySize = desc.subresourceRange.layerCount == 0 ? resourceDesc.arraySize : desc.subresourceRange.layerCount; d3d12desc.Texture2DArray.FirstArraySlice = desc.subresourceRange.baseArrayLayer; d3d12desc.Texture2DArray.PlaneSlice = D3DUtil::getPlaneSlice(d3d12desc.Format, desc.subresourceRange.aspectMask); } else { d3d12desc.Texture2D.MipSlice = desc.subresourceRange.mipLevel; d3d12desc.Texture2D.PlaneSlice = D3DUtil::getPlaneSlice(d3d12desc.Format, desc.subresourceRange.aspectMask); } break; case IResource::Type::TextureCube: d3d12desc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2DARRAY; d3d12desc.Texture2DArray.MipSlice = desc.subresourceRange.mipLevel; d3d12desc.Texture2DArray.ArraySize = desc.subresourceRange.layerCount == 0 ? resourceDesc.arraySize : desc.subresourceRange.layerCount; d3d12desc.Texture2DArray.FirstArraySlice = desc.subresourceRange.baseArrayLayer; d3d12desc.Texture2DArray.PlaneSlice = D3DUtil::getPlaneSlice(d3d12desc.Format, desc.subresourceRange.aspectMask); break; case IResource::Type::Texture3D: d3d12desc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE3D; d3d12desc.Texture3D.MipSlice = desc.subresourceRange.mipLevel; d3d12desc.Texture3D.FirstWSlice = desc.subresourceRange.baseArrayLayer; d3d12desc.Texture3D.WSize = resourceDesc.size.depth >> desc.subresourceRange.mipLevel; break; default: return SLANG_FAIL; } m_device->CreateUnorderedAccessView( resourceImpl->m_resource, nullptr, &d3d12desc, viewImpl->m_descriptor.cpuHandle); } break; case IResourceView::Type::ShaderResource: { SLANG_RETURN_ON_FAIL(m_cpuViewHeap->allocate(&viewImpl->m_descriptor)); viewImpl->m_allocator = m_cpuViewHeap; // Need to construct the D3D12_SHADER_RESOURCE_VIEW_DESC because otherwise TextureCube // is not accessed appropriately (rather than just passing nullptr to // CreateShaderResourceView) const D3D12_RESOURCE_DESC resourceDesc = resourceImpl->m_resource.getResource()->GetDesc(); const DXGI_FORMAT pixelFormat = desc.format == Format::Unknown ? resourceDesc.Format : D3DUtil::getMapFormat(desc.format); D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc; initSrvDesc( resourceImpl->getType(), *resourceImpl->getDesc(), resourceDesc, pixelFormat, desc.subresourceRange, srvDesc); m_device->CreateShaderResourceView( resourceImpl->m_resource, &srvDesc, viewImpl->m_descriptor.cpuHandle); } break; } returnComPtr(outView, viewImpl); return SLANG_OK; } Result DeviceImpl::getFormatSupportedResourceStates(Format format, ResourceStateSet* outStates) { D3D12_FEATURE_DATA_FORMAT_SUPPORT support; support.Format = D3DUtil::getMapFormat(format); SLANG_RETURN_ON_FAIL( m_device->CheckFeatureSupport(D3D12_FEATURE_FORMAT_SUPPORT, &support, sizeof(support))); ResourceStateSet allowedStates; auto dxgi1 = support.Support1; if (dxgi1 & D3D12_FORMAT_SUPPORT1_BUFFER) allowedStates.add(ResourceState::ConstantBuffer); if (dxgi1 & D3D12_FORMAT_SUPPORT1_IA_VERTEX_BUFFER) allowedStates.add(ResourceState::VertexBuffer); if (dxgi1 & D3D12_FORMAT_SUPPORT1_IA_INDEX_BUFFER) allowedStates.add(ResourceState::IndexBuffer); if (dxgi1 & D3D12_FORMAT_SUPPORT1_SO_BUFFER) allowedStates.add(ResourceState::StreamOutput); if (dxgi1 & D3D12_FORMAT_SUPPORT1_TEXTURE1D) allowedStates.add(ResourceState::ShaderResource); if (dxgi1 & D3D12_FORMAT_SUPPORT1_TEXTURE2D) allowedStates.add(ResourceState::ShaderResource); if (dxgi1 & D3D12_FORMAT_SUPPORT1_TEXTURE3D) allowedStates.add(ResourceState::ShaderResource); if (dxgi1 & D3D12_FORMAT_SUPPORT1_TEXTURECUBE) allowedStates.add(ResourceState::ShaderResource); if (dxgi1 & D3D12_FORMAT_SUPPORT1_SHADER_LOAD) allowedStates.add(ResourceState::ShaderResource); if (dxgi1 & D3D12_FORMAT_SUPPORT1_SHADER_SAMPLE) allowedStates.add(ResourceState::ShaderResource); if (dxgi1 & D3D12_FORMAT_SUPPORT1_SHADER_SAMPLE_COMPARISON) allowedStates.add(ResourceState::ShaderResource); if (dxgi1 & D3D12_FORMAT_SUPPORT1_SHADER_GATHER) allowedStates.add(ResourceState::ShaderResource); if (dxgi1 & D3D12_FORMAT_SUPPORT1_SHADER_GATHER_COMPARISON) allowedStates.add(ResourceState::ShaderResource); if (dxgi1 & D3D12_FORMAT_SUPPORT1_RENDER_TARGET) allowedStates.add(ResourceState::RenderTarget); if (dxgi1 & D3D12_FORMAT_SUPPORT1_DEPTH_STENCIL) allowedStates.add(ResourceState::DepthWrite); if (dxgi1 & D3D12_FORMAT_SUPPORT1_TYPED_UNORDERED_ACCESS_VIEW) allowedStates.add(ResourceState::UnorderedAccess); *outStates = allowedStates; return SLANG_OK; } Result DeviceImpl::createBufferView( IBufferResource* buffer, IBufferResource* counterBuffer, IResourceView::Desc const& desc, IResourceView** outView) { auto resourceImpl = (BufferResourceImpl*)buffer; auto resourceDesc = *resourceImpl->getDesc(); const auto counterResourceImpl = static_cast(counterBuffer); RefPtr viewImpl = new ResourceViewImpl(); viewImpl->m_resource = resourceImpl; viewImpl->m_counterResource = counterResourceImpl; viewImpl->m_desc = desc; // Buffer view descriptors are created on demand. viewImpl->m_descriptor = {0}; viewImpl->m_allocator = m_cpuViewHeap.get(); returnComPtr(outView, viewImpl); return SLANG_OK; } Result DeviceImpl::createFramebuffer(IFramebuffer::Desc const& desc, IFramebuffer** outFb) { RefPtr framebuffer = new FramebufferImpl(); framebuffer->renderTargetViews.setCount(desc.renderTargetCount); framebuffer->renderTargetDescriptors.setCount(desc.renderTargetCount); framebuffer->renderTargetClearValues.setCount(desc.renderTargetCount); for (GfxIndex i = 0; i < desc.renderTargetCount; i++) { framebuffer->renderTargetViews[i] = static_cast(desc.renderTargetViews[i]); framebuffer->renderTargetDescriptors[i] = framebuffer->renderTargetViews[i]->m_descriptor.cpuHandle; if (static_cast(desc.renderTargetViews[i])->m_resource.Ptr()) { auto clearValue = static_cast( static_cast(desc.renderTargetViews[i])->m_resource.Ptr()) ->getDesc() ->optimalClearValue; if (clearValue) { memcpy( &framebuffer->renderTargetClearValues[i], &clearValue->color, sizeof(ColorClearValue)); } } else { memset(&framebuffer->renderTargetClearValues[i], 0, sizeof(ColorClearValue)); } } framebuffer->depthStencilView = static_cast(desc.depthStencilView); if (desc.depthStencilView) { auto clearValue = static_cast( static_cast(desc.depthStencilView)->m_resource.Ptr()) ->getDesc() ->optimalClearValue; if (clearValue) { framebuffer->depthStencilClearValue = clearValue->depthStencil; } framebuffer->depthStencilDescriptor = static_cast(desc.depthStencilView)->m_descriptor.cpuHandle; } else { framebuffer->depthStencilDescriptor.ptr = 0; } returnComPtr(outFb, framebuffer); return SLANG_OK; } Result DeviceImpl::createFramebufferLayout( IFramebufferLayout::Desc const& desc, IFramebufferLayout** outLayout) { RefPtr layout = new FramebufferLayoutImpl(); layout->m_renderTargets.setCount(desc.renderTargetCount); for (GfxIndex i = 0; i < desc.renderTargetCount; i++) { layout->m_renderTargets[i] = desc.renderTargets[i]; } if (desc.depthStencil) { layout->m_hasDepthStencil = true; layout->m_depthStencil = *desc.depthStencil; } else { layout->m_hasDepthStencil = false; } returnComPtr(outLayout, layout); return SLANG_OK; } Result DeviceImpl::createRenderPassLayout( const IRenderPassLayout::Desc& desc, IRenderPassLayout** outRenderPassLayout) { RefPtr result = new RenderPassLayoutImpl(); result->init(desc); returnComPtr(outRenderPassLayout, result); return SLANG_OK; } Result DeviceImpl::createInputLayout(IInputLayout::Desc const& desc, IInputLayout** outLayout) { RefPtr layout(new InputLayoutImpl); // Work out a buffer size to hold all text Size textSize = 0; auto inputElementCount = desc.inputElementCount; auto inputElements = desc.inputElements; auto vertexStreamCount = desc.vertexStreamCount; auto vertexStreams = desc.vertexStreams; for (int i = 0; i < Int(inputElementCount); ++i) { const char* text = inputElements[i].semanticName; textSize += text ? (::strlen(text) + 1) : 0; } layout->m_text.setCount(textSize); char* textPos = layout->m_text.getBuffer(); List& elements = layout->m_elements; SLANG_ASSERT(inputElementCount > 0); elements.setCount(inputElementCount); for (Int i = 0; i < inputElementCount; ++i) { const InputElementDesc& srcEle = inputElements[i]; const auto& srcStream = vertexStreams[srcEle.bufferSlotIndex]; D3D12_INPUT_ELEMENT_DESC& dstEle = elements[i]; // Add text to the buffer const char* semanticName = srcEle.semanticName; if (semanticName) { const int len = int(::strlen(semanticName)); ::memcpy(textPos, semanticName, len + 1); semanticName = textPos; textPos += len + 1; } dstEle.SemanticName = semanticName; dstEle.SemanticIndex = (UINT)srcEle.semanticIndex; dstEle.Format = D3DUtil::getMapFormat(srcEle.format); dstEle.InputSlot = (UINT)srcEle.bufferSlotIndex; dstEle.AlignedByteOffset = (UINT)srcEle.offset; dstEle.InputSlotClass = D3DUtil::getInputSlotClass(srcStream.slotClass); dstEle.InstanceDataStepRate = (UINT)srcStream.instanceDataStepRate; } auto& vertexStreamStrides = layout->m_vertexStreamStrides; vertexStreamStrides.setCount(vertexStreamCount); for (GfxIndex i = 0; i < vertexStreamCount; ++i) { vertexStreamStrides[i] = (UINT)vertexStreams[i].stride; } returnComPtr(outLayout, layout); return SLANG_OK; } const gfx::DeviceInfo& DeviceImpl::getDeviceInfo() const { return m_info; } Result DeviceImpl::readBufferResource( IBufferResource* bufferIn, Offset offset, Size size, ISlangBlob** outBlob) { BufferResourceImpl* buffer = static_cast(bufferIn); const Size bufferSize = buffer->getDesc()->sizeInBytes; // This will be slow!!! - it blocks CPU on GPU completion D3D12Resource& resource = buffer->m_resource; D3D12Resource stageBuf; if (buffer->getDesc()->memoryType != MemoryType::ReadBack) { auto encodeInfo = encodeResourceCommands(); // Readback heap D3D12_HEAP_PROPERTIES heapProps; heapProps.Type = D3D12_HEAP_TYPE_READBACK; heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; heapProps.CreationNodeMask = 1; heapProps.VisibleNodeMask = 1; // Resource to readback to D3D12_RESOURCE_DESC stagingDesc; initBufferResourceDesc(size, stagingDesc); SLANG_RETURN_ON_FAIL(stageBuf.initCommitted( m_device, heapProps, D3D12_HEAP_FLAG_NONE, stagingDesc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr)); // Do the copy encodeInfo.d3dCommandList->CopyBufferRegion(stageBuf, 0, resource, offset, size); // Wait until complete submitResourceCommandsAndWait(encodeInfo); } D3D12Resource& stageBufRef = buffer->getDesc()->memoryType != MemoryType::ReadBack ? stageBuf : resource; // Map and copy List blobData; { UINT8* data; D3D12_RANGE readRange = {0, size}; SLANG_RETURN_ON_FAIL( stageBufRef.getResource()->Map(0, &readRange, reinterpret_cast(&data))); // Copy to memory buffer blobData.setCount(size); ::memcpy(blobData.getBuffer(), data, size); stageBufRef.getResource()->Unmap(0, nullptr); } auto blob = ListBlob::moveCreate(blobData); returnComPtr(outBlob, blob); return SLANG_OK; } Result DeviceImpl::createProgram( const IShaderProgram::Desc& desc, IShaderProgram** outProgram, ISlangBlob** outDiagnosticBlob) { RefPtr shaderProgram = new ShaderProgramImpl(); shaderProgram->init(desc); ComPtr d3dDiagnosticBlob; auto rootShaderLayoutResult = RootShaderObjectLayoutImpl::create( this, shaderProgram->linkedProgram, shaderProgram->linkedProgram->getLayout(), shaderProgram->m_rootObjectLayout.writeRef(), d3dDiagnosticBlob.writeRef()); if (!SLANG_SUCCEEDED(rootShaderLayoutResult)) { if (outDiagnosticBlob && d3dDiagnosticBlob) { String diagnostic((const char*)d3dDiagnosticBlob->GetBufferPointer()); auto diagnosticBlob = StringBlob::create(diagnostic); returnComPtr(outDiagnosticBlob, diagnosticBlob); } return rootShaderLayoutResult; } if (!shaderProgram->isSpecializable()) { SLANG_RETURN_ON_FAIL(shaderProgram->compileShaders(this)); } returnComPtr(outProgram, shaderProgram); return SLANG_OK; } Result DeviceImpl::createShaderObjectLayout( slang::ISession* session, slang::TypeLayoutReflection* typeLayout, ShaderObjectLayoutBase** outLayout) { RefPtr layout; SLANG_RETURN_ON_FAIL( ShaderObjectLayoutImpl::createForElementType(this, session, typeLayout, layout.writeRef())); returnRefPtrMove(outLayout, layout); return SLANG_OK; } Result DeviceImpl::createShaderObject(ShaderObjectLayoutBase* layout, IShaderObject** outObject) { RefPtr shaderObject; SLANG_RETURN_ON_FAIL(ShaderObjectImpl::create( this, reinterpret_cast(layout), shaderObject.writeRef())); returnComPtr(outObject, shaderObject); return SLANG_OK; } Result DeviceImpl::createMutableShaderObject( ShaderObjectLayoutBase* layout, IShaderObject** outObject) { auto result = createShaderObject(layout, outObject); SLANG_RETURN_ON_FAIL(result); static_cast(*outObject)->m_isMutable = true; return result; } Result DeviceImpl::createMutableRootShaderObject(IShaderProgram* program, IShaderObject** outObject) { RefPtr result = new MutableRootShaderObjectImpl(); result->init(this); auto programImpl = static_cast(program); result->resetImpl( this, programImpl->m_rootObjectLayout, m_cpuViewHeap.Ptr(), m_cpuSamplerHeap.Ptr(), true); returnComPtr(outObject, result); return SLANG_OK; } Result DeviceImpl::createShaderTable(const IShaderTable::Desc& desc, IShaderTable** outShaderTable) { RefPtr result = new ShaderTableImpl(); result->m_device = this; result->init(desc); returnComPtr(outShaderTable, result); return SLANG_OK; } Result DeviceImpl::createGraphicsPipelineState( const GraphicsPipelineStateDesc& desc, IPipelineState** outState) { RefPtr pipelineStateImpl = new PipelineStateImpl(this); pipelineStateImpl->init(desc); returnComPtr(outState, pipelineStateImpl); return SLANG_OK; } Result DeviceImpl::createComputePipelineState( const ComputePipelineStateDesc& desc, IPipelineState** outState) { RefPtr pipelineStateImpl = new PipelineStateImpl(this); pipelineStateImpl->init(desc); returnComPtr(outState, pipelineStateImpl); return SLANG_OK; } DeviceImpl::ResourceCommandRecordInfo DeviceImpl::encodeResourceCommands() { ResourceCommandRecordInfo info; m_resourceCommandTransientHeap->createCommandBuffer(info.commandBuffer.writeRef()); info.d3dCommandList = static_cast(info.commandBuffer.get())->m_cmdList; return info; } void DeviceImpl::submitResourceCommandsAndWait(const DeviceImpl::ResourceCommandRecordInfo& info) { info.commandBuffer->close(); m_resourceCommandQueue->executeCommandBuffer(info.commandBuffer); m_resourceCommandTransientHeap->finish(); m_resourceCommandTransientHeap->synchronizeAndReset(); } void DeviceImpl::processExperimentalFeaturesDesc(SharedLibrary::Handle d3dModule, void* inDesc) { typedef HRESULT(WINAPI * PFN_D3D12_ENABLE_EXPERIMENTAL_FEATURES)( UINT NumFeatures, const IID* pIIDs, void* pConfigurationStructs, UINT* pConfigurationStructSizes); D3D12ExperimentalFeaturesDesc desc = {}; memcpy(&desc, inDesc, sizeof(desc)); auto enableExperimentalFeaturesFunc = (PFN_D3D12_ENABLE_EXPERIMENTAL_FEATURES)loadProc( d3dModule, "D3D12EnableExperimentalFeatures"); if (!enableExperimentalFeaturesFunc) { getDebugCallback()->handleMessage( gfx::DebugMessageType::Warning, gfx::DebugMessageSource::Layer, "cannot enable D3D12 experimental features, 'D3D12EnableExperimentalFeatures' function " "not found."); return; } if (!SLANG_SUCCEEDED(enableExperimentalFeaturesFunc( desc.numFeatures, (IID*)desc.featureIIDs, desc.configurationStructs, desc.configurationStructSizes))) { getDebugCallback()->handleMessage( gfx::DebugMessageType::Warning, gfx::DebugMessageSource::Layer, "cannot enable D3D12 experimental features, 'D3D12EnableExperimentalFeatures' call " "failed."); return; } } Result DeviceImpl::createQueryPool(const IQueryPool::Desc& desc, IQueryPool** outState) { switch (desc.type) { case QueryType::AccelerationStructureCompactedSize: case QueryType::AccelerationStructureSerializedSize: case QueryType::AccelerationStructureCurrentSize: { RefPtr queryPoolImpl = new PlainBufferProxyQueryPoolImpl(); uint32_t stride = 8; if (desc.type == QueryType::AccelerationStructureSerializedSize) stride = 16; SLANG_RETURN_ON_FAIL(queryPoolImpl->init(desc, this, stride)); returnComPtr(outState, queryPoolImpl); return SLANG_OK; } default: { RefPtr queryPoolImpl = new QueryPoolImpl(); SLANG_RETURN_ON_FAIL(queryPoolImpl->init(desc, this)); returnComPtr(outState, queryPoolImpl); return SLANG_OK; } } } Result DeviceImpl::createFence(const IFence::Desc& desc, IFence** outFence) { RefPtr fence = new FenceImpl(); SLANG_RETURN_ON_FAIL(fence->init(this, desc)); returnComPtr(outFence, fence); return SLANG_OK; } Result DeviceImpl::waitForFences( GfxCount fenceCount, IFence** fences, uint64_t* fenceValues, bool waitForAll, uint64_t timeout) { ShortList waitHandles; for (GfxCount i = 0; i < fenceCount; ++i) { auto fenceImpl = static_cast(fences[i]); waitHandles.add(fenceImpl->getWaitEvent()); SLANG_RETURN_ON_FAIL( fenceImpl->m_fence->SetEventOnCompletion(fenceValues[i], fenceImpl->getWaitEvent())); } auto result = WaitForMultipleObjects( fenceCount, waitHandles.getArrayView().getBuffer(), waitForAll ? TRUE : FALSE, timeout == kTimeoutInfinite ? INFINITE : (DWORD)(timeout / 1000000)); if (result == WAIT_TIMEOUT) return SLANG_E_TIME_OUT; return result == WAIT_FAILED ? SLANG_FAIL : SLANG_OK; } Result DeviceImpl::getAccelerationStructurePrebuildInfo( const IAccelerationStructure::BuildInputs& buildInputs, IAccelerationStructure::PrebuildInfo* outPrebuildInfo) { if (!m_device5) return SLANG_E_NOT_AVAILABLE; D3DAccelerationStructureInputsBuilder inputsBuilder; SLANG_RETURN_ON_FAIL(inputsBuilder.build(buildInputs, getDebugCallback())); D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO prebuildInfo; m_device5->GetRaytracingAccelerationStructurePrebuildInfo(&inputsBuilder.desc, &prebuildInfo); outPrebuildInfo->resultDataMaxSize = (Size)prebuildInfo.ResultDataMaxSizeInBytes; outPrebuildInfo->scratchDataSize = (Size)prebuildInfo.ScratchDataSizeInBytes; outPrebuildInfo->updateScratchDataSize = (Size)prebuildInfo.UpdateScratchDataSizeInBytes; return SLANG_OK; } Result DeviceImpl::createAccelerationStructure( const IAccelerationStructure::CreateDesc& desc, IAccelerationStructure** outAS) { #if SLANG_GFX_HAS_DXR_SUPPORT assert(desc.buffer != nullptr); RefPtr result = new AccelerationStructureImpl(); result->m_device5 = m_device5; result->m_buffer = static_cast(desc.buffer); result->m_size = desc.size; result->m_offset = desc.offset; result->m_allocator = m_cpuViewHeap; result->m_desc.type = IResourceView::Type::AccelerationStructure; SLANG_RETURN_ON_FAIL(m_cpuViewHeap->allocate(&result->m_descriptor)); D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc; srvDesc.Format = DXGI_FORMAT_UNKNOWN; srvDesc.ViewDimension = D3D12_SRV_DIMENSION_RAYTRACING_ACCELERATION_STRUCTURE; srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; srvDesc.RaytracingAccelerationStructure.Location = result->m_buffer->getDeviceAddress() + desc.offset; m_device->CreateShaderResourceView(nullptr, &srvDesc, result->m_descriptor.cpuHandle); returnComPtr(outAS, result); return SLANG_OK; #else *outAS = nullptr; return SLANG_FAIL; #endif } Result DeviceImpl::createRayTracingPipelineState( const RayTracingPipelineStateDesc& inDesc, IPipelineState** outState) { if (!m_device5) { return SLANG_E_NOT_AVAILABLE; } RefPtr pipelineStateImpl = new RayTracingPipelineStateImpl(this); pipelineStateImpl->init(inDesc); returnComPtr(outState, pipelineStateImpl); return SLANG_OK; } Result DeviceImpl::createTransientResourceHeapImpl( ITransientResourceHeap::Flags::Enum flags, Size constantBufferSize, uint32_t viewDescriptors, uint32_t samplerDescriptors, TransientResourceHeapImpl** outHeap) { RefPtr result = new TransientResourceHeapImpl(); ITransientResourceHeap::Desc desc = {}; desc.flags = flags; desc.samplerDescriptorCount = samplerDescriptors; desc.constantBufferSize = constantBufferSize; desc.constantBufferDescriptorCount = viewDescriptors; desc.accelerationStructureDescriptorCount = viewDescriptors; desc.srvDescriptorCount = viewDescriptors; desc.uavDescriptorCount = viewDescriptors; SLANG_RETURN_ON_FAIL(result->init(desc, this, viewDescriptors, samplerDescriptors)); returnRefPtrMove(outHeap, result); return SLANG_OK; } Result DeviceImpl::createCommandQueueImpl(CommandQueueImpl** outQueue) { int queueIndex = m_queueIndexAllocator.alloc(1); // If we run out of queue index space, then the user is requesting too many queues. if (queueIndex == -1) return SLANG_FAIL; RefPtr queue = new CommandQueueImpl(); SLANG_RETURN_ON_FAIL(queue->init(this, (uint32_t)queueIndex)); returnRefPtrMove(outQueue, queue); return SLANG_OK; } void* DeviceImpl::loadProc(SharedLibrary::Handle module, char const* name) { void* proc = SharedLibrary::findSymbolAddressByName(module, name); if (!proc) { fprintf(stderr, "error: failed load symbol '%s'\n", name); return nullptr; } return proc; } DeviceImpl::~DeviceImpl() { m_shaderObjectLayoutCache = decltype(m_shaderObjectLayoutCache)(); } } // namespace d3d12 } // namespace gfx