diff --git a/Engine/common_dx/include/DXMipmapGenerator.hpp b/Engine/common_dx/include/DXMipmapGenerator.hpp new file mode 100644 index 00000000..d83991d2 --- /dev/null +++ b/Engine/common_dx/include/DXMipmapGenerator.hpp @@ -0,0 +1,28 @@ +//Author: JEYOON YU +//Project: CubeEngine +//File: DXMipmapGenerator.hpp +#pragma once +#define NOMINMAX +#include +#include + +using Microsoft::WRL::ComPtr; + +class DXMipmapGenerator +{ +public: + DXMipmapGenerator(const ComPtr& device); + ~DXMipmapGenerator() = default; + + DXMipmapGenerator(const DXMipmapGenerator&) = delete; + DXMipmapGenerator& operator=(const DXMipmapGenerator&) = delete; + DXMipmapGenerator(const DXMipmapGenerator&&) = delete; + DXMipmapGenerator& operator=(const DXMipmapGenerator&&) = delete; + + void Generate(const ComPtr& device, const ComPtr& commandList, const ComPtr& texture) const; +private: + ComPtr m_rootSignature; + ComPtr m_pipelineState; + ComPtr m_descriptorHeap; + UINT m_descriptorSize; +}; diff --git a/Engine/common_dx/include/DXSkybox.hpp b/Engine/common_dx/include/DXSkybox.hpp index 02ab809f..2d243bfd 100644 --- a/Engine/common_dx/include/DXSkybox.hpp +++ b/Engine/common_dx/include/DXSkybox.hpp @@ -9,6 +9,7 @@ #include "DXVertexBuffer.hpp" #include "DXPipeLine.hpp" +#include "DXMipmapGenerator.hpp" #include #include @@ -79,6 +80,7 @@ class DXSkybox ComPtr m_device; ComPtr m_commandQueue; + std::unique_ptr m_mipmapGenerator; ComPtr m_commandAllocator; ComPtr m_commandList; diff --git a/Engine/common_dx/source/DXMipmapGenerator.cpp b/Engine/common_dx/source/DXMipmapGenerator.cpp new file mode 100644 index 00000000..e75d9ac1 --- /dev/null +++ b/Engine/common_dx/source/DXMipmapGenerator.cpp @@ -0,0 +1,137 @@ +//Author: JEYOON YU +//Project: CubeEngine +//File: DXMipmapGenerator.cpp +#include "DXMipmapGenerator.hpp" + +#include +#include +#include +#include + +DXMipmapGenerator::DXMipmapGenerator(const ComPtr& device) +{ + // Create root signature + CD3DX12_DESCRIPTOR_RANGE1 srvRange(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0, 0, D3D12_DESCRIPTOR_RANGE_FLAG_DATA_VOLATILE); + CD3DX12_DESCRIPTOR_RANGE1 uavRange(D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 1, 0, 0, D3D12_DESCRIPTOR_RANGE_FLAG_DATA_VOLATILE); + + CD3DX12_ROOT_PARAMETER1 rootParameters[3]; + rootParameters[0].InitAsConstants(2, 0); + rootParameters[1].InitAsDescriptorTable(1, &srvRange); + rootParameters[2].InitAsDescriptorTable(1, &uavRange); + + D3D12_STATIC_SAMPLER_DESC sampler = {}; + sampler.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR; + sampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; + sampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; + sampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; + sampler.MipLODBias = 0.0f; + sampler.MaxAnisotropy = 1; + sampler.ComparisonFunc = D3D12_COMPARISON_FUNC_ALWAYS; + sampler.MinLOD = 0.0f; + sampler.MaxLOD = D3D12_FLOAT32_MAX; + sampler.ShaderRegister = 0; + sampler.RegisterSpace = 0; + sampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + + CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC rootSignatureDesc; + rootSignatureDesc.Init_1_1(_countof(rootParameters), rootParameters, 1, &sampler, D3D12_ROOT_SIGNATURE_FLAG_NONE); + + ComPtr signature, error; + if (FAILED(D3D12SerializeVersionedRootSignature(&rootSignatureDesc, &signature, &error))) + { + throw std::runtime_error("Failed to serialize mipmap root signature.\n" + std::string((char*)error->GetBufferPointer())); + } + device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&m_rootSignature)); + + // Compile compute shader + ComPtr computeShader; + HRESULT hr = D3DCompileFromFile(L"../Engine/shaders/hlsl/MipmapGen.comp.hlsl", nullptr, D3D_COMPILE_STANDARD_FILE_INCLUDE, "computeMain", "cs_5_1", 0, 0, &computeShader, &error); + if (FAILED(hr)) throw std::runtime_error("Failed to compile MipmapGen compute shader.\n" + std::string(static_cast(error->GetBufferPointer()))); + + D3D12_COMPUTE_PIPELINE_STATE_DESC psoDesc = {}; + psoDesc.pRootSignature = m_rootSignature.Get(); + psoDesc.CS = CD3DX12_SHADER_BYTECODE(computeShader.Get()); + device->CreateComputePipelineState(&psoDesc, IID_PPV_ARGS(&m_pipelineState)); + + // Create descriptor heap for SRV/UAV + D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {}; + heapDesc.NumDescriptors = 256; + heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; + heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; + device->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(&m_descriptorHeap)); + m_descriptorSize = device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); +} + +void DXMipmapGenerator::Generate(const ComPtr& device, const ComPtr& commandList, const ComPtr& texture) const +{ + D3D12_RESOURCE_DESC texDesc = texture->GetDesc(); + + if (texDesc.MipLevels <= 1 || !(texDesc.Flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS)) return; + + commandList->SetPipelineState(m_pipelineState.Get()); + commandList->SetComputeRootSignature(m_rootSignature.Get()); + + ID3D12DescriptorHeap* ppHeaps[] = { m_descriptorHeap.Get() }; + commandList->SetDescriptorHeaps(_countof(ppHeaps), ppHeaps); + + CD3DX12_CPU_DESCRIPTOR_HANDLE cpuHandle(m_descriptorHeap->GetCPUDescriptorHandleForHeapStart()); + CD3DX12_GPU_DESCRIPTOR_HANDLE gpuHandle(m_descriptorHeap->GetGPUDescriptorHandleForHeapStart()); + + for (uint32_t mip = 0; mip < texDesc.MipLevels - 1; ++mip) + { + uint32_t srcMip = mip; + uint32_t dstMip = mip + 1; + + uint32_t dstWidth = std::max(1u, static_cast(texDesc.Width >> dstMip)); + uint32_t dstHeight = std::max(1u, static_cast(texDesc.Height >> dstMip)); + + std::vector barriers; + for (uint32_t arraySlice = 0; arraySlice < texDesc.DepthOrArraySize; ++arraySlice) + { + uint32_t dstSubresource = D3D12CalcSubresource(dstMip, arraySlice, 0, texDesc.MipLevels, texDesc.DepthOrArraySize); + barriers.push_back(CD3DX12_RESOURCE_BARRIER::Transition(texture.Get(), D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, dstSubresource)); + } + commandList->ResourceBarrier(static_cast(barriers.size()), barriers.data()); + + // Create SRV for source mip level + D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {}; + srvDesc.Format = texDesc.Format; + srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY; + srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + srvDesc.Texture2DArray.MostDetailedMip = srcMip; + srvDesc.Texture2DArray.MipLevels = 1; + srvDesc.Texture2DArray.FirstArraySlice = 0; + srvDesc.Texture2DArray.ArraySize = texDesc.DepthOrArraySize; + + device->CreateShaderResourceView(texture.Get(), &srvDesc, cpuHandle); + commandList->SetComputeRootDescriptorTable(1, gpuHandle); + cpuHandle.Offset(1, m_descriptorSize); + gpuHandle.Offset(1, m_descriptorSize); + + // Create UAV for destination mip level + D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {}; + uavDesc.Format = texDesc.Format; + uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2DARRAY; + uavDesc.Texture2DArray.MipSlice = dstMip; + uavDesc.Texture2DArray.FirstArraySlice = 0; + uavDesc.Texture2DArray.ArraySize = texDesc.DepthOrArraySize; + + device->CreateUnorderedAccessView(texture.Get(), nullptr, &uavDesc, cpuHandle); + commandList->SetComputeRootDescriptorTable(2, gpuHandle); + cpuHandle.Offset(1, m_descriptorSize); + gpuHandle.Offset(1, m_descriptorSize); + + float texelSize[2] = { 1.0f / dstWidth, 1.0f / dstHeight }; + commandList->SetComputeRoot32BitConstants(0, 2, texelSize, 0); + + UINT dispatchX = (dstWidth + 7) / 8; + UINT dispatchY = (dstHeight + 7) / 8; + commandList->Dispatch(dispatchX, dispatchY, texDesc.DepthOrArraySize); + + for (auto& b : barriers) + { + std::swap(b.Transition.StateBefore, b.Transition.StateAfter); + } + commandList->ResourceBarrier(static_cast(barriers.size()), barriers.data()); + } +} diff --git a/Engine/common_dx/source/DXSkybox.cpp b/Engine/common_dx/source/DXSkybox.cpp index 35d1f0c0..8bd8c7e1 100644 --- a/Engine/common_dx/source/DXSkybox.cpp +++ b/Engine/common_dx/source/DXSkybox.cpp @@ -27,6 +27,8 @@ DXSkybox::DXSkybox(const ComPtr& device, m_srvDescriptorSize = m_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); m_skyboxVertexBuffer = std::make_unique(m_device, m_commandQueue, static_cast(sizeof(glm::vec3)), static_cast(sizeof(glm::vec3) * m_skyboxVertices.size()), m_skyboxVertices.data()); + + m_mipmapGenerator = std::make_unique(m_device); } DXSkybox::~DXSkybox() @@ -91,10 +93,11 @@ void DXSkybox::EquirectangularToCube() texDesc.Width = static_cast(faceSize); texDesc.Height = static_cast(faceSize); texDesc.DepthOrArraySize = 6; - texDesc.MipLevels = 1; + UINT16 maxMipLevels = static_cast(std::log2(faceSize)) + 1; + texDesc.MipLevels = maxMipLevels; texDesc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT; texDesc.SampleDesc.Count = 1; - texDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; + texDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; D3D12_CLEAR_VALUE clearValue = {}; clearValue.Format = texDesc.Format; @@ -213,13 +216,15 @@ void DXSkybox::EquirectangularToCube() barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_cubemap.Get(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); m_commandList->ResourceBarrier(1, &barrier); + m_mipmapGenerator->Generate(m_device, m_commandList, m_cubemap.Get()); + ExecuteCommandList(); D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {}; srvDesc.Format = texDesc.Format; srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE; srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; - srvDesc.TextureCube.MipLevels = 1; + srvDesc.TextureCube.MipLevels = maxMipLevels; // Store Cubemap texture in second array of m_srvDescriptorIndices offset m_device->CreateShaderResourceView(m_cubemap.Get(), &srvDesc, m_srvHandles[1].first); @@ -411,6 +416,9 @@ void DXSkybox::PrefilteredEnvironmentMap() sampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; sampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; sampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; + // MinLOD and MaxLOD are set to allow sampling across all mip levels of the cubemap, which is essential for accurate prefiltering results + sampler.MinLOD = 0.0f; + sampler.MaxLOD = D3D12_FLOAT32_MAX; sampler.ShaderRegister = 0; sampler.RegisterSpace = 1; sampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; diff --git a/Engine/include/Interface/IComponent.hpp b/Engine/include/Interface/IComponent.hpp index c608c7b9..a53d78b6 100644 --- a/Engine/include/Interface/IComponent.hpp +++ b/Engine/include/Interface/IComponent.hpp @@ -19,7 +19,7 @@ class IComponent Object* GetOwner() const { return owner; } void SetOwner(Object* owner_) { this->owner = owner_; } - ComponentTypes GetType() { return componentType; } + ComponentTypes GetType() const { return componentType; } private: Object* owner = nullptr; diff --git a/Engine/shaders/hlsl/Compute.compute.hlsl b/Engine/shaders/hlsl/Compute.comp.hlsl similarity index 100% rename from Engine/shaders/hlsl/Compute.compute.hlsl rename to Engine/shaders/hlsl/Compute.comp.hlsl diff --git a/Engine/shaders/hlsl/ConvolutionBlur.compute.hlsl b/Engine/shaders/hlsl/ConvolutionBlur.comp.hlsl similarity index 100% rename from Engine/shaders/hlsl/ConvolutionBlur.compute.hlsl rename to Engine/shaders/hlsl/ConvolutionBlur.comp.hlsl diff --git a/Engine/shaders/hlsl/MipmapGen.comp.hlsl b/Engine/shaders/hlsl/MipmapGen.comp.hlsl new file mode 100644 index 00000000..07bde873 --- /dev/null +++ b/Engine/shaders/hlsl/MipmapGen.comp.hlsl @@ -0,0 +1,101 @@ +#pragma pack_matrix(column_major) +#ifdef SLANG_HLSL_ENABLE_NVAPI +#include "nvHLSLExtns.h" +#endif + +#ifndef __DXC_VERSION_MAJOR +// warning X3557: loop doesn't seem to do anything, forcing loop to unroll +#pragma warning(disable : 3557) +#endif + + +#line 2 "slang/MipmapGen.slang" +RWTexture2DArray dstTexture_0 : register(u0); + + +struct MipGenConstants_0 +{ + float2 texelSize_0; +}; + + +#line 9 +cbuffer cb_0 : register(b0) +{ + MipGenConstants_0 cb_0; +} + +#line 1 +Texture2DArray srcTexture_0 : register(t0); + +SamplerState LinearClampSampler_0 : register(s0); + + +#line 12 +[numthreads(8, 8, 1)] +void computeMain(uint3 dispatchThreadID_0 : SV_DispatchThreadID) +{ + +#line 12 + uint3 _S1 = dispatchThreadID_0; + + uint dstWidth_0; + +#line 14 + uint dstHeight_0; + +#line 14 + uint elements_0; + dstTexture_0.GetDimensions(dstWidth_0, dstHeight_0, elements_0); + +#line 15 + bool _S2; + + if((_S1.x) >= dstWidth_0) + { + +#line 17 + _S2 = true; + +#line 17 + } + else + { + +#line 17 + _S2 = (_S1.y) >= dstHeight_0; + +#line 17 + } + +#line 17 + if(_S2) + { + +#line 17 + _S2 = true; + +#line 17 + } + else + { + +#line 17 + _S2 = (_S1.z) >= elements_0; + +#line 17 + } + +#line 17 + if(_S2) + { + +#line 17 + return; + } + + + dstTexture_0[_S1] = srcTexture_0.SampleLevel(LinearClampSampler_0, float3((float2(_S1.xy) + 0.5f) * cb_0.texelSize_0, float(_S1.z)), 0.0f); + return; +} + diff --git a/Engine/shaders/slang/MipmapGen.slang b/Engine/shaders/slang/MipmapGen.slang new file mode 100644 index 00000000..74b250f9 --- /dev/null +++ b/Engine/shaders/slang/MipmapGen.slang @@ -0,0 +1,22 @@ +Texture2DArray srcTexture : register(t0); +RWTexture2DArray dstTexture : register(u0); +SamplerState LinearClampSampler : register(s0); + +struct MipGenConstants +{ + float2 texelSize; +}; +ConstantBuffer cb : register(b0); + +[numthreads(8, 8, 1)] +void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + uint dstWidth, dstHeight, elements; + dstTexture.GetDimensions(dstWidth, dstHeight, elements); + + if (dispatchThreadID.x >= dstWidth || dispatchThreadID.y >= dstHeight || dispatchThreadID.z >= elements) return; + + float2 uv = (float2(dispatchThreadID.xy) + 0.5f) * cb.texelSize; + float4 color = srcTexture.SampleLevel(LinearClampSampler, float3(uv, dispatchThreadID.z), 0); + dstTexture[dispatchThreadID] = color; +} diff --git a/Engine/shaders/spirv/Compute.comp.spv b/Engine/shaders/spirv/Compute.comp.spv new file mode 100644 index 00000000..bc113885 Binary files /dev/null and b/Engine/shaders/spirv/Compute.comp.spv differ diff --git a/Engine/shaders/spirv/ConvolutionBlur.comp.spv b/Engine/shaders/spirv/ConvolutionBlur.comp.spv new file mode 100644 index 00000000..020e3db2 Binary files /dev/null and b/Engine/shaders/spirv/ConvolutionBlur.comp.spv differ diff --git a/Engine/shaders/spirv/MipmapGen.comp.spv b/Engine/shaders/spirv/MipmapGen.comp.spv new file mode 100644 index 00000000..0222f5b2 Binary files /dev/null and b/Engine/shaders/spirv/MipmapGen.comp.spv differ diff --git a/Engine/source/DXShadowMapContext.cpp b/Engine/source/DXShadowMapContext.cpp index a5c0f153..c8b21ac4 100644 --- a/Engine/source/DXShadowMapContext.cpp +++ b/Engine/source/DXShadowMapContext.cpp @@ -52,7 +52,7 @@ void DXShadowMapContext::Initialize() m_computeRootSignature->SetName(L"Shadow Blur Compute Root Signature"); ComPtr computeShader, errorMessages; - HRESULT hr = D3DCompileFromFile(L"../Engine/shaders/hlsl/ConvolutionBlur.compute.hlsl", nullptr, nullptr, "computeMain", "cs_5_1", 0, 0, &computeShader, &errorMessages); + HRESULT hr = D3DCompileFromFile(L"../Engine/shaders/hlsl/ConvolutionBlur.comp.hlsl", nullptr, nullptr, "computeMain", "cs_5_1", 0, 0, &computeShader, &errorMessages); if (FAILED(hr) && errorMessages) OutputDebugStringA(static_cast(errorMessages->GetBufferPointer())); D3D12_COMPUTE_PIPELINE_STATE_DESC computePsoDesc = {}; diff --git a/shader_compiler.py b/shader_compiler.py index c9c816d2..e5955ead 100644 --- a/shader_compiler.py +++ b/shader_compiler.py @@ -36,7 +36,7 @@ # Shadow Map Pass { "file": "ShadowMapPass.slang", "entry": "vertexMain", "stage": "vertex", "profile_hlsl": "sm_5_1", "out_name": "ShadowMapPass.vert" }, { "file": "ShadowMapPass.slang", "entry": "fragmentMain", "stage": "fragment", "profile_hlsl": "sm_5_1", "out_name": "ShadowMapPass.frag" }, - { "file": "ConvolutionBlur.slang", "entry": "computeMain", "stage": "compute", "profile_hlsl": "sm_5_1", "out_name": "ConvolutionBlur.compute" }, + { "file": "ConvolutionBlur.slang", "entry": "computeMain", "stage": "compute", "profile_hlsl": "sm_5_1", "out_name": "ConvolutionBlur.comp" }, # Geometry Pass, Use totally same vertex and mesh shader but different pixel shader { "file": "GBuffer.slang", "entry": "fragmentMain", "stage": "fragment", "profile_hlsl": "sm_5_1", "out_name": "GBuffer.frag" }, { "file": "GBuffer.slang", "entry": "fragmentMain", "stage": "fragment", "profile_hlsl": "sm_6_5", "out_name": "GBufferMesh.frag" }, # Also need to convert to DXIL @@ -66,7 +66,9 @@ { "file": "ToneMapping.slang", "entry": "vertexMain", "stage": "vertex", "profile_hlsl": "sm_5_1", "out_name": "ToneMapping.vert" }, { "file": "ToneMapping.slang", "entry": "fragmentMain", "stage": "fragment", "profile_hlsl": "sm_5_1", "out_name": "ToneMapping.frag" }, # Compute - { "file": "Compute.slang", "entry": "computeMain", "stage": "compute", "profile_hlsl": "sm_5_1", "out_name": "Compute.compute" } + { "file": "Compute.slang", "entry": "computeMain", "stage": "compute", "profile_hlsl": "sm_5_1", "out_name": "Compute.comp" }, + # Mipmap + { "file": "MipmapGen.slang", "entry": "computeMain", "stage": "compute", "profile_hlsl": "sm_5_1", "out_name": "MipmapGen.comp" } ] # DXC Shader List