diff --git a/src/d3d12/d3d12_buffer.cpp b/src/d3d12/d3d12_buffer.cpp index 31aa5d69e..3359b2c5c 100644 --- a/src/d3d12/d3d12_buffer.cpp +++ b/src/d3d12/d3d12_buffer.cpp @@ -19,6 +19,7 @@ #include "d3d12_device.hpp" #include "d3d12_pageable.hpp" #include "com/com_pointer.hpp" +#include "dxmt_format.hpp" namespace dxmt { @@ -53,7 +54,8 @@ class MTLD3D12Buffer : public MTLD3D12Pageable { }; ~MTLD3D12Buffer() { - device_->UnregisterResidencyAndVA(buffer->current()); + if (buffer) + device_->UnregisterResidencyAndVA(buffer->current()); } HRESULT @@ -138,8 +140,40 @@ class MTLD3D12Buffer : public MTLD3D12Pageable { CreateUnorderedAccessView( ID3D12Resource *pCounter, const D3D12_UNORDERED_ACCESS_VIEW_DESC *pDesc, D3D12_CPU_DESCRIPTOR_HANDLE Descriptor ) { - IMPLEMENT_ME - return S_OK; + HRESULT hr; + D3D12_UNORDERED_ACCESS_VIEW_DESC ViewDesc; + if (!pDesc) { + hr = ExtractEntireResourceViewDescription(desc_, &ViewDesc); + if (FAILED(hr)) + return hr; + } else { + ViewDesc = *pDesc; + } + + if (ViewDesc.ViewDimension != D3D12_UAV_DIMENSION_BUFFER) + return E_INVALIDARG; + + auto [Heap, Index] = GetShaderVisibleDescriptorHeap(device_, Descriptor); + BufferSlice Slice; + + if (ViewDesc.Format == DXGI_FORMAT_UNKNOWN || ViewDesc.Buffer.Flags & D3D12_BUFFER_UAV_FLAG_RAW) { + // TODO(d3d12): structured/raw buffer view + IMPLEMENT_ME + } + + MTL_DXGI_FORMAT_DESC Format; + if (FAILED(MTLQueryDXGIFormat(device_->GetMTLDevice(), ViewDesc.Format, Format))) { + ERR("D3D12Buffer::CreateUnorderedAccessView: not an ordinary or packed format: ", ViewDesc.Format); + return E_FAIL; + } + BufferViewDescriptor view_descriptor{Format.PixelFormat}; + Slice.firstElement = ViewDesc.Buffer.FirstElement; + Slice.elementCount = ViewDesc.Buffer.NumElements; + Slice.byteOffset = Format.BytesPerTexel * ViewDesc.Buffer.FirstElement; + Slice.byteLength = Format.BytesPerTexel * ViewDesc.Buffer.NumElements; + + auto view = buffer->createView(view_descriptor); + return Heap->AddUnorderedAccessView(Index, buffer.ptr(), view, Slice); }; virtual HRESULT STDMETHODCALLTYPE @@ -168,7 +202,6 @@ CreateCommittedBuffer( const D3D12_RESOURCE_DESC *pDesc, D3D12_RESOURCE_STATES InitialState, const D3D12_CLEAR_VALUE *OptimizedClearValue, REFIID riid, void **ppResource ) { - InitReturnPtr(ppResource); auto buffer = Com(new MTLD3D12Buffer(pDevice)); HRESULT hr = buffer->Initialize(pHeapProps, HeapFlags, pDesc, OptimizedClearValue, nullptr); if (FAILED(hr)) @@ -183,7 +216,6 @@ CreatePlacedBuffer( MTLD3D12Device *pDevice, MTLD3D12Heap *pHeap, const D3D12_RESOURCE_DESC *pDesc, D3D12_RESOURCE_STATES InitialState, const D3D12_CLEAR_VALUE *OptimizedClearValue, REFIID riid, void **ppResource ) { - InitReturnPtr(ppResource); auto buffer = Com(new MTLD3D12Buffer(pDevice)); D3D12_HEAP_DESC heap_desc = pHeap->GetDesc(); HRESULT hr = buffer->Initialize(&heap_desc.Properties, heap_desc.Flags, pDesc, OptimizedClearValue, pHeap); diff --git a/src/d3d12/d3d12_command_allocator.cpp b/src/d3d12/d3d12_command_allocator.cpp index cf65957c0..a78b9f817 100644 --- a/src/d3d12/d3d12_command_allocator.cpp +++ b/src/d3d12/d3d12_command_allocator.cpp @@ -118,6 +118,9 @@ MTLD3D12CommandAllocatorImpl::Reset() { case EncoderType::Blit: reinterpret_cast(next)->~BlitEncoderData(); break; + case EncoderType::Compute: + reinterpret_cast(next)->~ComputeEncoderData(); + break; } next = next->next; } diff --git a/src/d3d12/d3d12_command_allocator.hpp b/src/d3d12/d3d12_command_allocator.hpp index 798cbb803..0701562a2 100644 --- a/src/d3d12/d3d12_command_allocator.hpp +++ b/src/d3d12/d3d12_command_allocator.hpp @@ -159,6 +159,18 @@ class MTLD3D12CommandAllocatorImpl : public MTLD3D12Pageable + cmd_struct & + EncodeComputeCommand() { + assert(encoder_current->type == EncoderType::Compute); + auto encoder = static_cast(encoder_current); + auto storage = (cmd_struct *)AllocateCPUHeap(sizeof(cmd_struct), 16); + encoder->cmd_tail->next.set(storage); + encoder->cmd_tail = (wmtcmd_base *)storage; + storage->next.set(nullptr); + return *storage; + } + std::tuple AllocateGPUHeap(size_t Length, size_t Alignment) { if (!Length) diff --git a/src/d3d12/d3d12_command_encoder.hpp b/src/d3d12/d3d12_command_encoder.hpp index 3a1912854..79507d1ad 100644 --- a/src/d3d12/d3d12_command_encoder.hpp +++ b/src/d3d12/d3d12_command_encoder.hpp @@ -28,6 +28,7 @@ enum class EncoderType { Clear, Render, Blit, + Compute, }; struct EncoderData { @@ -107,4 +108,9 @@ struct BlitEncoderData : EncoderData { wmtcmd_base *cmd_tail; }; +struct ComputeEncoderData : EncoderData { + wmtcmd_compute_nop cmd_head; + wmtcmd_base *cmd_tail; +}; + }; // namespace dxmt diff --git a/src/d3d12/d3d12_command_list.cpp b/src/d3d12/d3d12_command_list.cpp index f576077b3..0367a56fa 100644 --- a/src/d3d12/d3d12_command_list.cpp +++ b/src/d3d12/d3d12_command_list.cpp @@ -28,6 +28,8 @@ enum class DirtyState { GraphicsRootSignature, Viewport, ScissorRect, + ComputeRootArguments, + ComputeRootSignature, }; enum class DrawCallStatus { @@ -139,6 +141,10 @@ class MTLD3D12GraphicsCommandListImpl : public MTLD3D12DeviceChild rootsig_graphics_; uint64_t rootarg_graphics_staging_[64]; + Com pso_compute_; + Com rootsig_compute_; + uint64_t rootarg_compute_staging_[64]; + public: MTLD3D12GraphicsCommandListImpl(MTLD3D12Device *pDevice) : MTLD3D12DeviceChild(pDevice) {} @@ -152,9 +158,12 @@ class MTLD3D12GraphicsCommandListImpl : public MTLD3D12DeviceChild(pInitialPipelineState)) { if (!pso->IsComputePipelineState) pso_graphics_ = static_cast(pInitialPipelineState); + else + pso_compute_ = static_cast(pInitialPipelineState); } num_rtvs = {}; @@ -172,6 +181,9 @@ class MTLD3D12GraphicsCommandListImpl : public MTLD3D12DeviceChildencoder_current || allocator_->encoder_current->type != EncoderType::Compute) { + allocator_->InvalidateCurrentPass(); + auto compute = allocator_->AllocatePass(); + compute->type = EncoderType::Compute; + compute->cmd_head.type = WMTComputeCommandNop; + compute->cmd_head.next.set(0); + compute->cmd_tail = (wmtcmd_base *)&compute->cmd_head; + dirty_state_.set(DirtyState::ComputeRootArguments, DirtyState::ComputeRootSignature); + if (pso_compute_) { + auto &cmd_setpso = allocator_->EncodeComputeCommand(); + cmd_setpso.type = WMTComputeCommandSetPSO; + cmd_setpso.pso = pso_compute_->pso; + cmd_setpso.threadgroup_size = pso_compute_->threadgroup_size; + } + } + + if (dirty_state_.test(DirtyState::ComputeRootArguments)) { + if (rootsig_compute_) { + auto [Ptr, Offset] = allocator_->AllocateGPUHeap(sizeof(uint64_t) * rootsig_compute_->UploadQwords, 64); + memcpy(Ptr, rootarg_compute_staging_, rootsig_compute_->UploadQwords * sizeof(uint64_t)); + auto &cmd_argbuf = allocator_->EncodeComputeCommand(); + cmd_argbuf.type = WMTComputeCommandSetBuffer; + cmd_argbuf.buffer = allocator_->gpu_heap_buffer_; + cmd_argbuf.offset = Offset; + cmd_argbuf.index = SM50_BINDING_INDEX_ROOT_ARGUMENTS; + } + dirty_state_.clr(DirtyState::ComputeRootArguments); + } + + if (dirty_state_.test(DirtyState::ComputeRootSignature)) { + if (rootsig_compute_) { + auto static_sampler_encode_size = sizeof(uint64_t) * rootsig_compute_->NumStaticSamplers * 4; + auto [Ptr, Offset] = allocator_->AllocateGPUHeap(static_sampler_encode_size, 64); + memcpy(Ptr, rootsig_compute_->EncodedStaticSamplers, static_sampler_encode_size); + auto &cmd_argbuf = allocator_->EncodeComputeCommand(); + cmd_argbuf.type = WMTComputeCommandSetBuffer; + cmd_argbuf.buffer = allocator_->gpu_heap_buffer_; + cmd_argbuf.offset = Offset; + cmd_argbuf.index = SM50_BINDING_INDEX_STATIC_SAMPLERS; + } + dirty_state_.clr(DirtyState::ComputeRootSignature); + } + + return true; + } + + void STDMETHODCALLTYPE + Dispatch(UINT X, UINT Y, UINT Z) { + if (!PreDispatch()) + return; + + auto &cmd_dispatch = allocator_->EncodeComputeCommand(); + cmd_dispatch.type = WMTComputeCommandDispatch; + cmd_dispatch.size = {X, Y, Z}; + }; bool PreBlit() { @@ -516,6 +584,8 @@ class MTLD3D12GraphicsCommandListImpl : public MTLD3D12DeviceChildpixelFormat()); + auto dst_subresource_index = pDst->SubresourceIndex / dst_planar_count; + auto dst_subresource_planar = pDst->SubresourceIndex % dst_planar_count; if (pSrc->Type == D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT) { auto &src = static_cast(pSrc->pResource)->buffer; @@ -525,7 +595,7 @@ class MTLD3D12GraphicsCommandListImpl : public MTLD3D12DeviceChildEncodeBlitCommand(); - cmd_cp.type = WMTBlitCommandCopyFromBufferToTexture; + cmd_cp.type = WMTBlitCommandCopyFromBufferToTextureWithBlitOption; cmd_cp.src = src->current()->buffer(); cmd_cp.src_offset = pSrc->PlacedFootprint.Offset; cmd_cp.bytes_per_row = pSrc->PlacedFootprint.Footprint.RowPitch; @@ -535,11 +605,11 @@ class MTLD3D12GraphicsCommandListImpl : public MTLD3D12DeviceChildPlacedFootprint.Footprint.Depth }; cmd_cp.dst = dst->current()->texture(); - cmd_cp.level = pDst->SubresourceIndex % dst->miplevelCount(); - cmd_cp.slice = pDst->SubresourceIndex / dst->miplevelCount(); - cmd_cp.options = dst_planar_count ? (pSrc->SubresourceIndex ? WMTBlitOptionStencilFromDepthStencil - : WMTBlitOptionDepthFromDepthStencil) - : WMTBlitOptionNone; + cmd_cp.level = dst_subresource_index % dst->miplevelCount(); + cmd_cp.slice = dst_subresource_index / dst->miplevelCount(); + cmd_cp.options = (dst_planar_count > 1) ? (dst_subresource_planar ? WMTBlitOptionStencilFromDepthStencil + : WMTBlitOptionDepthFromDepthStencil) + : WMTBlitOptionNone; cmd_cp.origin = {DstX, DstY, DstZ}; } else { auto &src = static_cast(pSrc->pResource)->texture; @@ -673,12 +743,31 @@ class MTLD3D12GraphicsCommandListImpl : public MTLD3D12DeviceChild(pPSO); + if (pso->IsComputePipelineState) { + auto compute_pso = static_cast(pPSO); + if (pso_compute_.ptr() == compute_pso) + return; + pso_compute_ = compute_pso; + pso_graphics_ = nullptr; + if (!allocator_->encoder_current || allocator_->encoder_current->type != EncoderType::Compute) + return; + auto &cmd_setpso = allocator_->EncodeComputeCommand(); + cmd_setpso.type = WMTComputeCommandSetPSO; + cmd_setpso.pso = pso_compute_->pso; + cmd_setpso.threadgroup_size = pso_compute_->threadgroup_size; return; } + auto graphics_pso = static_cast(pPSO); if (pso_graphics_.ptr() == graphics_pso) return; pso_graphics_ = graphics_pso; + pso_compute_ = nullptr; if (!allocator_->encoder_current || allocator_->encoder_current->type != EncoderType::Render) return; @@ -697,7 +786,18 @@ class MTLD3D12GraphicsCommandListImpl : public MTLD3D12DeviceChild(pRootSignature); + assert(rootsig_compute_->UploadQwords < std::size(rootarg_compute_staging_)); + } else { + rootsig_compute_ = nullptr; + } + dirty_state_.set(DirtyState::ComputeRootArguments, DirtyState::ComputeRootSignature); + }; void STDMETHODCALLTYPE SetGraphicsRootSignature(ID3D12RootSignature *pRootSignature) { @@ -713,7 +813,12 @@ class MTLD3D12GraphicsCommandListImpl : public MTLD3D12DeviceChild rootsig_compute_->ParameterSlots) + return; + rootarg_compute_staging_[rootsig_compute_->SlotQwordOffsets[Index]] = BaseDescriptor.ptr; + dirty_state_.set(DirtyState::ComputeRootArguments); }; void STDMETHODCALLTYPE @@ -726,7 +831,15 @@ class MTLD3D12GraphicsCommandListImpl : public MTLD3D12DeviceChild rootsig_compute_->ParameterSlots) + return; + auto dst = reinterpret_cast(rootarg_compute_staging_ + rootsig_compute_->SlotQwordOffsets[Index]); + dst[DstOffset] = Data; + dirty_state_.set(DirtyState::ComputeRootArguments); + }; void STDMETHODCALLTYPE SetGraphicsRoot32BitConstant(UINT Index, UINT Data, UINT DstOffset) { @@ -741,7 +854,16 @@ class MTLD3D12GraphicsCommandListImpl : public MTLD3D12DeviceChild rootsig_compute_->ParameterSlots) + return; + auto src = reinterpret_cast(pData); + auto dst = reinterpret_cast(rootarg_compute_staging_ + rootsig_compute_->SlotQwordOffsets[Index]); + for (unsigned i = 0; i < ConstantCount; i++) { + dst[i + DstOffset] = src[i]; + } + dirty_state_.set(DirtyState::ComputeRootArguments); }; void STDMETHODCALLTYPE @@ -758,7 +880,14 @@ class MTLD3D12GraphicsCommandListImpl : public MTLD3D12DeviceChild rootsig_compute_->ParameterSlots) + return; + rootarg_compute_staging_[rootsig_compute_->SlotQwordOffsets[Index]] = VA; + dirty_state_.set(DirtyState::ComputeRootArguments); + }; void STDMETHODCALLTYPE SetGraphicsRootConstantBufferView(UINT Index, D3D12_GPU_VIRTUAL_ADDRESS VA) { @@ -770,7 +899,14 @@ class MTLD3D12GraphicsCommandListImpl : public MTLD3D12DeviceChild rootsig_compute_->ParameterSlots) + return; + rootarg_compute_staging_[rootsig_compute_->SlotQwordOffsets[Index]] = VA; + dirty_state_.set(DirtyState::ComputeRootArguments); + }; void STDMETHODCALLTYPE SetGraphicsRootShaderResourceView(UINT Index, D3D12_GPU_VIRTUAL_ADDRESS VA) { @@ -782,7 +918,14 @@ class MTLD3D12GraphicsCommandListImpl : public MTLD3D12DeviceChild rootsig_compute_->ParameterSlots) + return; + rootarg_compute_staging_[rootsig_compute_->SlotQwordOffsets[Index]] = VA; + dirty_state_.set(DirtyState::ComputeRootArguments); + }; void STDMETHODCALLTYPE SetGraphicsRootUnorderedAccessView(UINT Index, D3D12_GPU_VIRTUAL_ADDRESS VA) { diff --git a/src/d3d12/d3d12_command_queue.cpp b/src/d3d12/d3d12_command_queue.cpp index 7df6b107b..ead7a31b5 100644 --- a/src/d3d12/d3d12_command_queue.cpp +++ b/src/d3d12/d3d12_command_queue.cpp @@ -210,6 +210,15 @@ class MTLD3D12CommandQueueImpl : public MTLD3D12Pageable(current); + auto encoder = cmdbuf.computeCommandEncoder(false); + encoder.waitForFence(fence_); + encoder.encodeCommands(&data->cmd_head); + encoder.updateFence(fence_); + encoder.endEncoding(); + break; + } } current = current->next; } diff --git a/src/d3d12/d3d12_command_signature.cpp b/src/d3d12/d3d12_command_signature.cpp new file mode 100644 index 000000000..1b2c094c9 --- /dev/null +++ b/src/d3d12/d3d12_command_signature.cpp @@ -0,0 +1,71 @@ +/* + * Copyright 2026 Feifan He for CodeWeavers + * + * 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 + */ + +#include "com/com_pointer.hpp" +#include "d3d12_device.hpp" +#include "d3d12_pageable.hpp" + +namespace dxmt { + +class MTLD3D12CommandSignatureImpl : public MTLD3D12Pageable { + +public: + MTLD3D12CommandSignatureImpl(MTLD3D12Device *pDevice) : MTLD3D12Pageable(pDevice) {} + + HRESULT + Initialize(const D3D12_COMMAND_SIGNATURE_DESC *pDesc, ID3D12RootSignature *pRootSignature) { + return S_OK; + }; + + ~MTLD3D12CommandSignatureImpl() {} + + HRESULT + STDMETHODCALLTYPE + QueryInterface(REFIID riid, void **ppvObject) { + if (ppvObject == nullptr) + return E_POINTER; + + *ppvObject = nullptr; + + if (riid == __uuidof(IUnknown) || riid == __uuidof(ID3D12Object) || riid == __uuidof(ID3D12DeviceChild) || + riid == __uuidof(ID3D12Pageable) || riid == __uuidof(ID3D12CommandSignature)) { + *ppvObject = ref(this); + return S_OK; + } + + if (logQueryInterfaceError(__uuidof(ID3D12Resource), riid)) { + WARN("D3D12CommandSignature: Unknown interface query ", str::format(riid)); + } + + return E_NOINTERFACE; + } +}; + +HRESULT +CreateCommandSignature( + MTLD3D12Device *pDevice, const D3D12_COMMAND_SIGNATURE_DESC *pDesc, ID3D12RootSignature *pRootSignature, + REFIID riid, void **ppCommandSignature +) { + auto sig = Com(new MTLD3D12CommandSignatureImpl(pDevice)); + HRESULT hr = sig->Initialize(pDesc, pRootSignature); + if (FAILED(hr)) + return hr; + return sig->QueryInterface(riid, ppCommandSignature); +} + +} // namespace dxmt \ No newline at end of file diff --git a/src/d3d12/d3d12_descriptor_heap.cpp b/src/d3d12/d3d12_descriptor_heap.cpp index 98574cb5c..5f175a7dc 100644 --- a/src/d3d12/d3d12_descriptor_heap.cpp +++ b/src/d3d12/d3d12_descriptor_heap.cpp @@ -20,6 +20,7 @@ #include "d3d12_descriptor_heap.hpp" #include "d3d12_pageable.hpp" #include "com/com_pointer.hpp" +#include "dxmt_sampler.hpp" #include "log/log.hpp" namespace dxmt { @@ -30,10 +31,20 @@ struct SRVTextureGPUStorage { uint64_t padding[2]; }; +using UAVTextureGPUStorage = SRVTextureGPUStorage; + +struct UAVTexelBufferGPUStorage { + uint64_t resource_id; + uint64_t metadata; + uint64_t padding[2]; +}; + struct ShaderVisibleDescriptorGPUStorage { union { SRVTextureGPUStorage SRVTexture; CBVCommonStorage ConstantBuffer; + UAVTextureGPUStorage UAVTexture; + UAVTexelBufferGPUStorage UAVTexelBuffer; }; ShaderVisibleDescriptorGPUStorage(); @@ -172,6 +183,45 @@ class MTLD3D12DescriptorHeapImpl : public MTLD3D12Pageable= descriptors_.size()) + return E_INVALIDARG; + auto &cpu_storage = descriptors_[Index]; + cpu_storage.type = ShaderVisibleDescriptorType::UAVTexture; + cpu_storage.UAVTexture.texture = Texture; // + cpu_storage.UAVTexture.view = View; + if (mapped_argument_buffer_) { + auto &texture_view = Texture->view(View); + auto &gpu_storage = mapped_argument_buffer_[Index]; + gpu_storage.UAVTexture.resource_id = texture_view.gpuResourceID; + gpu_storage.UAVTexture.metadata = TextureMetadata(Texture->arrayLength(View), 0); + } + return S_OK; + } + + virtual HRESULT + AddUnorderedAccessView(UINT Index, Buffer *UAVBuffer, BufferViewKey View, BufferSlice Slice) { + if (Index >= descriptors_.size()) + return E_INVALIDARG; + auto &cpu_storage = descriptors_[Index]; + cpu_storage.type = ShaderVisibleDescriptorType::UAVTexelBuffer; + cpu_storage.UAVTexelBuffer.buffer = UAVBuffer; + cpu_storage.UAVTexelBuffer.slice = Slice; + cpu_storage.UAVTexelBuffer.view = View; + if (mapped_argument_buffer_) { + auto &gpu_storage = mapped_argument_buffer_[Index]; + if (UAVBuffer) { + auto &buffer_view = UAVBuffer->view_(View); + gpu_storage.UAVTexelBuffer.resource_id = buffer_view.gpu_resource_id; + gpu_storage.UAVTexelBuffer.metadata = ((uint64_t)Slice.elementCount << 32) | (uint64_t)(Slice.firstElement); + } else { + gpu_storage.UAVTexelBuffer.resource_id = 0; + gpu_storage.UAVTexelBuffer.metadata = 0; + } + } + return S_OK; + } }; class MTLD3D12RenderTargetDescriptorHeapImpl : public MTLD3D12Pageable { @@ -259,10 +309,23 @@ class MTLD3D12RenderTargetDescriptorHeapImpl : public MTLD3D12Pageable { D3D12_DESCRIPTOR_HEAP_DESC desc_; + std::vector> samplers_; + + Rc buffer_; + SamplerGPUStorage *mapped_argument_buffer_ = nullptr; + uint64_t argument_buffer_gpu_address_ = 0; + public: MTLD3D12SamplerDescriptorHeapImpl(MTLD3D12Device *pDevice) : MTLD3D12Pageable(pDevice) {} @@ -279,10 +342,35 @@ class MTLD3D12SamplerDescriptorHeapImpl : public MTLD3D12PageableNumDescriptors); + + if (pDesc->Flags & D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE) { + buffer_ = new Buffer(samplers_.size() * sizeof(SamplerGPUStorage), device_->GetMTLDevice()); + + Flags flags; +#ifdef __i386__ + IMPLEMENT_ME +#endif + buffer_->rename(buffer_->allocate(flags)); + mapped_argument_buffer_ = reinterpret_cast(buffer_->current()->mappedMemory(0)); + argument_buffer_gpu_address_ = buffer_->current()->gpuAddress(); + // FIXME: is residency required for descriptor heap? Should be the case for Metal 4 + device_->RegisterResidencyAndVA(buffer_->current()); + } else { + mapped_argument_buffer_ = + reinterpret_cast(malloc(samplers_.size() * sizeof(SamplerGPUStorage))); + } + return S_OK; }; - ~MTLD3D12SamplerDescriptorHeapImpl() {} + ~MTLD3D12SamplerDescriptorHeapImpl() { + if (buffer_) { + device_->UnregisterResidencyAndVA(buffer_->current()); + } else { + free(mapped_argument_buffer_); + } + } HRESULT STDMETHODCALLTYPE @@ -313,15 +401,38 @@ class MTLD3D12SamplerDescriptorHeapImpl : public MTLD3D12Pageableptr = 0; + __ret->ptr = argument_buffer_gpu_address_; return __ret; } + + + virtual HRESULT + AddSampler(UINT Index, const D3D12_SAMPLER_DESC *pDesc) { + if (!pDesc) + return E_INVALIDARG; + if (Index >= samplers_.size()) + return E_INVALIDARG; + + WMTSamplerInfo info; + PopulateWMTSamplerInfo(device_->GetMTLDevice(), info, *pDesc); + auto sampler = Sampler::createSampler(device_->GetMTLDevice(), info, pDesc->MipLODBias); + + samplers_[Index] = sampler; + if (mapped_argument_buffer_) { + auto &gpu_storage = mapped_argument_buffer_[Index]; + gpu_storage.sampler = sampler->sampler_state_handle; + gpu_storage.cube_sampler = sampler->sampler_state_cube_handle; + gpu_storage.metadata = (uint64_t)std::bit_cast(sampler->lod_bias); + } + + return S_OK; + } }; HRESULT diff --git a/src/d3d12/d3d12_descriptor_heap.hpp b/src/d3d12/d3d12_descriptor_heap.hpp index dc093d92a..5b888b4a9 100644 --- a/src/d3d12/d3d12_descriptor_heap.hpp +++ b/src/d3d12/d3d12_descriptor_heap.hpp @@ -18,6 +18,7 @@ #pragma once #include "d3d12.h" +#include "dxmt_buffer.hpp" #include "dxmt_texture.hpp" #include @@ -78,6 +79,8 @@ enum class ShaderVisibleDescriptorType { Null, SRVTexture, ConstantBuffer, + UAVTexture, + UAVTexelBuffer, }; struct SRVTextureCPUStorage { @@ -85,6 +88,14 @@ struct SRVTextureCPUStorage { TextureViewKey view{}; }; +using UAVTextureCPUStorage = SRVTextureCPUStorage; + +struct UAVTexelBufferCPUStorage { + Buffer *buffer = nullptr; + BufferViewKey view{}; + BufferSlice slice{}; +}; + struct CBVCommonStorage { uint64_t address = 0; uint64_t size = 0; @@ -95,6 +106,8 @@ struct ShaderVisibleDescriptorCPUStorage { union { SRVTextureCPUStorage SRVTexture; CBVCommonStorage ConstantBuffer; + UAVTextureCPUStorage UAVTexture; + UAVTexelBufferCPUStorage UAVTexelBuffer; }; ShaderVisibleDescriptorCPUStorage() : type(ShaderVisibleDescriptorType::Null) {} @@ -106,10 +119,15 @@ class MTLD3D12DescriptorHeap : public ID3D12DescriptorHeap { AddShaderResourceView(UINT Index, Texture *Texture, TextureViewKey View, FLOAT ResourceMinLODClamp) = 0; virtual HRESULT AddConstantBufferView(UINT Index, UINT64 VA, UINT32 SizeInBytes) = 0; + + virtual HRESULT AddUnorderedAccessView(UINT Index, Texture *Texture, TextureViewKey View) = 0; + + virtual HRESULT AddUnorderedAccessView(UINT Index, Buffer *Buffer, BufferViewKey View, BufferSlice Slice) = 0; }; class MTLD3D12SamplerDescriptorHeap : public ID3D12DescriptorHeap { public: + virtual HRESULT AddSampler(UINT Index, const D3D12_SAMPLER_DESC *Desc) = 0; }; struct MTL_RENDER_TARGET_DESC { diff --git a/src/d3d12/d3d12_device.cpp b/src/d3d12/d3d12_device.cpp index 760dfc97b..7f9772945 100644 --- a/src/d3d12/d3d12_device.cpp +++ b/src/d3d12/d3d12_device.cpp @@ -32,6 +32,8 @@ class MTLD3D12DeviceImpl : public MTLD3D12Object> { Com adapter_; + bool advertise_numa_ = false; + dxmt::mutex residency_lock_; WMT::Reference residency_set_; std::map interval_map_; @@ -112,7 +114,7 @@ class MTLD3D12DeviceImpl : public MTLD3D12Object> { HRESULT STDMETHODCALLTYPE CreateComputePipelineState(const D3D12_COMPUTE_PIPELINE_STATE_DESC *pDesc, REFIID riid, void **ppPipelineState) { - return E_NOTIMPL; + return dxmt::CreateComputePipelineState(this, pDesc, riid, ppPipelineState); }; HRESULT STDMETHODCALLTYPE @@ -138,7 +140,7 @@ class MTLD3D12DeviceImpl : public MTLD3D12Object> { return E_INVALIDARG; out->CacheCoherentUMA = FALSE; out->TileBasedRenderer = TRUE; - out->UMA = TRUE; + out->UMA = !advertise_numa_; return S_OK; } case D3D12_FEATURE_ARCHITECTURE1: { @@ -149,7 +151,7 @@ class MTLD3D12DeviceImpl : public MTLD3D12Object> { return E_INVALIDARG; out->CacheCoherentUMA = FALSE; out->TileBasedRenderer = TRUE; - out->UMA = TRUE; + out->UMA = !advertise_numa_; out->IsolatedMMU = FALSE; return S_OK; } @@ -197,6 +199,93 @@ class MTLD3D12DeviceImpl : public MTLD3D12Object> { } return S_OK; } + case D3D12_FEATURE_FEATURE_LEVELS: { + if (DataSize != sizeof(D3D12_FEATURE_DATA_FEATURE_LEVELS)) + return E_INVALIDARG; + auto *out = reinterpret_cast(pFeatureData); + if (!out->NumFeatureLevels) + return E_INVALIDARG; + D3D_FEATURE_LEVEL max_level = {}; + for (unsigned i = 0; i < out->NumFeatureLevels; i++) + max_level = std::max(out->pFeatureLevelsRequested[i], max_level); + out->MaxSupportedFeatureLevel = std::min(max_level, D3D_FEATURE_LEVEL_11_1); + return S_OK; + } + case D3D12_FEATURE_FORMAT_INFO: { + if (DataSize != sizeof(D3D12_FEATURE_DATA_FORMAT_INFO)) + return E_INVALIDARG; + auto *out = reinterpret_cast(pFeatureData); + if (out->Format == DXGI_FORMAT_UNKNOWN) { + out->PlaneCount = 1; + return S_OK; + } + MTL_DXGI_FORMAT_DESC format_desc; + HRESULT hr = MTLQueryDXGIFormat(metal, out->Format, format_desc); + if (FAILED(hr)) + return E_FAIL; + + out->PlaneCount = format_desc.PlanarCount; + return S_OK; + } + case D3D12_FEATURE_GPU_VIRTUAL_ADDRESS_SUPPORT: { + if (DataSize != sizeof(D3D12_FEATURE_DATA_GPU_VIRTUAL_ADDRESS_SUPPORT)) + return E_INVALIDARG; + auto *out = reinterpret_cast(pFeatureData); + out->MaxGPUVirtualAddressBitsPerProcess = 48; + out->MaxGPUVirtualAddressBitsPerResource = 48; + return S_OK; + } + case D3D12_FEATURE_SHADER_MODEL: { + if (DataSize != sizeof(D3D12_FEATURE_DATA_SHADER_MODEL)) + return E_INVALIDARG; + reinterpret_cast(pFeatureData)->HighestShaderModel = D3D_SHADER_MODEL_5_1; + return S_OK; + } + case D3D12_FEATURE_D3D12_OPTIONS: { + if (DataSize != sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS)) + return E_INVALIDARG; + auto *out = reinterpret_cast(pFeatureData); + out->DoublePrecisionFloatShaderOps = FALSE; + out->OutputMergerLogicOp = FALSE; + out->MinPrecisionSupport = D3D12_SHADER_MIN_PRECISION_SUPPORT_16_BIT; + out->TiledResourcesTier = D3D12_TILED_RESOURCES_TIER_NOT_SUPPORTED; + out->ResourceBindingTier = D3D12_RESOURCE_BINDING_TIER_2; + out->PSSpecifiedStencilRefSupported = TRUE; + out->TypedUAVLoadAdditionalFormats = TRUE; + out->ROVsSupported = TRUE; + out->ConservativeRasterizationTier = D3D12_CONSERVATIVE_RASTERIZATION_TIER_NOT_SUPPORTED; + out->MaxGPUVirtualAddressBitsPerResource = 48; + out->StandardSwizzle64KBSupported = TRUE; + out->CrossNodeSharingTier = D3D12_CROSS_NODE_SHARING_TIER_NOT_SUPPORTED; + out->CrossAdapterRowMajorTextureSupported = FALSE; + out->VPAndRTArrayIndexFromAnyShaderFeedingRasterizerSupportedWithoutGSEmulation = TRUE; + out->ResourceHeapTier = D3D12_RESOURCE_HEAP_TIER_2; + return S_OK; + } + case D3D12_FEATURE_D3D12_OPTIONS16: { + if (DataSize != sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS16)) + return E_INVALIDARG; + auto *out = reinterpret_cast(pFeatureData); + out->GPUUploadHeapSupported = FALSE; // TODO(d3d12): gpu upload heap + out->DynamicDepthBiasSupported = FALSE; // TODO(d3d12): ID3D12GraphicsCommandList9::RSSetDepthBias + return S_OK; + } + case D3D12_FEATURE_FORMAT_SUPPORT: { + if (DataSize != sizeof(D3D12_FEATURE_DATA_FORMAT_SUPPORT)) + return E_INVALIDARG; + auto *out = reinterpret_cast(pFeatureData); + + if (out->Format == DXGI_FORMAT_UNKNOWN) { + out->Support1 = D3D12_FORMAT_SUPPORT1_BUFFER; + out->Support2 = {}; + return S_OK; + } + + // TODO(d3d12): report correct support + out->Support1 = (D3D12_FORMAT_SUPPORT1)0xffffffff; + out->Support2 = (D3D12_FORMAT_SUPPORT2)0xffffffff; + return S_OK; + } default: break; } @@ -285,8 +374,10 @@ class MTLD3D12DeviceImpl : public MTLD3D12Object> { d3d12res->CreateDepthStencilView(pDesc, Descriptor); }; - void STDMETHODCALLTYPE CreateSampler(const D3D12_SAMPLER_DESC *pDesc, D3D12_CPU_DESCRIPTOR_HANDLE Descriptor) { - IMPLEMENT_ME + void STDMETHODCALLTYPE + CreateSampler(const D3D12_SAMPLER_DESC *pDesc, D3D12_CPU_DESCRIPTOR_HANDLE Descriptor) { + auto [Heap, Index] = GetSamplerDescriptorHeap(this, Descriptor); + Heap->AddSampler(Index, pDesc); }; void STDMETHODCALLTYPE CopyDescriptors( @@ -313,7 +404,27 @@ class MTLD3D12DeviceImpl : public MTLD3D12Object> { D3D12_HEAP_PROPERTIES *STDMETHODCALLTYPE GetCustomHeapProperties(D3D12_HEAP_PROPERTIES *__ret, UINT NodeMask, D3D12_HEAP_TYPE HeapType) { - IMPLEMENT_ME + __ret->Type = D3D12_HEAP_TYPE_CUSTOM; + __ret->CreationNodeMask = 1; + __ret->VisibleNodeMask = 1; + switch (HeapType) { + case D3D12_HEAP_TYPE_DEFAULT: + __ret->CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_NOT_AVAILABLE; + __ret->MemoryPoolPreference = advertise_numa_ ? D3D12_MEMORY_POOL_L1 : D3D12_MEMORY_POOL_L0; + break; + case D3D12_HEAP_TYPE_UPLOAD: + __ret->CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE; + __ret->MemoryPoolPreference = D3D12_MEMORY_POOL_L0; + break; + case D3D12_HEAP_TYPE_READBACK: + __ret->CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_WRITE_BACK; + __ret->MemoryPoolPreference = D3D12_MEMORY_POOL_L0; + break; + default: + E_INVALIDARG; + } + + return __ret; }; HRESULT STDMETHODCALLTYPE @@ -321,6 +432,17 @@ class MTLD3D12DeviceImpl : public MTLD3D12Object> { const D3D12_HEAP_PROPERTIES *pHeapProps, D3D12_HEAP_FLAGS HeapFlags, const D3D12_RESOURCE_DESC *pDesc, D3D12_RESOURCE_STATES InitialState, const D3D12_CLEAR_VALUE *OptimizedClearValue, REFIID riid, void **ppResource ) { + InitReturnPtr(ppResource); + HRESULT hr = S_OK; + hr = ValidateHeapProperties(pHeapProps, HeapFlags, advertise_numa_); + if (FAILED(hr)) + return hr; + hr = ValidateResourceDescs(pDesc, pHeapProps->Type); + if (FAILED(hr)) + return hr; + hr = ValidateResourceStates(InitialState, pHeapProps); + if (FAILED(hr)) + return hr; switch (pDesc->Dimension) { case D3D12_RESOURCE_DIMENSION_TEXTURE1D: case D3D12_RESOURCE_DIMENSION_TEXTURE2D: @@ -340,6 +462,10 @@ class MTLD3D12DeviceImpl : public MTLD3D12Object> { HRESULT STDMETHODCALLTYPE CreateHeap(const D3D12_HEAP_DESC *pDesc, REFIID riid, void **ppHeap) { + HRESULT hr = S_OK; + hr = ValidateHeapProperties(&pDesc->Properties, pDesc->Flags, advertise_numa_); + if (FAILED(hr)) + return hr; return dxmt::CreateHeap(this, pDesc, riid, ppHeap); }; @@ -348,9 +474,21 @@ class MTLD3D12DeviceImpl : public MTLD3D12Object> { ID3D12Heap *pHeap, UINT64 Offset, const D3D12_RESOURCE_DESC *pDesc, D3D12_RESOURCE_STATES InitialState, const D3D12_CLEAR_VALUE *OptimizedClearValue, REFIID riid, void **ppResource ) { + InitReturnPtr(ppResource); if (!pHeap) return E_INVALIDARG; auto d3d12heap = static_cast(pHeap); + auto heap_desc = d3d12heap->GetDesc(); + HRESULT hr = S_OK; + hr = ValidateHeapProperties(&heap_desc.Properties, heap_desc.Flags, advertise_numa_); + if (FAILED(hr)) + return hr; + hr = ValidateResourceDescs(pDesc, heap_desc.Properties.Type); + if (FAILED(hr)) + return hr; + hr = ValidateResourceStates(InitialState, &heap_desc.Properties); + if (FAILED(hr)) + return hr; switch (pDesc->Dimension) { case D3D12_RESOURCE_DIMENSION_TEXTURE1D: case D3D12_RESOURCE_DIMENSION_TEXTURE2D: @@ -519,7 +657,7 @@ class MTLD3D12DeviceImpl : public MTLD3D12Object> { const D3D12_COMMAND_SIGNATURE_DESC *pDesc, ID3D12RootSignature *pRootSignature, REFIID riid, void **ppCommandSignature ) { - return E_NOTIMPL; + return dxmt::CreateCommandSignature(this, pDesc, pRootSignature, riid, ppCommandSignature); }; void STDMETHODCALLTYPE GetResourceTiling( diff --git a/src/d3d12/d3d12_device.hpp b/src/d3d12/d3d12_device.hpp index b81e0ef01..610e3245b 100644 --- a/src/d3d12/d3d12_device.hpp +++ b/src/d3d12/d3d12_device.hpp @@ -105,6 +105,10 @@ class MTLD3D12RootSignature : public ID3D12RootSignature { uint64_t const *EncodedStaticSamplers; }; +class MTLD3D12CommandSignature : public ID3D12CommandSignature { +public: +}; + class MTLD3D12QueryHeap : public ID3D12QueryHeap { public: }; @@ -132,6 +136,15 @@ class MTLD3D12GraphicsPipelineState : public MTLD3D12PipelineState { virtual void ReleasePrivate() = 0; }; +class MTLD3D12ComputePipelineState : public MTLD3D12PipelineState { +public: + WMT::Reference pso; + WMTSize threadgroup_size; + + virtual void AddRefPrivate() = 0; + virtual void ReleasePrivate() = 0; +}; + class MTLD3D12Device : public ID3D12Device1 { public: virtual WMT::Device GetMTLDevice() = 0; @@ -202,11 +215,22 @@ CreateRootSignature( void **ppRootSignature ); +HRESULT +CreateCommandSignature( + MTLD3D12Device *pDevice, const D3D12_COMMAND_SIGNATURE_DESC *pDesc, ID3D12RootSignature *pRootSignature, + REFIID riid, void **ppCommandSignature +); + HRESULT CreateGraphicsPipelineState( MTLD3D12Device *pDevice, const D3D12_GRAPHICS_PIPELINE_STATE_DESC *pDesc, REFIID riid, void **ppPipelineState ); +HRESULT +CreateComputePipelineState( + MTLD3D12Device *pDevice, const D3D12_COMPUTE_PIPELINE_STATE_DESC *pDesc, REFIID riid, void **ppPipelineState +); + HRESULT CreateSwapChain( IDXGIFactory1 *pFactory, MTLD3D12Device *pDevice, MTLD3D12CommandQueue *pQueue, HWND hWnd, @@ -264,9 +288,36 @@ GetShaderVisibleDescriptor(MTLD3D12DescriptorHeap *pHeap, UINT Index) { // } +inline std::tuple +GetSamplerDescriptorHeap(MTLD3D12Device *pDevice, D3D12_CPU_DESCRIPTOR_HANDLE Handle) { +#ifdef DXMT_USE_EMBEDDED_HEAP_POINTER + EMBEDDED_DESCRIPTOR_HANDLE impl(Handle); + return {impl.extract(), (UINT)impl.Descriptor}; +#else + IMPLEMENT_ME + return {}; +#endif +} + +inline D3D12_CPU_DESCRIPTOR_HANDLE +GetSamplerDescriptor(MTLD3D12SamplerDescriptorHeap *pHeap, UINT Index) { +#ifdef DXMT_USE_EMBEDDED_HEAP_POINTER + return EMBEDDED_DESCRIPTOR_HANDLE(pHeap, Index); +#else + IMPLEMENT_ME + return {}; +#endif +} + template HRESULT ExtractEntireResourceViewDescription(const D3D12_RESOURCE_DESC &ResourceDesc, VIEW_DESC *pViewDescOut); constexpr auto kDefaultShader4Component = 0b1'011'010'001'000; +HRESULT ValidateResourceStates(D3D12_RESOURCE_STATES State, const D3D12_HEAP_PROPERTIES *pHeapProps); + +HRESULT ValidateResourceDescs(const D3D12_RESOURCE_DESC *pDesc, D3D12_HEAP_TYPE HeapType); + +HRESULT ValidateHeapProperties(const D3D12_HEAP_PROPERTIES *pHeapProps, D3D12_HEAP_FLAGS Flags, bool AdapterIsNUMA); + } // namespace dxmt \ No newline at end of file diff --git a/src/d3d12/d3d12_device_child.hpp b/src/d3d12/d3d12_device_child.hpp index 9f2e95530..094fd72d6 100644 --- a/src/d3d12/d3d12_device_child.hpp +++ b/src/d3d12/d3d12_device_child.hpp @@ -56,7 +56,7 @@ template class MTLD3D12Object : public Base... { } private: - ComPrivateData private_data_; + ConcurrentComPrivateData private_data_; }; template class MTLD3D12DeviceChild : public MTLD3D12Object> { diff --git a/src/d3d12/d3d12_heap.cpp b/src/d3d12/d3d12_heap.cpp index 161bc3360..2f49e27b8 100644 --- a/src/d3d12/d3d12_heap.cpp +++ b/src/d3d12/d3d12_heap.cpp @@ -54,6 +54,29 @@ class MTLD3D12HeapImpl : public MTLD3D12Pageable { HRESULT Initialize(const D3D12_HEAP_DESC *pDesc) { + if (pDesc->Flags & D3D12_HEAP_FLAG_ALLOW_DISPLAY) + return E_INVALIDARG; // must be committed resource + + desc_ = *pDesc; + desc_.Properties.CreationNodeMask = 1; + desc_.Properties.VisibleNodeMask = 1; + + + switch (pDesc->Alignment) { + case 0: + desc_.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT; + [[fallthrough]]; + case D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT: + case D3D12_DEFAULT_MSAA_RESOURCE_PLACEMENT_ALIGNMENT: + break; + default: + return E_INVALIDARG; + } + + auto size_aligned = align(pDesc->SizeInBytes, desc_.Alignment); + if (!size_aligned) + return E_INVALIDARG; + return S_OK; } diff --git a/src/d3d12/d3d12_pipeline_compute.cpp b/src/d3d12/d3d12_pipeline_compute.cpp new file mode 100644 index 000000000..bd0c8e3f6 --- /dev/null +++ b/src/d3d12/d3d12_pipeline_compute.cpp @@ -0,0 +1,142 @@ +/* + * Copyright 2026 Feifan He for CodeWeavers + * + * 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 + */ + +#include "Metal.hpp" +#include "com/com_pointer.hpp" +#include "d3d12_device.hpp" +#include "d3d12_pageable.hpp" +#include "log/log.hpp" +#include "airconv_public.h" + +namespace dxmt { + +class MTLD3D12ComputePipelineStateImpl : public MTLD3D12Pageable { + + sm50_shader_t shader_cs; + MTL_SHADER_REFLECTION ref_cs; + +public: + MTLD3D12ComputePipelineStateImpl(MTLD3D12Device *pDevice) : MTLD3D12Pageable(pDevice) { + IsComputePipelineState = 1; + } + + HRESULT + Initialize(const D3D12_COMPUTE_PIPELINE_STATE_DESC *pDesc) { + + sm50_error_t sm50_err; + + SM50_SHADER_ROOT_SIGNATURE_DATA rootsig; + rootsig.type = SM50_SHADER_ROOT_SIGNATURE; + if (pDesc->pRootSignature) { + rootsig.bytecode_length = static_cast(pDesc->pRootSignature)->GetBlob(&rootsig.bytecode); + } else { + rootsig.bytecode = pDesc->CS.pShaderBytecode; + rootsig.bytecode_length = pDesc->CS.BytecodeLength; + } + rootsig.next = nullptr; + + SM50_SHADER_COMMON_DATA common; + common.flags = {}; + common.type = SM50_SHADER_COMMON; + common.metal_version = SM50_SHADER_METAL_310; + common.next = &rootsig; + + if (SM50Initialize(pDesc->CS.pShaderBytecode, pDesc->CS.BytecodeLength, &shader_cs, &ref_cs, &sm50_err)) { + ERR("Failed to parse cs shader"); + return E_FAIL; + } + + threadgroup_size = {ref_cs.ThreadgroupSize[0], ref_cs.ThreadgroupSize[1], ref_cs.ThreadgroupSize[2]}; + + sm50_bitcode_t cs_bitcode; + + if (SM50Compile(shader_cs, (SM50_SHADER_COMPILATION_ARGUMENT_DATA *)&common, "cs_main", &cs_bitcode, &sm50_err)) { + ERR("Failed to compile cs shader"); + return E_FAIL; + } + + SM50_COMPILED_BITCODE cs_bitcode_compiled; + + SM50GetCompiledBitcode(cs_bitcode, &cs_bitcode_compiled); + + auto cs_data = WMT::MakeDispatchData(cs_bitcode_compiled.Data, cs_bitcode_compiled.Size); + + auto metal = device_->GetMTLDevice(); + + WMT::Reference err; + + auto cs_lib = metal.newLibrary(cs_data, err); + + auto cs_func = cs_lib.newFunction("cs_main"); + + // PSO + { + WMTComputePipelineInfo info; + WMT::InitializeComputePipelineInfo(info); + info.compute_function = cs_func; + + pso = metal.newComputePipelineState(info, err); + if (!pso) { + ERR("Failed to create compute PSO: ", err.description().getUTF8String()); + return E_FAIL; + } + } + + return S_OK; + } + + HRESULT + STDMETHODCALLTYPE + QueryInterface(REFIID riid, void **ppvObject) { + if (ppvObject == nullptr) + return E_POINTER; + + *ppvObject = nullptr; + + if (riid == __uuidof(IUnknown) || riid == __uuidof(ID3D12Object) || riid == __uuidof(ID3D12DeviceChild) || + riid == __uuidof(ID3D12Pageable) || riid == __uuidof(ID3D12PipelineState)) { + *ppvObject = ref(this); + return S_OK; + } + + if (logQueryInterfaceError(__uuidof(ID3D12PipelineState), riid)) { + WARN("D3D12ComputePipelineState: Unknown interface query ", str::format(riid)); + } + + return E_NOINTERFACE; + } + + virtual HRESULT STDMETHODCALLTYPE + GetCachedBlob(ID3DBlob **blob) { + IMPLEMENT_ME + return E_NOTIMPL; + } +}; + +HRESULT +CreateComputePipelineState( + MTLD3D12Device *pDevice, const D3D12_COMPUTE_PIPELINE_STATE_DESC *pDesc, REFIID riid, void **ppPipelineState +) { + auto pso = Com(new MTLD3D12ComputePipelineStateImpl(pDevice)); + HRESULT hr = pso->Initialize(pDesc); + if (FAILED(hr)) + return hr; + return pso->QueryInterface(riid, ppPipelineState); +}; + +} // namespace dxmt \ No newline at end of file diff --git a/src/d3d12/d3d12_resource_helper.cpp b/src/d3d12/d3d12_resource_helper.cpp index df54903f8..324fc4677 100644 --- a/src/d3d12/d3d12_resource_helper.cpp +++ b/src/d3d12/d3d12_resource_helper.cpp @@ -214,4 +214,168 @@ ExtractEntireResourceViewDescription( return S_OK; } +template <> +HRESULT +ExtractEntireResourceViewDescription( + const D3D12_RESOURCE_DESC &ResourceDesc, D3D12_UNORDERED_ACCESS_VIEW_DESC *pViewDescOut +) { + pViewDescOut->Format = ResourceDesc.Format; + switch (ResourceDesc.Dimension) { + case D3D12_RESOURCE_DIMENSION_BUFFER: { + ERR("Unsupported buffer UAV"); + return E_FAIL; + } + case D3D12_RESOURCE_DIMENSION_TEXTURE1D: { + if (ResourceDesc.DepthOrArraySize > 1) { + pViewDescOut->ViewDimension = D3D12_UAV_DIMENSION_TEXTURE1DARRAY; + pViewDescOut->Texture1DArray.MipSlice = 0; + pViewDescOut->Texture1DArray.FirstArraySlice = 0; + pViewDescOut->Texture1DArray.ArraySize = ResourceDesc.DepthOrArraySize; + } else { + pViewDescOut->ViewDimension = D3D12_UAV_DIMENSION_TEXTURE1D; + pViewDescOut->Texture1D.MipSlice = 0; + } + break; + } + case D3D12_RESOURCE_DIMENSION_TEXTURE2D: { + if (ResourceDesc.SampleDesc.Count > 1) + return E_FAIL; + if (ResourceDesc.DepthOrArraySize > 1) { + pViewDescOut->ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2DARRAY; + pViewDescOut->Texture2DArray.MipSlice = 0; + pViewDescOut->Texture2DArray.FirstArraySlice = 0; + pViewDescOut->Texture2DArray.ArraySize = ResourceDesc.DepthOrArraySize; + pViewDescOut->Texture2DArray.PlaneSlice = 0; // FIXME(resource-planar) + } else { + pViewDescOut->ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D; + pViewDescOut->Texture2D.MipSlice = 0; + pViewDescOut->Texture2D.PlaneSlice = 0; // FIXME(resource-planar) + } + break; + } + case D3D12_RESOURCE_DIMENSION_TEXTURE3D: { + pViewDescOut->ViewDimension = D3D12_UAV_DIMENSION_TEXTURE3D; + pViewDescOut->Texture3D.FirstWSlice = 0; + pViewDescOut->Texture3D.WSize = ResourceDesc.DepthOrArraySize; + pViewDescOut->Texture3D.MipSlice = 0; + break; + } + default: + return E_INVALIDARG; + } + + return S_OK; +} + +constexpr D3D12_RESOURCE_STATES kExclusiveWrite = + D3D12_RESOURCE_STATE_RENDER_TARGET | D3D12_RESOURCE_STATE_UNORDERED_ACCESS | D3D12_RESOURCE_STATE_DEPTH_WRITE | + D3D12_RESOURCE_STATE_STREAM_OUT | D3D12_RESOURCE_STATE_COPY_DEST | D3D12_RESOURCE_STATE_RESOLVE_DEST | + D3D12_RESOURCE_STATE_VIDEO_DECODE_WRITE | D3D12_RESOURCE_STATE_VIDEO_PROCESS_WRITE | + D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE; + +HRESULT +ValidateResourceStates(D3D12_RESOURCE_STATES State, const D3D12_HEAP_PROPERTIES *pHeapProps) { + if (State & kExclusiveWrite) { + if (State & ~kExclusiveWrite) + return E_INVALIDARG; + if (bit::popcnt(State) != 1) + return E_INVALIDARG; + } + + switch (pHeapProps->Type) { + case D3D12_HEAP_TYPE_READBACK: { + if (State != D3D12_RESOURCE_STATE_COPY_DEST && State != D3D12_RESOURCE_STATE_COMMON) + return E_INVALIDARG; + break; + } + default: + break; + } + + return S_OK; +} + +HRESULT +ValidateResourceDescs(const D3D12_RESOURCE_DESC *pDesc, D3D12_HEAP_TYPE HeapType) { + switch (HeapType) { + case D3D12_HEAP_TYPE_UPLOAD: { + if (pDesc->Flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET) + return E_INVALIDARG; + if (pDesc->Flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS) + return E_INVALIDARG; + if (pDesc->Dimension != D3D12_RESOURCE_DIMENSION_BUFFER) + return E_INVALIDARG; + break; + } + case D3D12_HEAP_TYPE_READBACK: { + if (pDesc->Flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET) + return E_INVALIDARG; + if (pDesc->Flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS) + return E_INVALIDARG; + if (pDesc->Dimension != D3D12_RESOURCE_DIMENSION_BUFFER) + return E_INVALIDARG; + break; + } + case D3D12_HEAP_TYPE_DEFAULT: + case D3D12_HEAP_TYPE_CUSTOM: + break; + default: + return E_INVALIDARG; + } + + switch (pDesc->Dimension) { + case D3D12_RESOURCE_DIMENSION_BUFFER: { + if (pDesc->Flags & D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS) + return E_INVALIDARG; + break; + } + default: + break; + } + + return S_OK; +} + +HRESULT +ValidateHeapProperties(const D3D12_HEAP_PROPERTIES *pHeapProps, D3D12_HEAP_FLAGS Flags, bool AdapterIsNUMA) { + switch (pHeapProps->Type) { + case D3D12_HEAP_TYPE_DEFAULT: + case D3D12_HEAP_TYPE_READBACK: { + if (Flags & D3D12_HEAP_FLAG_ALLOW_WRITE_WATCH) + return E_INVALIDARG; + [[fallthrough]]; + } + case D3D12_HEAP_TYPE_UPLOAD: { + if (pHeapProps->CPUPageProperty != D3D12_CPU_PAGE_PROPERTY_UNKNOWN) + return E_INVALIDARG; + if (pHeapProps->MemoryPoolPreference != D3D12_MEMORY_POOL_UNKNOWN) + return E_INVALIDARG; + break; + } + case D3D12_HEAP_TYPE_CUSTOM: { + if (pHeapProps->CPUPageProperty == D3D12_CPU_PAGE_PROPERTY_UNKNOWN) + return E_INVALIDARG; + if (pHeapProps->MemoryPoolPreference == D3D12_MEMORY_POOL_UNKNOWN) + return E_INVALIDARG; + if (pHeapProps->MemoryPoolPreference == D3D12_MEMORY_POOL_L1 && AdapterIsNUMA) { + if (pHeapProps->CPUPageProperty != D3D12_CPU_PAGE_PROPERTY_NOT_AVAILABLE) + return E_INVALIDARG; + } + break; + } + default: + return E_INVALIDARG; + } + + if (pHeapProps->MemoryPoolPreference == D3D12_MEMORY_POOL_L1) { + if (!AdapterIsNUMA) + return E_INVALIDARG; + if (pHeapProps->CPUPageProperty != D3D12_CPU_PAGE_PROPERTY_NOT_AVAILABLE) + return E_INVALIDARG; + } + + // TODO(d3d12): check NodeMask + return S_OK; +} + } // namespace dxmt \ No newline at end of file diff --git a/src/d3d12/d3d12_swapchain.cpp b/src/d3d12/d3d12_swapchain.cpp index 8e59b8c98..5270baff3 100644 --- a/src/d3d12/d3d12_swapchain.cpp +++ b/src/d3d12/d3d12_swapchain.cpp @@ -321,9 +321,7 @@ class MTLD3D12SwapChain final : public MTLDXGISubObjectGetCustomHeapProperties(0, D3D12_HEAP_TYPE_DEFAULT); for (unsigned i = 0; i < BufferCount; i++) { Com backbuffer; diff --git a/src/d3d12/d3d12_texture.cpp b/src/d3d12/d3d12_texture.cpp index 542bdeacc..729e43978 100644 --- a/src/d3d12/d3d12_texture.cpp +++ b/src/d3d12/d3d12_texture.cpp @@ -100,10 +100,10 @@ PopulateWMTTextureInfo(WMT::Device Device, WMTTextureInfo &InfoOut, const D3D12_ break; } } + + InfoOut.mipmap_level_count = 32 - __builtin_clz(InfoOut.width | InfoOut.height | InfoOut.depth); if (Desc.MipLevels) - InfoOut.mipmap_level_count = Desc.MipLevels; - else - InfoOut.mipmap_level_count = 32 - __builtin_clz(InfoOut.width | InfoOut.height | InfoOut.depth); + InfoOut.mipmap_level_count = std::min(InfoOut.mipmap_level_count, Desc.MipLevels); WMTTextureUsage Usage = WMTTextureUsagePixelFormatView; if (Desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET) @@ -168,6 +168,9 @@ class MTLD3D12Texture : public MTLD3D12Pageable { if (FAILED(hr)) return hr; + if (!desc_.MipLevels) + desc_.MipLevels = texture_info.mipmap_level_count; + texture = new Texture(texture_info, device_->GetMTLDevice()); Flags flags = {}; texture->rename(texture->allocate(flags)); @@ -177,7 +180,8 @@ class MTLD3D12Texture : public MTLD3D12Pageable { }; ~MTLD3D12Texture() { - device_->UnregisterResidency(texture->current()->texture()); + if (texture) + device_->UnregisterResidency(texture->current()->texture()); } HRESULT @@ -401,8 +405,87 @@ class MTLD3D12Texture : public MTLD3D12Pageable { CreateUnorderedAccessView( ID3D12Resource *pCounter, const D3D12_UNORDERED_ACCESS_VIEW_DESC *pDesc, D3D12_CPU_DESCRIPTOR_HANDLE Descriptor ) { - IMPLEMENT_ME - return S_OK; + HRESULT hr; + D3D12_UNORDERED_ACCESS_VIEW_DESC ViewDesc; + if (!pDesc) { + hr = ExtractEntireResourceViewDescription(desc_, &ViewDesc); + if (FAILED(hr)) + return hr; + } else { + ViewDesc = *pDesc; + } + + auto [Heap, Index] = GetShaderVisibleDescriptorHeap(device_, Descriptor); + TextureViewKey View = texture->fullView; + + TextureViewDescriptor view_descriptor; + MTL_DXGI_FORMAT_DESC metal_format; + hr = MTLQueryDXGIFormat(device_->GetMTLDevice(), ViewDesc.Format, metal_format); + if (FAILED(hr)) + return hr; + + view_descriptor.format = metal_format.PixelFormat; + + switch (ViewDesc.ViewDimension) { + case D3D12_UAV_DIMENSION_TEXTURE1D: { + view_descriptor.type = WMTTextureType2D; // FIXME: lowering to 2d array + view_descriptor.firstMiplevel = ViewDesc.Texture1D.MipSlice; + view_descriptor.miplevelCount = 1; + view_descriptor.firstArraySlice = 0; + view_descriptor.arraySize = 1; + View = texture->createView(view_descriptor); + break; + } + case D3D12_UAV_DIMENSION_TEXTURE2D: { + view_descriptor.type = WMTTextureType2D; // FIXME: lowering to 2d array + view_descriptor.firstMiplevel = ViewDesc.Texture2D.MipSlice; + view_descriptor.miplevelCount = 1; + view_descriptor.firstArraySlice = 0; + view_descriptor.arraySize = 1; + View = texture->createView(view_descriptor); + break; + } + case D3D12_UAV_DIMENSION_TEXTURE1DARRAY: { + view_descriptor.type = WMTTextureType2DArray; + view_descriptor.firstMiplevel = ViewDesc.Texture1DArray.MipSlice; + view_descriptor.miplevelCount = 1; + view_descriptor.firstArraySlice = ViewDesc.Texture1DArray.FirstArraySlice; + if (ViewDesc.Texture1DArray.ArraySize == ~0u) + view_descriptor.arraySize = desc_.DepthOrArraySize - ViewDesc.Texture1DArray.FirstArraySlice; + else + view_descriptor.arraySize = ViewDesc.Texture1DArray.ArraySize; + View = texture->createView(view_descriptor); + break; + } + case D3D12_UAV_DIMENSION_TEXTURE2DARRAY: { + view_descriptor.type = WMTTextureType2DArray; + view_descriptor.firstMiplevel = ViewDesc.Texture2DArray.MipSlice; + view_descriptor.miplevelCount = 1; + view_descriptor.firstArraySlice = ViewDesc.Texture2DArray.FirstArraySlice; + if (ViewDesc.Texture2DArray.ArraySize == ~0u) + view_descriptor.arraySize = desc_.DepthOrArraySize - ViewDesc.Texture2DArray.FirstArraySlice; + else + view_descriptor.arraySize = ViewDesc.Texture2DArray.ArraySize; + View = texture->createView(view_descriptor); + break; + } + case D3D12_UAV_DIMENSION_TEXTURE3D: { + view_descriptor.type = WMTTextureType3D; + view_descriptor.firstMiplevel = ViewDesc.Texture3D.MipSlice; + view_descriptor.miplevelCount = 1; + view_descriptor.firstArraySlice = 0; + view_descriptor.arraySize = 1; + View = texture->createView(view_descriptor); + if (ViewDesc.Texture3D.FirstWSlice > 0) { + ERR("3D UAV with WSlice unsupported"); + } + break; + } + default: + return E_INVALIDARG; + } + + return Heap->AddUnorderedAccessView(Index, texture.ptr(), View); }; virtual HRESULT STDMETHODCALLTYPE @@ -647,7 +730,6 @@ CreateCommittedTexture( const D3D12_RESOURCE_DESC *pDesc, D3D12_RESOURCE_STATES InitialState, const D3D12_CLEAR_VALUE *OptimizedClearValue, REFIID riid, void **ppResource ) { - InitReturnPtr(ppResource); auto texture = Com(new MTLD3D12Texture(pDevice)); HRESULT hr = texture->Initialize(pHeapProps, HeapFlags, pDesc, InitialState, nullptr); if (FAILED(hr)) @@ -662,7 +744,6 @@ CreatePlacedTexture( MTLD3D12Device *pDevice, MTLD3D12Heap *pHeap, const D3D12_RESOURCE_DESC *pDesc, D3D12_RESOURCE_STATES InitialState, const D3D12_CLEAR_VALUE *OptimizedClearValue, REFIID riid, void **ppResource ) { - InitReturnPtr(ppResource); auto texture = Com(new MTLD3D12Texture(pDevice)); D3D12_HEAP_DESC heap_desc = pHeap->GetDesc(); diff --git a/src/d3d12/meson.build b/src/d3d12/meson.build index 0fc351a5f..ce23ea65b 100644 --- a/src/d3d12/meson.build +++ b/src/d3d12/meson.build @@ -4,10 +4,12 @@ d3d12_src = [ 'd3d12_command_allocator.cpp', 'd3d12_command_list.cpp', 'd3d12_command_queue.cpp', + 'd3d12_command_signature.cpp', 'd3d12_descriptor_heap.cpp', 'd3d12_device.cpp', 'd3d12_fence.cpp', 'd3d12_heap.cpp', + 'd3d12_pipeline_compute.cpp', 'd3d12_pipeline_graphics.cpp', 'd3d12_query_heap.cpp', 'd3d12_resource_helper.cpp', diff --git a/src/dxmt/dxmt_format.cpp b/src/dxmt/dxmt_format.cpp index a34b7ace3..658b3f69f 100644 --- a/src/dxmt/dxmt_format.cpp +++ b/src/dxmt/dxmt_format.cpp @@ -439,6 +439,7 @@ MTLQueryDXGIFormat(WMT::Device device, uint32_t format, MTL_DXGI_FORMAT_DESC &de description.PixelFormat = WMTPixelFormatInvalid; description.AttributeFormat = WMTAttributeFormatInvalid; description.BytesPerTexel = 0; + description.PlanarCount = 1; description.Flag = 0; switch (format) { @@ -556,21 +557,25 @@ MTLQueryDXGIFormat(WMT::Device device, uint32_t format, MTL_DXGI_FORMAT_DESC &de description.Flag = MTL_DXGI_FORMAT_TYPELESS | MTL_DXGI_FORMAT_DEPTH_PLANER | MTL_DXGI_FORMAT_STENCIL_PLANER | MTL_DXGI_FORMAT_EMULATED_LINEAR_DEPTH_STENCIL; description.BytesPerTexel = 8; + description.PlanarCount = 2; break; } case DXGI_FORMAT_D32_FLOAT_S8X24_UINT: { description.PixelFormat = WMTPixelFormatDepth32Float_Stencil8; description.Flag = MTL_DXGI_FORMAT_DEPTH_PLANER | MTL_DXGI_FORMAT_STENCIL_PLANER; + description.PlanarCount = 2; break; } case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS: { description.PixelFormat = WMTPixelFormatR32X8X32; description.Flag = MTL_DXGI_FORMAT_TYPELESS | MTL_DXGI_FORMAT_DEPTH_PLANER; + description.PlanarCount = 2; break; } case DXGI_FORMAT_X32_TYPELESS_G8X24_UINT: { description.PixelFormat = WMTPixelFormatX32G8X32; description.Flag = MTL_DXGI_FORMAT_TYPELESS | MTL_DXGI_FORMAT_STENCIL_PLANER; + description.PlanarCount = 2; break; } case DXGI_FORMAT_R10G10B10A2_TYPELESS: { @@ -707,21 +712,25 @@ MTLQueryDXGIFormat(WMT::Device device, uint32_t format, MTL_DXGI_FORMAT_DESC &de MTL_DXGI_FORMAT_EMULATED_LINEAR_DEPTH_STENCIL | MTL_DXGI_FORMAT_EMULATED_D24; description.PixelFormat = WMTPixelFormatDepth32Float_Stencil8; description.BytesPerTexel = 4; + description.PlanarCount = 2; break; } case DXGI_FORMAT_D24_UNORM_S8_UINT: { description.Flag = MTL_DXGI_FORMAT_DEPTH_PLANER | MTL_DXGI_FORMAT_STENCIL_PLANER | MTL_DXGI_FORMAT_EMULATED_D24; description.PixelFormat = WMTPixelFormatDepth32Float_Stencil8; + description.PlanarCount = 2; break; } case DXGI_FORMAT_R24_UNORM_X8_TYPELESS: { description.Flag = MTL_DXGI_FORMAT_DEPTH_PLANER | MTL_DXGI_FORMAT_EMULATED_D24; description.PixelFormat = WMTPixelFormatR32X8X32; + description.PlanarCount = 2; break; } case DXGI_FORMAT_X24_TYPELESS_G8_UINT: { description.Flag = MTL_DXGI_FORMAT_STENCIL_PLANER | MTL_DXGI_FORMAT_EMULATED_D24; description.PixelFormat = WMTPixelFormatX32G8X32; + description.PlanarCount = 2; break; } case DXGI_FORMAT_R8G8_TYPELESS: { diff --git a/src/dxmt/dxmt_format.hpp b/src/dxmt/dxmt_format.hpp index 216c1c454..21a48198d 100644 --- a/src/dxmt/dxmt_format.hpp +++ b/src/dxmt/dxmt_format.hpp @@ -55,7 +55,8 @@ struct MTL_DXGI_FORMAT_DESC { uint32_t BytesPerTexel; uint32_t BlockSize; }; - uint32_t Flag; + uint16_t PlanarCount; + uint16_t Flag; }; int32_t MTLQueryDXGIFormat(WMT::Device device, uint32_t format, MTL_DXGI_FORMAT_DESC &description); diff --git a/src/util/com/com_private_data.cpp b/src/util/com/com_private_data.cpp index 06f9b6783..a53ca6f35 100644 --- a/src/util/com/com_private_data.cpp +++ b/src/util/com/com_private_data.cpp @@ -147,4 +147,40 @@ void ComPrivateData::insertEntry(ComPrivateDataEntry &&entry) { m_entries.push_back(std::move(srcEntry)); } +HRESULT ConcurrentComPrivateData::setData(REFGUID guid, UINT size, const void *data) { + std::lock_guard lock(mutex_); + if (!data) { + for (auto it = m_entries.begin(); it != m_entries.end(); ++it) { + if (it->hasGuid(guid)) { + m_entries.erase(it); + return S_OK; + } + } + return S_FALSE; + } + this->insertEntry(ComPrivateDataEntry(guid, size, data)); + return S_OK; +} + +HRESULT ConcurrentComPrivateData::setInterface(REFGUID guid, const IUnknown *iface) { + std::lock_guard lock(mutex_); + this->insertEntry(ComPrivateDataEntry(guid, iface)); + return S_OK; +} + +HRESULT ConcurrentComPrivateData::getData(REFGUID guid, UINT *size, void *data) { + std::lock_guard lock(mutex_); + if (!size) + return E_INVALIDARG; + + auto entry = this->findEntry(guid); + + if (!entry) { + *size = 0; + return DXGI_ERROR_NOT_FOUND; + } + + return entry->get(*size, data); +} + } // namespace dxmt diff --git a/src/util/com/com_private_data.hpp b/src/util/com/com_private_data.hpp index 991c5424b..88afe828b 100644 --- a/src/util/com/com_private_data.hpp +++ b/src/util/com/com_private_data.hpp @@ -9,6 +9,7 @@ */ #pragma once +#include "thread.hpp" #include #include @@ -89,11 +90,24 @@ class ComPrivateData { HRESULT getData(REFGUID guid, UINT *size, void *data); -private: +protected: std::vector m_entries; ComPrivateDataEntry *findEntry(REFGUID guid); void insertEntry(ComPrivateDataEntry &&entry); }; +class ConcurrentComPrivateData : public ComPrivateData { + +public: + HRESULT setData(REFGUID guid, UINT size, const void *data); + + HRESULT setInterface(REFGUID guid, const IUnknown *iface); + + HRESULT getData(REFGUID guid, UINT *size, void *data); + +private: + dxmt::mutex mutex_; +}; + } // namespace dxmt