From ab6ad40af328ca65a9f977dccf753d6d850eb9df Mon Sep 17 00:00:00 2001 From: kindem Date: Sat, 1 Aug 2026 13:01:59 +0800 Subject: [PATCH 01/14] fix: align Vulkan device feature enablement --- .../RHI-Vulkan/Include/RHI/Vulkan/Device.h | 2 + .../Source/RHI-Vulkan/Src/CommandRecorder.cpp | 3 +- Engine/Source/RHI-Vulkan/Src/Device.cpp | 40 +++++++++++++++---- 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/Engine/Source/RHI-Vulkan/Include/RHI/Vulkan/Device.h b/Engine/Source/RHI-Vulkan/Include/RHI/Vulkan/Device.h index b6f6d45c7..951705ff2 100644 --- a/Engine/Source/RHI-Vulkan/Include/RHI/Vulkan/Device.h +++ b/Engine/Source/RHI-Vulkan/Include/RHI/Vulkan/Device.h @@ -49,6 +49,7 @@ namespace RHI::Vulkan { VkDevice GetNative() const; VmaAllocator& GetNativeAllocator(); const std::vector& GetActiveQueueFamilyIndices() const; + const VkPhysicalDeviceFeatures& GetEnabledFeatures() const; #if BUILD_CONFIG_DEBUG void SetObjectName(VkObjectType inObjectType, uint64_t inObjectHandle, const char* inObjectName) const; @@ -67,6 +68,7 @@ namespace RHI::Vulkan { VulkanGpu& gpu; VkDevice nativeDevice; VmaAllocator nativeAllocator; + VkPhysicalDeviceFeatures enabledFeatures; std::vector activeQueueFamilyIndices; std::unordered_map queueFamilyMappings; std::unordered_map>> queues; diff --git a/Engine/Source/RHI-Vulkan/Src/CommandRecorder.cpp b/Engine/Source/RHI-Vulkan/Src/CommandRecorder.cpp index f27b22a24..a16938138 100644 --- a/Engine/Source/RHI-Vulkan/Src/CommandRecorder.cpp +++ b/Engine/Source/RHI-Vulkan/Src/CommandRecorder.cpp @@ -629,7 +629,8 @@ namespace RHI::Vulkan { { activeOcclusionQuerySet = static_cast(inQuerySet); activeOcclusionQueryIndex = inQueryIndex; - vkCmdBeginQuery(commandBuffer.GetNative(), activeOcclusionQuerySet->GetNative(), inQueryIndex, VK_QUERY_CONTROL_PRECISE_BIT); + const VkQueryControlFlags queryFlags = device.GetEnabledFeatures().occlusionQueryPrecise == VK_TRUE ? VK_QUERY_CONTROL_PRECISE_BIT : 0; + vkCmdBeginQuery(commandBuffer.GetNative(), activeOcclusionQuerySet->GetNative(), inQueryIndex, queryFlags); } void VulkanRasterPassCommandRecorder::EndOcclusionQuery() diff --git a/Engine/Source/RHI-Vulkan/Src/Device.cpp b/Engine/Source/RHI-Vulkan/Src/Device.cpp index 065f44dbc..eb0d581bc 100644 --- a/Engine/Source/RHI-Vulkan/Src/Device.cpp +++ b/Engine/Source/RHI-Vulkan/Src/Device.cpp @@ -261,6 +261,11 @@ namespace RHI::Vulkan { return activeQueueFamilyIndices; } + const VkPhysicalDeviceFeatures& VulkanDevice::GetEnabledFeatures() const + { + return enabledFeatures; + } + void VulkanDevice::CreateNativeDevice(const DeviceCreateInfo& inCreateInfo) { uint32_t queueFamilyPropertyCnt = 0; @@ -324,28 +329,49 @@ namespace RHI::Vulkan { queueCreateInfos.emplace_back(queueCreateInfo); } - VkPhysicalDeviceFeatures deviceFeatures = {}; - deviceFeatures.samplerAnisotropy = VK_TRUE; - deviceFeatures.occlusionQueryPrecise = VK_TRUE; + VkPhysicalDeviceDynamicRenderingFeatures supportedDynamicRenderingFeatures = {}; + supportedDynamicRenderingFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES; + + VkPhysicalDeviceExtendedDynamicStateFeaturesEXT supportedExtendedDynamicStateFeatures = {}; + supportedExtendedDynamicStateFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT; + supportedDynamicRenderingFeatures.pNext = &supportedExtendedDynamicStateFeatures; + + VkPhysicalDeviceFeatures2 supportedFeatures = {}; + supportedFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; + supportedFeatures.pNext = &supportedDynamicRenderingFeatures; + vkGetPhysicalDeviceFeatures2(gpu.GetNative(), &supportedFeatures); + + if (supportedDynamicRenderingFeatures.dynamicRendering != VK_TRUE) { + QuickFailWithReason("required vulkan dynamic rendering feature is not supported"); + } + if (supportedExtendedDynamicStateFeatures.extendedDynamicState != VK_TRUE) { + QuickFailWithReason("required vulkan extended dynamic state feature is not supported"); + } + + enabledFeatures = {}; + enabledFeatures.independentBlend = supportedFeatures.features.independentBlend; + enabledFeatures.multiDrawIndirect = supportedFeatures.features.multiDrawIndirect; + enabledFeatures.drawIndirectFirstInstance = supportedFeatures.features.drawIndirectFirstInstance; + enabledFeatures.fillModeNonSolid = supportedFeatures.features.fillModeNonSolid; + enabledFeatures.samplerAnisotropy = supportedFeatures.features.samplerAnisotropy; + enabledFeatures.textureCompressionBC = supportedFeatures.features.textureCompressionBC; + enabledFeatures.occlusionQueryPrecise = supportedFeatures.features.occlusionQueryPrecise; VkDeviceCreateInfo deviceCreateInfo = {}; deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; deviceCreateInfo.queueCreateInfoCount = queueCreateInfos.size(); deviceCreateInfo.pQueueCreateInfos = queueCreateInfos.data(); - deviceCreateInfo.pEnabledFeatures = &deviceFeatures; + deviceCreateInfo.pEnabledFeatures = &enabledFeatures; VkPhysicalDeviceDynamicRenderingFeatures dynamicRenderingFeatures = {}; dynamicRenderingFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES; dynamicRenderingFeatures.dynamicRendering = VK_TRUE; deviceCreateInfo.pNext = &dynamicRenderingFeatures; -#if PLATFORM_MACOS - // MoltenVK not support use vkCmdSetPrimitiveTopology() directly current VkPhysicalDeviceExtendedDynamicStateFeaturesEXT extendedDynamicStateFeatures = {}; extendedDynamicStateFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT; extendedDynamicStateFeatures.extendedDynamicState = VK_TRUE; dynamicRenderingFeatures.pNext = &extendedDynamicStateFeatures; -#endif deviceCreateInfo.ppEnabledExtensionNames = requiredExtensions.data(); deviceCreateInfo.enabledExtensionCount = static_cast(requiredExtensions.size()); From e4ef4828f4bc450589e1436c16180d825a58a158 Mon Sep 17 00:00:00 2001 From: kindem Date: Sat, 1 Aug 2026 13:05:26 +0800 Subject: [PATCH 02/14] fix: set DX12 instance data step rate --- Engine/Source/RHI-DirectX12/Src/Pipeline.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Engine/Source/RHI-DirectX12/Src/Pipeline.cpp b/Engine/Source/RHI-DirectX12/Src/Pipeline.cpp index 1e0ef56e1..9b94d49a6 100644 --- a/Engine/Source/RHI-DirectX12/Src/Pipeline.cpp +++ b/Engine/Source/RHI-DirectX12/Src/Pipeline.cpp @@ -129,6 +129,7 @@ namespace RHI::DirectX12 { desc.Format = EnumCast(attribute.format); desc.InputSlot = i; desc.InputSlotClass = EnumCast(layout.stepMode); + desc.InstanceDataStepRate = layout.stepMode == VertexStepMode::perInstance ? 1 : 0; desc.AlignedByteOffset = attribute.offset; desc.SemanticName = vertexBinding.semanticName.c_str(); desc.SemanticIndex = vertexBinding.semanticIndex; From b0192527abc82815967e1a33c55e3e366c0d8617 Mon Sep 17 00:00:00 2001 From: kindem Date: Sat, 1 Aug 2026 13:11:43 +0800 Subject: [PATCH 03/14] fix: enable DX12 comparison and anisotropic filtering --- Engine/Source/RHI-DirectX12/Src/Sampler.cpp | 30 ++++++++++----------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/Engine/Source/RHI-DirectX12/Src/Sampler.cpp b/Engine/Source/RHI-DirectX12/Src/Sampler.cpp index 66fff06c5..5f449898f 100644 --- a/Engine/Source/RHI-DirectX12/Src/Sampler.cpp +++ b/Engine/Source/RHI-DirectX12/Src/Sampler.cpp @@ -6,21 +6,21 @@ #include #include -namespace RHI::DirectX12 { - static D3D12_FILTER GetDX12Filter(const SamplerCreateInfo& createInfo) +namespace RHI::DirectX12::Internal { + static D3D12_FILTER_TYPE GetNativeFilterType(const FilterMode inFilterMode) + { + return inFilterMode == FilterMode::linear ? D3D12_FILTER_TYPE_LINEAR : D3D12_FILTER_TYPE_POINT; + } + + static D3D12_FILTER GetNativeFilter(const SamplerCreateInfo& inCreateInfo) { - const auto& minFilter = createInfo.minFilter; - const auto& magFilter = createInfo.magFilter; - const auto& mipFilter = createInfo.mipFilter; - if (minFilter == FilterMode::nearest && magFilter == FilterMode::nearest && mipFilter == FilterMode::nearest) { return D3D12_FILTER_MIN_MAG_MIP_POINT; } - if (minFilter == FilterMode::nearest && magFilter == FilterMode::nearest && mipFilter == FilterMode::linear) { return D3D12_FILTER_MIN_MAG_POINT_MIP_LINEAR; } - if (minFilter == FilterMode::nearest && magFilter == FilterMode::linear && mipFilter == FilterMode::nearest) { return D3D12_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT; } - if (minFilter == FilterMode::nearest && magFilter == FilterMode::linear && mipFilter == FilterMode::linear) { return D3D12_FILTER_MIN_POINT_MAG_MIP_LINEAR; } - if (minFilter == FilterMode::linear && magFilter == FilterMode::nearest && mipFilter == FilterMode::nearest) { return D3D12_FILTER_MIN_LINEAR_MAG_MIP_POINT; } - if (minFilter == FilterMode::linear && magFilter == FilterMode::nearest && mipFilter == FilterMode::linear) { return D3D12_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR; } - if (minFilter == FilterMode::linear && magFilter == FilterMode::linear && mipFilter == FilterMode::nearest) { return D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT; } - if (minFilter == FilterMode::linear && magFilter == FilterMode::linear && mipFilter == FilterMode::linear) { return D3D12_FILTER_MIN_MAG_MIP_LINEAR; } - return D3D12_FILTER_MIN_MAG_MIP_POINT; + const D3D12_FILTER_REDUCTION_TYPE reductionType = inCreateInfo.comparisonFunc == CompareFunc::never + ? D3D12_FILTER_REDUCTION_TYPE_STANDARD + : D3D12_FILTER_REDUCTION_TYPE_COMPARISON; + if (inCreateInfo.maxAnisotropy > 1) { + return D3D12_ENCODE_ANISOTROPIC_FILTER(reductionType); + } + return D3D12_ENCODE_BASIC_FILTER(GetNativeFilterType(inCreateInfo.minFilter), GetNativeFilterType(inCreateInfo.magFilter), GetNativeFilterType(inCreateInfo.mipFilter), reductionType); } } @@ -44,7 +44,7 @@ namespace RHI::DirectX12 { desc.AddressU = EnumCast(inCreateInfo.addressModeU); desc.AddressV = EnumCast(inCreateInfo.addressModeV); desc.AddressW = EnumCast(inCreateInfo.addressModeW); - desc.Filter = GetDX12Filter(inCreateInfo); + desc.Filter = Internal::GetNativeFilter(inCreateInfo); desc.MinLOD = inCreateInfo.lodMinClamp; desc.MaxLOD = inCreateInfo.lodMaxClamp; desc.ComparisonFunc = EnumCast(inCreateInfo.comparisonFunc); From d60e1409cf1c4c23b41c796150045931d0c45fab Mon Sep 17 00:00:00 2001 From: kindem Date: Sat, 1 Aug 2026 13:56:15 +0800 Subject: [PATCH 04/14] fix: make buffer view offsets consistent --- Editor/Src/EditorWindow.cpp | 12 ++-- .../Include/RHI/DirectX12/Buffer.h | 2 +- Engine/Source/RHI-DirectX12/Src/Buffer.cpp | 2 +- .../Source/RHI-DirectX12/Src/BufferView.cpp | 46 +++++++++---- .../RHI-DirectX12/Src/CommandRecorder.cpp | 2 + .../RHI-Dummy/Include/RHI/Dummy/Buffer.h | 3 +- Engine/Source/RHI-Dummy/Src/Buffer.cpp | 2 +- .../RHI-Vulkan/Include/RHI/Vulkan/Buffer.h | 2 +- .../Include/RHI/Vulkan/BufferView.h | 8 --- Engine/Source/RHI-Vulkan/Src/BindGroup.cpp | 5 +- Engine/Source/RHI-Vulkan/Src/Buffer.cpp | 2 +- Engine/Source/RHI-Vulkan/Src/BufferView.cpp | 36 +--------- .../Source/RHI-Vulkan/Src/CommandRecorder.cpp | 19 ++++-- Engine/Source/RHI/Include/RHI/Buffer.h | 3 +- Engine/Source/RHI/Include/RHI/BufferView.h | 18 +++-- Engine/Source/RHI/Src/Buffer.cpp | 65 +++++++++++++++++++ Engine/Source/RHI/Src/BufferView.cpp | 26 +++++--- Engine/Source/Render/Src/RenderCache.cpp | 4 +- 18 files changed, 160 insertions(+), 97 deletions(-) diff --git a/Editor/Src/EditorWindow.cpp b/Editor/Src/EditorWindow.cpp index 15f1a8fea..543414a52 100644 --- a/Editor/Src/EditorWindow.cpp +++ b/Editor/Src/EditorWindow.cpp @@ -361,8 +361,8 @@ namespace Editor { imguiPassParamsBufferView = imguiPassParamsBuffer->CreateBufferView( RHI::BufferViewCreateInfo() .SetType(RHI::BufferViewType::uniformBinding) - .SetSize(sizeof(ImGuiPassParams)) - .SetOffset(0)); + .SetSizeInBytes(sizeof(ImGuiPassParams)) + .SetOffsetInBytes(0)); imguiBindGroupLayout = device.CreateBindGroupLayout( RHI::BindGroupLayoutCreateInfo(0, "imguiBindGroupLayout") @@ -487,8 +487,8 @@ namespace Editor { imguiVertexBufferView = imguiVertexBuffer->CreateBufferView( RHI::BufferViewCreateInfo() .SetType(RHI::BufferViewType::vertex) - .SetSize(vertexBufferSize) - .SetOffset(0) + .SetSizeInBytes(vertexBufferSize) + .SetOffsetInBytes(0) .SetExtendVertex(sizeof(ImDrawVert))); const uint32_t indexBufferSize = static_cast(imguiIndexBufferCapacity * sizeof(ImDrawIdx)); @@ -501,8 +501,8 @@ namespace Editor { imguiIndexBufferView = imguiIndexBuffer->CreateBufferView( RHI::BufferViewCreateInfo() .SetType(RHI::BufferViewType::index) - .SetSize(indexBufferSize) - .SetOffset(0) + .SetSizeInBytes(indexBufferSize) + .SetOffsetInBytes(0) .SetExtendIndex(Internal::imguiIndexFormat)); } diff --git a/Engine/Source/RHI-DirectX12/Include/RHI/DirectX12/Buffer.h b/Engine/Source/RHI-DirectX12/Include/RHI/DirectX12/Buffer.h index b9b22d10a..58cf1ce52 100644 --- a/Engine/Source/RHI-DirectX12/Include/RHI/DirectX12/Buffer.h +++ b/Engine/Source/RHI-DirectX12/Include/RHI/DirectX12/Buffer.h @@ -22,13 +22,13 @@ namespace RHI::DirectX12 { void* Map(MapMode inMapMode, size_t inOffset, size_t inLength) override; void Unmap() override; - Common::UniquePtr CreateBufferView(const BufferViewCreateInfo& inCreateInfo) override; ID3D12Resource* GetNative() const; DX12Device& GetDevice() const; BufferUsageFlags GetUsages() const; private: + Common::UniquePtr CreateBufferViewInternal(const BufferViewCreateInfo& inCreateInfo) override; void CreateNativeBuffer(DX12Device& inDevice, const BufferCreateInfo& inCreateInfo); DX12Device& device; diff --git a/Engine/Source/RHI-DirectX12/Src/Buffer.cpp b/Engine/Source/RHI-DirectX12/Src/Buffer.cpp index b9afe4fd9..5ff927c5c 100644 --- a/Engine/Source/RHI-DirectX12/Src/Buffer.cpp +++ b/Engine/Source/RHI-DirectX12/Src/Buffer.cpp @@ -89,7 +89,7 @@ namespace RHI::DirectX12 { nativeResource->Unmap(0, nullptr); } - Common::UniquePtr DX12Buffer::CreateBufferView(const BufferViewCreateInfo& inCreateInfo) + Common::UniquePtr DX12Buffer::CreateBufferViewInternal(const BufferViewCreateInfo& inCreateInfo) { return Common::UniquePtr(new DX12BufferView(*this, inCreateInfo)); } diff --git a/Engine/Source/RHI-DirectX12/Src/BufferView.cpp b/Engine/Source/RHI-DirectX12/Src/BufferView.cpp index 9e1a0fe45..4c8fefe87 100644 --- a/Engine/Source/RHI-DirectX12/Src/BufferView.cpp +++ b/Engine/Source/RHI-DirectX12/Src/BufferView.cpp @@ -9,6 +9,24 @@ #include #include +namespace RHI::DirectX12::Internal { + struct StructuredBufferRange { + UINT64 firstElement; + UINT elementCount; + UINT stride; + }; + + static StructuredBufferRange GetStructuredBufferRange(const BufferViewCreateInfo& inCreateInfo) + { + const auto& storageViewInfo = std::get(inCreateInfo.extend); + return { + inCreateInfo.offsetInBytes / storageViewInfo.stride, + inCreateInfo.sizeInBytes / storageViewInfo.stride, + storageViewInfo.stride + }; + } +} + namespace RHI::DirectX12 { DX12BufferView::DX12BufferView(DX12Buffer& inBuffer, const BufferViewCreateInfo& inCreateInfo) : BufferView(inCreateInfo), buffer(inBuffer) @@ -41,36 +59,36 @@ namespace RHI::DirectX12 { Assert((bufferUsages & BufferUsageBits::uniform) != 0); D3D12_CONSTANT_BUFFER_VIEW_DESC desc {}; - desc.BufferLocation = buffer.GetNative()->GetGPUVirtualAddress() + inCreateInfo.offset; - desc.SizeInBytes = Common::AlignUp(inCreateInfo.size); + desc.BufferLocation = buffer.GetNative()->GetGPUVirtualAddress() + inCreateInfo.offsetInBytes; + desc.SizeInBytes = Common::AlignUp(inCreateInfo.sizeInBytes); nativeView = buffer.GetDevice().AllocateCbvSrvUavDescriptor(); buffer.GetDevice().GetNative()->CreateConstantBufferView(&desc, std::get>(nativeView)->GetCpuHandle()); } else if (inCreateInfo.type == BufferViewType::storageBinding) { Assert((bufferUsages & BufferUsageBits::storage) != 0); - auto storageViewInfo = std::get(inCreateInfo.extend); + const auto range = Internal::GetStructuredBufferRange(inCreateInfo); D3D12_SHADER_RESOURCE_VIEW_DESC desc {}; desc.Format = DXGI_FORMAT_UNKNOWN; desc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER; desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; - desc.Buffer.FirstElement = inCreateInfo.offset; - desc.Buffer.NumElements = inCreateInfo.size / storageViewInfo.stride; - desc.Buffer.StructureByteStride = storageViewInfo.stride; + desc.Buffer.FirstElement = range.firstElement; + desc.Buffer.NumElements = range.elementCount; + desc.Buffer.StructureByteStride = range.stride; nativeView = buffer.GetDevice().AllocateCbvSrvUavDescriptor(); buffer.GetDevice().GetNative()->CreateShaderResourceView(buffer.GetNative(), &desc, std::get>(nativeView)->GetCpuHandle()); } else if (inCreateInfo.type == BufferViewType::rwStorageBinding) { Assert((bufferUsages & BufferUsageBits::rwStorage) != 0); - auto storageViewInfo = std::get(inCreateInfo.extend); + const auto range = Internal::GetStructuredBufferRange(inCreateInfo); // TODO: check the uav typed load when it is necessary D3D12_UNORDERED_ACCESS_VIEW_DESC desc {}; desc.Format = DXGI_FORMAT_UNKNOWN; desc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER; - desc.Buffer.FirstElement = inCreateInfo.offset; - desc.Buffer.NumElements = inCreateInfo.size / storageViewInfo.stride; - desc.Buffer.StructureByteStride = storageViewInfo.stride; + desc.Buffer.FirstElement = range.firstElement; + desc.Buffer.NumElements = range.elementCount; + desc.Buffer.StructureByteStride = range.stride; nativeView = buffer.GetDevice().AllocateCbvSrvUavDescriptor(); buffer.GetDevice().GetNative()->CreateUnorderedAccessView(buffer.GetNative(), nullptr, &desc, std::get>(nativeView)->GetCpuHandle()); @@ -79,16 +97,16 @@ namespace RHI::DirectX12 { nativeView = D3D12_VERTEX_BUFFER_VIEW(); D3D12_VERTEX_BUFFER_VIEW& vertexBufferView = std::get(nativeView); - vertexBufferView.BufferLocation = buffer.GetNative()->GetGPUVirtualAddress() + inCreateInfo.offset; - vertexBufferView.SizeInBytes = inCreateInfo.size; + vertexBufferView.BufferLocation = buffer.GetNative()->GetGPUVirtualAddress() + inCreateInfo.offsetInBytes; + vertexBufferView.SizeInBytes = inCreateInfo.sizeInBytes; vertexBufferView.StrideInBytes = std::get(inCreateInfo.extend).stride; } else if (inCreateInfo.type == BufferViewType::index) { Assert((bufferUsages & BufferUsageBits::index) != 0); nativeView = D3D12_INDEX_BUFFER_VIEW(); D3D12_INDEX_BUFFER_VIEW& indexBufferView = std::get(nativeView); - indexBufferView.BufferLocation = buffer.GetNative()->GetGPUVirtualAddress() + inCreateInfo.offset; - indexBufferView.SizeInBytes = inCreateInfo.size; + indexBufferView.BufferLocation = buffer.GetNative()->GetGPUVirtualAddress() + inCreateInfo.offsetInBytes; + indexBufferView.SizeInBytes = inCreateInfo.sizeInBytes; indexBufferView.Format = EnumCast(std::get(inCreateInfo.extend).format); } else { Unimplement(); diff --git a/Engine/Source/RHI-DirectX12/Src/CommandRecorder.cpp b/Engine/Source/RHI-DirectX12/Src/CommandRecorder.cpp index c755c3619..842dcf97c 100644 --- a/Engine/Source/RHI-DirectX12/Src/CommandRecorder.cpp +++ b/Engine/Source/RHI-DirectX12/Src/CommandRecorder.cpp @@ -345,12 +345,14 @@ namespace RHI::DirectX12 { void DX12RasterPassCommandRecorder::SetIndexBuffer(BufferView* inBufferView) { const auto* bufferView = static_cast(inBufferView); + Assert(bufferView->GetCreateInfo().type == BufferViewType::index); commandBuffer.GetNativeCmdList()->IASetIndexBuffer(&bufferView->GetNativeIndexBufferView()); } void DX12RasterPassCommandRecorder::SetVertexBuffer(const size_t inSlot, BufferView* inBufferView) { const auto* bufferView = static_cast(inBufferView); + Assert(bufferView->GetCreateInfo().type == BufferViewType::vertex); commandBuffer.GetNativeCmdList()->IASetVertexBuffers(inSlot, 1, &bufferView->GetNativeVertexBufferView()); } diff --git a/Engine/Source/RHI-Dummy/Include/RHI/Dummy/Buffer.h b/Engine/Source/RHI-Dummy/Include/RHI/Dummy/Buffer.h index 763b2caa1..c430f4818 100644 --- a/Engine/Source/RHI-Dummy/Include/RHI/Dummy/Buffer.h +++ b/Engine/Source/RHI-Dummy/Include/RHI/Dummy/Buffer.h @@ -16,8 +16,9 @@ namespace RHI::Dummy { void* Map(MapMode mapMode, size_t offset, size_t length) override; void Unmap() override; - Common::UniquePtr CreateBufferView(const BufferViewCreateInfo& createInfo) override; private: + Common::UniquePtr CreateBufferViewInternal(const BufferViewCreateInfo& createInfo) override; + std::vector dummyData; }; } diff --git a/Engine/Source/RHI-Dummy/Src/Buffer.cpp b/Engine/Source/RHI-Dummy/Src/Buffer.cpp index 00d43d0b0..d580c953f 100644 --- a/Engine/Source/RHI-Dummy/Src/Buffer.cpp +++ b/Engine/Source/RHI-Dummy/Src/Buffer.cpp @@ -26,7 +26,7 @@ namespace RHI::Dummy { { } - Common::UniquePtr DummyBuffer::CreateBufferView(const BufferViewCreateInfo& createInfo) + Common::UniquePtr DummyBuffer::CreateBufferViewInternal(const BufferViewCreateInfo& createInfo) { return Common::UniquePtr(new DummyBufferView(createInfo)); } diff --git a/Engine/Source/RHI-Vulkan/Include/RHI/Vulkan/Buffer.h b/Engine/Source/RHI-Vulkan/Include/RHI/Vulkan/Buffer.h index 8a679008e..dcaa5c3e6 100644 --- a/Engine/Source/RHI-Vulkan/Include/RHI/Vulkan/Buffer.h +++ b/Engine/Source/RHI-Vulkan/Include/RHI/Vulkan/Buffer.h @@ -21,12 +21,12 @@ namespace RHI::Vulkan { void* Map(MapMode inMapMode, size_t inOffset, size_t inLength) override; void Unmap() override; - Common::UniquePtr CreateBufferView(const BufferViewCreateInfo& inCreateInfo) override; VkBuffer GetNative() const; BufferUsageFlags GetUsages() const; private: + Common::UniquePtr CreateBufferViewInternal(const BufferViewCreateInfo& inCreateInfo) override; void CreateNativeBuffer(const BufferCreateInfo& inCreateInfo); void TransitionToInitState(const BufferCreateInfo& inCreateInfo); diff --git a/Engine/Source/RHI-Vulkan/Include/RHI/Vulkan/BufferView.h b/Engine/Source/RHI-Vulkan/Include/RHI/Vulkan/BufferView.h index 857ced249..1a0b9745b 100644 --- a/Engine/Source/RHI-Vulkan/Include/RHI/Vulkan/BufferView.h +++ b/Engine/Source/RHI-Vulkan/Include/RHI/Vulkan/BufferView.h @@ -16,17 +16,9 @@ namespace RHI::Vulkan { VulkanBufferView(VulkanBuffer& inBuffer, const BufferViewCreateInfo& inCreateInfo); ~VulkanBufferView() override; - size_t GetOffset() const; - size_t GetBufferSize() const; - IndexFormat GetIndexFormat() const; VulkanBuffer& GetBuffer() const; private: - void InitializeBufferAttrib(const BufferViewCreateInfo& inCreateInfo); - VulkanBuffer& buffer; - size_t size; - size_t offset; - IndexFormat indexFormat; }; } diff --git a/Engine/Source/RHI-Vulkan/Src/BindGroup.cpp b/Engine/Source/RHI-Vulkan/Src/BindGroup.cpp index 008b97929..2e3efb6b8 100644 --- a/Engine/Source/RHI-Vulkan/Src/BindGroup.cpp +++ b/Engine/Source/RHI-Vulkan/Src/BindGroup.cpp @@ -108,11 +108,12 @@ namespace RHI::Vulkan { || entry.binding.type == BindingType::storageBuffer || entry.binding.type == BindingType::rwStorageBuffer) { auto* bufferView = static_cast(std::get(entry.entity)); + const auto& bufferViewCreateInfo = bufferView->GetCreateInfo(); bufferInfos.emplace_back(); bufferInfos.back().buffer = bufferView->GetBuffer().GetNative(); - bufferInfos.back().offset = bufferView->GetOffset(); - bufferInfos.back().range = bufferView->GetBufferSize(); + bufferInfos.back().offset = bufferViewCreateInfo.offsetInBytes; + bufferInfos.back().range = bufferViewCreateInfo.sizeInBytes; descriptorWrites[i].pBufferInfo = &bufferInfos.back(); } else if (entry.binding.type == BindingType::sampler) { diff --git a/Engine/Source/RHI-Vulkan/Src/Buffer.cpp b/Engine/Source/RHI-Vulkan/Src/Buffer.cpp index fa3eb4db3..77b94c810 100644 --- a/Engine/Source/RHI-Vulkan/Src/Buffer.cpp +++ b/Engine/Source/RHI-Vulkan/Src/Buffer.cpp @@ -53,7 +53,7 @@ namespace RHI::Vulkan { vmaUnmapMemory(device.GetNativeAllocator(), nativeAllocation); } - Common::UniquePtr VulkanBuffer::CreateBufferView(const BufferViewCreateInfo& inCreateInfo) + Common::UniquePtr VulkanBuffer::CreateBufferViewInternal(const BufferViewCreateInfo& inCreateInfo) { return Common::UniquePtr(new VulkanBufferView(*this, inCreateInfo)); } diff --git a/Engine/Source/RHI-Vulkan/Src/BufferView.cpp b/Engine/Source/RHI-Vulkan/Src/BufferView.cpp index d78b0e4c4..54c7c6cc6 100644 --- a/Engine/Source/RHI-Vulkan/Src/BufferView.cpp +++ b/Engine/Source/RHI-Vulkan/Src/BufferView.cpp @@ -2,54 +2,20 @@ // Created by swtpotato on 2022/8/2. // -#include - #include #include -namespace RHI::Vulkan { - static bool IsIndexBuffer(const BufferUsageFlags bufferUsages) - { - return (bufferUsages & BufferUsageBits::index) != 0; - } -} - namespace RHI::Vulkan { VulkanBufferView::VulkanBufferView(VulkanBuffer& inBuffer, const BufferViewCreateInfo& inCreateInfo) : BufferView(inCreateInfo) , buffer(inBuffer) { - InitializeBufferAttrib(inCreateInfo); } VulkanBufferView::~VulkanBufferView() = default; - void VulkanBufferView::InitializeBufferAttrib(const BufferViewCreateInfo& inCreateInfo) - { - offset = inCreateInfo.offset; - size = inCreateInfo.size; - if (IsIndexBuffer(buffer.GetUsages())) { - indexFormat = std::get(inCreateInfo.extend).format; - } - } - - size_t VulkanBufferView::GetBufferSize() const - { - return size; - } - - size_t VulkanBufferView::GetOffset() const - { - return offset; - } - - IndexFormat VulkanBufferView::GetIndexFormat() const - { - return indexFormat; - } - VulkanBuffer& VulkanBufferView::GetBuffer() const { return buffer; } -} \ No newline at end of file +} diff --git a/Engine/Source/RHI-Vulkan/Src/CommandRecorder.cpp b/Engine/Source/RHI-Vulkan/Src/CommandRecorder.cpp index a16938138..e90208825 100644 --- a/Engine/Source/RHI-Vulkan/Src/CommandRecorder.cpp +++ b/Engine/Source/RHI-Vulkan/Src/CommandRecorder.cpp @@ -534,20 +534,25 @@ namespace RHI::Vulkan { void VulkanRasterPassCommandRecorder::SetIndexBuffer(BufferView *inBufferView) { - const auto* mBufferView = static_cast(inBufferView); + const auto* bufferView = static_cast(inBufferView); + const auto& createInfo = bufferView->GetCreateInfo(); + Assert(createInfo.type == BufferViewType::index); - const VkBuffer indexBuffer = mBufferView->GetBuffer().GetNative(); - const auto vkFormat = EnumCast(mBufferView->GetIndexFormat()); + const VkBuffer indexBuffer = bufferView->GetBuffer().GetNative(); + const auto indexFormat = std::get(createInfo.extend).format; + const auto vkFormat = EnumCast(indexFormat); - vkCmdBindIndexBuffer(commandBuffer.GetNative(), indexBuffer, 0, vkFormat); + vkCmdBindIndexBuffer(commandBuffer.GetNative(), indexBuffer, createInfo.offsetInBytes, vkFormat); } void VulkanRasterPassCommandRecorder::SetVertexBuffer(size_t inSlot, BufferView *inBufferView) { - const auto* mBufferView = static_cast(inBufferView); + const auto* bufferView = static_cast(inBufferView); + const auto& createInfo = bufferView->GetCreateInfo(); + Assert(createInfo.type == BufferViewType::vertex); - const VkBuffer vertexBuffer = mBufferView->GetBuffer().GetNative(); - const VkDeviceSize offset[] = { mBufferView->GetOffset() }; + const VkBuffer vertexBuffer = bufferView->GetBuffer().GetNative(); + const VkDeviceSize offset[] = { createInfo.offsetInBytes }; vkCmdBindVertexBuffers(commandBuffer.GetNative(), inSlot, 1, &vertexBuffer, offset); } diff --git a/Engine/Source/RHI/Include/RHI/Buffer.h b/Engine/Source/RHI/Include/RHI/Buffer.h index 77f5320bf..22e35ed5d 100644 --- a/Engine/Source/RHI/Include/RHI/Buffer.h +++ b/Engine/Source/RHI/Include/RHI/Buffer.h @@ -37,10 +37,11 @@ namespace RHI { const BufferCreateInfo& GetCreateInfo() const; virtual void* Map(MapMode mapMode, size_t offset, size_t length) = 0; virtual void Unmap() = 0; - virtual Common::UniquePtr CreateBufferView(const BufferViewCreateInfo& createInfo) = 0; + Common::UniquePtr CreateBufferView(const BufferViewCreateInfo& createInfo); protected: explicit Buffer(const BufferCreateInfo& inCreateInfo); + virtual Common::UniquePtr CreateBufferViewInternal(const BufferViewCreateInfo& createInfo) = 0; BufferCreateInfo createInfo; }; diff --git a/Engine/Source/RHI/Include/RHI/BufferView.h b/Engine/Source/RHI/Include/RHI/BufferView.h index f3c0296ec..34520e47c 100644 --- a/Engine/Source/RHI/Include/RHI/BufferView.h +++ b/Engine/Source/RHI/Include/RHI/BufferView.h @@ -31,19 +31,19 @@ namespace RHI { struct BufferViewCreateInfo { BufferViewType type; - uint32_t size; - uint32_t offset; + uint32_t sizeInBytes; + uint32_t offsetInBytes; std::variant extend; explicit BufferViewCreateInfo( BufferViewType inType = BufferViewType::max, - uint32_t inSize = 0, - uint32_t inOffset = 0, + uint32_t inSizeInBytes = 0, + uint32_t inOffsetInBytes = 0, const std::variant& inExtent = {}); BufferViewCreateInfo& SetType(BufferViewType inType); - BufferViewCreateInfo& SetOffset(uint32_t inOffset); - BufferViewCreateInfo& SetSize(uint32_t inSize); + BufferViewCreateInfo& SetOffsetInBytes(uint32_t inOffsetInBytes); + BufferViewCreateInfo& SetSizeInBytes(uint32_t inSizeInBytes); BufferViewCreateInfo& SetExtendVertex(uint32_t inStride); BufferViewCreateInfo& SetExtendIndex(IndexFormat inFormat); BufferViewCreateInfo& SetExtendStorage(uint32_t inStride); @@ -54,7 +54,11 @@ namespace RHI { NonCopyable(BufferView) virtual ~BufferView(); + const BufferViewCreateInfo& GetCreateInfo() const; + protected: - explicit BufferView(const BufferViewCreateInfo& createInfo); + explicit BufferView(const BufferViewCreateInfo& inCreateInfo); + + BufferViewCreateInfo createInfo; }; } diff --git a/Engine/Source/RHI/Src/Buffer.cpp b/Engine/Source/RHI/Src/Buffer.cpp index a1d9479d7..cca632c2e 100644 --- a/Engine/Source/RHI/Src/Buffer.cpp +++ b/Engine/Source/RHI/Src/Buffer.cpp @@ -3,6 +3,65 @@ // #include +#include + +namespace RHI::Internal { + static BufferUsageBits GetRequiredBufferUsage(const BufferViewType type) + { + switch (type) { + case BufferViewType::vertex: + return BufferUsageBits::vertex; + case BufferViewType::index: + return BufferUsageBits::index; + case BufferViewType::uniformBinding: + return BufferUsageBits::uniform; + case BufferViewType::storageBinding: + return BufferUsageBits::storage; + case BufferViewType::rwStorageBinding: + return BufferUsageBits::rwStorage; + default: + Unimplement(); + return BufferUsageBits::max; + } + } + + static uint32_t GetIndexElementSize(const IndexFormat format) + { + switch (format) { + case IndexFormat::uint16: + return sizeof(uint16_t); + case IndexFormat::uint32: + return sizeof(uint32_t); + default: + Unimplement(); + return 0; + } + } + + static void ValidateBufferViewCreateInfo(const BufferCreateInfo& bufferCreateInfo, const BufferViewCreateInfo& viewCreateInfo) + { + Assert(viewCreateInfo.sizeInBytes > 0); + Assert(viewCreateInfo.offsetInBytes <= bufferCreateInfo.size); + Assert(viewCreateInfo.sizeInBytes <= bufferCreateInfo.size - viewCreateInfo.offsetInBytes); + Assert((bufferCreateInfo.usages & GetRequiredBufferUsage(viewCreateInfo.type)) != 0); + + if (viewCreateInfo.type == BufferViewType::vertex) { + Assert(std::holds_alternative(viewCreateInfo.extend)); + Assert(std::get(viewCreateInfo.extend).stride > 0); + } else if (viewCreateInfo.type == BufferViewType::index) { + Assert(std::holds_alternative(viewCreateInfo.extend)); + const uint32_t elementSize = GetIndexElementSize(std::get(viewCreateInfo.extend).format); + Assert(viewCreateInfo.offsetInBytes % elementSize == 0); + Assert(viewCreateInfo.sizeInBytes % elementSize == 0); + } else if (viewCreateInfo.type == BufferViewType::storageBinding || viewCreateInfo.type == BufferViewType::rwStorageBinding) { + Assert(std::holds_alternative(viewCreateInfo.extend)); + const uint32_t stride = std::get(viewCreateInfo.extend).stride; + Assert(stride > 0); + Assert(viewCreateInfo.offsetInBytes % stride == 0); + Assert(viewCreateInfo.sizeInBytes % stride == 0); + } + } +} namespace RHI { BufferCreateInfo::BufferCreateInfo() = default; @@ -55,4 +114,10 @@ namespace RHI { { return createInfo; } + + Common::UniquePtr Buffer::CreateBufferView(const BufferViewCreateInfo& inCreateInfo) + { + Internal::ValidateBufferViewCreateInfo(createInfo, inCreateInfo); + return CreateBufferViewInternal(inCreateInfo); + } } diff --git a/Engine/Source/RHI/Src/BufferView.cpp b/Engine/Source/RHI/Src/BufferView.cpp index ce040c7b7..ed18c4674 100644 --- a/Engine/Source/RHI/Src/BufferView.cpp +++ b/Engine/Source/RHI/Src/BufferView.cpp @@ -22,12 +22,12 @@ namespace RHI { BufferViewCreateInfo::BufferViewCreateInfo( const BufferViewType inType, - const uint32_t inSize, - const uint32_t inOffset, + const uint32_t inSizeInBytes, + const uint32_t inOffsetInBytes, const std::variant& inExtent) : type(inType) - , size(inSize) - , offset(inOffset) + , sizeInBytes(inSizeInBytes) + , offsetInBytes(inOffsetInBytes) , extend(inExtent) { } @@ -38,15 +38,15 @@ namespace RHI { return *this; } - BufferViewCreateInfo& BufferViewCreateInfo::SetOffset(const uint32_t inOffset) + BufferViewCreateInfo& BufferViewCreateInfo::SetOffsetInBytes(const uint32_t inOffsetInBytes) { - offset = inOffset; + offsetInBytes = inOffsetInBytes; return *this; } - BufferViewCreateInfo& BufferViewCreateInfo::SetSize(const uint32_t inSize) + BufferViewCreateInfo& BufferViewCreateInfo::SetSizeInBytes(const uint32_t inSizeInBytes) { - size = inSize; + sizeInBytes = inSizeInBytes; return *this; } @@ -68,7 +68,15 @@ namespace RHI { return *this; } - BufferView::BufferView(const BufferViewCreateInfo&) {} + BufferView::BufferView(const BufferViewCreateInfo& inCreateInfo) + : createInfo(inCreateInfo) + { + } BufferView::~BufferView() = default; + + const BufferViewCreateInfo& BufferView::GetCreateInfo() const + { + return createInfo; + } } diff --git a/Engine/Source/Render/Src/RenderCache.cpp b/Engine/Source/Render/Src/RenderCache.cpp index 6c3e3be58..45f5934cd 100644 --- a/Engine/Source/Render/Src/RenderCache.cpp +++ b/Engine/Source/Render/Src/RenderCache.cpp @@ -67,8 +67,8 @@ namespace Render::Internal { createInfo.extend); return CombineHashes({ static_cast(createInfo.type), - static_cast(createInfo.size), - static_cast(createInfo.offset), + static_cast(createInfo.sizeInBytes), + static_cast(createInfo.offsetInBytes), static_cast(createInfo.extend.index()), extendHash }); From 421eaed2500b9a3435489590b5fa5a4f72b4d5b9 Mon Sep 17 00:00:00 2001 From: kindem Date: Sat, 1 Aug 2026 14:56:10 +0800 Subject: [PATCH 05/14] fix: model cube-compatible textures in RHI --- Editor/Src/EditorWindow.cpp | 2 +- .../Include/RHI/DirectX12/Common.h | 11 ++- .../Include/RHI/DirectX12/Texture.h | 3 +- .../RHI-DirectX12/Src/CommandRecorder.cpp | 2 +- Engine/Source/RHI-DirectX12/Src/Device.cpp | 2 +- Engine/Source/RHI-DirectX12/Src/Gpu.cpp | 3 +- Engine/Source/RHI-DirectX12/Src/SwapChain.cpp | 2 +- Engine/Source/RHI-DirectX12/Src/Texture.cpp | 4 +- .../RHI-Dummy/Include/RHI/Dummy/Texture.h | 3 +- Engine/Source/RHI-Dummy/Src/Texture.cpp | 2 +- .../RHI-Vulkan/Include/RHI/Vulkan/Common.h | 12 ++- .../RHI-Vulkan/Include/RHI/Vulkan/Texture.h | 5 +- Engine/Source/RHI-Vulkan/Src/Device.cpp | 3 +- Engine/Source/RHI-Vulkan/Src/Gpu.cpp | 1 + Engine/Source/RHI-Vulkan/Src/SwapChain.cpp | 2 +- Engine/Source/RHI-Vulkan/Src/Texture.cpp | 16 +++- Engine/Source/RHI-Vulkan/Src/TextureView.cpp | 6 +- Engine/Source/RHI/Include/RHI/Common.h | 10 ++- Engine/Source/RHI/Include/RHI/Texture.h | 7 +- Engine/Source/RHI/Src/Texture.cpp | 83 ++++++++++++++++++- Engine/Source/Render/Src/Renderer.cpp | 2 +- Engine/Source/Render/Test/RenderGraphTest.cpp | 6 +- .../Source/Render/Test/ResourcePoolTest.cpp | 2 +- Engine/Source/Runtime/Src/Asset/Texture.cpp | 33 ++++---- Engine/Source/Runtime/Src/Canvas.cpp | 2 +- Sample/Rendering-BaseTexture/BaseTexture.cpp | 2 +- Sample/Rendering-SSAO/SSAOApplication.cpp | 16 ++-- 27 files changed, 174 insertions(+), 68 deletions(-) diff --git a/Editor/Src/EditorWindow.cpp b/Editor/Src/EditorWindow.cpp index 543414a52..1efc4cfc7 100644 --- a/Editor/Src/EditorWindow.cpp +++ b/Editor/Src/EditorWindow.cpp @@ -404,7 +404,7 @@ namespace Editor { imguiFontTexture = device.CreateTexture( RHI::TextureCreateInfo() - .SetDimension(RHI::TextureDimension::t2D) + .SetType(RHI::TextureType::t2D) .SetWidth(static_cast(width)) .SetHeight(static_cast(height)) .SetDepthOrArraySize(1) diff --git a/Engine/Source/RHI-DirectX12/Include/RHI/DirectX12/Common.h b/Engine/Source/RHI-DirectX12/Include/RHI/DirectX12/Common.h index 06eebdf9e..3cbb2cc6c 100644 --- a/Engine/Source/RHI-DirectX12/Include/RHI/DirectX12/Common.h +++ b/Engine/Source/RHI-DirectX12/Include/RHI/DirectX12/Common.h @@ -255,10 +255,13 @@ namespace RHI::DirectX12 { ECIMPL_ITEM(BufferState::indirect, D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT) ECIMPL_END(D3D12_RESOURCE_STATES) - ECIMPL_BEGIN(TextureDimension, D3D12_RESOURCE_DIMENSION) - ECIMPL_ITEM(TextureDimension::t1D, D3D12_RESOURCE_DIMENSION_TEXTURE1D) - ECIMPL_ITEM(TextureDimension::t2D, D3D12_RESOURCE_DIMENSION_TEXTURE2D) - ECIMPL_ITEM(TextureDimension::t3D, D3D12_RESOURCE_DIMENSION_TEXTURE3D) + ECIMPL_BEGIN(TextureType, D3D12_RESOURCE_DIMENSION) + ECIMPL_ITEM(TextureType::t1D, D3D12_RESOURCE_DIMENSION_TEXTURE1D) + ECIMPL_ITEM(TextureType::t2D, D3D12_RESOURCE_DIMENSION_TEXTURE2D) + ECIMPL_ITEM(TextureType::t2DArray, D3D12_RESOURCE_DIMENSION_TEXTURE2D) + ECIMPL_ITEM(TextureType::tCube, D3D12_RESOURCE_DIMENSION_TEXTURE2D) + ECIMPL_ITEM(TextureType::tCubeArray, D3D12_RESOURCE_DIMENSION_TEXTURE2D) + ECIMPL_ITEM(TextureType::t3D, D3D12_RESOURCE_DIMENSION_TEXTURE3D) ECIMPL_END(D3D12_RESOURCE_DIMENSION) ECIMPL_BEGIN(TextureState, D3D12_RESOURCE_STATES) diff --git a/Engine/Source/RHI-DirectX12/Include/RHI/DirectX12/Texture.h b/Engine/Source/RHI-DirectX12/Include/RHI/DirectX12/Texture.h index 1e00c4a98..9bd8f3fec 100644 --- a/Engine/Source/RHI-DirectX12/Include/RHI/DirectX12/Texture.h +++ b/Engine/Source/RHI-DirectX12/Include/RHI/DirectX12/Texture.h @@ -19,11 +19,10 @@ namespace RHI::DirectX12 { DX12Texture(DX12Device& inDevice, const TextureCreateInfo& inCreateInfo, ComPtr&& nativeResource); ~DX12Texture() override; - Common::UniquePtr CreateTextureView(const TextureViewCreateInfo& inCreateInfo) override; - ID3D12Resource* GetNative() const; private: + Common::UniquePtr CreateTextureViewInternal(const TextureViewCreateInfo& inCreateInfo) override; void CreateNativeTexture(const TextureCreateInfo& inCreateInfo); DX12Device& device; diff --git a/Engine/Source/RHI-DirectX12/Src/CommandRecorder.cpp b/Engine/Source/RHI-DirectX12/Src/CommandRecorder.cpp index 842dcf97c..570699a52 100644 --- a/Engine/Source/RHI-DirectX12/Src/CommandRecorder.cpp +++ b/Engine/Source/RHI-DirectX12/Src/CommandRecorder.cpp @@ -37,7 +37,7 @@ namespace RHI::DirectX12 { static size_t GetNativeSubResourceIndex(const DX12Texture& texture, const TextureSubResourceInfo& subResource) { const auto& createInfo = texture.GetCreateInfo(); - return D3D12CalcSubresource(subResource.mipLevel, subResource.arrayLayer, 0, createInfo.mipLevels, createInfo.dimension == TextureDimension::t3D ? 1 : createInfo.depthOrArraySize); + return D3D12CalcSubresource(subResource.mipLevel, subResource.arrayLayer, 0, createInfo.mipLevels, createInfo.type == TextureType::t3D ? 1 : createInfo.depthOrArraySize); } static CD3DX12_TEXTURE_COPY_LOCATION GetNativeTextureCopyLocation(const DX12Texture& texture, const TextureSubResourceInfo& subResource) diff --git a/Engine/Source/RHI-DirectX12/Src/Device.cpp b/Engine/Source/RHI-DirectX12/Src/Device.cpp index 950e19ea3..11036fb98 100644 --- a/Engine/Source/RHI-DirectX12/Src/Device.cpp +++ b/Engine/Source/RHI-DirectX12/Src/Device.cpp @@ -286,7 +286,7 @@ namespace RHI::DirectX12 { const auto createInfo = texture.GetCreateInfo(); const auto nativeResourceDesc = dx12Texture.GetNative()->GetDesc(); - const auto arraySize = createInfo.dimension == TextureDimension::t3D ? 1 : createInfo.depthOrArraySize; + const auto arraySize = createInfo.type == TextureType::t3D ? 1 : createInfo.depthOrArraySize; const size_t nativeSubResourceIndex = D3D12CalcSubresource(subResourceInfo.mipLevel, subResourceInfo.arrayLayer, 0, createInfo.mipLevels, arraySize); D3D12_PLACED_SUBRESOURCE_FOOTPRINT footprint; diff --git a/Engine/Source/RHI-DirectX12/Src/Gpu.cpp b/Engine/Source/RHI-DirectX12/Src/Gpu.cpp index 086d2cb67..363395b27 100644 --- a/Engine/Source/RHI-DirectX12/Src/Gpu.cpp +++ b/Engine/Source/RHI-DirectX12/Src/Gpu.cpp @@ -33,7 +33,8 @@ namespace RHI::DirectX12 { | FeatureBits::textureCompressionBc | FeatureBits::timestampQuery | FeatureBits::multiDrawIndirect - | FeatureBits::drawIndirectFirstInstance; + | FeatureBits::drawIndirectFirstInstance + | FeatureBits::textureCubeArray; } GpuLimits DX12Gpu::GetLimits() diff --git a/Engine/Source/RHI-DirectX12/Src/SwapChain.cpp b/Engine/Source/RHI-DirectX12/Src/SwapChain.cpp index 972f4fd52..867231cb6 100644 --- a/Engine/Source/RHI-DirectX12/Src/SwapChain.cpp +++ b/Engine/Source/RHI-DirectX12/Src/SwapChain.cpp @@ -132,7 +132,7 @@ namespace RHI::DirectX12 { Assert(SUCCEEDED(nativeSwapChain->GetBuffer(i, IID_PPV_ARGS(&dx12Resource)))); TextureCreateInfo textureCreateInfo = TextureCreateInfo() - .SetDimension(TextureDimension::t2D) + .SetType(TextureType::t2D) .SetWidth(inCreateInfo.width) .SetHeight(inCreateInfo.height) .SetDepthOrArraySize(1) diff --git a/Engine/Source/RHI-DirectX12/Src/Texture.cpp b/Engine/Source/RHI-DirectX12/Src/Texture.cpp index ca61de5de..9280d84c2 100644 --- a/Engine/Source/RHI-DirectX12/Src/Texture.cpp +++ b/Engine/Source/RHI-DirectX12/Src/Texture.cpp @@ -27,7 +27,7 @@ namespace RHI::DirectX12 { DX12Texture::~DX12Texture() = default; - Common::UniquePtr DX12Texture::CreateTextureView(const TextureViewCreateInfo& inCreateInfo) + Common::UniquePtr DX12Texture::CreateTextureViewInternal(const TextureViewCreateInfo& inCreateInfo) { return Common::UniquePtr(new DX12TextureView(static_cast(device), *this, inCreateInfo)); } @@ -49,7 +49,7 @@ namespace RHI::DirectX12 { textureDesc.DepthOrArraySize = inCreateInfo.depthOrArraySize; textureDesc.SampleDesc.Count = inCreateInfo.samples; textureDesc.SampleDesc.Quality = 0; - textureDesc.Dimension = EnumCast(inCreateInfo.dimension); + textureDesc.Dimension = EnumCast(inCreateInfo.type); bool success = SUCCEEDED(device.GetNative()->CreateCommittedResource( &heapProperties, diff --git a/Engine/Source/RHI-Dummy/Include/RHI/Dummy/Texture.h b/Engine/Source/RHI-Dummy/Include/RHI/Dummy/Texture.h index c395132d7..d10bbe5ae 100644 --- a/Engine/Source/RHI-Dummy/Include/RHI/Dummy/Texture.h +++ b/Engine/Source/RHI-Dummy/Include/RHI/Dummy/Texture.h @@ -13,6 +13,7 @@ namespace RHI::Dummy { explicit DummyTexture(const TextureCreateInfo& createInfo); ~DummyTexture() override; - Common::UniquePtr CreateTextureView(const TextureViewCreateInfo& createInfo) override; + private: + Common::UniquePtr CreateTextureViewInternal(const TextureViewCreateInfo& createInfo) override; }; } diff --git a/Engine/Source/RHI-Dummy/Src/Texture.cpp b/Engine/Source/RHI-Dummy/Src/Texture.cpp index 9dca25f7e..2ab979cf6 100644 --- a/Engine/Source/RHI-Dummy/Src/Texture.cpp +++ b/Engine/Source/RHI-Dummy/Src/Texture.cpp @@ -13,7 +13,7 @@ namespace RHI::Dummy { DummyTexture::~DummyTexture() = default; - Common::UniquePtr DummyTexture::CreateTextureView(const TextureViewCreateInfo& createInfo) + Common::UniquePtr DummyTexture::CreateTextureViewInternal(const TextureViewCreateInfo& createInfo) { return Common::UniquePtr(new DummyTextureView(createInfo)); } diff --git a/Engine/Source/RHI-Vulkan/Include/RHI/Vulkan/Common.h b/Engine/Source/RHI-Vulkan/Include/RHI/Vulkan/Common.h index 7454d1ad7..d11a605c0 100644 --- a/Engine/Source/RHI-Vulkan/Include/RHI/Vulkan/Common.h +++ b/Engine/Source/RHI-Vulkan/Include/RHI/Vulkan/Common.h @@ -79,10 +79,13 @@ namespace RHI::Vulkan { ECIMPL_ITEM(QueueType::transfer, VK_QUEUE_TRANSFER_BIT) ECIMPL_END(VkQueueFlagBits) - ECIMPL_BEGIN(TextureDimension, VkImageType) - ECIMPL_ITEM(TextureDimension::t1D, VK_IMAGE_TYPE_1D) - ECIMPL_ITEM(TextureDimension::t2D, VK_IMAGE_TYPE_2D) - ECIMPL_ITEM(TextureDimension::t3D, VK_IMAGE_TYPE_3D) + ECIMPL_BEGIN(TextureType, VkImageType) + ECIMPL_ITEM(TextureType::t1D, VK_IMAGE_TYPE_1D) + ECIMPL_ITEM(TextureType::t2D, VK_IMAGE_TYPE_2D) + ECIMPL_ITEM(TextureType::t2DArray, VK_IMAGE_TYPE_2D) + ECIMPL_ITEM(TextureType::tCube, VK_IMAGE_TYPE_2D) + ECIMPL_ITEM(TextureType::tCubeArray, VK_IMAGE_TYPE_2D) + ECIMPL_ITEM(TextureType::t3D, VK_IMAGE_TYPE_3D) ECIMPL_END(VkImageType) ECIMPL_BEGIN(TextureViewDimension, VkImageViewType) @@ -313,4 +316,5 @@ namespace RHI::Vulkan { FCIMPL_ITEM(TextureUsageBits::renderAttachment, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) FCIMPL_ITEM(TextureUsageBits::depthStencilAttachment, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) FCIMPL_END(VkImageUsageFlagBits) + } diff --git a/Engine/Source/RHI-Vulkan/Include/RHI/Vulkan/Texture.h b/Engine/Source/RHI-Vulkan/Include/RHI/Vulkan/Texture.h index ecadd0bb8..e40b39434 100644 --- a/Engine/Source/RHI-Vulkan/Include/RHI/Vulkan/Texture.h +++ b/Engine/Source/RHI-Vulkan/Include/RHI/Vulkan/Texture.h @@ -23,12 +23,11 @@ namespace RHI::Vulkan { VulkanTexture(VulkanDevice& inDevice, const TextureCreateInfo& inCreateInfo); ~VulkanTexture() override; - Common::UniquePtr CreateTextureView(const TextureViewCreateInfo& inCreateInfo) override; - VkImage GetNative() const; VkImageSubresourceRange GetNativeSubResourceFullRange() const; private: + Common::UniquePtr CreateTextureViewInternal(const TextureViewCreateInfo& inCreateInfo) override; void CreateNativeImage(const TextureCreateInfo& inCreateInfo); void GetAspect(const TextureCreateInfo& inCreateInfo); void TransitionToInitState(const TextureCreateInfo& inCreateInfo); @@ -39,4 +38,4 @@ namespace RHI::Vulkan { VkImageAspectFlags nativeAspect; bool ownMemory; }; -} \ No newline at end of file +} diff --git a/Engine/Source/RHI-Vulkan/Src/Device.cpp b/Engine/Source/RHI-Vulkan/Src/Device.cpp index eb0d581bc..44c6fe00a 100644 --- a/Engine/Source/RHI-Vulkan/Src/Device.cpp +++ b/Engine/Source/RHI-Vulkan/Src/Device.cpp @@ -236,7 +236,7 @@ namespace RHI::Vulkan { { const auto& createInfo = texture.GetCreateInfo(); const auto mipLevel = subResourceInfo.mipLevel; - const auto baseDepth = createInfo.dimension == TextureDimension::t3D ? createInfo.depthOrArraySize : 1; + const auto baseDepth = createInfo.type == TextureType::t3D ? createInfo.depthOrArraySize : 1; TextureSubResourceCopyFootprint result {}; result.extent = { @@ -356,6 +356,7 @@ namespace RHI::Vulkan { enabledFeatures.samplerAnisotropy = supportedFeatures.features.samplerAnisotropy; enabledFeatures.textureCompressionBC = supportedFeatures.features.textureCompressionBC; enabledFeatures.occlusionQueryPrecise = supportedFeatures.features.occlusionQueryPrecise; + enabledFeatures.imageCubeArray = supportedFeatures.features.imageCubeArray; VkDeviceCreateInfo deviceCreateInfo = {}; deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; diff --git a/Engine/Source/RHI-Vulkan/Src/Gpu.cpp b/Engine/Source/RHI-Vulkan/Src/Gpu.cpp index 06bf4db07..482312ad7 100644 --- a/Engine/Source/RHI-Vulkan/Src/Gpu.cpp +++ b/Engine/Source/RHI-Vulkan/Src/Gpu.cpp @@ -43,6 +43,7 @@ namespace RHI::Vulkan { if (properties.limits.timestampComputeAndGraphics) { result = result | FeatureBits::timestampQuery; } if (features.multiDrawIndirect) { result = result | FeatureBits::multiDrawIndirect; } if (features.drawIndirectFirstInstance) { result = result | FeatureBits::drawIndirectFirstInstance; } + if (features.imageCubeArray) { result = result | FeatureBits::textureCubeArray; } return result; } diff --git a/Engine/Source/RHI-Vulkan/Src/SwapChain.cpp b/Engine/Source/RHI-Vulkan/Src/SwapChain.cpp index 54676a0e7..e0d69209c 100644 --- a/Engine/Source/RHI-Vulkan/Src/SwapChain.cpp +++ b/Engine/Source/RHI-Vulkan/Src/SwapChain.cpp @@ -131,7 +131,7 @@ namespace RHI::Vulkan { textureInfo.usages = TextureUsageBits::renderAttachment; textureInfo.mipLevels = 1; textureInfo.samples = 1; - textureInfo.dimension = TextureDimension::t2D; + textureInfo.type = TextureType::t2D; textureInfo.width = extent.width; textureInfo.height = extent.height; textureInfo.depthOrArraySize = 1; diff --git a/Engine/Source/RHI-Vulkan/Src/Texture.cpp b/Engine/Source/RHI-Vulkan/Src/Texture.cpp index 4f87a39ee..14243b38a 100644 --- a/Engine/Source/RHI-Vulkan/Src/Texture.cpp +++ b/Engine/Source/RHI-Vulkan/Src/Texture.cpp @@ -11,6 +11,13 @@ #include #include +namespace RHI::Vulkan::Internal { + static VkImageCreateFlags GetNativeImageCreateFlags(TextureType inType) + { + return inType == TextureType::tCube || inType == TextureType::tCubeArray ? VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT : 0; + } +} + namespace RHI::Vulkan { VulkanTexture::VulkanTexture(VulkanDevice& inDevice, const TextureCreateInfo& inCreateInfo, VkImage inNativeImage) : Texture(inCreateInfo) @@ -39,7 +46,7 @@ namespace RHI::Vulkan { } } - Common::UniquePtr VulkanTexture::CreateTextureView(const TextureViewCreateInfo& inCreateInfo) + Common::UniquePtr VulkanTexture::CreateTextureViewInternal(const TextureViewCreateInfo& inCreateInfo) { return Common::UniquePtr(new VulkanTextureView(*this, device, inCreateInfo)); } @@ -61,7 +68,7 @@ namespace RHI::Vulkan { VkImageSubresourceRange VulkanTexture::GetNativeSubResourceFullRange() const { - if (createInfo.dimension == TextureDimension::t3D) { + if (createInfo.type == TextureType::t3D) { return { nativeAspect, 0, createInfo.mipLevels, 0, 1 }; } else { return { nativeAspect, 0, createInfo.mipLevels, 0, createInfo.depthOrArraySize }; @@ -74,8 +81,9 @@ namespace RHI::Vulkan { VkImageCreateInfo imageInfo = {}; imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + imageInfo.flags = Internal::GetNativeImageCreateFlags(inCreateInfo.type); imageInfo.mipLevels = inCreateInfo.mipLevels; - if (inCreateInfo.dimension == TextureDimension::t3D) { + if (inCreateInfo.type == TextureType::t3D) { imageInfo.extent = { inCreateInfo.width, inCreateInfo.height, inCreateInfo.depthOrArraySize }; imageInfo.arrayLayers = 1; } else { @@ -83,7 +91,7 @@ namespace RHI::Vulkan { imageInfo.arrayLayers = inCreateInfo.depthOrArraySize; } imageInfo.samples = static_cast(inCreateInfo.samples); - imageInfo.imageType = EnumCast(inCreateInfo.dimension); + imageInfo.imageType = EnumCast(inCreateInfo.type); imageInfo.format = EnumCast(inCreateInfo.format); imageInfo.usage = FlagsCast(inCreateInfo.usages); diff --git a/Engine/Source/RHI-Vulkan/Src/TextureView.cpp b/Engine/Source/RHI-Vulkan/Src/TextureView.cpp index fb6f2dbb6..7f2377657 100644 --- a/Engine/Source/RHI-Vulkan/Src/TextureView.cpp +++ b/Engine/Source/RHI-Vulkan/Src/TextureView.cpp @@ -27,6 +27,10 @@ namespace RHI::Vulkan { void VulkanTextureView::CreateImageView(const TextureViewCreateInfo& inCreateInfo) { + if (inCreateInfo.dimension == TextureViewDimension::tvCubeArray) { + AssertWithReason(device.GetEnabledFeatures().imageCubeArray == VK_TRUE, "Vulkan image cube array feature is not supported"); + } + VkImageViewCreateInfo viewInfo = {}; viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; viewInfo.format = EnumCast(texture.GetCreateInfo().format); @@ -58,4 +62,4 @@ namespace RHI::Vulkan { { return arrayLayerNum; } -} \ No newline at end of file +} diff --git a/Engine/Source/RHI/Include/RHI/Common.h b/Engine/Source/RHI/Include/RHI/Common.h index d2b1dac7a..19ad04ce2 100644 --- a/Engine/Source/RHI/Include/RHI/Common.h +++ b/Engine/Source/RHI/Include/RHI/Common.h @@ -165,9 +165,12 @@ namespace RHI { max }; - enum class TextureDimension : uint8_t { + enum class TextureType : uint8_t { t1D, t2D, + t2DArray, + tCube, + tCubeArray, t3D, max }; @@ -480,7 +483,8 @@ namespace RHI { timestampQuery = 0x4, multiDrawIndirect = 0x8, drawIndirectFirstInstance = 0x10, - max = 0x20 + textureCubeArray = 0x20, + max = 0x40 }; using FeatureFlags = Common::Flags; DECLARE_FLAG_BITS_OP(FeatureFlags, FeatureBits) @@ -488,4 +492,4 @@ namespace RHI { namespace RHI { size_t GetBytesPerPixel(PixelFormat format); -} \ No newline at end of file +} diff --git a/Engine/Source/RHI/Include/RHI/Texture.h b/Engine/Source/RHI/Include/RHI/Texture.h index 132a40623..537a79eb9 100644 --- a/Engine/Source/RHI/Include/RHI/Texture.h +++ b/Engine/Source/RHI/Include/RHI/Texture.h @@ -14,7 +14,7 @@ namespace RHI { class Device; struct TextureCreateInfo { - TextureDimension dimension; + TextureType type; uint32_t width; uint32_t height; uint32_t depthOrArraySize; @@ -26,7 +26,7 @@ namespace RHI { std::string debugName; TextureCreateInfo(); - TextureCreateInfo& SetDimension(TextureDimension inDimension); + TextureCreateInfo& SetType(TextureType inType); TextureCreateInfo& SetWidth(uint32_t inWidth); TextureCreateInfo& SetHeight(uint32_t inHeight); TextureCreateInfo& SetDepthOrArraySize(uint32_t inDepthOrArraySize); @@ -46,10 +46,11 @@ namespace RHI { virtual ~Texture(); const TextureCreateInfo& GetCreateInfo() const; - virtual Common::UniquePtr CreateTextureView(const TextureViewCreateInfo& createInfo) = 0; + Common::UniquePtr CreateTextureView(const TextureViewCreateInfo& createInfo); protected: explicit Texture(const TextureCreateInfo& inCreateInfo); + virtual Common::UniquePtr CreateTextureViewInternal(const TextureViewCreateInfo& createInfo) = 0; TextureCreateInfo createInfo; }; diff --git a/Engine/Source/RHI/Src/Texture.cpp b/Engine/Source/RHI/Src/Texture.cpp index 0464678d8..20f0f8cb6 100644 --- a/Engine/Source/RHI/Src/Texture.cpp +++ b/Engine/Source/RHI/Src/Texture.cpp @@ -3,10 +3,78 @@ // #include +#include + +namespace RHI::Internal { + static void ValidateTextureCreateInfo(const TextureCreateInfo& createInfo) + { + switch (createInfo.type) { + case TextureType::t1D: + case TextureType::t2D: + AssertWithReason(createInfo.depthOrArraySize == 1, "non-array textures must have exactly one array layer"); + break; + case TextureType::t2DArray: + AssertWithReason(createInfo.depthOrArraySize >= 1, "texture arrays must have at least one array layer"); + break; + case TextureType::tCube: + AssertWithReason(createInfo.width == createInfo.height, "cube textures must be square"); + AssertWithReason(createInfo.depthOrArraySize == 6, "cube textures must have exactly six array layers"); + AssertWithReason(createInfo.samples == 1, "cube textures must be single-sampled"); + break; + case TextureType::tCubeArray: + AssertWithReason(createInfo.width == createInfo.height, "cube texture arrays must be square"); + AssertWithReason(createInfo.depthOrArraySize >= 6 && createInfo.depthOrArraySize % 6 == 0, "cube texture arrays must have a positive multiple of six array layers"); + AssertWithReason(createInfo.samples == 1, "cube texture arrays must be single-sampled"); + break; + case TextureType::t3D: + AssertWithReason(createInfo.depthOrArraySize >= 1, "3D textures must have positive depth"); + break; + default: + break; + } + } + + static bool IsTextureViewDimensionCompatible(TextureType textureType, TextureViewDimension viewDimension) + { + switch (textureType) { + case TextureType::t1D: + return viewDimension == TextureViewDimension::tv1D; + case TextureType::t2D: + return viewDimension == TextureViewDimension::tv2D; + case TextureType::t2DArray: + return viewDimension == TextureViewDimension::tv2D || viewDimension == TextureViewDimension::tv2DArray; + case TextureType::tCube: + return viewDimension == TextureViewDimension::tv2D || viewDimension == TextureViewDimension::tv2DArray || viewDimension == TextureViewDimension::tvCube; + case TextureType::tCubeArray: + return viewDimension == TextureViewDimension::tv2D || viewDimension == TextureViewDimension::tv2DArray || viewDimension == TextureViewDimension::tvCube || viewDimension == TextureViewDimension::tvCubeArray; + case TextureType::t3D: + return viewDimension == TextureViewDimension::tv3D; + default: + return false; + } + } + + static void ValidateTextureViewCreateInfo(const TextureCreateInfo& textureCreateInfo, const TextureViewCreateInfo& viewCreateInfo) + { + AssertWithReason(IsTextureViewDimensionCompatible(textureCreateInfo.type, viewCreateInfo.dimension), "texture view dimension is incompatible with the texture type"); + AssertWithReason(viewCreateInfo.mipLevelNum > 0 && viewCreateInfo.baseMipLevel + viewCreateInfo.mipLevelNum <= textureCreateInfo.mipLevels, "texture view mip range is out of bounds"); + + const auto arrayLayerNum = textureCreateInfo.type == TextureType::t3D ? 1 : textureCreateInfo.depthOrArraySize; + AssertWithReason(viewCreateInfo.arrayLayerNum > 0 && viewCreateInfo.baseArrayLayer + viewCreateInfo.arrayLayerNum <= arrayLayerNum, "texture view array layer range is out of bounds"); + + if (viewCreateInfo.dimension == TextureViewDimension::tv1D || viewCreateInfo.dimension == TextureViewDimension::tv2D || viewCreateInfo.dimension == TextureViewDimension::tv3D) { + AssertWithReason(viewCreateInfo.arrayLayerNum == 1, "non-array texture views must have exactly one array layer"); + } else if (viewCreateInfo.dimension == TextureViewDimension::tvCube) { + AssertWithReason(viewCreateInfo.baseArrayLayer % 6 == 0 && viewCreateInfo.arrayLayerNum == 6, "cube texture views must select one aligned group of six array layers"); + } else if (viewCreateInfo.dimension == TextureViewDimension::tvCubeArray) { + AssertWithReason(viewCreateInfo.baseArrayLayer % 6 == 0 && viewCreateInfo.arrayLayerNum % 6 == 0, "cube texture array views must select aligned groups of six array layers"); + } + } +} namespace RHI { TextureCreateInfo::TextureCreateInfo() - : dimension(TextureDimension::max) + : type(TextureType::max) , width(0) , height(0) , depthOrArraySize(0) @@ -18,9 +86,9 @@ namespace RHI { { } - TextureCreateInfo& TextureCreateInfo::SetDimension(const TextureDimension inDimension) + TextureCreateInfo& TextureCreateInfo::SetType(const TextureType inType) { - dimension = inDimension; + type = inType; return *this; } @@ -80,7 +148,7 @@ namespace RHI { bool TextureCreateInfo::operator==(const TextureCreateInfo& rhs) const { - return dimension == rhs.dimension + return type == rhs.type && width == rhs.width && height == rhs.height && depthOrArraySize == rhs.depthOrArraySize @@ -94,6 +162,7 @@ namespace RHI { Texture::Texture(const TextureCreateInfo& inCreateInfo) : createInfo(inCreateInfo) { + Internal::ValidateTextureCreateInfo(inCreateInfo); } Texture::~Texture() = default; @@ -102,4 +171,10 @@ namespace RHI { { return createInfo; } + + Common::UniquePtr Texture::CreateTextureView(const TextureViewCreateInfo& inCreateInfo) + { + Internal::ValidateTextureViewCreateInfo(createInfo, inCreateInfo); + return CreateTextureViewInternal(inCreateInfo); + } } diff --git a/Engine/Source/Render/Src/Renderer.cpp b/Engine/Source/Render/Src/Renderer.cpp index a51c00a6d..9bf832b58 100644 --- a/Engine/Source/Render/Src/Renderer.cpp +++ b/Engine/Source/Render/Src/Renderer.cpp @@ -74,7 +74,7 @@ namespace Render { auto* backTextureView = rgBuilder.CreateTextureView(backTexture, RGTextureViewDesc(RHI::TextureViewType::colorAttachment, RHI::TextureViewDimension::tv2D)); auto* depthTexture = rgBuilder.CreateTexture( RGTextureDesc() - .SetDimension(RHI::TextureDimension::t2D) + .SetType(RHI::TextureType::t2D) .SetWidth(surfaceExtent.x) .SetHeight(surfaceExtent.y) .SetDepthOrArraySize(1) diff --git a/Engine/Source/Render/Test/RenderGraphTest.cpp b/Engine/Source/Render/Test/RenderGraphTest.cpp index 7625e80b0..6e92e0a4e 100644 --- a/Engine/Source/Render/Test/RenderGraphTest.cpp +++ b/Engine/Source/Render/Test/RenderGraphTest.cpp @@ -89,7 +89,7 @@ namespace Render { RGBuilder builder(*device); auto* texture = builder.CreateTexture( RGTextureDesc() - .SetDimension(RHI::TextureDimension::t2D) + .SetType(RHI::TextureType::t2D) .SetWidth(4) .SetHeight(4) .SetDepthOrArraySize(1) @@ -122,7 +122,7 @@ namespace Render { RGBuilder builder(*device); auto* depthTexture = builder.CreateTexture( RGTextureDesc() - .SetDimension(RHI::TextureDimension::t2D) + .SetType(RHI::TextureType::t2D) .SetWidth(4) .SetHeight(4) .SetDepthOrArraySize(1) @@ -139,7 +139,7 @@ namespace Render { RHI::TextureAspect::depth)); auto* colorTexture = builder.CreateTexture( RGTextureDesc() - .SetDimension(RHI::TextureDimension::t2D) + .SetType(RHI::TextureType::t2D) .SetWidth(4) .SetHeight(4) .SetDepthOrArraySize(1) diff --git a/Engine/Source/Render/Test/ResourcePoolTest.cpp b/Engine/Source/Render/Test/ResourcePoolTest.cpp index 3997d9046..9a47ccfa6 100644 --- a/Engine/Source/Render/Test/ResourcePoolTest.cpp +++ b/Engine/Source/Render/Test/ResourcePoolTest.cpp @@ -32,7 +32,7 @@ TEST_F(ResourcePoolTest, BasicTest) { auto& texturePool = TexturePool::Get(*device); PooledTextureDesc textureDesc {}; - textureDesc.dimension = RHI::TextureDimension::t2D; + textureDesc.type = RHI::TextureType::t2D; textureDesc.width = 1920; textureDesc.height = 1080; textureDesc.depthOrArraySize = 1; diff --git a/Engine/Source/Runtime/Src/Asset/Texture.cpp b/Engine/Source/Runtime/Src/Asset/Texture.cpp index 01acfbc3d..ab0cc576e 100644 --- a/Engine/Source/Runtime/Src/Asset/Texture.cpp +++ b/Engine/Source/Runtime/Src/Asset/Texture.cpp @@ -5,15 +5,20 @@ #include namespace Runtime::Internal { - static RHI::TextureDimension GetTextureDimension(TextureType inType) - { - static std::unordered_map map = { - { TextureType::t1D, RHI::TextureDimension::t1D }, - { TextureType::t2D, RHI::TextureDimension::t2D }, - { TextureType::t2DArray, RHI::TextureDimension::t2D }, - { TextureType::tCube, RHI::TextureDimension::t2D }, - { TextureType::tCubeArray, RHI::TextureDimension::t2D }, - { TextureType::t3D, RHI::TextureDimension::t3D } + struct TextureTypeInfo { + RHI::TextureType rhiType; + RHI::TextureViewDimension rhiViewDimension; + }; + + static const TextureTypeInfo& GetTextureTypeInfo(TextureType inType) + { + static std::unordered_map map = { + { TextureType::t1D, { RHI::TextureType::t1D, RHI::TextureViewDimension::tv1D } }, + { TextureType::t2D, { RHI::TextureType::t2D, RHI::TextureViewDimension::tv2D } }, + { TextureType::t2DArray, { RHI::TextureType::t2DArray, RHI::TextureViewDimension::tv2DArray } }, + { TextureType::tCube, { RHI::TextureType::tCube, RHI::TextureViewDimension::tvCube } }, + { TextureType::tCubeArray, { RHI::TextureType::tCubeArray, RHI::TextureViewDimension::tvCubeArray } }, + { TextureType::t3D, { RHI::TextureType::t3D, RHI::TextureViewDimension::tv3D } } }; return map.at(inType); } @@ -208,7 +213,7 @@ namespace Runtime { texture = device->CreateTexture( RHI::TextureCreateInfo() - .SetDimension(Internal::GetTextureDimension(type)) + .SetType(Internal::GetTextureTypeInfo(type).rhiType) .SetWidth(width) .SetHeight(height) .SetDepthOrArraySize(depthOrArraySize) @@ -222,7 +227,7 @@ namespace Runtime { textureView = texture->CreateTextureView( RHI::TextureViewCreateInfo() .SetType(Internal::IsDepthOrStencilFormat(format) ? RHI::TextureViewType::depthStencil : RHI::TextureViewType::textureBinding) - .SetDimension(static_cast(type)) + .SetDimension(Internal::GetTextureTypeInfo(type).rhiViewDimension) .SetAspect(Internal::GetTextureAspect(format)) .SetMipLevels(0, mipLevels) .SetArrayLayers(0, type == TextureType::t3D ? 1 : depthOrArraySize)); @@ -438,7 +443,7 @@ namespace Runtime { texture = device->CreateTexture( RHI::TextureCreateInfo() - .SetDimension(Internal::GetTextureDimension(type)) + .SetType(Internal::GetTextureTypeInfo(type).rhiType) .SetWidth(width) .SetHeight(height) .SetDepthOrArraySize(depthOrArraySize) @@ -452,7 +457,7 @@ namespace Runtime { renderTargetView = texture->CreateTextureView( RHI::TextureViewCreateInfo() .SetType(Internal::IsDepthOrStencilFormat(format) ? RHI::TextureViewType::depthStencil : RHI::TextureViewType::colorAttachment) - .SetDimension(static_cast(type)) + .SetDimension(Internal::GetTextureTypeInfo(type).rhiViewDimension) .SetAspect(Internal::GetTextureAspect(format)) .SetMipLevels(0, mipLevels) .SetArrayLayers(0, type == TextureType::t3D ? 1 : depthOrArraySize)); @@ -460,7 +465,7 @@ namespace Runtime { shaderResourceView = texture->CreateTextureView( RHI::TextureViewCreateInfo() .SetType(Internal::IsDepthOrStencilFormat(format) ? RHI::TextureViewType::depthStencil : RHI::TextureViewType::textureBinding) - .SetDimension(static_cast(type)) + .SetDimension(Internal::GetTextureTypeInfo(type).rhiViewDimension) .SetAspect(Internal::GetTextureAspect(format)) .SetMipLevels(0, mipLevels) .SetArrayLayers(0, type == TextureType::t3D ? 1 : depthOrArraySize)); diff --git a/Engine/Source/Runtime/Src/Canvas.cpp b/Engine/Source/Runtime/Src/Canvas.cpp index be28af1af..6c35594eb 100644 --- a/Engine/Source/Runtime/Src/Canvas.cpp +++ b/Engine/Source/Runtime/Src/Canvas.cpp @@ -69,7 +69,7 @@ namespace Runtime { { texture = device.CreateTexture( RHI::TextureCreateInfo() - .SetDimension(RHI::TextureDimension::t2D) + .SetType(RHI::TextureType::t2D) .SetWidth(width) .SetHeight(height) .SetDepthOrArraySize(1) diff --git a/Sample/Rendering-BaseTexture/BaseTexture.cpp b/Sample/Rendering-BaseTexture/BaseTexture.cpp index 9f9f61577..6ecf498b8 100644 --- a/Sample/Rendering-BaseTexture/BaseTexture.cpp +++ b/Sample/Rendering-BaseTexture/BaseTexture.cpp @@ -333,7 +333,7 @@ void BaseTexApp::CreateTextureAndSampler() .SetWidth(width) .SetHeight(height) .SetDepthOrArraySize(1) - .SetDimension(TextureDimension::t2D) + .SetType(TextureType::t2D) .SetSamples(1) .SetUsages(TextureUsageBits::copyDst | TextureUsageBits::textureBinding) .SetInitialState(TextureState::undefined) diff --git a/Sample/Rendering-SSAO/SSAOApplication.cpp b/Sample/Rendering-SSAO/SSAOApplication.cpp index 8d76607bb..cc3612b2f 100644 --- a/Sample/Rendering-SSAO/SSAOApplication.cpp +++ b/Sample/Rendering-SSAO/SSAOApplication.cpp @@ -682,7 +682,7 @@ class SSAOApp final : public Application { .SetWidth(GetWindowWidth()) .SetHeight(GetWindowHeight()) .SetDepthOrArraySize(1) - .SetDimension(TextureDimension::t2D) + .SetType(TextureType::t2D) .SetSamples(1) .SetUsages(TextureUsageBits::textureBinding | TextureUsageBits::renderAttachment) .SetInitialState(TextureState::shaderReadOnly)); @@ -695,7 +695,7 @@ class SSAOApp final : public Application { .SetWidth(GetWindowWidth()) .SetHeight(GetWindowHeight()) .SetDepthOrArraySize(1) - .SetDimension(TextureDimension::t2D) + .SetType(TextureType::t2D) .SetSamples(1) .SetUsages(TextureUsageBits::textureBinding | TextureUsageBits::renderAttachment) .SetInitialState(TextureState::shaderReadOnly)); @@ -708,7 +708,7 @@ class SSAOApp final : public Application { .SetWidth(GetWindowWidth()) .SetHeight(GetWindowHeight()) .SetDepthOrArraySize(1) - .SetDimension(TextureDimension::t2D) + .SetType(TextureType::t2D) .SetSamples(1) .SetUsages(TextureUsageBits::textureBinding | TextureUsageBits::renderAttachment) .SetInitialState(TextureState::shaderReadOnly)); @@ -721,7 +721,7 @@ class SSAOApp final : public Application { .SetWidth(GetWindowWidth()) .SetHeight(GetWindowHeight()) .SetDepthOrArraySize(1) - .SetDimension(TextureDimension::t2D) + .SetType(TextureType::t2D) .SetSamples(1) .SetUsages(TextureUsageBits::depthStencilAttachment) .SetInitialState(TextureState::depthStencilWrite)); @@ -737,7 +737,7 @@ class SSAOApp final : public Application { .SetWidth(GetWindowWidth()) .SetHeight(GetWindowHeight()) .SetDepthOrArraySize(1) - .SetDimension(TextureDimension::t2D) + .SetType(TextureType::t2D) .SetSamples(1) .SetUsages(TextureUsageBits::textureBinding | TextureUsageBits::renderAttachment) .SetInitialState(TextureState::shaderReadOnly)); @@ -750,7 +750,7 @@ class SSAOApp final : public Application { .SetWidth(GetWindowWidth()) .SetHeight(GetWindowHeight()) .SetDepthOrArraySize(1) - .SetDimension(TextureDimension::t2D) + .SetType(TextureType::t2D) .SetSamples(1) .SetUsages(TextureUsageBits::textureBinding | TextureUsageBits::renderAttachment) .SetInitialState(TextureState::shaderReadOnly)); @@ -862,7 +862,7 @@ class SSAOApp final : public Application { .SetWidth(ssaoNoiseDim) .SetHeight(ssaoNoiseDim) .SetDepthOrArraySize(1) - .SetDimension(TextureDimension::t2D) + .SetType(TextureType::t2D) .SetSamples(1) .SetUsages(TextureUsageBits::copyDst | TextureUsageBits::textureBinding) .SetInitialState(TextureState::undefined)); @@ -904,7 +904,7 @@ class SSAOApp final : public Application { diffuseTex = device->CreateTexture( TextureCreateInfo() .SetFormat(PixelFormat::rgba8Unorm) - .SetDimension(TextureDimension::t2D) + .SetType(TextureType::t2D) .SetMipLevels(1) .SetWidth(texData->width) .SetHeight(texData->height) From 630631aad3303f54d94a54b7962c262b6c5c3924 Mon Sep 17 00:00:00 2001 From: kindem Date: Sat, 1 Aug 2026 19:13:30 +0800 Subject: [PATCH 06/14] fix: derive Vulkan raster extent from attachments --- .../Include/RHI/Vulkan/TextureView.h | 7 +----- .../Source/RHI-Vulkan/Src/CommandRecorder.cpp | 24 +++++++++++++------ Engine/Source/RHI-Vulkan/Src/TextureView.cpp | 11 +-------- Engine/Source/RHI/Include/RHI/TextureView.h | 6 ++++- Engine/Source/RHI/Src/TextureView.cpp | 10 +++++++- 5 files changed, 33 insertions(+), 25 deletions(-) diff --git a/Engine/Source/RHI-Vulkan/Include/RHI/Vulkan/TextureView.h b/Engine/Source/RHI-Vulkan/Include/RHI/Vulkan/TextureView.h index e8dccebbd..658897afe 100644 --- a/Engine/Source/RHI-Vulkan/Include/RHI/Vulkan/TextureView.h +++ b/Engine/Source/RHI-Vulkan/Include/RHI/Vulkan/TextureView.h @@ -23,7 +23,6 @@ namespace RHI::Vulkan { VkImageView GetNative() const; VulkanTexture& GetTexture() const; - uint8_t GetArrayLayerNum() const; private: void CreateImageView(const TextureViewCreateInfo& inCreateInfo); @@ -31,10 +30,6 @@ namespace RHI::Vulkan { VulkanDevice& device; VulkanTexture& texture; - uint8_t baseMipLevel; - uint8_t mipLevelNum; - uint8_t baseArrayLayer; - uint8_t arrayLayerNum; VkImageView nativeImageView; }; -} \ No newline at end of file +} diff --git a/Engine/Source/RHI-Vulkan/Src/CommandRecorder.cpp b/Engine/Source/RHI-Vulkan/Src/CommandRecorder.cpp index e90208825..169d02841 100644 --- a/Engine/Source/RHI-Vulkan/Src/CommandRecorder.cpp +++ b/Engine/Source/RHI-Vulkan/Src/CommandRecorder.cpp @@ -18,6 +18,8 @@ #include #include +#include + namespace RHI::Vulkan { static VkAccessFlags GetBufferMemoryBarrierAccessFlags(const BufferState inState) { @@ -428,9 +430,13 @@ namespace RHI::Vulkan { , activeOcclusionQueryIndex(0) { std::vector colorAttachmentInfos(inBeginInfo.colorAttachments.size()); + const VulkanTextureView* referenceAttachmentView = nullptr; for (size_t i = 0; i < inBeginInfo.colorAttachments.size(); i++) { - auto* colorTextureView = static_cast(inBeginInfo.colorAttachments[i].view); + const auto* colorTextureView = static_cast(inBeginInfo.colorAttachments[i].view); + if (referenceAttachmentView == nullptr) { + referenceAttachmentView = colorTextureView; + } colorAttachmentInfos[i].sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO; colorAttachmentInfos[i].imageView = colorTextureView->GetNative(); colorAttachmentInfos[i].imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; @@ -444,15 +450,10 @@ namespace RHI::Vulkan { }; } - auto* textureView = static_cast(inBeginInfo.colorAttachments[0].view); - const auto& textureCreateInfo = textureView->GetTexture().GetCreateInfo(); - VkRenderingInfoKHR renderingInfo = {}; renderingInfo.sType = VK_STRUCTURE_TYPE_RENDERING_INFO; renderingInfo.colorAttachmentCount = colorAttachmentInfos.size(); - renderingInfo.pColorAttachments = colorAttachmentInfos.data(); - renderingInfo.layerCount = textureView->GetArrayLayerNum(); - renderingInfo.renderArea = {{0, 0}, {static_cast(textureCreateInfo.width), static_cast(textureCreateInfo.height)}}; + renderingInfo.pColorAttachments = colorAttachmentInfos.empty() ? nullptr : colorAttachmentInfos.data(); renderingInfo.viewMask = 0; VkRenderingAttachmentInfo depthAttachmentInfo = {}; @@ -461,6 +462,9 @@ namespace RHI::Vulkan { if (inBeginInfo.depthStencilAttachment.has_value()) { const auto* depthStencilTextureView = static_cast(inBeginInfo.depthStencilAttachment->view); + if (referenceAttachmentView == nullptr) { + referenceAttachmentView = depthStencilTextureView; + } depthAttachmentInfo.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO; depthAttachmentInfo.imageView = depthStencilTextureView->GetNative(); @@ -487,6 +491,12 @@ namespace RHI::Vulkan { } } + AssertWithReason(referenceAttachmentView != nullptr, "raster passes must have at least one attachment"); + const auto& textureCreateInfo = referenceAttachmentView->GetTexture().GetCreateInfo(); + const auto& textureViewCreateInfo = referenceAttachmentView->GetCreateInfo(); + renderingInfo.layerCount = textureViewCreateInfo.arrayLayerNum; + renderingInfo.renderArea = {{0, 0}, {std::max(textureCreateInfo.width >> textureViewCreateInfo.baseMipLevel, 1u), std::max(textureCreateInfo.height >> textureViewCreateInfo.baseMipLevel, 1u)}}; + auto* pfn = device.GetGpu().GetInstance().FindOrGetTypedDynamicFuncPointer("vkCmdBeginRenderingKHR"); pfn(commandBuffer.GetNative(), &renderingInfo); } diff --git a/Engine/Source/RHI-Vulkan/Src/TextureView.cpp b/Engine/Source/RHI-Vulkan/Src/TextureView.cpp index 7f2377657..26d7eddcb 100644 --- a/Engine/Source/RHI-Vulkan/Src/TextureView.cpp +++ b/Engine/Source/RHI-Vulkan/Src/TextureView.cpp @@ -12,10 +12,6 @@ namespace RHI::Vulkan { : TextureView(inCreateInfo) , device(nDevice) , texture(inTexture) - , baseMipLevel(inCreateInfo.baseMipLevel) - , mipLevelNum(inCreateInfo.mipLevelNum) - , baseArrayLayer(inCreateInfo.baseArrayLayer) - , arrayLayerNum(inCreateInfo.arrayLayerNum) { CreateImageView(inCreateInfo); } @@ -36,7 +32,7 @@ namespace RHI::Vulkan { viewInfo.format = EnumCast(texture.GetCreateInfo().format); viewInfo.image = texture.GetNative(); viewInfo.viewType = EnumCast(inCreateInfo.dimension); - viewInfo.subresourceRange = { EnumCast(inCreateInfo.aspect), baseMipLevel, mipLevelNum, baseArrayLayer, arrayLayerNum }; + viewInfo.subresourceRange = { EnumCast(inCreateInfo.aspect), inCreateInfo.baseMipLevel, inCreateInfo.mipLevelNum, inCreateInfo.baseArrayLayer, inCreateInfo.arrayLayerNum }; Assert(vkCreateImageView(device.GetNative(), &viewInfo, nullptr, &nativeImageView) == VK_SUCCESS); } @@ -57,9 +53,4 @@ namespace RHI::Vulkan { { return texture; } - - uint8_t VulkanTextureView::GetArrayLayerNum() const - { - return arrayLayerNum; - } } diff --git a/Engine/Source/RHI/Include/RHI/TextureView.h b/Engine/Source/RHI/Include/RHI/TextureView.h index 9044c4757..8e3b75b21 100644 --- a/Engine/Source/RHI/Include/RHI/TextureView.h +++ b/Engine/Source/RHI/Include/RHI/TextureView.h @@ -38,7 +38,11 @@ namespace RHI { NonCopyable(TextureView) virtual ~TextureView(); + const TextureViewCreateInfo& GetCreateInfo() const; + protected: - explicit TextureView(const TextureViewCreateInfo& createInfo); + explicit TextureView(const TextureViewCreateInfo& inCreateInfo); + + TextureViewCreateInfo createInfo; }; } diff --git a/Engine/Source/RHI/Src/TextureView.cpp b/Engine/Source/RHI/Src/TextureView.cpp index 0221a67ec..b01a188dc 100644 --- a/Engine/Source/RHI/Src/TextureView.cpp +++ b/Engine/Source/RHI/Src/TextureView.cpp @@ -55,7 +55,15 @@ namespace RHI { return *this; } - TextureView::TextureView(const TextureViewCreateInfo&) {} + TextureView::TextureView(const TextureViewCreateInfo& inCreateInfo) + : createInfo(inCreateInfo) + { + } TextureView::~TextureView() = default; + + const TextureViewCreateInfo& TextureView::GetCreateInfo() const + { + return createInfo; + } } From d56154a22512000cc29b67525653e12b20c22b30 Mon Sep 17 00:00:00 2001 From: kindem Date: Sat, 1 Aug 2026 19:42:59 +0800 Subject: [PATCH 07/14] fix: enable Vulkan blend and stencil dynamic states --- Engine/Source/RHI-Vulkan/Src/Pipeline.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Engine/Source/RHI-Vulkan/Src/Pipeline.cpp b/Engine/Source/RHI-Vulkan/Src/Pipeline.cpp index c7ec0b984..3089216d0 100644 --- a/Engine/Source/RHI-Vulkan/Src/Pipeline.cpp +++ b/Engine/Source/RHI-Vulkan/Src/Pipeline.cpp @@ -201,10 +201,12 @@ namespace RHI::Vulkan { setStage(inCreateInfo.vertexShader, VK_SHADER_STAGE_VERTEX_BIT); setStage(inCreateInfo.pixelShader, VK_SHADER_STAGE_FRAGMENT_BIT); - std::vector dynamicStates = { + const std::array dynamicStates = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, - VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY + VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY, + VK_DYNAMIC_STATE_BLEND_CONSTANTS, + VK_DYNAMIC_STATE_STENCIL_REFERENCE }; VkPipelineDynamicStateCreateInfo dynStateInfo = {}; dynStateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; From 6b365ede0b9f4d18b2b569de595e4ee8d8d323c0 Mon Sep 17 00:00:00 2001 From: kindem Date: Sat, 1 Aug 2026 19:47:33 +0800 Subject: [PATCH 08/14] fix: correct DX12 cube array SRV count --- Engine/Source/RHI-DirectX12/Src/TextureView.cpp | 2 +- Engine/Source/RHI/Include/RHI/Common.h | 2 ++ Engine/Source/RHI/Src/Texture.cpp | 8 ++++---- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Engine/Source/RHI-DirectX12/Src/TextureView.cpp b/Engine/Source/RHI-DirectX12/Src/TextureView.cpp index 164dec0c3..4ce797403 100644 --- a/Engine/Source/RHI-DirectX12/Src/TextureView.cpp +++ b/Engine/Source/RHI-DirectX12/Src/TextureView.cpp @@ -81,7 +81,7 @@ namespace RHI::DirectX12 { srv.MostDetailedMip = createInfo.baseMipLevel; srv.MipLevels = createInfo.mipLevelNum; srv.First2DArrayFace = createInfo.baseArrayLayer; - srv.NumCubes = createInfo.arrayLayerNum; + srv.NumCubes = createInfo.arrayLayerNum / textureCubeFaceNum; srv.ResourceMinLODClamp = static_cast(createInfo.baseMipLevel); } diff --git a/Engine/Source/RHI/Include/RHI/Common.h b/Engine/Source/RHI/Include/RHI/Common.h index 19ad04ce2..87858f7b9 100644 --- a/Engine/Source/RHI/Include/RHI/Common.h +++ b/Engine/Source/RHI/Include/RHI/Common.h @@ -48,6 +48,8 @@ #define ALIGN_AS_GPU alignas(16) namespace RHI { + constexpr uint8_t textureCubeFaceNum = 6; + enum class RHIType : uint8_t { directX12, vulkan, diff --git a/Engine/Source/RHI/Src/Texture.cpp b/Engine/Source/RHI/Src/Texture.cpp index 20f0f8cb6..289924a35 100644 --- a/Engine/Source/RHI/Src/Texture.cpp +++ b/Engine/Source/RHI/Src/Texture.cpp @@ -18,12 +18,12 @@ namespace RHI::Internal { break; case TextureType::tCube: AssertWithReason(createInfo.width == createInfo.height, "cube textures must be square"); - AssertWithReason(createInfo.depthOrArraySize == 6, "cube textures must have exactly six array layers"); + AssertWithReason(createInfo.depthOrArraySize == textureCubeFaceNum, "cube textures must have exactly six array layers"); AssertWithReason(createInfo.samples == 1, "cube textures must be single-sampled"); break; case TextureType::tCubeArray: AssertWithReason(createInfo.width == createInfo.height, "cube texture arrays must be square"); - AssertWithReason(createInfo.depthOrArraySize >= 6 && createInfo.depthOrArraySize % 6 == 0, "cube texture arrays must have a positive multiple of six array layers"); + AssertWithReason(createInfo.depthOrArraySize >= textureCubeFaceNum && createInfo.depthOrArraySize % textureCubeFaceNum == 0, "cube texture arrays must have a positive multiple of six array layers"); AssertWithReason(createInfo.samples == 1, "cube texture arrays must be single-sampled"); break; case TextureType::t3D: @@ -65,9 +65,9 @@ namespace RHI::Internal { if (viewCreateInfo.dimension == TextureViewDimension::tv1D || viewCreateInfo.dimension == TextureViewDimension::tv2D || viewCreateInfo.dimension == TextureViewDimension::tv3D) { AssertWithReason(viewCreateInfo.arrayLayerNum == 1, "non-array texture views must have exactly one array layer"); } else if (viewCreateInfo.dimension == TextureViewDimension::tvCube) { - AssertWithReason(viewCreateInfo.baseArrayLayer % 6 == 0 && viewCreateInfo.arrayLayerNum == 6, "cube texture views must select one aligned group of six array layers"); + AssertWithReason(viewCreateInfo.baseArrayLayer % textureCubeFaceNum == 0 && viewCreateInfo.arrayLayerNum == textureCubeFaceNum, "cube texture views must select one aligned group of six array layers"); } else if (viewCreateInfo.dimension == TextureViewDimension::tvCubeArray) { - AssertWithReason(viewCreateInfo.baseArrayLayer % 6 == 0 && viewCreateInfo.arrayLayerNum % 6 == 0, "cube texture array views must select aligned groups of six array layers"); + AssertWithReason(viewCreateInfo.baseArrayLayer % textureCubeFaceNum == 0 && viewCreateInfo.arrayLayerNum % textureCubeFaceNum == 0, "cube texture array views must select aligned groups of six array layers"); } } } From ae8883dd2071909c6b8c7a5530c83ce163f43bd8 Mon Sep 17 00:00:00 2001 From: kindem Date: Sat, 1 Aug 2026 19:53:23 +0800 Subject: [PATCH 09/14] fix: derive Vulkan texture aspect from format --- .../RHI-Vulkan/Include/RHI/Vulkan/Texture.h | 1 - Engine/Source/RHI-Vulkan/Src/CommandRecorder.cpp | 2 +- Engine/Source/RHI-Vulkan/Src/Texture.cpp | 16 ++-------------- Engine/Source/RHI/Include/RHI/Common.h | 1 + Engine/Source/RHI/Src/Common.cpp | 14 ++++++++++++++ 5 files changed, 18 insertions(+), 16 deletions(-) diff --git a/Engine/Source/RHI-Vulkan/Include/RHI/Vulkan/Texture.h b/Engine/Source/RHI-Vulkan/Include/RHI/Vulkan/Texture.h index e40b39434..e6b44cd18 100644 --- a/Engine/Source/RHI-Vulkan/Include/RHI/Vulkan/Texture.h +++ b/Engine/Source/RHI-Vulkan/Include/RHI/Vulkan/Texture.h @@ -29,7 +29,6 @@ namespace RHI::Vulkan { private: Common::UniquePtr CreateTextureViewInternal(const TextureViewCreateInfo& inCreateInfo) override; void CreateNativeImage(const TextureCreateInfo& inCreateInfo); - void GetAspect(const TextureCreateInfo& inCreateInfo); void TransitionToInitState(const TextureCreateInfo& inCreateInfo); VulkanDevice& device; diff --git a/Engine/Source/RHI-Vulkan/Src/CommandRecorder.cpp b/Engine/Source/RHI-Vulkan/Src/CommandRecorder.cpp index 169d02841..107b99ead 100644 --- a/Engine/Source/RHI-Vulkan/Src/CommandRecorder.cpp +++ b/Engine/Source/RHI-Vulkan/Src/CommandRecorder.cpp @@ -478,7 +478,7 @@ namespace RHI::Vulkan { // depth-only formats must not present a stencil attachment, otherwise validation requires the // pipeline's (undefined) stencil format to match the view format const auto depthStencilFormat = depthStencilTextureView->GetTexture().GetCreateInfo().format; - const bool hasStencil = depthStencilFormat == PixelFormat::d32FloatS8Uint || depthStencilFormat == PixelFormat::d24UnormS8Uint; + const bool hasStencil = GetTextureAspect(depthStencilFormat) == TextureAspect::depthStencil; if (hasStencil && !inBeginInfo.depthStencilAttachment->depthReadOnly) { stencilAttachmentInfo.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO; stencilAttachmentInfo.imageView = depthStencilTextureView->GetNative(); diff --git a/Engine/Source/RHI-Vulkan/Src/Texture.cpp b/Engine/Source/RHI-Vulkan/Src/Texture.cpp index 14243b38a..17a75405a 100644 --- a/Engine/Source/RHI-Vulkan/Src/Texture.cpp +++ b/Engine/Source/RHI-Vulkan/Src/Texture.cpp @@ -23,7 +23,7 @@ namespace RHI::Vulkan { : Texture(inCreateInfo) , device(inDevice) , nativeImage(inNativeImage) - , nativeAspect(VK_IMAGE_ASPECT_COLOR_BIT) + , nativeAspect(EnumCast(GetTextureAspect(inCreateInfo.format))) , ownMemory(false) { } @@ -32,7 +32,7 @@ namespace RHI::Vulkan { : Texture(inCreateInfo) , device(inDevice) , nativeImage(VK_NULL_HANDLE) - , nativeAspect(VK_IMAGE_ASPECT_COLOR_BIT) + , nativeAspect(EnumCast(GetTextureAspect(inCreateInfo.format))) , ownMemory(true) { CreateNativeImage(inCreateInfo); @@ -56,16 +56,6 @@ namespace RHI::Vulkan { return nativeImage; } - void VulkanTexture::GetAspect(const RHI::TextureCreateInfo& inCreateInfo) - { - if (inCreateInfo.usages & TextureUsageBits::depthStencilAttachment) { - nativeAspect = VK_IMAGE_ASPECT_DEPTH_BIT; - if (inCreateInfo.format == PixelFormat::d32FloatS8Uint || inCreateInfo.format == PixelFormat::d24UnormS8Uint) { - nativeAspect |= VK_IMAGE_ASPECT_STENCIL_BIT; - } - } - } - VkImageSubresourceRange VulkanTexture::GetNativeSubResourceFullRange() const { if (createInfo.type == TextureType::t3D) { @@ -77,8 +67,6 @@ namespace RHI::Vulkan { void VulkanTexture::CreateNativeImage(const TextureCreateInfo& inCreateInfo) { - GetAspect(inCreateInfo); - VkImageCreateInfo imageInfo = {}; imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; imageInfo.flags = Internal::GetNativeImageCreateFlags(inCreateInfo.type); diff --git a/Engine/Source/RHI/Include/RHI/Common.h b/Engine/Source/RHI/Include/RHI/Common.h index 87858f7b9..fa2e2efbd 100644 --- a/Engine/Source/RHI/Include/RHI/Common.h +++ b/Engine/Source/RHI/Include/RHI/Common.h @@ -494,4 +494,5 @@ namespace RHI { namespace RHI { size_t GetBytesPerPixel(PixelFormat format); + TextureAspect GetTextureAspect(PixelFormat format); } diff --git a/Engine/Source/RHI/Src/Common.cpp b/Engine/Source/RHI/Src/Common.cpp index 0016c3f3a..10c6bc61b 100644 --- a/Engine/Source/RHI/Src/Common.cpp +++ b/Engine/Source/RHI/Src/Common.cpp @@ -27,4 +27,18 @@ namespace RHI { } return Assert(false), 1; } + + TextureAspect GetTextureAspect(const PixelFormat format) + { + switch (format) { + case PixelFormat::d16Unorm: + case PixelFormat::d32Float: + return TextureAspect::depth; + case PixelFormat::d24UnormS8Uint: + case PixelFormat::d32FloatS8Uint: + return TextureAspect::depthStencil; + default: + return TextureAspect::color; + } + } } From 8266dbf088f405bedfefd814734a4d43b82f61ec Mon Sep 17 00:00:00 2001 From: kindem Date: Sun, 2 Aug 2026 12:15:15 +0800 Subject: [PATCH 10/14] fix: support read-only depth stencil attachments --- .../Include/RHI/DirectX12/Common.h | 2 + .../Include/RHI/DirectX12/TextureView.h | 5 +- .../RHI-DirectX12/Src/CommandRecorder.cpp | 29 +++++++---- Engine/Source/RHI-DirectX12/Src/Pipeline.cpp | 2 +- .../Source/RHI-DirectX12/Src/TextureView.cpp | 41 +++++++++++---- .../Source/RHI-Vulkan/Src/CommandRecorder.cpp | 51 ++++++++++++------- Engine/Source/RHI-Vulkan/Src/Pipeline.cpp | 2 +- Engine/Source/RHI/Include/RHI/Common.h | 3 ++ Engine/Source/RHI/Include/RHI/Pipeline.h | 5 +- Engine/Source/RHI/Src/Common.cpp | 22 ++++++++ Engine/Source/RHI/Src/Pipeline.cpp | 10 +++- Engine/Source/Render/Src/RenderCache.cpp | 1 + Engine/Source/Render/Src/RenderGraph.cpp | 2 +- Engine/Source/Render/Test/RenderGraphTest.cpp | 10 ++++ 14 files changed, 139 insertions(+), 46 deletions(-) diff --git a/Engine/Source/RHI-DirectX12/Include/RHI/DirectX12/Common.h b/Engine/Source/RHI-DirectX12/Include/RHI/DirectX12/Common.h index 3cbb2cc6c..dfda56ae0 100644 --- a/Engine/Source/RHI-DirectX12/Include/RHI/DirectX12/Common.h +++ b/Engine/Source/RHI-DirectX12/Include/RHI/DirectX12/Common.h @@ -273,6 +273,8 @@ namespace RHI::DirectX12 { ECIMPL_ITEM(TextureState::storage, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE) ECIMPL_ITEM(TextureState::rwStorage, D3D12_RESOURCE_STATE_UNORDERED_ACCESS) ECIMPL_ITEM(TextureState::depthStencilReadonly, D3D12_RESOURCE_STATE_DEPTH_READ) + ECIMPL_ITEM(TextureState::depthReadStencilWrite, D3D12_RESOURCE_STATE_DEPTH_WRITE) + ECIMPL_ITEM(TextureState::depthWriteStencilRead, D3D12_RESOURCE_STATE_DEPTH_WRITE) ECIMPL_ITEM(TextureState::depthStencilWrite, D3D12_RESOURCE_STATE_DEPTH_WRITE) ECIMPL_ITEM(TextureState::present, D3D12_RESOURCE_STATE_PRESENT) ECIMPL_END(D3D12_RESOURCE_STATES) diff --git a/Engine/Source/RHI-DirectX12/Include/RHI/DirectX12/TextureView.h b/Engine/Source/RHI-DirectX12/Include/RHI/DirectX12/TextureView.h index 79eb1c968..ddbd8b8e0 100644 --- a/Engine/Source/RHI-DirectX12/Include/RHI/DirectX12/TextureView.h +++ b/Engine/Source/RHI-DirectX12/Include/RHI/DirectX12/TextureView.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include using Microsoft::WRL::ComPtr; @@ -13,6 +14,7 @@ using Microsoft::WRL::ComPtr; namespace RHI::DirectX12 { class DX12Device; class DX12Texture; + class DescriptorAllocation; class DX12TextureView final : public TextureView { public: @@ -21,11 +23,12 @@ namespace RHI::DirectX12 { ~DX12TextureView() override; CD3DX12_CPU_DESCRIPTOR_HANDLE GetNativeCpuDescriptorHandle() const; + CD3DX12_CPU_DESCRIPTOR_HANDLE GetNativeDepthStencilCpuDescriptorHandle(bool depthReadOnly, bool stencilReadOnly) const; private: void CreateNativeDescriptor(DX12Device& inDevice, const TextureViewCreateInfo& inCreateInfo); DX12Texture& texture; - Common::UniquePtr descriptorAllocation; + std::array, 4> descriptorAllocations; }; } diff --git a/Engine/Source/RHI-DirectX12/Src/CommandRecorder.cpp b/Engine/Source/RHI-DirectX12/Src/CommandRecorder.cpp index 570699a52..152b19e8a 100644 --- a/Engine/Source/RHI-DirectX12/Src/CommandRecorder.cpp +++ b/Engine/Source/RHI-DirectX12/Src/CommandRecorder.cpp @@ -21,17 +21,17 @@ #include namespace RHI::DirectX12 { - static D3D12_CLEAR_FLAGS GetDX12ClearFlags(const DepthStencilAttachment& depthStencilAttachment) + static D3D12_CLEAR_FLAGS GetDX12ClearFlags(const DepthStencilAttachment& depthStencilAttachment, const TextureAspect aspect) { - Assert(depthStencilAttachment.depthLoadOp == LoadOp::clear || depthStencilAttachment.stencilLoadOp == LoadOp::clear); - - if (depthStencilAttachment.stencilLoadOp != LoadOp::clear) { - return D3D12_CLEAR_FLAG_DEPTH; + UINT flags = 0; + if ((aspect == TextureAspect::depth || aspect == TextureAspect::depthStencil) && depthStencilAttachment.depthLoadOp == LoadOp::clear) { + flags |= D3D12_CLEAR_FLAG_DEPTH; } - if (depthStencilAttachment.depthLoadOp != LoadOp::clear) { - return D3D12_CLEAR_FLAG_STENCIL; + if ((aspect == TextureAspect::stencil || aspect == TextureAspect::depthStencil) && depthStencilAttachment.stencilLoadOp == LoadOp::clear) { + flags |= D3D12_CLEAR_FLAG_STENCIL; } - return D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL; + Assert(flags != 0); + return static_cast(flags); } static size_t GetNativeSubResourceIndex(const DX12Texture& texture, const TextureSubResourceInfo& subResource) @@ -264,10 +264,13 @@ namespace RHI::DirectX12 { rtvHandles[i] = view->GetNativeCpuDescriptorHandle(); } std::optional dsvHandle; + std::optional dsvAspect; if (inBeginInfo.depthStencilAttachment.has_value()) { auto* view = static_cast(inBeginInfo.depthStencilAttachment->view); Assert(view); - dsvHandle = view->GetNativeCpuDescriptorHandle(); + const auto& attachment = inBeginInfo.depthStencilAttachment.value(); + dsvHandle = view->GetNativeDepthStencilCpuDescriptorHandle(attachment.depthReadOnly, attachment.stencilReadOnly); + dsvAspect = view->GetCreateInfo().aspect; } inCmdBuffer.GetNativeCmdList()->OMSetRenderTargets(rtvHandles.size(), rtvHandles.data(), false, dsvHandle.has_value() ? &dsvHandle.value() : nullptr); @@ -282,10 +285,14 @@ namespace RHI::DirectX12 { } if (dsvHandle.has_value()) { const auto& depthStencilAttachment = *inBeginInfo.depthStencilAttachment; - if (depthStencilAttachment.depthLoadOp != LoadOp::clear && depthStencilAttachment.stencilLoadOp != LoadOp::clear) { + const bool clearDepth = (*dsvAspect == TextureAspect::depth || *dsvAspect == TextureAspect::depthStencil) && depthStencilAttachment.depthLoadOp == LoadOp::clear; + const bool clearStencil = (*dsvAspect == TextureAspect::stencil || *dsvAspect == TextureAspect::depthStencil) && depthStencilAttachment.stencilLoadOp == LoadOp::clear; + AssertWithReason(!clearDepth || !depthStencilAttachment.depthReadOnly, "read-only depth attachments cannot be cleared"); + AssertWithReason(!clearStencil || !depthStencilAttachment.stencilReadOnly, "read-only stencil attachments cannot be cleared"); + if (!clearDepth && !clearStencil) { return; } - inCmdBuffer.GetNativeCmdList()->ClearDepthStencilView(dsvHandle.value(), GetDX12ClearFlags(depthStencilAttachment), depthStencilAttachment.depthClearValue, depthStencilAttachment.stencilClearValue, 0, nullptr); + inCmdBuffer.GetNativeCmdList()->ClearDepthStencilView(dsvHandle.value(), GetDX12ClearFlags(depthStencilAttachment, *dsvAspect), depthStencilAttachment.depthClearValue, depthStencilAttachment.stencilClearValue, 0, nullptr); } } diff --git a/Engine/Source/RHI-DirectX12/Src/Pipeline.cpp b/Engine/Source/RHI-DirectX12/Src/Pipeline.cpp index 9b94d49a6..d5cd0d594 100644 --- a/Engine/Source/RHI-DirectX12/Src/Pipeline.cpp +++ b/Engine/Source/RHI-DirectX12/Src/Pipeline.cpp @@ -73,7 +73,7 @@ namespace RHI::DirectX12 { { CD3DX12_DEPTH_STENCIL_DESC desc(D3D12_DEFAULT); desc.DepthEnable = createInfo.depthStencilState.depthEnabled; - desc.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; + desc.DepthWriteMask = createInfo.depthStencilState.depthEnabled && createInfo.depthStencilState.depthWriteEnabled ? D3D12_DEPTH_WRITE_MASK_ALL : D3D12_DEPTH_WRITE_MASK_ZERO; desc.DepthFunc = EnumCast(createInfo.depthStencilState.depthCompareFunc); desc.StencilEnable = createInfo.depthStencilState.stencilEnabled; desc.StencilReadMask = createInfo.depthStencilState.stencilReadMask; diff --git a/Engine/Source/RHI-DirectX12/Src/TextureView.cpp b/Engine/Source/RHI-DirectX12/Src/TextureView.cpp index 4ce797403..198c75e10 100644 --- a/Engine/Source/RHI-DirectX12/Src/TextureView.cpp +++ b/Engine/Source/RHI-DirectX12/Src/TextureView.cpp @@ -202,7 +202,7 @@ namespace RHI::DirectX12 { DX12TextureView::DX12TextureView(DX12Device& inDevice, DX12Texture& inTexture, const TextureViewCreateInfo& inCreateInfo) : TextureView(inCreateInfo) , texture(inTexture) - , descriptorAllocation() + , descriptorAllocations() { CreateNativeDescriptor(inDevice, inCreateInfo); } @@ -211,7 +211,18 @@ namespace RHI::DirectX12 { CD3DX12_CPU_DESCRIPTOR_HANDLE DX12TextureView::GetNativeCpuDescriptorHandle() const { - return descriptorAllocation->GetCpuHandle(); + Assert(descriptorAllocations[0].Get() != nullptr); + return descriptorAllocations[0]->GetCpuHandle(); + } + + CD3DX12_CPU_DESCRIPTOR_HANDLE DX12TextureView::GetNativeDepthStencilCpuDescriptorHandle(bool depthReadOnly, bool stencilReadOnly) const + { + const auto aspect = GetCreateInfo().aspect; + depthReadOnly |= aspect != TextureAspect::depth && aspect != TextureAspect::depthStencil; + stencilReadOnly |= aspect != TextureAspect::stencil && aspect != TextureAspect::depthStencil; + const size_t descriptorIndex = static_cast(depthReadOnly) | (static_cast(stencilReadOnly) << 1); + Assert(descriptorAllocations[descriptorIndex].Get() != nullptr); + return descriptorAllocations[descriptorIndex]->GetCpuHandle(); } void DX12TextureView::CreateNativeDescriptor(DX12Device& inDevice, const TextureViewCreateInfo& inCreateInfo) @@ -228,8 +239,8 @@ namespace RHI::DirectX12 { FillTextureCubeArraySRV(desc.TextureCubeArray, inCreateInfo); FillTexture3DSRV(desc.Texture3D, inCreateInfo); - descriptorAllocation = inDevice.AllocateCbvSrvUavDescriptor(); - inDevice.GetNative()->CreateShaderResourceView(texture.GetNative(), &desc, descriptorAllocation->GetCpuHandle()); + descriptorAllocations[0] = inDevice.AllocateCbvSrvUavDescriptor(); + inDevice.GetNative()->CreateShaderResourceView(texture.GetNative(), &desc, descriptorAllocations[0]->GetCpuHandle()); } else if (IsUnorderedAccess(inCreateInfo.type)) { D3D12_UNORDERED_ACCESS_VIEW_DESC desc {}; desc.Format = EnumCast(texture.GetCreateInfo().format); @@ -239,8 +250,8 @@ namespace RHI::DirectX12 { FillTexture2DArrayUAV(desc.Texture2DArray, inCreateInfo); FillTexture3DUAV(desc.Texture3D, inCreateInfo); - descriptorAllocation = inDevice.AllocateCbvSrvUavDescriptor(); - inDevice.GetNative()->CreateUnorderedAccessView(texture.GetNative(), nullptr, &desc, descriptorAllocation->GetCpuHandle()); + descriptorAllocations[0] = inDevice.AllocateCbvSrvUavDescriptor(); + inDevice.GetNative()->CreateUnorderedAccessView(texture.GetNative(), nullptr, &desc, descriptorAllocations[0]->GetCpuHandle()); } else if (IsRenderTarget(inCreateInfo.type)) { D3D12_RENDER_TARGET_VIEW_DESC desc {}; desc.Format = EnumCast(texture.GetCreateInfo().format); @@ -250,8 +261,8 @@ namespace RHI::DirectX12 { FillTexture2DArrayRTV(desc.Texture2DArray, inCreateInfo); FillTexture3DRTV(desc.Texture3D, inCreateInfo); - descriptorAllocation = inDevice.AllocateRtvDescriptor(); - inDevice.GetNative()->CreateRenderTargetView(texture.GetNative(), &desc, descriptorAllocation->GetCpuHandle()); + descriptorAllocations[0] = inDevice.AllocateRtvDescriptor(); + inDevice.GetNative()->CreateRenderTargetView(texture.GetNative(), &desc, descriptorAllocations[0]->GetCpuHandle()); } else if (IsDepthStencil(inCreateInfo.type)) { D3D12_DEPTH_STENCIL_VIEW_DESC desc {}; desc.Format = EnumCast(texture.GetCreateInfo().format); @@ -260,8 +271,18 @@ namespace RHI::DirectX12 { FillTexture2DDSV(desc.Texture2D, inCreateInfo); FillTexture2DArrayDSV(desc.Texture2DArray, inCreateInfo); - descriptorAllocation = inDevice.AllocateDsvDescriptor(); - inDevice.GetNative()->CreateDepthStencilView(texture.GetNative(), &desc, descriptorAllocation->GetCpuHandle()); + const auto textureAspect = GetTextureAspect(texture.GetCreateInfo().format); + const bool hasDepth = textureAspect == TextureAspect::depth || textureAspect == TextureAspect::depthStencil; + const bool hasStencil = textureAspect == TextureAspect::stencil || textureAspect == TextureAspect::depthStencil; + for (size_t descriptorIndex = 0; descriptorIndex < descriptorAllocations.size(); ++descriptorIndex) { + const bool depthReadOnly = descriptorIndex & 1; + const bool stencilReadOnly = descriptorIndex & 2; + const auto flags = (hasDepth && depthReadOnly ? D3D12_DSV_FLAG_READ_ONLY_DEPTH : D3D12_DSV_FLAG_NONE) + | (hasStencil && stencilReadOnly ? D3D12_DSV_FLAG_READ_ONLY_STENCIL : D3D12_DSV_FLAG_NONE); + desc.Flags = static_cast(flags); + descriptorAllocations[descriptorIndex] = inDevice.AllocateDsvDescriptor(); + inDevice.GetNative()->CreateDepthStencilView(texture.GetNative(), &desc, descriptorAllocations[descriptorIndex]->GetCpuHandle()); + } } } } diff --git a/Engine/Source/RHI-Vulkan/Src/CommandRecorder.cpp b/Engine/Source/RHI-Vulkan/Src/CommandRecorder.cpp index 107b99ead..3e57c04cf 100644 --- a/Engine/Source/RHI-Vulkan/Src/CommandRecorder.cpp +++ b/Engine/Source/RHI-Vulkan/Src/CommandRecorder.cpp @@ -77,7 +77,9 @@ namespace RHI::Vulkan { { TextureState::storage, VK_ACCESS_SHADER_READ_BIT }, { TextureState::rwStorage, VK_ACCESS_SHADER_WRITE_BIT }, { TextureState::depthStencilReadonly, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT }, - { TextureState::depthStencilWrite, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT }, + { TextureState::depthReadStencilWrite, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT }, + { TextureState::depthWriteStencilRead, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT }, + { TextureState::depthStencilWrite, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT }, { TextureState::present, VK_ACCESS_MEMORY_READ_BIT } }; return map.at(inState); @@ -94,6 +96,8 @@ namespace RHI::Vulkan { { TextureState::storage, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT }, { TextureState::rwStorage, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT }, { TextureState::depthStencilReadonly, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT }, + { TextureState::depthReadStencilWrite, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT }, + { TextureState::depthWriteStencilRead, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT }, { TextureState::depthStencilWrite, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT }, { TextureState::present, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT } }; @@ -111,6 +115,8 @@ namespace RHI::Vulkan { { TextureState::storage, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT }, { TextureState::rwStorage, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT }, { TextureState::depthStencilReadonly, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT }, + { TextureState::depthReadStencilWrite, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT }, + { TextureState::depthWriteStencilRead, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT }, { TextureState::depthStencilWrite, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT }, { TextureState::present, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT } }; @@ -128,6 +134,8 @@ namespace RHI::Vulkan { { TextureState::storage, VK_IMAGE_LAYOUT_GENERAL }, { TextureState::rwStorage, VK_IMAGE_LAYOUT_GENERAL }, { TextureState::depthStencilReadonly, VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL }, + { TextureState::depthReadStencilWrite, VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL }, + { TextureState::depthWriteStencilRead, VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL }, { TextureState::depthStencilWrite, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL }, { TextureState::present, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR } }; @@ -466,27 +474,32 @@ namespace RHI::Vulkan { referenceAttachmentView = depthStencilTextureView; } - depthAttachmentInfo.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO; - depthAttachmentInfo.imageView = depthStencilTextureView->GetNative(); - depthAttachmentInfo.imageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - depthAttachmentInfo.loadOp = EnumCast(inBeginInfo.depthStencilAttachment->depthLoadOp); - depthAttachmentInfo.storeOp = EnumCast(inBeginInfo.depthStencilAttachment->depthStoreOp); - depthAttachmentInfo.clearValue.depthStencil = {inBeginInfo.depthStencilAttachment->depthClearValue, inBeginInfo.depthStencilAttachment->stencilClearValue }; - - renderingInfo.pDepthAttachment = &depthAttachmentInfo; + const auto& attachment = inBeginInfo.depthStencilAttachment.value(); + const auto aspect = depthStencilTextureView->GetCreateInfo().aspect; + const bool hasDepth = aspect == TextureAspect::depth || aspect == TextureAspect::depthStencil; + const bool hasStencil = aspect == TextureAspect::stencil || aspect == TextureAspect::depthStencil; + const auto imageLayout = GetTextureLayout(GetDepthStencilTextureState(aspect, attachment.depthReadOnly, attachment.stencilReadOnly)); + + AssertWithReason(!hasDepth || !attachment.depthReadOnly || attachment.depthLoadOp != LoadOp::clear, "read-only depth attachments cannot be cleared"); + AssertWithReason(!hasStencil || !attachment.stencilReadOnly || attachment.stencilLoadOp != LoadOp::clear, "read-only stencil attachments cannot be cleared"); + + if (hasDepth) { + depthAttachmentInfo.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO; + depthAttachmentInfo.imageView = depthStencilTextureView->GetNative(); + depthAttachmentInfo.imageLayout = imageLayout; + depthAttachmentInfo.loadOp = EnumCast(attachment.depthLoadOp); + depthAttachmentInfo.storeOp = EnumCast(attachment.depthStoreOp); + depthAttachmentInfo.clearValue.depthStencil = { attachment.depthClearValue, attachment.stencilClearValue }; + renderingInfo.pDepthAttachment = &depthAttachmentInfo; + } - // depth-only formats must not present a stencil attachment, otherwise validation requires the - // pipeline's (undefined) stencil format to match the view format - const auto depthStencilFormat = depthStencilTextureView->GetTexture().GetCreateInfo().format; - const bool hasStencil = GetTextureAspect(depthStencilFormat) == TextureAspect::depthStencil; - if (hasStencil && !inBeginInfo.depthStencilAttachment->depthReadOnly) { + if (hasStencil) { stencilAttachmentInfo.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO; stencilAttachmentInfo.imageView = depthStencilTextureView->GetNative(); - stencilAttachmentInfo.imageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - stencilAttachmentInfo.loadOp = EnumCast(inBeginInfo.depthStencilAttachment->stencilLoadOp); - stencilAttachmentInfo.storeOp = EnumCast(inBeginInfo.depthStencilAttachment->stencilStoreOp); - stencilAttachmentInfo.clearValue.depthStencil = {inBeginInfo.depthStencilAttachment->depthClearValue, inBeginInfo.depthStencilAttachment->stencilClearValue }; - + stencilAttachmentInfo.imageLayout = imageLayout; + stencilAttachmentInfo.loadOp = EnumCast(attachment.stencilLoadOp); + stencilAttachmentInfo.storeOp = EnumCast(attachment.stencilStoreOp); + stencilAttachmentInfo.clearValue.depthStencil = { attachment.depthClearValue, attachment.stencilClearValue }; renderingInfo.pStencilAttachment = &stencilAttachmentInfo; } } diff --git a/Engine/Source/RHI-Vulkan/Src/Pipeline.cpp b/Engine/Source/RHI-Vulkan/Src/Pipeline.cpp index 3089216d0..219aefa8f 100644 --- a/Engine/Source/RHI-Vulkan/Src/Pipeline.cpp +++ b/Engine/Source/RHI-Vulkan/Src/Pipeline.cpp @@ -33,7 +33,7 @@ namespace RHI::Vulkan { VkPipelineDepthStencilStateCreateInfo dsInfo = {}; dsInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; dsInfo.depthTestEnable = dsState.depthEnabled ? VK_TRUE : VK_FALSE; - dsInfo.depthWriteEnable = dsState.depthEnabled ? VK_TRUE : VK_FALSE; + dsInfo.depthWriteEnable = dsState.depthEnabled && dsState.depthWriteEnabled ? VK_TRUE : VK_FALSE; dsInfo.stencilTestEnable = dsState.stencilEnabled ? VK_TRUE : VK_FALSE; dsInfo.front = ConvertStencilOp(dsState.stencilFront, dsState.stencilReadMask, dsState.stencilWriteMask); dsInfo.back = ConvertStencilOp(dsState.stencilBack, dsState.stencilReadMask, dsState.stencilWriteMask); diff --git a/Engine/Source/RHI/Include/RHI/Common.h b/Engine/Source/RHI/Include/RHI/Common.h index fa2e2efbd..c7082188f 100644 --- a/Engine/Source/RHI/Include/RHI/Common.h +++ b/Engine/Source/RHI/Include/RHI/Common.h @@ -412,6 +412,8 @@ namespace RHI { storage, rwStorage, depthStencilReadonly, + depthReadStencilWrite, + depthWriteStencilRead, depthStencilWrite, present, max @@ -495,4 +497,5 @@ namespace RHI { namespace RHI { size_t GetBytesPerPixel(PixelFormat format); TextureAspect GetTextureAspect(PixelFormat format); + TextureState GetDepthStencilTextureState(TextureAspect aspect, bool depthReadOnly, bool stencilReadOnly); } diff --git a/Engine/Source/RHI/Include/RHI/Pipeline.h b/Engine/Source/RHI/Include/RHI/Pipeline.h index 574405eac..016a9d53b 100644 --- a/Engine/Source/RHI/Include/RHI/Pipeline.h +++ b/Engine/Source/RHI/Include/RHI/Pipeline.h @@ -126,6 +126,7 @@ namespace RHI { struct DepthStencilState { bool depthEnabled; + bool depthWriteEnabled; bool stencilEnabled; PixelFormat format; CompareFunc depthCompareFunc; @@ -148,9 +149,11 @@ namespace RHI { const StencilFaceState& inStencilFront = StencilFaceState(), const StencilFaceState& inStencilBack = StencilFaceState(), uint8_t inStencilReadMask = 0, - uint8_t inStencilWriteMask = 0); + uint8_t inStencilWriteMask = 0, + bool inDepthWriteEnabled = true); DepthStencilState& SetDepthEnabled(bool inDepthEnabled); + DepthStencilState& SetDepthWriteEnabled(bool inDepthWriteEnabled); DepthStencilState& SetStencilEnabled(bool inStencilEnabled); DepthStencilState& SetFormat(PixelFormat inFormat); DepthStencilState& SetDepthCompareFunc(CompareFunc inFunc); diff --git a/Engine/Source/RHI/Src/Common.cpp b/Engine/Source/RHI/Src/Common.cpp index 10c6bc61b..eac789f14 100644 --- a/Engine/Source/RHI/Src/Common.cpp +++ b/Engine/Source/RHI/Src/Common.cpp @@ -41,4 +41,26 @@ namespace RHI { return TextureAspect::color; } } + + TextureState GetDepthStencilTextureState(const TextureAspect aspect, const bool depthReadOnly, const bool stencilReadOnly) + { + if (aspect == TextureAspect::depth) { + return depthReadOnly ? TextureState::depthStencilReadonly : TextureState::depthStencilWrite; + } + if (aspect == TextureAspect::stencil) { + return stencilReadOnly ? TextureState::depthStencilReadonly : TextureState::depthStencilWrite; + } + + Assert(aspect == TextureAspect::depthStencil); + if (depthReadOnly && stencilReadOnly) { + return TextureState::depthStencilReadonly; + } + if (depthReadOnly) { + return TextureState::depthReadStencilWrite; + } + if (stencilReadOnly) { + return TextureState::depthWriteStencilRead; + } + return TextureState::depthStencilWrite; + } } diff --git a/Engine/Source/RHI/Src/Pipeline.cpp b/Engine/Source/RHI/Src/Pipeline.cpp index b73d53569..71890f7f2 100644 --- a/Engine/Source/RHI/Src/Pipeline.cpp +++ b/Engine/Source/RHI/Src/Pipeline.cpp @@ -134,8 +134,10 @@ namespace RHI { const StencilFaceState& inStencilFront, const StencilFaceState& inStencilBack, const uint8_t inStencilReadMask, - const uint8_t inStencilWriteMask) + const uint8_t inStencilWriteMask, + const bool inDepthWriteEnabled) : depthEnabled(inDepthEnabled) + , depthWriteEnabled(inDepthWriteEnabled) , stencilEnabled(inStencilEnabled) , format(inFormat) , depthCompareFunc(inDepthCompareFunc) @@ -155,6 +157,12 @@ namespace RHI { return *this; } + DepthStencilState& DepthStencilState::SetDepthWriteEnabled(const bool inDepthWriteEnabled) + { + depthWriteEnabled = inDepthWriteEnabled; + return *this; + } + DepthStencilState& DepthStencilState::SetStencilEnabled(const bool inStencilEnabled) { stencilEnabled = inStencilEnabled; diff --git a/Engine/Source/Render/Src/RenderCache.cpp b/Engine/Source/Render/Src/RenderCache.cpp index 45f5934cd..ef573a078 100644 --- a/Engine/Source/Render/Src/RenderCache.cpp +++ b/Engine/Source/Render/Src/RenderCache.cpp @@ -47,6 +47,7 @@ namespace Render::Internal { { return CombineHashes({ static_cast(state.depthEnabled), + static_cast(state.depthWriteEnabled), static_cast(state.stencilEnabled), static_cast(state.format), static_cast(state.depthCompareFunc), diff --git a/Engine/Source/Render/Src/RenderGraph.cpp b/Engine/Source/Render/Src/RenderGraph.cpp index ba7c4c218..0d438d468 100644 --- a/Engine/Source/Render/Src/RenderGraph.cpp +++ b/Engine/Source/Render/Src/RenderGraph.cpp @@ -1142,7 +1142,7 @@ namespace Render { { if (inDesc.depthStencilAttachment.has_value()) { const auto& dsa = inDesc.depthStencilAttachment.value(); - TransitionTexture(inRecoder, dsa.view->GetTexture(), dsa.depthReadOnly ? RHI::TextureState::depthStencilReadonly : RHI::TextureState::depthStencilWrite); + TransitionTexture(inRecoder, dsa.view->GetTexture(), RHI::GetDepthStencilTextureState(dsa.view->GetDesc().aspect, dsa.depthReadOnly, dsa.stencilReadOnly)); } for (const auto& ca : inDesc.colorAttachments) { TransitionTexture(inRecoder, ca.view->GetTexture(), RHI::TextureState::renderTarget); diff --git a/Engine/Source/Render/Test/RenderGraphTest.cpp b/Engine/Source/Render/Test/RenderGraphTest.cpp index 6e92e0a4e..b1cdd8f41 100644 --- a/Engine/Source/Render/Test/RenderGraphTest.cpp +++ b/Engine/Source/Render/Test/RenderGraphTest.cpp @@ -8,6 +8,16 @@ #include namespace Render { + TEST(RenderGraphStateTest, MapsIndependentDepthStencilAccess) + { + ASSERT_EQ(RHI::GetDepthStencilTextureState(RHI::TextureAspect::depthStencil, true, true), RHI::TextureState::depthStencilReadonly); + ASSERT_EQ(RHI::GetDepthStencilTextureState(RHI::TextureAspect::depthStencil, true, false), RHI::TextureState::depthReadStencilWrite); + ASSERT_EQ(RHI::GetDepthStencilTextureState(RHI::TextureAspect::depthStencil, false, true), RHI::TextureState::depthWriteStencilRead); + ASSERT_EQ(RHI::GetDepthStencilTextureState(RHI::TextureAspect::depthStencil, false, false), RHI::TextureState::depthStencilWrite); + ASSERT_EQ(RHI::GetDepthStencilTextureState(RHI::TextureAspect::depth, false, true), RHI::TextureState::depthStencilWrite); + ASSERT_EQ(RHI::GetDepthStencilTextureState(RHI::TextureAspect::stencil, true, false), RHI::TextureState::depthStencilWrite); + } + struct RenderGraphTest : testing::Test { void SetUp() override { From ee78c6eddf6d87fe9feab3487d4d431885260068 Mon Sep 17 00:00:00 2001 From: kindem Date: Sun, 2 Aug 2026 16:06:35 +0800 Subject: [PATCH 11/14] fix: correct Vulkan depth rasterization state --- Engine/Source/RHI-Vulkan/Src/Device.cpp | 2 ++ Engine/Source/RHI-Vulkan/Src/Pipeline.cpp | 32 +++++++++++++---------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/Engine/Source/RHI-Vulkan/Src/Device.cpp b/Engine/Source/RHI-Vulkan/Src/Device.cpp index 44c6fe00a..0d73c3989 100644 --- a/Engine/Source/RHI-Vulkan/Src/Device.cpp +++ b/Engine/Source/RHI-Vulkan/Src/Device.cpp @@ -353,6 +353,8 @@ namespace RHI::Vulkan { enabledFeatures.multiDrawIndirect = supportedFeatures.features.multiDrawIndirect; enabledFeatures.drawIndirectFirstInstance = supportedFeatures.features.drawIndirectFirstInstance; enabledFeatures.fillModeNonSolid = supportedFeatures.features.fillModeNonSolid; + enabledFeatures.depthClamp = supportedFeatures.features.depthClamp; + enabledFeatures.depthBiasClamp = supportedFeatures.features.depthBiasClamp; enabledFeatures.samplerAnisotropy = supportedFeatures.features.samplerAnisotropy; enabledFeatures.textureCompressionBC = supportedFeatures.features.textureCompressionBC; enabledFeatures.occlusionQueryPrecise = supportedFeatures.features.occlusionQueryPrecise; diff --git a/Engine/Source/RHI-Vulkan/Src/Pipeline.cpp b/Engine/Source/RHI-Vulkan/Src/Pipeline.cpp index 219aefa8f..14f9620fa 100644 --- a/Engine/Source/RHI-Vulkan/Src/Pipeline.cpp +++ b/Engine/Source/RHI-Vulkan/Src/Pipeline.cpp @@ -54,24 +54,28 @@ namespace RHI::Vulkan { return assemblyInfo; } - static VkPipelineRasterizationStateCreateInfo ConstructRasterization(const RasterPipelineCreateInfo& createInfo) + static VkPipelineRasterizationStateCreateInfo ConstructRasterization(const VulkanDevice& device, const RasterPipelineCreateInfo& createInfo) { + const auto& primitiveState = createInfo.primitiveState; + const auto& depthStencilState = createInfo.depthStencilState; + const bool depthClampEnabled = !primitiveState.depthClip; + const bool depthBiasEnabled = depthStencilState.depthBias != 0 || depthStencilState.depthBiasSlopeScale != 0.0f; + + AssertWithReason(!depthClampEnabled || device.GetEnabledFeatures().depthClamp == VK_TRUE, "Vulkan depth clamp feature is not supported"); + AssertWithReason(!depthBiasEnabled || depthStencilState.depthBiasClamp == 0.0f || device.GetEnabledFeatures().depthBiasClamp == VK_TRUE, "Vulkan depth bias clamp feature is not supported"); + VkPipelineRasterizationStateCreateInfo rasterState = {}; - rasterState.polygonMode = EnumCast(createInfo.primitiveState.fillMode); + rasterState.polygonMode = EnumCast(primitiveState.fillMode); rasterState.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; - rasterState.cullMode = EnumCast(createInfo.primitiveState.cullMode); - rasterState.frontFace = createInfo.primitiveState.frontFace == FrontFace::cw ? VK_FRONT_FACE_CLOCKWISE : VK_FRONT_FACE_COUNTER_CLOCKWISE; - rasterState.depthBiasClamp = createInfo.depthStencilState.depthBiasClamp; - rasterState.depthBiasSlopeFactor = createInfo.depthStencilState.depthBiasSlopeScale; - rasterState.depthBiasEnable = createInfo.depthStencilState.depthBias == 0 ? VK_FALSE : VK_TRUE; - rasterState.depthBiasConstantFactor = static_cast(createInfo.depthStencilState.depthBias); + rasterState.cullMode = EnumCast(primitiveState.cullMode); + rasterState.frontFace = primitiveState.frontFace == FrontFace::cw ? VK_FRONT_FACE_CLOCKWISE : VK_FRONT_FACE_COUNTER_CLOCKWISE; + rasterState.depthClampEnable = depthClampEnabled ? VK_TRUE : VK_FALSE; + rasterState.depthBiasEnable = depthBiasEnabled ? VK_TRUE : VK_FALSE; + rasterState.depthBiasConstantFactor = static_cast(depthStencilState.depthBias); + rasterState.depthBiasClamp = depthBiasEnabled ? depthStencilState.depthBiasClamp : 0.0f; + rasterState.depthBiasSlopeFactor = depthStencilState.depthBiasSlopeScale; rasterState.lineWidth = 1.0; - // TODO DepthClampEnable requires check depth clamping feature - rasterState.depthClampEnable = VK_FALSE; - // rasterState.setDepthClampEnable(createInfo.primitive.depthClip ? VK_FALSE : VK_TRUE); - // TODO DepthClipEnable requires VK_EXT_depth_clip_enable - return rasterState; } @@ -216,7 +220,7 @@ namespace RHI::Vulkan { VkPipelineMultisampleStateCreateInfo multiSampleInfo = ConstructMultiSampleState(inCreateInfo); VkPipelineDepthStencilStateCreateInfo dsInfo = ConstructDepthStencil(inCreateInfo); VkPipelineInputAssemblyStateCreateInfo assemblyInfo = ConstructInputAssembly(inCreateInfo); - VkPipelineRasterizationStateCreateInfo rasterState = ConstructRasterization(inCreateInfo); + VkPipelineRasterizationStateCreateInfo rasterState = ConstructRasterization(device, inCreateInfo); VkPipelineViewportStateCreateInfo viewportState = ConstructViewportInfo(inCreateInfo); std::vector blendStates; From 5249905e2930e9432023263da4cb67a194ed013b Mon Sep 17 00:00:00 2001 From: kindem Date: Sun, 2 Aug 2026 16:19:58 +0800 Subject: [PATCH 12/14] fix: align texture upload buffer offsets --- Engine/Source/Common/Include/Common/Utility.h | 11 +++++++++++ Engine/Source/Common/Test/UtilityTest.cpp | 2 ++ .../Source/RHI-DirectX12/Src/CommandRecorder.cpp | 1 + Engine/Source/Runtime/Src/Asset/Texture.cpp | 14 +++++++++----- 4 files changed, 23 insertions(+), 5 deletions(-) diff --git a/Engine/Source/Common/Include/Common/Utility.h b/Engine/Source/Common/Include/Common/Utility.h index 898ca86cb..3b3ca6472 100644 --- a/Engine/Source/Common/Include/Common/Utility.h +++ b/Engine/Source/Common/Include/Common/Utility.h @@ -49,6 +49,9 @@ namespace Common { template T AlignUp(T value); + template + T AlignUp(T value, T alignment); + template struct IsAllSame {}; @@ -116,6 +119,14 @@ namespace Common { return (value + (A - 1)) & ~(A - 1); } + template + T AlignUp(T value, T alignment) + { + Assert(alignment > 0); + const T remainder = value % alignment; + return remainder == 0 ? value : value + alignment - remainder; + } + template struct IsAllSame { static constexpr bool value = std::is_same_v, std::remove_cvref_t> && IsAllSame::value; diff --git a/Engine/Source/Common/Test/UtilityTest.cpp b/Engine/Source/Common/Test/UtilityTest.cpp index 882613d12..8a72d0e46 100644 --- a/Engine/Source/Common/Test/UtilityTest.cpp +++ b/Engine/Source/Common/Test/UtilityTest.cpp @@ -12,4 +12,6 @@ TEST(UtilityTest, AlignUpTest) ASSERT_EQ(AlignUp<4>(3), 4); ASSERT_EQ(AlignUp<4>(7), 8); ASSERT_EQ(AlignUp<256>(258), 512); + ASSERT_EQ(AlignUp(768, 512), 1024); + ASSERT_EQ(AlignUp(1024, 512), 1024); } diff --git a/Engine/Source/RHI-DirectX12/Src/CommandRecorder.cpp b/Engine/Source/RHI-DirectX12/Src/CommandRecorder.cpp index 152b19e8a..38ad5a9f4 100644 --- a/Engine/Source/RHI-DirectX12/Src/CommandRecorder.cpp +++ b/Engine/Source/RHI-DirectX12/Src/CommandRecorder.cpp @@ -47,6 +47,7 @@ namespace RHI::DirectX12 { static CD3DX12_TEXTURE_COPY_LOCATION GetNativeBufferCopyLocationFromTextureLayout(DX12Device& device, const DX12Buffer& buffer, const DX12Texture& texture, const BufferTextureCopyInfo& copyInfo) { + Assert(copyInfo.bufferOffset % D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT == 0); const auto aspectLayout = device.GetTextureSubResourceCopyFootprint(texture, copyInfo.textureSubResource); // NOLINT // The buffer is laid out as the full sub-resource footprint (so the slice stride is RowPitch * full height); diff --git a/Engine/Source/Runtime/Src/Asset/Texture.cpp b/Engine/Source/Runtime/Src/Asset/Texture.cpp index ab0cc576e..0083607e6 100644 --- a/Engine/Source/Runtime/Src/Asset/Texture.cpp +++ b/Engine/Source/Runtime/Src/Asset/Texture.cpp @@ -253,8 +253,15 @@ namespace Runtime { } } + const auto bufferCopyOffsetAlignment = static_cast(device->GetGpu().GetLimits().optimalBufferCopyOffsetAlignment); + Assert(bufferCopyOffsetAlignment > 0); + + std::vector copyOffsets; + copyOffsets.reserve(copyFootprints.size()); size_t totalBytes = 0; for (const auto& copyFootprint : copyFootprints) { + totalBytes = Common::AlignUp(totalBytes, bufferCopyOffsetAlignment); + copyOffsets.emplace_back(totalBytes); totalBytes += copyFootprint.totalBytes; } @@ -267,13 +274,13 @@ namespace Runtime { const auto bytesPerPixel = RHI::GetBytesPerPixel(static_cast(format)); - size_t dstSubResourceOffset = 0; auto* dstData = static_cast(stagingBuffer->Map(RHI::MapMode::write, 0, totalBytes)); for (auto m = 0; m < mipLevels; m++) { for (auto a = 0; a < arraySize; a++) { const auto subResourceIndex = Internal::GetSubResourceIndex(m, a, arraySize); const auto& srcPixels = subResourcePixelsData[subResourceIndex]; const auto& dstCopyFootprint = copyFootprints[subResourceIndex]; + const auto dstSubResourceOffset = copyOffsets[subResourceIndex]; const auto srcRowPitch = dstCopyFootprint.extent.x * bytesPerPixel; const auto srcSlicePitch = srcRowPitch * dstCopyFootprint.extent.y; @@ -284,7 +291,6 @@ namespace Runtime { memcpy(dst, src, srcRowPitch); } } - dstSubResourceOffset += dstCopyFootprint.totalBytes; } } stagingBuffer->Unmap(); @@ -294,7 +300,6 @@ namespace Runtime { { const auto passRecoder = recoder->BeginCopyPass(); { - dstSubResourceOffset = 0; for (auto m = 0; m < mipLevels; m++) { for (auto a = 0; a < arraySize; a++) { const auto subResourceIndex = Internal::GetSubResourceIndex(m, a, arraySize); @@ -302,11 +307,10 @@ namespace Runtime { stagingBuffer.Get(), texturePtr, RHI::BufferTextureCopyInfo() - .SetBufferOffset(dstSubResourceOffset) + .SetBufferOffset(copyOffsets[subResourceIndex]) .SetTextureSubResource(RHI::TextureSubResourceInfo(m, a, aspect)) .SetTextureOrigin({ 0, 0, 0 }) .SetCopyRegion(copyFootprints[subResourceIndex].extent)); - dstSubResourceOffset += copyFootprints[subResourceIndex].totalBytes; } } } From 58fe6cc9677b81867e63f5b6f3e11633ce3a89d6 Mon Sep 17 00:00:00 2001 From: kindem Date: Sun, 2 Aug 2026 16:41:44 +0800 Subject: [PATCH 13/14] fix: honor bind group layout indices in Vulkan --- .../Include/RHI/DirectX12/BindGroupLayout.h | 2 -- .../RHI-DirectX12/Src/BindGroupLayout.cpp | 10 ++---- .../Source/RHI-Vulkan/Src/PipelineLayout.cpp | 32 ++++++++++++++++--- .../Source/RHI/Include/RHI/BindGroupLayout.h | 5 +++ Engine/Source/RHI/Src/BindGroupLayout.cpp | 10 +++++- 5 files changed, 43 insertions(+), 16 deletions(-) diff --git a/Engine/Source/RHI-DirectX12/Include/RHI/DirectX12/BindGroupLayout.h b/Engine/Source/RHI-DirectX12/Include/RHI/DirectX12/BindGroupLayout.h index 3173a4c2f..98adc8571 100644 --- a/Engine/Source/RHI-DirectX12/Include/RHI/DirectX12/BindGroupLayout.h +++ b/Engine/Source/RHI-DirectX12/Include/RHI/DirectX12/BindGroupLayout.h @@ -28,14 +28,12 @@ namespace RHI::DirectX12 { explicit DX12BindGroupLayout(const BindGroupLayoutCreateInfo& inCreateInfo); ~DX12BindGroupLayout() override; - uint8_t GetLayoutIndex() const; [[nodiscard]] const std::vector& GetRootParameterKeyInfos() const; [[nodiscard]] const std::vector& GetNativeRootParameters() const; private: void CreateNativeRootParameters(const BindGroupLayoutCreateInfo& inCreateInfo); - uint8_t layoutIndex; std::vector rootParameterKeyInfos; std::vector nativeRootParameters; std::vector nativeDescriptorRanges; diff --git a/Engine/Source/RHI-DirectX12/Src/BindGroupLayout.cpp b/Engine/Source/RHI-DirectX12/Src/BindGroupLayout.cpp index ffd5efb6d..92c5d9942 100644 --- a/Engine/Source/RHI-DirectX12/Src/BindGroupLayout.cpp +++ b/Engine/Source/RHI-DirectX12/Src/BindGroupLayout.cpp @@ -33,18 +33,12 @@ namespace RHI::DirectX12 { DX12BindGroupLayout::DX12BindGroupLayout(const BindGroupLayoutCreateInfo& inCreateInfo) : BindGroupLayout(inCreateInfo) - , layoutIndex(inCreateInfo.layoutIndex) { CreateNativeRootParameters(inCreateInfo); } DX12BindGroupLayout::~DX12BindGroupLayout() = default; - uint8_t DX12BindGroupLayout::GetLayoutIndex() const - { - return layoutIndex; - } - const std::vector& DX12BindGroupLayout::GetRootParameterKeyInfos() const { return rootParameterKeyInfos; @@ -66,10 +60,10 @@ namespace RHI::DirectX12 { nativeDescriptorRanges.emplace_back(); const auto& hlslBinding = std::get(entry.binding.platformBinding); - nativeDescriptorRanges.back().Init(EnumCast(hlslBinding.rangeType), 1, hlslBinding.index, inCreateInfo.layoutIndex); + nativeDescriptorRanges.back().Init(EnumCast(hlslBinding.rangeType), 1, hlslBinding.index, GetLayoutIndex()); nativeRootParameters.back().InitAsDescriptorTable(1, &nativeDescriptorRanges.back(), GetShaderVisibility(entry.shaderVisibility)); - rootParameterKeyInfos.emplace_back(entry.binding.type, inCreateInfo.layoutIndex, hlslBinding, entry.shaderVisibility); + rootParameterKeyInfos.emplace_back(entry.binding.type, GetLayoutIndex(), hlslBinding, entry.shaderVisibility); } } } diff --git a/Engine/Source/RHI-Vulkan/Src/PipelineLayout.cpp b/Engine/Source/RHI-Vulkan/Src/PipelineLayout.cpp index 1a2ddd528..17cd95305 100644 --- a/Engine/Source/RHI-Vulkan/Src/PipelineLayout.cpp +++ b/Engine/Source/RHI-Vulkan/Src/PipelineLayout.cpp @@ -35,10 +35,28 @@ namespace RHI::Vulkan { void VulkanPipelineLayout::CreateNativePipelineLayout(const PipelineLayoutCreateInfo& inCreateInfo) { - std::vector setLayouts(inCreateInfo.bindGroupLayouts.size()); - for (uint32_t i = 0; i < inCreateInfo.bindGroupLayouts.size(); ++i) { - const auto* vulkanBindGroup = static_cast(inCreateInfo.bindGroupLayouts[i]); - setLayouts[i] = vulkanBindGroup->GetNative(); + std::vector setLayouts; + for (const auto* bindGroupLayout : inCreateInfo.bindGroupLayouts) { + const auto layoutIndex = bindGroupLayout->GetLayoutIndex(); + if (setLayouts.size() <= layoutIndex) { + setLayouts.resize(layoutIndex + 1, VK_NULL_HANDLE); + } + + AssertWithReason(setLayouts[layoutIndex] == VK_NULL_HANDLE, "pipeline layouts cannot contain duplicate bind group layout indices"); + setLayouts[layoutIndex] = static_cast(bindGroupLayout)->GetNative(); + } + + VkDescriptorSetLayout emptySetLayout = VK_NULL_HANDLE; + for (auto& setLayout : setLayouts) { + if (setLayout != VK_NULL_HANDLE) { + continue; + } + if (emptySetLayout == VK_NULL_HANDLE) { + VkDescriptorSetLayoutCreateInfo emptySetLayoutInfo = {}; + emptySetLayoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + Assert(vkCreateDescriptorSetLayout(device.GetNative(), &emptySetLayoutInfo, nullptr, &emptySetLayout) == VK_SUCCESS); + } + setLayout = emptySetLayout; } pushConstantRanges.resize(inCreateInfo.pipelineConstantLayouts.size()); @@ -57,6 +75,10 @@ namespace RHI::Vulkan { pipelineLayoutInfo.pPushConstantRanges = pushConstantRanges.data(); Assert(vkCreatePipelineLayout(device.GetNative(), &pipelineLayoutInfo, nullptr, &nativePipelineLayout) == VK_SUCCESS); + if (emptySetLayout != VK_NULL_HANDLE) { + vkDestroyDescriptorSetLayout(device.GetNative(), emptySetLayout, nullptr); + } + #if BUILD_CONFIG_DEBUG if (!inCreateInfo.debugName.empty()) { device.SetObjectName(VK_OBJECT_TYPE_PIPELINE_LAYOUT, reinterpret_cast(nativePipelineLayout), inCreateInfo.debugName.c_str()); @@ -64,4 +86,4 @@ namespace RHI::Vulkan { #endif } -} \ No newline at end of file +} diff --git a/Engine/Source/RHI/Include/RHI/BindGroupLayout.h b/Engine/Source/RHI/Include/RHI/BindGroupLayout.h index 41ea0d14c..9202cb4ee 100644 --- a/Engine/Source/RHI/Include/RHI/BindGroupLayout.h +++ b/Engine/Source/RHI/Include/RHI/BindGroupLayout.h @@ -52,7 +52,12 @@ namespace RHI { NonCopyable(BindGroupLayout) virtual ~BindGroupLayout(); + uint8_t GetLayoutIndex() const; + protected: explicit BindGroupLayout(const BindGroupLayoutCreateInfo& createInfo); + + private: + uint8_t layoutIndex; }; } diff --git a/Engine/Source/RHI/Src/BindGroupLayout.cpp b/Engine/Source/RHI/Src/BindGroupLayout.cpp index f14855927..c873a7a3d 100644 --- a/Engine/Source/RHI/Src/BindGroupLayout.cpp +++ b/Engine/Source/RHI/Src/BindGroupLayout.cpp @@ -40,7 +40,15 @@ namespace RHI { return *this; } - BindGroupLayout::BindGroupLayout(const BindGroupLayoutCreateInfo&) {} + BindGroupLayout::BindGroupLayout(const BindGroupLayoutCreateInfo& inCreateInfo) + : layoutIndex(inCreateInfo.layoutIndex) + { + } BindGroupLayout::~BindGroupLayout() = default; + + uint8_t BindGroupLayout::GetLayoutIndex() const + { + return layoutIndex; + } } From bd2dd616b2f566f32424c3ff72afa4ef303f39a4 Mon Sep 17 00:00:00 2001 From: kindem Date: Sun, 2 Aug 2026 16:46:33 +0800 Subject: [PATCH 14/14] fix: enable depth clipping by default --- Engine/Source/RHI/Include/RHI/Pipeline.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Engine/Source/RHI/Include/RHI/Pipeline.h b/Engine/Source/RHI/Include/RHI/Pipeline.h index 016a9d53b..d3a6ccbf6 100644 --- a/Engine/Source/RHI/Include/RHI/Pipeline.h +++ b/Engine/Source/RHI/Include/RHI/Pipeline.h @@ -93,7 +93,7 @@ namespace RHI { IndexFormat stripIndexFormat; FrontFace frontFace; CullMode cullMode; - bool depthClip = false; + bool depthClip = true; explicit PrimitiveState( PrimitiveTopologyType inTopologyType = PrimitiveTopologyType::triangle, @@ -101,7 +101,7 @@ namespace RHI { IndexFormat inStripIndexFormat = IndexFormat::uint16, FrontFace inFrontFace = FrontFace::ccw, CullMode inCullMode = CullMode::back, - bool inDepthClip = false); + bool inDepthClip = true); PrimitiveState& SetTopologyType(PrimitiveTopologyType inTopologyType); PrimitiveState& SetFillMode(FillMode inFillMode);