diff --git a/.gitignore b/.gitignore index 2985a8c5..b478653e 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,4 @@ CTestTestfile.cmake _deps .vs out -docs \ No newline at end of file +.codex_temp \ No newline at end of file diff --git a/Multithreading.md b/Multithreading.md index 71b1207f..03e329de 100644 --- a/Multithreading.md +++ b/Multithreading.md @@ -58,7 +58,7 @@ SH_RENDER_API void Renderer::Sync() std::swap(drawcall[core::ThreadType::Game], drawcall[core::ThreadType::Render]); } ``` -> swap방식은 메모리를 추가로 먹지만 매우 빠르게 작동하니 성능에 중요한 곳에서 사용하면 좋습니다. +> swap방식은 메모리를 추가로 먹지만 매우 빠르게 작동하니 성능에 중요한 곳에서 사용하면 됩니다. # 스레드 풀 core모듈의 ThreadPool클래스를 이용하여 작업을 할당하고 future로 값을 받아올 수 있습니다. @@ -89,3 +89,18 @@ for (auto& renderPipeline : renderPipelines) for (auto& futureCommand : futureCommands) recordedCommands.push_back(futureCommand.get()); ``` +# MPSC큐 +MSPC큐는 Multi Producer Single Consumer큐의 약자로, 다수의 스레드에서 쓰기 작업을 수행하고, 하나의 스레드에서 읽기 작업을 하는데 특화된 락-프리 큐입니다. + +```cpp +core::LockFreeMPSCQueue requestID; +// 다수의 스레드에서 실행 +requestID.Push(threadID); +// 하나의 스레드에서 읽어오기 +requestID.Drain( + [this](int threadID) + { + SH_INFO_FORMAT("Request: {}", threadID); + } +) +``` diff --git a/README.md b/README.md index 9a340937..b7409b6f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ # ShellEngine image +스크린샷 2026-05-11 012031 + + [![Video Label](http://img.youtube.com/vi/SEiktv0WtOM/0.jpg)](https://youtu.be/SEiktv0WtOM)
[간단한 에디터 데모 영상(Youtube)](https://youtu.be/SEiktv0WtOM) @@ -117,6 +120,6 @@ cmake --build out/build/linux-release ``` # TODO -- [ ]스켈레탈 메쉬 -- [ ]애니메이션, 애니메이터 -- [ ]Undo/Redo +- [x] 스켈레탈 메쉬 +- [ ] 애니메이션, 애니메이터 +- [x] Undo/Redo diff --git a/RenderArchitecture.md b/RenderArchitecture.md index 901f5ee1..9aa66dbf 100644 --- a/RenderArchitecture.md +++ b/RenderArchitecture.md @@ -1,161 +1,251 @@ -# ShellEngine 패스 기반 렌더링 구조 +# ShellEngine 패스 기반 렌더링 구조 ## 개요 +ShellEngine의 렌더링은 `RenderData`가 정의한 뷰/타겟 단위 작업을 `ScriptableRenderer`가 패스 목록으로 풀고, 각 `ScriptableRenderPass`가 자신의 리소스 사용 의도를 선언한 뒤, 백엔드 `CommandBuffer`가 실제 Vulkan 커맨드로 기록하는 구조입니다. + | 계층 | 책임 | |---|---| -| `ScriptableRenderPass` | 렌더링 의도 선언 (타겟, 샘플링 텍스처, 드로우 목록) | -| `ScriptableRenderer` | 패스 간 리소스 상태 추적 및 전이 정보 계산 | -| Vulkan 구현 계층 | 전이 정보를 실제 Vulkan API 호출로 번역 | - -ShellEngine의 Vulkan 렌더링 시스템은 드로우 호출을 단일 흐름으로 나열하는 방식 대신, **패스 단위로 렌더링 작업을 분리**하고 각 패스가 사용하는 렌더 텍스처와 일반 텍스쳐의 읽기/쓰기 관계를 추적하여, 리소스 전이와 실행 흐름을 자동으로 조립하는 구조로 설계되어 있습니다. +| `Renderer` | 렌더 스레드 진입점, drawable/render data 큐 반영, 커맨드 제출 조율 | +| `RenderDataManager` | 카메라/뷰 데이터를 GPU 버퍼에 업로드하고 `RenderData`를 priority 순으로 정렬 | +| `ScriptableRenderer` | 활성 패스 선택, render queue 정렬, 리소스 전이 계산, 패스별 커맨드 버퍼 기록 | +| `ScriptableRenderPass` | 패스 이름, render queue, 렌더 타겟/샘플링 텍스처 사용 의도, 드로우 기록 | +| `CommandBuffer` | 배리어, 렌더 타겟 설정, draw/dispatch/blit 같은 백엔드 독립 커맨드 인터페이스 | +| Vulkan 구현 계층 | `CommandBuffer` 호출을 dynamic rendering, pipeline bind, descriptor bind, barrier 호출로 변환 | -해당 구조로 인해 프로젝트 목적에 맞게 코드를 크게 수정하지 않고 패스를 재배치 할 수 있으며 멀티스레딩과도 자연스럽게 연결됩니다. +핵심은 패스가 “무엇을 렌더링하고 어떤 이미지를 읽고 쓰는지”만 표현하고, 이미지 레이아웃 전이와 Vulkan 동기화 코드는 `ScriptableRenderer`와 `VulkanCommandBuffer`가 처리한다는 점입니다. -## 멀티스레딩 -`ScriptableRenderer`에서 렌더큐에 맞는 패스 순서를 조정하고 리소스 의존 상태를 계산 후, 스레드 풀에 넣어 각 패스의 렌더링을 병렬적으로 수행합니다. +## 프레임 흐름 -1. **직렬 단계** - 중앙에서 리소스 상태 정리 및 의존 상태 계산 -2. **병렬 단계** - 각 패스의 커맨드 버퍼 기록을 스레드 풀에 task로 분배하여 병렬 실행 +![프레임 흐름](/docs/RenderFrameFlow.svg) -리소스 전이 판단이 중앙에서 선행되므로, 이후 커맨드 기록 단계는 패스 간 의존성 없이 병렬화할 수 있습니다. - -## 패스 추가 방법 +1. 게임/에디터 쪽에서 `Renderer::PushDrawAble()`과 `Renderer::PushRenderData()`로 렌더링할 객체와 뷰 정보를 큐에 넣습니다. +2. 렌더 스레드의 `Renderer::Render()`가 큐를 비우고 `RenderDataManager::UploadToGPU()`를 호출합니다. +3. `RenderDataManager`는 각 `RenderViewer`의 view/proj/camera position을 GPU 버퍼에 업로드하고 `RenderData`를 `priority` 기준으로 정렬합니다. +4. `VulkanRenderer::Render()`가 스왑체인 이미지를 획득한 뒤 각 `RenderData`에 frame index와 drawable 목록을 연결합니다. +5. `ScriptableRenderer::Setup()`이 해당 `RenderData`에서 사용할 패스를 활성화합니다. +6. `ScriptableRenderer::Execute()`가 활성 패스를 `RenderQueue` 순으로 정렬하고, 각 패스의 `Configure()`를 호출해 리소스 사용 정보를 확정합니다. +7. 렌더러가 `BarrierInfo`를 계산한 뒤, 각 패스의 커맨드 버퍼 기록을 스레드 풀 task로 분배합니다. +8. 모든 패스 커맨드 기록이 끝나면 `VulkanRenderer`가 기록된 커맨드들을 순서대로 제출하고 마지막 커맨드가 present semaphore를 signal합니다. +9. 마지막에 `ExecuteTransfer()`가 readback/copy 작업을 위한 `CopyPass`를 기록하고, 스왑체인을 `Present` 상태로 전이합니다. -새 패스를 추가할 때 필요한 작업은 다음과 같습니다. +## RenderData와 RenderViewer -1. `ScriptableRenderPass` 구현체 작성 -2. `renderQueue` 설정 -3. 필요 시 ```BuildDrawList``` 오버라이딩 하여 드로우 할 물체 필터링 -4. `Configure()`에서 드로우 목록 및 리소스 등록 -5. `Record()`에서 커맨드 기록 +`RenderData`는 한 번의 렌더링 작업이 사용할 뷰어, 렌더 타겟, 태그, 우선순위를 담습니다. -기존 패스나 배리어 코드를 사용자가 명시적으로 수정할 필요가 없습니다. `CopyPass`처럼 드로우와 성격이 다른 패스도 동일한 방법으로 이루어집니다. - -예시 코드: ```cpp -/// 단순히 우선 순위 정렬기능만 추가된 패스 -class TransparentPass : public ScriptableRenderPass +class RenderData { public: - SH_RENDER_API TransparentPass(std::string_view name = "Transparent", RenderQueue renderQueue = RenderQueue::Transparent); + void SetRenderTarget(const RenderTexture* renderTarget); + void SetRenderTargets(std::initializer_list renderTargets); + void ClearRenderTargets(); - SH_RENDER_API auto BuildDrawList(const RenderTarget& renderData) -> DrawList override - { - // renderData로 들어온 카메라 정보와 드로우 할 물체들의 정보를 가지고 DrawList 구조체를 만들어서 반환 - } + int priority = 0; + core::Name tag{ "Camera" }; + std::vector renderViewers; }; ``` -## 상세 -### 설계 배경 +`RenderViewer`는 view/proj 행렬, 카메라 위치/방향, viewport/scissor, GPU camera buffer offset을 가집니다. `RenderDataManager`는 각 viewer마다 `RenderDataManager::BufferData`를 업로드하고, 그 offset을 `RenderViewer::offset`에 기록합니다. Vulkan draw 단계에서는 이 offset이 카메라 descriptor set의 dynamic offset으로 전달됩니다. + +최근 구조에서는 `RenderData`가 여러 렌더 타겟을 가질 수 있습니다. -Vulkan 렌더링 시스템을 구현할 때 드로우 호출 자체보다 **리소스 상태 관리와 실행 순서**가 더 큰 문제가 됩니다. +- `SetRenderTarget()`은 단일 타겟용 편의 함수입니다. +- `SetRenderTargets()`는 MRT를 구성합니다. +- 타겟이 `nullptr`이면 현재 스왑체인 이미지를 의미합니다. +- depth 전용 `RenderTexture`는 color attachment가 아니라 depth attachment로 취급됩니다. -구체적으로 아래 사항들을 매 패스마다 직접 추적해야 합니다. +## 기본 패스 구성 -- 패스 간 실행 순서 -- 각 패스의 렌더 타겟 및 샘플링 텍스처 -- 이미지 레이아웃 및 접근 권한 변환 -- `vkCmdPipelineBarrier` 삽입 위치 +`GameRenderer::Init()`은 기본 패스를 생성하고, `GameRenderer::Setup()`은 `RenderData::tag`에 따라 필요한 패스만 enqueue합니다. + +| 태그 | 실행 패스 | +|---|---| +| `"Depth"` | `DepthPass` | +| `"SSAO"` | `SSAOPass` | +| `"Combine"` | `CombinePass` | +| `"ImGUI"` | `GUIPass` | +| 그 외 카메라 | `Opaque`, `Transparent`, `UI` | -렌더링 코드를 단일 흐름으로 구성할 경우, 패스가 늘어날수록 다음과 같은 문제가 발생합니다. +현재 기본 패스의 역할은 다음과 같습니다. -- 패스 추가 시 앞뒤 리소스 상태 전이를 전체적으로 재검토해야 함 -- 배리어 누락 또는 잘못된 레이아웃 전이로 인한 런타임 오류 발생 -- 렌더링 의도 코드와 Vulkan 동기화 코드가 강하게 결합됨 +| 패스 | Queue | 역할 | +|---|---|---| +| `DepthPass` | `BeforeRendering` | depth/shadow 계열 렌더링. depth 전용 타겟도 지원 | +| `Opaque` | `Opaque` | 기본 불투명 렌더링 | +| `SSAOPass` | `AfterOpaque` | 풀스크린 plane으로 depth 등을 샘플링해 AO 타겟에 기록 | +| `CombinePass` | `AfterRendering` | 풀스크린 plane으로 AO 등 후처리 결과를 최종 타겟에 합성 | +| `Transparent` | `Transparent` | 카메라 방향 기준 back-to-front 정렬 후 개별 draw | +| `UI` | `Transparent` | UI shader pass용 transparent pass | +| `GUIPass` | `UI` | ImGui draw data 기록 | +| `CopyPass` | `AfterRendering` | readback/copy transfer 전용 내부 패스 | -ShellEngine은 이 문제를 해결하기 위해서, 그리고 유연한 확장을 위해 패스의 렌더링 의도와 API 레벨의 전이 처리를 분리하는 구조를 채택했습니다. +`SSAOPass`와 `CombinePass`는 패스 내부에서 fullscreen plane `Mesh`와 `Drawable`을 보유하고, 외부에서 `SetMaterial()`로 지정한 material을 사용합니다. 두 패스 모두 material이 참조하는 `RenderTexture`를 `Configure()`에서 샘플링 리소스로 등록합니다. ---- +## ScriptableRenderPass -### Configure 단계 +패스는 `passName`과 `renderQueue`를 갖고, 크게 두 단계로 동작합니다. -`ScriptableRenderPass::Configure()`는 렌더러가 전이를 계산하는 데 필요한 데이터를 구성하는 단계입니다. +### Configure -1. `BuildDrawList()` - 이번 패스의 드로우 목록 생성 -2. `CollectRenderImages()` - 렌더 텍스처 수집 및 용도 분류 - - 현재 렌더 타겟 -> `ColorAttachment` - - DrawList로 전달 된 메테리얼이 참조하는 `RenderTexture` -> `SampledRead` -3. 동일 패스 내에서 같은 텍스처를 출력과 입력으로 동시에 사용하는 경우 검증 +`Configure(const RenderData&)`는 커맨드 기록 전에 리소스 사용 의도를 확정합니다. -이 단계를 통해 패스의 리소스 사용 관계가 확정됩니다. +- 기본 구현은 현재 `passName`을 가진 shader pass가 있는 drawable만 `RenderBatch`로 묶습니다. +- `SetRenderTargetImageUsages()`가 `RenderData::GetRenderTargets()`를 순회해 color/depth attachment 사용을 등록합니다. +- `SetImageUsages()`가 drawable/material의 `MaterialData::CachedRT`를 확인해 샘플링하는 `RenderTexture`를 등록합니다. +- depth texture를 샘플링하는 경우 `DepthStencilSampledRead`, 일반 texture는 `SampledRead`로 등록합니다. -> [!Note] -> ```BuildDrawList```에는 RenderTarget구조체가 전달 됩니다. -> ```cpp -> struct RenderTarget -> { -> uint32_t frameIndex; -> const Camera* camera; -> const RenderTexture* target; -> const std::vector* drawables; -> }; -> ``` ---- +예전 문서의 `BuildDrawList()` 단계는 현재 `CreateRenderBatch()`와 `Configure()` 흐름으로 대체되었습니다. -### ScriptableRenderer +### Record -`ScriptableRenderer::Execute()`는 패스 간 리소스 연결 관계를 계산하고 커맨드 버퍼 기록을 조율합니다. +`Record(CommandBuffer&, const IRenderContext&, const RenderData&)`는 실제 커맨드를 기록합니다. -1. 활성 패스 수집 -2. renderQueue 기준 정렬 -3. 각 패스 Configure() 호출 -> 리소스 사용 정보 확정 -4. 각 패스 BuildBarrierInfo() 호출 -> 전이 정보 계산 -5. 커맨드 버퍼 기록 +- 기본 구현은 `cmd.SetRenderData(renderData, true, true, true, true)`로 렌더링을 시작합니다. +- viewer마다 viewport/scissor를 설정합니다. +- `RenderBatch` 단위로 `cmd.DrawMeshBatch()`를 호출합니다. +- `TransparentPass`처럼 정렬이 필요한 패스는 batch 대신 개별 `DrawMesh()`를 호출할 수 있습니다. +- 풀스크린 후처리 패스는 내부 drawable 하나를 직접 그립니다. -패스는 직접 배리어를 생성하지 않으며, 리소스 사용 의도만 제공합니다. 실제 전이 계산은 렌더러가 전담합니다. +## 리소스 전이 ---- +패스는 직접 Vulkan barrier를 만들지 않습니다. 대신 `renderTextures`에 “이번 패스에서 이 이미지를 어떤 용도로 쓸 것인지”를 기록합니다. -### BarrierInfo +```cpp +enum class ResourceUsage +{ + Undefined, + ColorAttachment, + SampledRead, + Present, + TransferSrc, + DepthStencilAttachment, + DepthStencilSampledRead +}; +``` -`BarrierInfo`는 패스 간 리소스 의존성을 실행 가능한 형태로 담는 구조체입니다. +`ScriptableRenderer::BuildBarrierInfo()`는 각 패스의 `renderTextures`를 보고 이전 사용 상태와 현재 사용 상태를 `BarrierInfo`로 만듭니다. -| 필드 | 설명 | +| 대상 | 이전 상태 출처 | |---|---| -| 대상 이미지 | 전이 대상 리소스 | -| `lastUsage` | 이전 패스의 사용 상태 | -| `curUsage` | 현재 패스의 사용 상태 | +| `RenderTexture*` | `RenderTexture::GetUsage()` | +| `nullptr` 스왑체인 | `ScriptableRenderer::swapChainStates[frameIndex]` | -렌더러는 각 텍스처의 사용 이력을 추적하여 패스 사이의 상태 전이를 `BarrierInfo`로 구체화합니다. +계산된 `BarrierInfo`는 해당 패스 커맨드 버퍼 시작 직후 `cmd.EmitBarrier()`로 기록됩니다. Vulkan에서는 `VulkanCommandBuffer::EmitBarrier()`가 `ResourceUsage`를 image layout, pipeline stage, access mask로 매핑하고 `VulkanImageBuffer::BarrierCommand()`를 호출합니다. -**예시 - 3패스 렌더링 흐름:** +예시 흐름: +```text +depth texture Undefined -> DepthStencilAttachment +depth texture DepthStencilAttachment -> DepthStencilSampledRead +ao texture Undefined -> ColorAttachment +ao texture ColorAttachment -> SampledRead +swapchain Undefined -> ColorAttachment +readback src ColorAttachment -> TransferSrc +swapchain ColorAttachment -> Present ``` -패스 1: 씬 렌더링 -> Undefined -> ColorAttachment -패스 2: 후처리 샘플링 -> ColorAttachment -> SampledRead -패스 3: 스왑체인 출력 -> SampledRead -> Present -``` ---- +## 멀티스레딩 + +`ScriptableRenderer::Execute()`는 리소스 전이를 먼저 직렬로 계산한 뒤, 각 패스의 커맨드 버퍼 기록을 스레드 풀에 task로 분배합니다. + +직렬 단계: + +- 활성 패스 정렬 +- 모든 패스 `Configure()` +- 모든 패스 `BarrierInfo` 계산 -### Vulkan 구현 계층 +병렬 단계: -`VulkanRenderImpl::EmitBarrier()`는 렌더러가 계산한 `BarrierInfo`를 Vulkan API 호출로 번역합니다. +- 패스별 `CommandBuffer` 할당 +- `Begin()` +- `EmitBarrier()` +- `Record()` +- `End()` -- `ImageUsage`를 image layout, pipeline stage, access mask로 매핑 -- `VulkanImageBuffer::BarrierCommand()`를 통해 `vkCmdPipelineBarrier` 기록 +커맨드 버퍼 기록은 병렬이지만, 제출은 `recordedCmds` 순서대로 수행됩니다. 따라서 패스 간 이미지 상태 전이는 정렬된 패스 순서를 기준으로 안정적으로 계산되고, 실제 제출 순서도 그 순서를 유지합니다. -이 계층은 전이가 필요한지 판단을 내리지 않으며, 상위에서 계산된 전이 정보를 API 호출로 변환하는 역할만 수행합니다. +## Vulkan 구현 -## 한계 +`VulkanCommandBuffer`는 현재 Vulkan dynamic rendering을 사용합니다. -현재 구조는 아래 기능은 포함되어 있지 않습니다. +- `SetRenderData()`가 `RenderData::GetRenderTargets()`로 `VkRenderingAttachmentInfoKHR` 배열을 구성합니다. +- 여러 color attachment를 지원하며, 첫 번째 타겟 또는 depth 전용 타겟에서 depth attachment를 가져옵니다. +- 스왑체인 렌더링에서는 `nullptr` 타겟을 swapchain image로 해석합니다. +- MSAA 타겟이 있으면 color attachment에 MSAA image를 사용하고 resolve image를 원본 color image로 지정합니다. +- `RenderTargetLayout`은 color format 목록, depth format, MSAA 여부를 담고 pipeline cache key로 사용됩니다. -- 의존성 기반 패스 자동 재배치 -- 버퍼 리소스 상태 추적 (현재는 `ImageUsage` 위주로 구현됨) +draw 단계에서는 shader pass 이름으로 material의 `ShaderPass` 목록을 찾고, topology/skinned 여부/render target layout에 맞는 pipeline을 가져옵니다. descriptor set은 usage 기준으로 다음처럼 바인딩됩니다. -## 구조 요약 +| Set usage | 데이터 | +|---|---| +| `Camera` | `RenderDataManager` GPU buffer, dynamic offset 사용 | +| `Object` | drawable별 material data | +| `Material` | material별 uniform/texture data | + +Shader DSL에서 `MATRIX_VIEW`, `MATRIX_PROJ`, `CAMERA`를 사용하면 parser가 `CAMERA` uniform buffer를 자동으로 구성합니다. 현재 camera buffer는 `view`, `proj`, `pos`를 담고, `MATRIX_VIEW`와 `MATRIX_PROJ`는 각각 `CAMERA.view`, `CAMERA.proj`로 치환됩니다. + +## MaterialData와 텍스처 추적 + +렌더 텍스처 샘플링 추적은 `MaterialData`의 `CachedRT`에 저장됩니다. + +```cpp +struct CachedRT +{ + uint32_t set = 0; + uint32_t binding = 0; + core::SObjWeakPtr pass; + core::SObjWeakPtr rt; +}; +``` -ScriptableRenderPass (N개) -- renderTextures: 리소스 사용 의도 선언 +패스의 `Configure()`는 material/drawable이 가진 cached render texture 중 현재 `passName`과 lighting pass 이름이 같은 항목만 읽기 리소스로 등록합니다. 이 구조 덕분에 후처리 material이 depth, SSAO, color texture를 참조하더라도 패스 쪽에서 별도 barrier 코드를 직접 작성하지 않아도 됩니다. -ScriptableRenderer -- 패스 순서 정렬 -- BuildBarrierInfo(): 패스 간 상태 전이 계산 -- 커맨드 버퍼 병렬 기록 +## 패스 추가 방법 + +새 패스를 추가할 때는 다음 순서로 작업합니다. + +1. `ScriptableRenderPass`를 상속한 클래스를 만든다. +2. 생성자에서 `passName`과 `RenderQueue`를 정한다. +3. 기본 batch 드로우면 기본 `Configure()`를 쓰고, 풀스크린/특수 패스면 `Configure()`에서 `SetRenderTargetImageUsages()`와 `SetImageUsages()`를 직접 호출한다. +4. `Record()`에서 `CommandBuffer` API로 렌더 타겟 설정, viewport/scissor, draw/dispatch/blit을 기록한다. +5. `ScriptableRenderer` 파생 클래스의 `Init()`에서 `AddRenderPass()`로 생성한다. +6. `Setup()`에서 `RenderData::tag`나 상황에 맞춰 `EnqueRenderPass()` 한다. -VulkanRenderImpl -- EmitBarrier(): ImageUsage -> Vulkan barrier 변환 -- vkCmdPipelineBarrier 기록 +예시: + +```cpp +class MyPostPass : public ScriptableRenderPass +{ +public: + MyPostPass(const IRenderContext& ctx) + : ScriptableRenderPass(core::Name{ "MyPostPass" }, RenderQueue::AfterRendering), + ctx(ctx) + { + } + +protected: + void Configure(const RenderData& renderData) override + { + renderTextures.clear(); + SetRenderTargetImageUsages(renderData); + SetImageUsages(*material); + } + + void Record(CommandBuffer& cmd, const IRenderContext& ctx, const RenderData& renderData) override + { + cmd.SetRenderData(renderData, false, false, true, false); + for (std::size_t i = 0; i < renderData.renderViewers.size(); ++i) + { + SetViewportScissor(cmd, ctx, renderData.renderViewers[i]); + cmd.DrawMesh(*fullscreenDrawable, passName, i); + } + } + +private: + const IRenderContext& ctx; + Material* material = nullptr; + Drawable* fullscreenDrawable = nullptr; +}; +``` diff --git a/Shader.md b/Shader.md index 73a1055a..2f0e31a0 100644 --- a/Shader.md +++ b/Shader.md @@ -1,95 +1,459 @@ -# 개요 -코드를 통해 렌더링 파이프라인을 설정 할 필요 없이, 셰이더를 통해 처리 할 수 있습니다. +# Shader 문서 -Unity엔진의 ShaderLab + GLSL의 문법을 합친 것이 특징 입니다. +ShellEngine의 `.shader` 파일은 Unity ShaderLab처럼 렌더 상태와 패스를 선언하고, 각 `Stage` 안에는 GLSL 코드를 직접 작성하는 형식입니다. 엔진은 이 파일을 파싱해 Vulkan용 GLSL을 생성하고, `glslc`로 SPIR-V를 컴파일한 뒤 `Shader`, `ShaderPass`, 머티리얼 유니폼 레이아웃으로 연결합니다. -# 상세 원리 -1. [ShaderLexer](https://github.com/Shell4026/ShellEngine/blob/main/src/Render/ShaderLexer.cpp)클래스로 코드를 토큰 단위로 나눕니다 -2. [ShaderParser](https://github.com/Shell4026/ShellEngine/blob/main/src/Render/ShaderParser.cpp)클래스로 토큰을 하나하나 읽어 문법을 검사하고 파싱 트리 구조를 완성해나갑니다. -3. ShaderParse내 Generate함수로 파싱 트리를 기반으로 GLSL문법으로 변환한 후 셰이더를 컴파일 합니다. +관련 구현은 주로 다음 파일에 있습니다. -## 예시 코드 +- `src/Render/ShaderLexer.cpp`: 소스 문자열을 토큰으로 분해 +- `src/Render/ShaderParser.cpp`: 토큰을 AST로 파싱하고 GLSL 코드 생성 +- `src/Render/ShaderGenerator.cpp`: 패스별 `.vert`, `.frag` 임시 파일 생성 +- `src/Game/Asset/ShaderLoader.cpp`: `.shader` 로드, `glslc` 실행, SPIR-V 로드 +- `src/Render/ShaderPass.cpp`: 파싱 결과에서 어트리뷰트/유니폼 레이아웃 구성 +- `src/Render/VulkanImpl/VulkanShaderPass.cpp`: Vulkan 디스크립터 세트와 파이프라인 레이아웃 생성 + +## 처리 흐름 + +1. `ShaderLoader::Load(path)`가 `.shader` 파일을 텍스트로 읽습니다. +2. `ShaderLexer::Lex()`가 키워드, 식별자, 숫자, 문자열, 연산자, 중괄호 등을 토큰으로 나눕니다. +3. `ShaderParser::Parse()`가 토큰을 `ShaderAST::ShaderNode`로 변환합니다. +4. 파서는 `Stage` 본문을 분석하며 `VERTEX`, `MATRIX_PROJ`, `LIGHT` 같은 엔진 예약 심볼을 발견하면 필요한 입력 어트리뷰트와 버퍼를 자동으로 추가합니다. +5. `GenerateStageCode()`가 각 `Stage`의 최종 GLSL 코드를 만듭니다. +6. `ShaderGenerator::GenerateShaderFile()`이 패스별 `.vert`, `.frag` 파일을 캐시 경로에 저장합니다. +7. `ShaderLoader`가 `glslc`로 각 스테이지를 `.spv`로 컴파일합니다. +8. `ShaderPassBuilder`가 SPIR-V를 `ShaderPass`로 만들고, Vulkan 구현에서는 `VkShaderModule`, descriptor set layout, pipeline layout을 구성합니다. +9. 최종 `Shader` 객체는 프로퍼티 목록과 패스 목록을 보관하고, `Material`은 이를 기준으로 유니폼/샘플러 데이터를 업로드합니다. + +## 기본 구조 + +```glsl +#version 430 core + +Shader "Shader Name" +{ + Property + { + float ambient; + vec4 color; + sampler2D albedo; + } + + Pass + { + LightingPass "Opaque" + Cull Back; + ZWrite On; + ZTest On; + + Stage Vertex + { + layout(location = 0) out vec2 fragUV; + + void main() + { + fragUV = UV; + gl_Position = MATRIX_PROJ * MATRIX_VIEW * MATRIX_MODEL * vec4(VERTEX, 1.0); + } + } + + Stage Fragment + { + layout(location = 0) in vec2 fragUV; + layout(location = 0) out vec4 outColor; + + uniform float ambient; + uniform vec4 color; + uniform sampler2D albedo; + + void main() + { + outColor = texture(albedo, fragUV) * color; + outColor.rgb += ambient; + } + } + } +} +``` + +최상단 `#version`은 현재 파서에서 `#version 430 core` 형태를 기대합니다. `Shader "..."`는 생성될 `Shader` 객체의 이름입니다. `Property`는 머티리얼에서 조작할 수 있는 값 목록이며 생략할 수 있습니다. `Pass`는 하나 이상 선언할 수 있고, 각 패스는 렌더 패스 이름과 렌더 상태, `Vertex`/`Fragment` 스테이지를 가집니다. + +## Property + +`Property`에 선언한 변수는 `Material::SetProperty()`로 값을 넣을 수 있는 머티리얼 프로퍼티가 됩니다. 스테이지 안에서 `uniform`으로 사용하려면 같은 타입과 이름이 `Property`에 먼저 선언되어 있어야 합니다. + +지원되는 주요 타입은 다음과 같습니다. + +| Shader 타입 | C++ 저장 타입 | +| --- | --- | +| `int` | `int` | +| `float` | `float` | +| `vec2` | `glm::vec2` | +| `vec3` | `glm::vec3` | +| `vec4` | `glm::vec4` | +| `mat3` | `glm::mat3` | +| `mat4` | `glm::mat4` | +| `sampler2D` | `Texture` | + +배열도 선언할 수 있습니다. + +```glsl +Property +{ + vec3 kernel[64]; + sampler2D normalTex; +} +``` + +### Local 프로퍼티 + +`[Local]`을 붙인 프로퍼티는 머티리얼 공통 값이 아니라 오브젝트별 값으로 취급됩니다. 예를 들어 같은 머티리얼을 공유하되 오브젝트마다 텍스처를 다르게 넣고 싶을 때 사용합니다. + +```glsl +Property +{ + vec4 color; + [Local] sampler2D tex; +} +``` + +일반 프로퍼티는 `UniformStructLayout::Usage::Material` 세트로, `[Local]` 프로퍼티는 `UniformStructLayout::Usage::Object` 세트로 배치됩니다. 런타임에서는 `MaterialData`와 `MaterialPropertyBlock`이 이 구분을 기준으로 버퍼와 샘플러를 갱신합니다. + +## Pass + +`Pass`는 실제 draw에 사용되는 렌더링 단위입니다. 같은 `LightingPass` 이름을 가진 패스들은 `Shader::GetShaderPasses(name)`로 조회되어 해당 렌더 단계에서 순서대로 실행됩니다. + +```glsl +Pass "Optional Pass Name" +{ + LightingPass "Opaque" + Cull Back; + ZWrite On; + ZTest On; + ColorMask RGBA; + + Stage Vertex { ... } + Stage Fragment { ... } +} +``` + +패스 이름은 생략할 수 있습니다. 생략하면 파서가 `Pass1`, `Pass2`처럼 자동 이름을 붙입니다. + +### 렌더 상태 + +| 구문 | 값 | 기본값 | 설명 | +| --- | --- | --- | --- | +| `LightingPass "name"` | 문자열 | 빈 이름 | 렌더러가 패스를 찾을 때 쓰는 분류 이름 | +| `Cull` | `Off`, `Front`, `Back` | `Back` | 컬링 모드 | +| `ZWrite` | `On`, `Off` | `On` | 깊이 버퍼 쓰기 여부 | +| `ZTest` | `On`, `Off` | `On` | 깊이 테스트 여부 | +| `ColorMask` | `RGBA` 조합 또는 숫자 | `15` | 컬러 채널 쓰기 마스크 | + +`ColorMask`는 비트마스크로 저장됩니다. `R=1`, `G=2`, `B=4`, `A=8`이며 `ColorMask 0;`은 컬러 출력을 막을 때 사용합니다. + +### Stencil + +```glsl +Stencil +{ + Ref 1; + ReadMask 255; + WriteMask 255; + Comp Always; + Pass Replace; + Fail Keep; + ZFail Keep; +} +``` + +`Comp` 값은 `Never`, `Less`, `Equal`, `LessEqual`, `Greater`, `NotEqual`, `GreaterEqual`, `Always`를 사용할 수 있습니다. `Pass`, `Fail`, `ZFail` 값은 `Keep`, `Zero`, `Replace`, `IncrementClamp`, `DecrementClamp`, `Invert`, `IncrementWrap`, `DecrementWrap`를 사용할 수 있습니다. + +## Stage + +현재 그래픽 셰이더는 `Stage Vertex`와 `Stage Fragment`를 사용합니다. 각 `Stage` 내부는 GLSL과 거의 같은 문법을 쓰지만, 파서가 일부 선언과 예약 심볼을 인식해 최종 GLSL 앞부분을 자동 생성합니다. + +```glsl +Stage Vertex +{ + layout(location = 0) out vec2 fragUV; + + const float scale = 1.0; + + void main() + { + fragUV = UV; + gl_Position = MATRIX_PROJ * MATRIX_VIEW * MATRIX_MODEL * vec4(VERTEX * scale, 1.0); + } +} +``` + +### layout 입출력 + +`layout(location = N) in/out ...;` 구문은 스테이지 입출력으로 파싱됩니다. + +```glsl +layout(location = 0) out vec2 fragUV; +layout(location = 1) out vec3 fragNormal; +``` + +버텍스 스테이지에서 엔진 예약 입력(`VERTEX`, `UV`, `NORMAL` 등)을 사용하면 직접 `in`을 선언하지 않아도 파서가 자동으로 입력 어트리뷰트를 추가합니다. + +## 엔진 예약 심볼 + +예약 심볼은 셰이더 코드 안에서 일반 GLSL 변수처럼 사용할 수 있습니다. 파서는 사용 여부를 감지해 필요한 layout, uniform buffer, storage buffer, push constant를 자동으로 생성합니다. + +| 심볼 | 타입/의미 | 자동 생성 | +| --- | --- | --- | +| `VERTEX` | `vec3`, 정점 위치 | vertex input location 0 | +| `UV` | `vec2`, UV | vertex input location 1 | +| `NORMAL` | `vec3`, 노멀 | vertex input location 2 | +| `TANGENT` | `vec3`, 탄젠트 | vertex input location 3 | +| `BONE_INDICES` | `ivec4`, 본 인덱스 | skinned input location 4 | +| `BONE_WEIGHTS` | `vec4`, 본 가중치 | skinned input location 5 | +| `MATRIX_MODEL` | 모델 행렬 | push constant `CONSTANTS.model` | +| `MATRIX_VIEW` | 카메라 view 행렬 | camera UBO `CAMERA.view` | +| `MATRIX_PROJ` | 카메라 projection 행렬 | camera UBO `CAMERA.proj` | +| `CAMERA` | 카메라 UBO 인스턴스 | `view`, `proj`, `pos` 멤버 | +| `LIGHT` | 라이트 SSBO | object set의 storage buffer | +| `SKIN` | 스키닝 SSBO | object set의 storage buffer | +| `MATRIX_SKIN` | 스키닝 행렬 | `BONE_*`와 `SKIN.ibm[]` 기반 계산식 삽입 | +| `TEXTURE_SHADOW` | 그림자 텍스처 | object set의 local sampler | + +`MATRIX_MODEL`, `MATRIX_VIEW`, `MATRIX_PROJ`는 최종 GLSL에서 각각 `CONSTANTS.model`, `CAMERA.view`, `CAMERA.proj`로 치환됩니다. + +### LIGHT 버퍼 형태 + +현재 파서는 `LIGHT` 사용 시 다음과 같은 구조를 생성합니다. + +```glsl +struct Light +{ + vec4 pos; + vec4 other; + vec4 shadowRect; + mat4 lightSpaceMatrix; +}; + +layout(std430, set = 1, binding = N) readonly buffer UNIFORM_LIGHT +{ + int count; + Light lights[]; +} LIGHT; +``` + +따라서 코드에서는 `LIGHT.count`, `LIGHT.lights[i].pos`, `LIGHT.lights[i].other`처럼 접근합니다. + +```glsl +for (int i = 0; i < LIGHT.count; ++i) +{ + int type = int(LIGHT.lights[i].other.w); + vec3 lightPos = LIGHT.lights[i].pos.xyz; +} +``` + +### MATRIX_SKIN + +`MATRIX_SKIN`을 사용하면 파서가 `BONE_WEIGHTS`, `BONE_INDICES`, `SKIN` 버퍼를 자동 등록하고 함수 본문 앞에 다음 계산식을 삽입합니다. + +```glsl +mat4 MATRIX_SKIN = + BONE_WEIGHTS.x * SKIN.ibm[BONE_INDICES.x] + + BONE_WEIGHTS.y * SKIN.ibm[BONE_INDICES.y] + + BONE_WEIGHTS.z * SKIN.ibm[BONE_INDICES.z] + + BONE_WEIGHTS.w * SKIN.ibm[BONE_INDICES.w]; +``` + +스키닝 셰이더에서는 보통 다음처럼 사용합니다. + +```glsl +vec4 skinnedPos = MATRIX_SKIN * vec4(VERTEX, 1.0); +vec3 skinnedNormal = mat3(MATRIX_SKIN) * NORMAL; +``` + +## Uniform 바인딩 규칙 + +스테이지 내부의 단일 `uniform` 선언은 반드시 `Property`에 같은 타입과 이름으로 선언되어 있어야 합니다. + +```glsl +Property +{ + float roughness; + sampler2D albedo; +} + +Stage Fragment +{ + uniform float roughness; + uniform sampler2D albedo; +} +``` + +파서는 스칼라/벡터/행렬 프로퍼티를 `UBO`라는 uniform block으로 묶습니다. `sampler2D`는 별도의 combined image sampler binding이 됩니다. + +일반 프로퍼티는 material set에 배치되고, `[Local]` 프로퍼티는 object set에 배치됩니다. 내부 enum 기준은 다음과 같습니다. + +| Usage | set | 용도 | +| --- | --- | --- | +| `Camera` | `0` | 카메라 공통 버퍼 | +| `Object` | `1` | 오브젝트별 값, push/local/lighting/skinning | +| `Material` | `2` | 머티리얼 공유 값 | + +명시적 descriptor layout도 선언할 수 있습니다. 이 경우 `Property` 검사를 거치지 않고 직접 버퍼 또는 샘플러 layout으로 추가됩니다. + +```glsl +layout(set = 2, binding = 0) uniform MyBlock +{ + vec4 color; + float strength; +} customData; + +layout(set = 2, binding = 1) uniform sampler2D customTex; +``` + +UBO는 `std140`, SSBO는 `std430` 기준으로 `UniformStructLayout`이 CPU 측 offset과 size를 계산합니다. `ShaderParser::Optimize()`는 자동 생성된 `UBO` 멤버를 스칼라, `vec2`, 그 외 큰 타입 순서로 정렬해 패딩을 줄입니다. + +## constexpr와 specialization constant + +스테이지 안에서 `constexpr`를 선언하면 Vulkan specialization constant로 변환됩니다. + +```glsl +Stage Fragment +{ + constexpr int SAMPLE_COUNT = 16; + + void main() + { + for (int i = 0; i < SAMPLE_COUNT; ++i) + { + ... + } + } +} +``` + +생성되는 GLSL은 `layout (constant_id = N) const ...` 형태입니다. 런타임에서는 `Material::SetConstant(name, value)`가 패스별 constant 데이터를 갱신하고, `VulkanPipelineManager`는 constant 데이터 해시를 파이프라인 캐시 키에 포함합니다. + +현재 `ShaderPass`가 크기를 계산하는 specialization constant 타입은 `bool`, `int`, `float` 계열입니다. + +## 생성되는 GLSL 예시 + +작성한 코드: + +```glsl +Stage Vertex +{ + layout(location = 0) out vec2 fragUV; + + void main() + { + fragUV = UV; + gl_Position = MATRIX_PROJ * MATRIX_VIEW * MATRIX_MODEL * vec4(VERTEX, 1.0); + } +} +``` + +대략 다음과 같은 GLSL로 확장됩니다. + +```glsl +#version 430 core +layout(location = 0) in vec3 VERTEX; +layout(location = 1) in vec2 UV; +layout(location = 0) out vec2 fragUV; + +layout(set = 0, binding = 0) uniform UNIFORM_CAMERA +{ + mat4 view; + mat4 proj; + vec3 pos; +} CAMERA; + +layout(push_constant) uniform UNIFORM_CONSTANTS +{ + mat4 model; +} CONSTANTS; + +void main() +{ + fragUV = UV; + gl_Position = CAMERA.proj * CAMERA.view * CONSTANTS.model * vec4(VERTEX, 1.0); +} +``` + +## 예시: 조명 텍스처 셰이더 ```glsl #version 430 core -Shader "Default Shader" +Shader "Lit Texture" { Property { float ambient; sampler2D tex; - sampler2D normalTex; } - + Pass { - LightingPass "Forward" - + LightingPass "Opaque" + Stage Vertex { - layout(location = 0) out vec2 fragUvs; + layout(location = 0) out vec2 fragUV; layout(location = 1) out vec3 fragPos; - layout(location = 2) out mat3 TBN; + layout(location = 2) out vec3 fragNormal; + void main() { - gl_Position = MATRIX_PROJ * MATRIX_VIEW * MATRIX_MODEL * vec4(VERTEX, 1.0f); - fragUvs = UV; - fragPos = (MATRIX_MODEL * vec4(VERTEX, 1.0f)).xyz; - - mat3 normalMatrix = transpose(inverse(mat3(MATRIX_MODEL))); - - vec3 t = normalize(normalMatrix * TANGENT); - vec3 n = normalize(normalMatrix * NORMAL); - vec3 b = cross(n, t); - - TBN = mat3(t, b, n); + fragUV = UV; + fragPos = (MATRIX_MODEL * vec4(VERTEX, 1.0)).xyz; + fragNormal = normalize(mat3(transpose(inverse(MATRIX_MODEL))) * NORMAL); + gl_Position = MATRIX_PROJ * MATRIX_VIEW * vec4(fragPos, 1.0); } } + Stage Fragment { + layout(location = 0) in vec2 fragUV; + layout(location = 1) in vec3 fragPos; + layout(location = 2) in vec3 fragNormal; layout(location = 0) out vec4 outColor; - layout(location = 0) in vec2 uvs; - layout(location = 1) in vec3 fragPos; - layout(location = 2) in mat3 TBN; uniform float ambient; uniform sampler2D tex; - uniform sampler2D normalTex; - void main() + void main() { - vec3 normal = texture(normalTex, uvs).xyz; - normal = normal * 2.0 - 1.0; - normal = normalize(TBN * normal); - float diffuse = 0.0; + for (int i = 0; i < LIGHT.count; ++i) { - int type = int(LIGHT.other[i].w); + int type = int(LIGHT.lights[i].other.w); if (type == 0) { - vec3 toLightDir = -LIGHT.pos[i].xyz; - float intensity = LIGHT.pos[i].w; - diffuse += max(dot(normal, toLightDir), 0.0) * intensity; + vec3 toLightDir = -LIGHT.lights[i].pos.xyz; + float intensity = LIGHT.lights[i].pos.w; + diffuse += max(dot(fragNormal, toLightDir), 0.0) * intensity; } else if (type == 1) { - vec3 toLightVec = LIGHT.pos[i].xyz - fragPos; + vec3 toLightVec = LIGHT.lights[i].pos.xyz - fragPos; vec3 toLightDir = normalize(toLightVec); float lightDis = length(toLightVec); - float attenuation = clamp(1.0 - (lightDis / LIGHT.pos[i].w), 0.0, 1.0); - diffuse += max(dot(normal, toLightDir), 0.0) * attenuation; + float attenuation = clamp(1.0 - (lightDis / LIGHT.lights[i].pos.w), 0.0, 1.0); + diffuse += max(dot(fragNormal, toLightDir), 0.0) * attenuation; } } - outColor = texture(tex, uvs); - outColor.xyz *= diffuse + ambient; + + outColor = texture(tex, fragUV); + outColor.rgb *= diffuse + ambient; } } } } ``` -### 스텐실을 이용한 아웃라인 셰이더 예시 + +## 예시: 스텐실 아웃라인 + +첫 번째 패스는 원본 오브젝트를 스텐실에 기록하고 컬러 출력을 막습니다. 두 번째 패스는 노멀 방향으로 정점을 밀어낸 뒤, 스텐실 값이 다른 영역만 칠합니다. + ```glsl #version 430 core @@ -100,11 +464,12 @@ Shader "Outline Shader" float outlineWidth; vec4 color; } + Pass { - LightingPass "Forward" + LightingPass "Opaque" ColorMask 0; - + Stencil { Ref 1; @@ -115,26 +480,31 @@ Shader "Outline Shader" Fail Replace; ZFail Replace; } - + Stage Vertex { void main() { - gl_Position = MATRIX_PROJ * MATRIX_VIEW * MATRIX_MODEL * vec4(VERTEX, 1.0f); + gl_Position = MATRIX_PROJ * MATRIX_VIEW * MATRIX_MODEL * vec4(VERTEX, 1.0); } } + Stage Fragment { layout(location = 0) out vec4 outColor; + void main() { - outColor = vec4(0.0f, 0.0f, 0.0f, 0.0f); + outColor = vec4(0.0); } } } + Pass { - LightingPass "Forward" + LightingPass "Opaque" + Cull Back; + Stencil { Ref 1; @@ -145,25 +515,25 @@ Shader "Outline Shader" Fail Keep; ZFail Keep; } - Cull Back; - + Stage Vertex { uniform float outlineWidth; void main() { - vec4 vert = vec4(VERTEX + NORMAL * outlineWidth, 1.0f); + vec4 vert = vec4(VERTEX + NORMAL * outlineWidth, 1.0); gl_Position = MATRIX_PROJ * MATRIX_VIEW * MATRIX_MODEL * vert; } } + Stage Fragment { layout(location = 0) out vec4 outColor; uniform vec4 color; - void main() + void main() { outColor = color; } @@ -171,49 +541,15 @@ Shader "Outline Shader" } } ``` -### 로컬 키워드를 사용해 오브젝트별 텍스쳐 설정 가능한 반투명 셰이더 예시 -로컬 키워드로 된 유니폼 변수는 MeshRenderer MaterialPropertyBlock을 지정해준 후 값을 넣어주면 됩니다. - -```glsl -#version 430 core -Shader "Unlit Transparent Shader2" -{ - Property - { - vec3 color; - [Local] sampler2D tex; - } - - Pass - { - LightingPass "Transparent" - - Cull Off; - ZWrite Off; - Stage Vertex - { - layout(location = 0) out vec2 fragUvs; - - void main() - { - gl_Position = MATRIX_PROJ * MATRIX_VIEW * MATRIX_MODEL * vec4(VERTEX, 1.0f); - fragUvs = UV; - } - } - Stage Fragment - { - layout(location = 0) out vec4 outColor; - layout(location = 0) in vec2 uvs; - uniform vec3 color; - uniform sampler2D tex; +## 작성 시 주의할 점 - void main() - { - outColor = texture(tex, uvs); - outColor.xyz *= color.xyz; - } - } - } -} -``` +- `Stage` 안의 일반 `uniform`은 `Property`에 먼저 선언해야 합니다. 선언이 없으면 파서가 오류를 냅니다. +- `LIGHT`는 `LIGHT.pos[i]`가 아니라 `LIGHT.lights[i].pos` 형태로 접근해야 합니다. +- `MATRIX_MODEL`을 사용하면 push constant가 생성됩니다. Vulkan 구현은 현재 모델 행렬 하나(`mat4`)를 push constant로 사용합니다. +- `MATRIX_VIEW`, `MATRIX_PROJ`, `CAMERA` 중 하나를 사용하면 camera UBO가 생성됩니다. +- `[Local]`은 오브젝트별 데이터를 위한 선언입니다. 같은 머티리얼을 여러 오브젝트가 공유할 때 개별 값을 넣어야 하는 프로퍼티에 사용합니다. +- `sampler2D`는 UBO 멤버가 아니라 별도 sampler descriptor로 생성됩니다. +- `mat2`는 파서와 `Shader` 프로퍼티 타입에는 존재하지만, 현재 `ShaderPass::FillAttributes()`의 UBO 멤버 구성에는 반영되어 있지 않습니다. 머티리얼 프로퍼티로 안정적으로 쓰려면 `mat3` 또는 `mat4`를 사용하거나 해당 경로를 보강해야 합니다. +- 자동 생성되는 UBO 멤버는 최종 레이아웃 최적화를 위해 선언 순서와 다르게 정렬될 수 있습니다. CPU 측 업로드는 `UniformStructLayout`의 offset을 기준으로 하므로 이름으로 접근하는 경로를 사용해야 합니다. +- 패스의 `LightingPass` 이름은 렌더러 쪽에서 조회하는 키입니다. 새 이름을 만들 때는 해당 렌더 단계가 실제로 그 이름을 요청하는지 함께 확인해야 합니다. diff --git a/docs/RenderFrameFlow.svg b/docs/RenderFrameFlow.svg new file mode 100644 index 00000000..b1b46b02 --- /dev/null +++ b/docs/RenderFrameFlow.svg @@ -0,0 +1,134 @@ + + ShellEngine render frame flow + Horizontal frame flow diagram from game/editor queues through render data upload, pass setup, parallel command recording, Vulkan submission, present, and cleanup. + + + + + + + + + + + 1. 입력 수집 + + 2. RenderData 준비와 패스 선택 + + 3. 커맨드 기록, 제출, 정리 + + + + 1 + Game / Editor + drawable, render data 생성 + + + + 2 + Renderer queue + PushDrawAble / PushRenderData + + + + 3 + Renderer::Render + 렌더 스레드에서 큐 drain + + + + 4 + UploadToGPU + camera buffer 업로드 + + + + + + + + + 5 + Acquire image + swapchain frame index + RenderData에 연결 + + + + 6 + Setup + RenderData::tag 기준 + 활성 패스 enqueue + + + + 7 + Execute + RenderQueue 정렬 + Configure 호출 + + + + 8 + BarrierInfo + RenderTexture 사용 이력 + 스왑체인 상태 계산 + + + + + + + + + + 9 + ThreadPool + 패스별 병렬 기록 + Begin → Barrier → Record → End + + + + 10 + recordedCmds + 패스 순서대로 수집 + 제출 순서 유지 + + + + 11 + Submit + VulkanQueueManager + semaphore 연결 + + + + 12 + Present / cleanup + ExecuteTransfer, Present + readback, command 반환 + + + + + + + + + 프레임 종료 후 다음 프레임에서 큐 수집부터 반복 + diff --git a/include/Core/ArrayView.hpp b/include/Core/ArrayView.hpp index fd49d469..b92b3a3f 100644 --- a/include/Core/ArrayView.hpp +++ b/include/Core/ArrayView.hpp @@ -9,17 +9,17 @@ namespace sh::core class ArrayView { private: - const T* ptr; + T* const ptr; std::size_t len; public: - ArrayView(const T* ptr, std::size_t len) noexcept : + ArrayView(T* ptr, std::size_t len) noexcept : ptr(ptr), len(len) {} auto operator[](int i) const noexcept -> T const& { return ptr[i]; } auto size() const noexcept -> std::size_t { return len; } - auto begin() noexcept -> const T* { return ptr; } - auto end() noexcept -> const T* { return ptr + len; } + auto begin() noexcept -> T* { return ptr; } + auto end() noexcept -> T* { return ptr + len; } }; -} \ No newline at end of file +}//namespace \ No newline at end of file diff --git a/include/Core/Name.h b/include/Core/Name.h index cc8e1d11..23f799e3 100644 --- a/include/Core/Name.h +++ b/include/Core/Name.h @@ -1,5 +1,6 @@ #pragma once #include "Export.h" +#include "Util.h" #include #include @@ -10,6 +11,24 @@ namespace sh::core /// @brief 같은 문자열은 같은 주소를 가르키며 생성 시 스레드 안전하다. class Name { + public: + SH_CORE_API Name(std::string_view str); + SH_CORE_API Name(const Name& other) noexcept; + SH_CORE_API Name(Name&& other) noexcept; + SH_CORE_API ~Name(); + + SH_CORE_API operator const std::string& () const; + SH_CORE_API auto ToString() const -> const std::string&; + + SH_CORE_API auto operator=(const Name& other) noexcept -> Name&; + SH_CORE_API auto operator=(Name&& other) noexcept -> Name&; + + auto operator==(const Name& other) const -> bool { return hash == other.hash; } + auto operator!=(const Name& other) const -> bool { return hash != other.hash; } + auto operator==(const std::string_view str) const -> bool { return hash == core::Util::ConstexprHash(str); } + auto operator!=(const std::string_view str) const -> bool { return hash != core::Util::ConstexprHash(str); } + auto operator==(std::size_t hash) const -> bool { return this->hash == hash; } + auto operator!=(std::size_t hash) const -> bool { return this->hash != hash; } private: friend struct std::hash; @@ -20,30 +39,13 @@ namespace sh::core std::string debugString; #endif std::size_t hash; - public: - SH_CORE_API Name(std::string_view str); - SH_CORE_API Name(const Name& other) noexcept; - SH_CORE_API Name(Name&& other) noexcept; - SH_CORE_API ~Name(); - - SH_CORE_API auto operator=(const Name& other) noexcept -> Name&; - SH_CORE_API auto operator=(Name&& other) noexcept -> Name&; - SH_CORE_API auto operator==(const Name& other) const -> bool; - SH_CORE_API auto operator!=(const Name& other) const -> bool; - SH_CORE_API auto operator==(const std::string_view str) const -> bool; - SH_CORE_API auto operator!=(const std::string_view str) const -> bool; - SH_CORE_API auto operator==(std::size_t hash) const -> bool; - SH_CORE_API auto operator!=(std::size_t hash) const -> bool; - - SH_CORE_API operator const std::string& () const; - SH_CORE_API auto ToString() const -> const std::string&; }; - static auto operator==(std::string_view str, const sh::core::Name& name) -> bool + inline static auto operator==(std::string_view str, const sh::core::Name& name) -> bool { return name == str; } - static auto operator!=(std::string_view str, const sh::core::Name& name) -> bool + inline static auto operator!=(std::string_view str, const sh::core::Name& name) -> bool { return name != str; } @@ -59,4 +61,4 @@ namespace std return name.hash; } }; -} +}//namespace \ No newline at end of file diff --git a/include/Editor/AssetExtensions.h b/include/Editor/AssetExtensions.h index fbbb8b2b..4c7c695f 100644 --- a/include/Editor/AssetExtensions.h +++ b/include/Editor/AssetExtensions.h @@ -22,7 +22,8 @@ namespace sh::editor Font, Binary, Sound, - ScriptableObject + ScriptableObject, + ComputeShader, }; public: SH_EDITOR_API static void AddExtension(const std::string& ext, Type type); diff --git a/include/Editor/BuildSystem.h b/include/Editor/BuildSystem.h index a0455179..de8b2246 100644 --- a/include/Editor/BuildSystem.h +++ b/include/Editor/BuildSystem.h @@ -32,6 +32,7 @@ namespace sh::editor void ExtractUUIDs(std::unordered_set& set, const core::Json& world); void PackingAssets(core::AssetBundle& bundle, game::World& world); void ExportGameManager(const std::filesystem::path& outputPath); + void CopyRuntimeBinaries(const std::filesystem::path& outputPath); private: Project* currentProject = nullptr; diff --git a/include/Editor/EditorOutlinePass.h b/include/Editor/EditorOutlinePass.h index 8ee4a871..a301c690 100644 --- a/include/Editor/EditorOutlinePass.h +++ b/include/Editor/EditorOutlinePass.h @@ -3,6 +3,7 @@ #include "Core/SContainer.hpp" +#include "Render/RenderData.h" #include "Render/ScriptableRenderPass.h" #include "Game/Component/Render/Camera.h" @@ -22,9 +23,9 @@ namespace sh::editor SH_EDITOR_API void SetOutTexture(render::RenderTexture& tex); protected: - SH_EDITOR_API void Configure(const render::RenderTarget& renderData) override; - SH_EDITOR_API void Record(render::CommandBuffer& cmd, const render::IRenderContext& ctx, const render::RenderTarget& renderData) override; + SH_EDITOR_API void Configure(const render::RenderData& renderData) override; + SH_EDITOR_API void Record(render::CommandBuffer& cmd, const render::IRenderContext& ctx, const render::RenderData& renderData) override; private: - core::SObjWeakPtr output = nullptr; + render::RenderData rd; }; }//namespace \ No newline at end of file diff --git a/include/Editor/EditorPostOutlinePass.h b/include/Editor/EditorPostOutlinePass.h index 31ed0e5a..c8478ccd 100644 --- a/include/Editor/EditorPostOutlinePass.h +++ b/include/Editor/EditorPostOutlinePass.h @@ -19,8 +19,8 @@ namespace sh::editor SH_EDITOR_API ~EditorPostOutlinePass(); SH_EDITOR_API void SetOutlineMaterial(render::Material& mat); protected: - SH_EDITOR_API void Configure(const render::RenderTarget& renderData) override; - SH_EDITOR_API auto BuildDrawList(const render::RenderTarget& renderData) -> render::DrawList override; + SH_EDITOR_API void Configure(const render::RenderData& renderData) override; + SH_EDITOR_API void Record(render::CommandBuffer& cmd, const render::IRenderContext& ctx, const render::RenderData& renderData) override; private: const render::IRenderContext& ctx; diff --git a/include/Editor/EditorRenderer.h b/include/Editor/EditorRenderer.h index 320a2140..7dc4b83f 100644 --- a/include/Editor/EditorRenderer.h +++ b/include/Editor/EditorRenderer.h @@ -3,42 +3,32 @@ #include "EditorOutlinePass.h" #include "EditorPostOutlinePass.h" -#include "Render/ScriptableRenderer.h" - #include "Game/Component/Render/Camera.h" +#include "Game/GameRenderer.h" #include namespace sh::game { class ImGUImpl; - class UIPass; + class GUIPass; + class World; } namespace sh::editor { - class EditorRenderer : public render::ScriptableRenderer + class EditorRenderer : public game::GameRenderer { public: - SH_EDITOR_API EditorRenderer(render::IRenderContext& ctx, game::ImGUImpl& guictx); + SH_EDITOR_API EditorRenderer(render::IRenderContext& ctx, game::ImGUImpl& guictx, game::World& world); - SH_EDITOR_API auto GetOutlinePass() const -> EditorOutlinePass* { return outlinePass; } - SH_EDITOR_API auto GetPostOutlinePass() const -> EditorPostOutlinePass* { return postOutlinePass; } + SH_EDITOR_API void Init() override; - SH_EDITOR_API void SetImGUICamera(const render::Camera& camera); - SH_EDITOR_API void SetEditorCamera(const render::Camera& camera); - SH_EDITOR_API void SetPickingCamera(const render::Camera& camera); + auto GetOutlinePass() const -> EditorOutlinePass* { return outlinePass; } + auto GetPostOutlinePass() const -> EditorPostOutlinePass* { return postOutlinePass; } protected: - SH_EDITOR_API void Setup(const render::RenderTarget& data) override; + SH_EDITOR_API void Setup(const render::RenderData& data) override; private: - const render::Camera* editorCamera = nullptr; - const render::Camera* ImGUICamera = nullptr; - const render::Camera* pickingCamera = nullptr; - + render::ScriptableRenderPass* pickingPass = nullptr; EditorOutlinePass* outlinePass = nullptr; EditorPostOutlinePass* postOutlinePass = nullptr; - game::UIPass* uiPass = nullptr; - //render::TransparentPipeline* transParentPass = nullptr; - - std::unordered_map> allowedCamera; - std::unordered_map> ignoreCamera; }; }//namespace \ No newline at end of file diff --git a/include/Editor/UI/ProjectExplorer.h b/include/Editor/UI/ProjectExplorer.h index 4538cb81..fc25d666 100644 --- a/include/Editor/UI/ProjectExplorer.h +++ b/include/Editor/UI/ProjectExplorer.h @@ -1,6 +1,8 @@ #pragma once #include "Editor/Export.h" +#include "Core/SObject.h" + #include "Game/ImGUImpl.h" #include diff --git a/include/Game/Asset/ComputeShaderAsset.h b/include/Game/Asset/ComputeShaderAsset.h new file mode 100644 index 00000000..42507bc0 --- /dev/null +++ b/include/Game/Asset/ComputeShaderAsset.h @@ -0,0 +1,32 @@ +#pragma once +#include "../Export.h" + +#include "Core/Asset.h" +#include "Core/SContainer.hpp" +#include "Core/ISerializable.h" + +#include "Render/ComputeShader.h" + +namespace sh::game +{ + class ComputeShaderAsset : public core::Asset + { + SASSET(ComputeShaderAsset, "comp") + public: + constexpr static const char* ASSET_NAME = "comp"; + public: + SH_GAME_API ComputeShaderAsset(); + SH_GAME_API ComputeShaderAsset(const render::ComputeShader& shader); + + SH_GAME_API void SetAsset(const core::SObject& obj) override; + + SH_GAME_API auto GetShaderObjectJson() const -> const core::Json& { return shaderObjJson; } + protected: + SH_GAME_API void SetAssetData() const override; + SH_GAME_API auto ParseAssetData() -> bool override; + private: + core::SObjWeakPtr shaderPtr; + + core::Json shaderObjJson; + }; +}//namespace diff --git a/include/Game/Asset/ComputeShaderLoader.h b/include/Game/Asset/ComputeShaderLoader.h new file mode 100644 index 00000000..a076a4c7 --- /dev/null +++ b/include/Game/Asset/ComputeShaderLoader.h @@ -0,0 +1,37 @@ +#pragma once +#include "../Export.h" + +#include "Core/Asset.h" +#include "Core/IAssetLoader.h" + +#include "Render/ComputeShader.h" + +#include + +namespace sh::render +{ + class IRenderContext; +} +namespace sh::game +{ + + class ComputeShaderLoader : public core::IAssetLoader + { + public: + SH_GAME_API ComputeShaderLoader(const render::IRenderContext& ctx); + SH_GAME_API ~ComputeShaderLoader(); + + SH_GAME_API void SetCachePath(const std::filesystem::path& path) { cachePath = path; } + + SH_GAME_API auto Load(const std::filesystem::path& filePath) const -> core::SObject* override; + SH_GAME_API auto Load(const core::Asset& asset) const -> core::SObject* override; + + SH_GAME_API auto GetCachePath() const -> const std::filesystem::path& { return cachePath; } + SH_GAME_API auto GetAssetName() const -> const char* override { return ASSET_NAME; } + private: + static constexpr const char* ASSET_NAME = "comp"; + const render::IRenderContext& ctx; + + std::filesystem::path cachePath; + }; +}//namespace diff --git a/include/Game/Asset/ShaderAsset.h b/include/Game/Asset/ShaderAsset.h index 06113798..fe44c221 100644 --- a/include/Game/Asset/ShaderAsset.h +++ b/include/Game/Asset/ShaderAsset.h @@ -12,15 +12,8 @@ namespace sh::game class ShaderAsset : public core::Asset { SASSET(ShaderAsset, "shad") - private: - core::SObjWeakPtr shaderPtr; - - core::Json shaderObjJson; public: constexpr static const char* ASSET_NAME = "shad"; - protected: - SH_GAME_API void SetAssetData() const override; - SH_GAME_API auto ParseAssetData() -> bool override; public: SH_GAME_API ShaderAsset(); SH_GAME_API ShaderAsset(const render::Shader& shader); @@ -28,5 +21,12 @@ namespace sh::game SH_GAME_API void SetAsset(const core::SObject& obj) override; SH_GAME_API auto GetShaderObjectJson() const -> const core::Json&; + protected: + SH_GAME_API void SetAssetData() const override; + SH_GAME_API auto ParseAssetData() -> bool override; + private: + core::SObjWeakPtr shaderPtr; + + core::Json shaderObjJson; }; }//namespace \ No newline at end of file diff --git a/include/Game/Component/Render/Camera.h b/include/Game/Component/Render/Camera.h index 7ce847db..bb046178 100644 --- a/include/Game/Component/Render/Camera.h +++ b/include/Game/Component/Render/Camera.h @@ -3,9 +3,8 @@ #include "Game/Vector.h" #include "Game/Component/Component.h" -#include "Render/Renderer.h" +#include "Render/RenderData.h" #include "Render/RenderTexture.h" -#include "Render/Camera.h" #include "glm/mat4x4.hpp" @@ -33,65 +32,64 @@ namespace sh::game SH_GAME_API void BeginUpdate() override; SH_GAME_API void OnDestroy() override; - SH_GAME_API void SetActive(bool b) override; - - SH_GAME_API auto GetProjMatrix() const -> const glm::mat4&; - SH_GAME_API auto GetViewMatrix() const -> const glm::mat4&; + SH_GAME_API auto ScreenPointToRay(const Vec2& mousePos) const -> phys::Ray; + SH_GAME_API auto ScreenPointToRayOrtho(const Vec2& mousePos) const -> phys::Ray; - SH_GAME_API void SetDepth(int depth); - SH_GAME_API auto GetDepth() const -> int { return depth; } + SH_GAME_API void SetPriority(int priority); SH_GAME_API void SetFov(float degree); + void SetRenderTexture(render::RenderTexture* renderTexture) { this->renderTexture = renderTexture; } + void SetLookPos(const Vec3& pos) { lookPos = pos; } + void SetUpVector(const Vec3& up) { this->up = up; } + void SetProjection(Projection proj) { projection = proj; } + void SetNearPlane(float nearPlane) { this->nearPlane = nearPlane; } + void SetFarPlane(float farPlane) { this->farPlane = farPlane; } + void SetTag(const core::Name& tag) { renderData.tag = tag; } + auto GetPriority() const -> int { return priority; } /// @brief 세로 시야각을 가져오는 함수. /// @return Degree로 반환한다. - SH_GAME_API auto GetFov() const -> float { return fov; } + auto GetFov() const -> float { return fov; } /// @brief 가로 시야각을 가져오는 함수. /// @return Degree로 반환한다. - SH_GAME_API auto GetFovx() const -> float { return fovx; } - - SH_GAME_API void SetRenderTexture(render::RenderTexture* renderTexture); - SH_GAME_API auto GetRenderTexture() const -> render::RenderTexture*; - - SH_GAME_API auto GetNative() -> render::Camera&; - SH_GAME_API auto GetNative() const -> const render::Camera&; - - SH_GAME_API auto ScreenPointToRay(const Vec2& mousePos) const -> phys::Ray; - SH_GAME_API auto ScreenPointToRayOrtho(const Vec2& mousePos) const -> phys::Ray; - - SH_GAME_API void SetLookPos(const Vec3& pos); - SH_GAME_API auto GetLookPos() const -> const Vec3&; - SH_GAME_API void SetUpVector(const Vec3& up); - SH_GAME_API auto GetUpVector() const -> Vec3; - - SH_GAME_API void SetWidth(float width); - SH_GAME_API auto GetWidth() const -> float; - SH_GAME_API void SetHeight(float height); - SH_GAME_API auto GetHeight() const -> float; - - SH_GAME_API void SetProjection(Projection proj); - SH_GAME_API auto GetProjection() const -> Projection; - - SH_GAME_API void SetNearPlane(float near); - SH_GAME_API auto GetNearPlane() const -> float; - SH_GAME_API void SetFarPlane(float near); - SH_GAME_API auto GetFarPlane() const -> float; - - SH_GAME_API void OnPropertyChanged(const core::reflection::Property& prop) override; + auto GetFovx() const -> float { return fovx; } + auto GetWidth() const -> float { return width; } + auto GetHeight() const -> float { return height; } + auto GetNearPlane() const -> float { return nearPlane; } + auto GetFarPlane() const -> float { return farPlane; } + auto GetUpVector() const -> const Vec3& { return up; } + auto GetProjMatrix() const -> const glm::mat4& { return renderData.renderViewers.front().projMatrix; } + auto GetViewMatrix() const -> const glm::mat4& { return renderData.renderViewers.front().viewMatrix; } + auto GetLookPos() const -> const Vec3& { return lookPos; } + auto GetTag() const -> const core::Name& { return renderData.tag; } + auto GetProjection() const -> Projection { return projection; } + auto GetRenderTexture() const -> render::RenderTexture* { return renderTexture; } + auto GetRenderData() const -> const render::RenderData& { return renderData; } + protected: + SH_GAME_API virtual void UpdateViewMatrix(); + SH_GAME_API virtual void UpdateProjMatrix(); protected: - render::Camera camera; - PROPERTY(lookPos) Vec3 lookPos; private: PROPERTY(renderTexture) render::RenderTexture* renderTexture; - PROPERTY(depth) - int depth; + PROPERTY(priority) + int priority = 0; PROPERTY(fov) - float fov; - float fovx; + float fov = 60.f; + float fovRadians = 0.f; + float fovx = 0.f; PROPERTY(projection) Projection projection = Projection::Perspective; + Vec3 up{ 0.f, 1.f, 0.f }; + PROPERTY(nearPlane) + float nearPlane = 0.1f; + PROPERTY(farPlane) + float farPlane = 1000.f; + float width = 1.f; + float height = 1.f; + + render::RenderData renderData; }; -} \ No newline at end of file +}//namespace \ No newline at end of file diff --git a/include/Game/Component/Render/DirectionalLight.h b/include/Game/Component/Render/DirectionalLight.h index 5a2ca5ab..781eaaa4 100644 --- a/include/Game/Component/Render/DirectionalLight.h +++ b/include/Game/Component/Render/DirectionalLight.h @@ -1,33 +1,47 @@ #pragma once #include "Game/Export.h" -#include "Game/ILight.h" -#include "Game/Component/Component.h" +#include "Game/Component/Render/LightBase.h" + +#include "Render/RenderData.h" +#include "Render/ShadowMapManager.h" + +#include + +namespace sh::render +{ + class RenderTexture; +} namespace sh::game { - class DirectionalLight : public Component, public ILight + class DirectionalLight : public LightBase { COMPONENT(DirectionalLight) - private: - PROPERTY(direction) - Vec3 direction{ -1.f, -1.f, -1.f }; - PROPERTY(intensity) - float intensity = 1.f; public: SH_GAME_API DirectionalLight(GameObject& owner); SH_GAME_API ~DirectionalLight(); - SH_GAME_API void OnDestroy() override; - - SH_GAME_API bool Intersect(const render::AABB& aabb) const override; - - SH_GAME_API void SetIntensity(float intensity); - SH_GAME_API auto GetIntensity() const -> float; + SH_GAME_API auto Intersect(const render::AABB& aabb) const -> bool override; + SH_GAME_API void SetIntensity(float intensity) override; SH_GAME_API void SetDirection(const Vec3& dir); - SH_GAME_API auto GetDirection() const -> const Vec3&; + /// @brief 라이트의 광원 공간 변환 행렬 (proj * view)을 반환한다. + SH_GAME_API auto GetLightSpaceMatrix() const -> glm::mat4 override; + + SH_GAME_API auto GetShadowViewMatrix() const -> glm::mat4 override; + SH_GAME_API auto GetShadowProjMatrix() const -> glm::mat4 override; + SH_GAME_API auto GetShadowPos() const -> glm::vec3 override; + SH_GAME_API auto GetShadowLookAt() const -> glm::vec3 override; SH_GAME_API auto GetPos() const -> const Vec3& override; - SH_GAME_API auto GetLightType() const -> ILight::Type override; + + auto GetLightType() const -> ILight::Type override { return ILight::Type::Directional; } + auto GetIntensity() const -> float override { return intensity; } + auto GetDirection() const -> const Vec3& { return direction; } + private: + PROPERTY(direction) + Vec3 direction{ -1.f, -1.f, -1.f }; + PROPERTY(intensity) + float intensity = 1.f; }; -}//namespace \ No newline at end of file +}//namespace diff --git a/include/Game/Component/Render/LightBase.h b/include/Game/Component/Render/LightBase.h new file mode 100644 index 00000000..d33e3520 --- /dev/null +++ b/include/Game/Component/Render/LightBase.h @@ -0,0 +1,67 @@ +#pragma once +#include "Game/Export.h" +#include "Game/Component/Component.h" +#include "Game/ILight.h" + +#include "Render/ShadowMapManager.h" + +namespace sh::game +{ + /// @brief 광원 컴포넌트 추상 클래스 + class LightBase : public Component, public ILight, public render::IShadowCaster + { + SCLASS(LightBase) + public: + SH_GAME_API LightBase(GameObject& owner); + + SH_GAME_API void Awake() override; + SH_GAME_API void OnDestroy() override; + SH_GAME_API void OnEnable() override; + SH_GAME_API void OnDisable() override; + SH_GAME_API void OnPropertyChanged(const core::reflection::Property& prop) override; + + SH_GAME_API void SetCastShadow(bool bCastShadow); + + /// @brief 그림자 아틀라스 텍스처를 반환한다 (모든 광원이 공유) + SH_GAME_API auto GetShadowMap() const -> render::RenderTexture*; + /// @brief 아틀라스 내에서 이 광원의 슬롯 정보를 반환한다. + SH_GAME_API auto GetShadowSlot() const -> render::ShadowMapManager::Slot; + + virtual void SetIntensity(float intensity) = 0; + virtual auto GetIntensity() const -> float = 0; + virtual auto GetLightType() const -> Type = 0; + virtual auto GetLightSpaceMatrix() const -> glm::mat4 = 0; + + virtual auto GetShadowBias() const -> float { return shadowBias; } + virtual auto GetShadowMapResolution() const -> uint32_t { return shadowMapResolution; } + virtual auto GetShadowViewMatrix() const -> glm::mat4 = 0; + virtual auto GetShadowProjMatrix() const -> glm::mat4 = 0; + virtual auto GetShadowPos() const -> glm::vec3 = 0; + virtual auto GetShadowLookAt() const -> glm::vec3 = 0; + + void SetShadowBias(float bias) { shadowBias = bias; } + void SetShadowMapResolution(uint32_t res) { shadowMapResolution = res; } + auto IsCastShadow() const -> bool { return bCastShadow; } + auto GetShadowOrthoSize() const -> float { return shadowOrthoSize; } + auto GetShadowNearPlane() const -> float { return shadowNearPlane; } + auto GetShadowFarPlane() const -> float { return shadowFarPlane; } + protected: + SH_GAME_API void UpdateLightOctree(); + SH_GAME_API void RegisterToShadowManager(); + SH_GAME_API void UnregisterFromShadowManager(); + private: + PROPERTY(shadowBias) + float shadowBias = 0.005f; + PROPERTY(shadowMapResolution) + uint32_t shadowMapResolution = 1024; + PROPERTY(shadowOrthoSize) + float shadowOrthoSize = 20.f; + PROPERTY(shadowNearPlane) + float shadowNearPlane = 0.1f; + PROPERTY(shadowFarPlane) + float shadowFarPlane = 100.f; + PROPERTY(bCastShadow) + bool bCastShadow = false; + bool bRegistered = false; + }; +}//namespace \ No newline at end of file diff --git a/include/Game/Component/Render/MeshRenderer.h b/include/Game/Component/Render/MeshRenderer.h index 58449e6d..661c4001 100644 --- a/include/Game/Component/Render/MeshRenderer.h +++ b/include/Game/Component/Render/MeshRenderer.h @@ -69,7 +69,7 @@ namespace sh::game std::memcpy(uniformData.data() + offset, &data, sizeof(T)); } - void FillLightStruct(render::Drawable& drawable, render::Shader& shader) const; + void FillLightStruct(render::Drawable& drawable, render::Shader& shader); protected: PROPERTY(drawables, core::PropertyOption::invisible, core::PropertyOption::noSave) std::vector drawables; @@ -78,12 +78,14 @@ namespace sh::game PROPERTY(mats) std::vector mats; private: - struct Light + struct alignas(16) Light { - alignas(16) int lightCount = 0; - alignas(16) glm::vec4 lightPos[10]; - alignas(16) glm::vec4 other[10]; + glm::vec4 pos; + glm::vec4 other; + glm::vec4 shadow; // offset, size + glm::mat4 lightSpaceMatrix; }; + std::vector lightDatas; render::AABB worldAABB; std::vector> propertyBlocks; diff --git a/include/Game/Component/Render/PickingCamera.h b/include/Game/Component/Render/PickingCamera.h index 0cae30da..5a2cd0e3 100644 --- a/include/Game/Component/Render/PickingCamera.h +++ b/include/Game/Component/Render/PickingCamera.h @@ -39,7 +39,7 @@ namespace sh::game int x = 0, y = 0; - uint8_t* pixels = nullptr; + std::vector pixels; std::future> bufferFuture; diff --git a/include/Game/Component/Render/PointLight.h b/include/Game/Component/Render/PointLight.h index c0b3bb32..dbb311df 100644 --- a/include/Game/Component/Render/PointLight.h +++ b/include/Game/Component/Render/PointLight.h @@ -1,39 +1,45 @@ #pragma once #include "Game/Export.h" -#include "Game/ILight.h" -#include "Game/Component/Component.h" +#include "Game/Component/Render/LightBase.h" namespace sh::game { - class PointLight : public Component, public ILight + class PointLight : public LightBase { COMPONENT(PointLight) - private: - PROPERTY(range) - float range = 5.f; - PROPERTY(intensity) - float intensity = 1.f; - - Vec3 lastPos{ 0.f, 0.f, 0.f }; - bool bUpdateOctree = false; public: SH_GAME_API PointLight(GameObject& owner); SH_GAME_API ~PointLight(); - SH_GAME_API void Destroy() override; - SH_GAME_API void BeginUpdate() override; - SH_GAME_API bool Intersect(const render::AABB& aabb) const override; + SH_GAME_API auto Intersect(const render::AABB& aabb) const -> bool override; SH_GAME_API void SetRadius(float radius); - SH_GAME_API void SetIntensity(float intensity); - SH_GAME_API auto GetRadius() const -> float; - SH_GAME_API auto GetIntensity() const -> float; + /// @brief 라이트의 광원 공간 변환 행렬 (proj * view)을 반환한다. + SH_GAME_API auto GetLightSpaceMatrix() const -> glm::mat4 override; + + SH_GAME_API auto GetShadowViewMatrix() const -> glm::mat4 override; + SH_GAME_API auto GetShadowProjMatrix() const -> glm::mat4 override; + SH_GAME_API auto GetShadowPos() const -> glm::vec3 override; + SH_GAME_API auto GetShadowLookAt() const -> glm::vec3 override; + SH_GAME_API auto GetPos() const -> const Vec3& override; - SH_GAME_API auto GetLightType() const -> ILight::Type override; - + SH_GAME_API void OnPropertyChanged(const core::reflection::Property& prop) override; + + void SetIntensity(float intensity) override { this->intensity = intensity; } + auto GetIntensity() const -> float override { return intensity; } + auto GetLightType() const -> ILight::Type override { return ILight::Type::Point; } + auto GetRadius() const -> float { return range; } + private: + PROPERTY(range) + float range = 5.f; + PROPERTY(intensity) + float intensity = 1.f; + + Vec3 lastPos{ 0.f, 0.f, 0.f }; + bool bUpdateOctree = false; }; }//namespace \ No newline at end of file diff --git a/include/Game/Component/Render/SSAOComponent.h b/include/Game/Component/Render/SSAOComponent.h new file mode 100644 index 00000000..26a97036 --- /dev/null +++ b/include/Game/Component/Render/SSAOComponent.h @@ -0,0 +1,74 @@ +#pragma once +#include "Game/Export.h" +#include "Game/Component/Component.h" + +#include "Render/RenderData.h" + +#include +namespace sh::render +{ + class Material; + class RenderTexture; + class Texture; +} + +namespace sh::game +{ + class Camera; + + /// @brief 카메라에 부착해 SSAO 를 활성화한다. + class SSAOComponent : public Component + { + COMPONENT(SSAOComponent) + public: + SH_GAME_API SSAOComponent(GameObject& owner); + SH_GAME_API ~SSAOComponent(); + + SH_GAME_API void Awake() override; + SH_GAME_API void OnDestroy() override; + SH_GAME_API void BeginUpdate() override; + + SH_GAME_API void SetMaterial(render::Material* mat); + + auto GetDepthRT() const -> render::RenderTexture* { return depthRT; } + auto GetAORT() const -> render::RenderTexture* { return aoRT; } + auto GetRadius() const -> float { return radius; } + auto GetBias() const -> float { return bias; } + auto GetPower() const -> float { return power; } + private: + void EnsureRenderTextures(); + void UpdateMaterialUniforms(); + static void CreateKernel(); + private: + PROPERTY(mat, core::PropertyOption::sobjPtr, core::PropertyOption::invisible, core::PropertyOption::noSave) + render::Material* mat = nullptr; + + PROPERTY(radius) + float radius = 0.5f; + PROPERTY(bias) + float bias = 0.025f; + PROPERTY(power) + float power = 1.5f; + + PROPERTY(depthRT, core::PropertyOption::sobjPtr, core::PropertyOption::invisible, core::PropertyOption::noSave) + render::RenderTexture* depthRT = nullptr; + PROPERTY(normalRT, core::PropertyOption::sobjPtr, core::PropertyOption::invisible, core::PropertyOption::noSave) + render::RenderTexture* normalRT = nullptr; + PROPERTY(aoRT, core::PropertyOption::sobjPtr, core::PropertyOption::invisible, core::PropertyOption::noSave) + render::RenderTexture* aoRT = nullptr; + PROPERTY(camera, core::PropertyOption::sobjPtr, core::PropertyOption::invisible, core::PropertyOption::noSave); + Camera* camera = nullptr; + PROPERTY(noiseTex, core::PropertyOption::sobjPtr, core::PropertyOption::invisible, core::PropertyOption::noSave); + render::Texture* noiseTex = nullptr; + + render::RenderData depthRenderData; + render::RenderData ssaoRenderData; + render::RenderData combineRenderData; + + uint32_t cachedWidth = 0; + uint32_t cachedHeight = 0; + + static std::array kernel; + static bool bKernelInit; + }; +}//namespace diff --git a/include/Game/Component/Render/SkinnedMeshRenderer.h b/include/Game/Component/Render/SkinnedMeshRenderer.h index be63fe14..15bfdb0a 100644 --- a/include/Game/Component/Render/SkinnedMeshRenderer.h +++ b/include/Game/Component/Render/SkinnedMeshRenderer.h @@ -21,6 +21,7 @@ namespace sh::game SH_GAME_API ~SkinnedMeshRenderer(); SH_GAME_API void Awake() override; + SH_GAME_API void OnPropertyChanged(const core::reflection::Property& prop) override; SH_GAME_API void SetSkinnedMesh(render::SkinnedMesh* mesh); SH_GAME_API void SetBones(std::vector bones); @@ -32,10 +33,11 @@ namespace sh::game private: void ComputeBoneMatrices(); void UploadBoneMatrices(); + void InitIBM(); private: PROPERTY(bones, core::PropertyOption::invisible) std::vector bones; std::vector inverseBindMatrices; - std::array finalBoneMatrices; + std::vector finalBoneMatrices; }; }//namespace diff --git a/include/Game/Component/TestComputeShader.h b/include/Game/Component/TestComputeShader.h new file mode 100644 index 00000000..6a299106 --- /dev/null +++ b/include/Game/Component/TestComputeShader.h @@ -0,0 +1,23 @@ +#pragma once +#include "Export.h" +#include "Game/Component/Component.h" + +namespace sh::render +{ + class ComputeShader; +} + +namespace sh::game +{ + class TestComputeShader : public Component + { + COMPONENT(TestComputeShader) + public: + SH_GAME_API TestComputeShader(GameObject& owner); + + SH_GAME_API void Update() override; + private: + PROPERTY(shader) + render::ComputeShader* shader = nullptr; + }; +}//namespace diff --git a/include/Game/GUIPass.h b/include/Game/GUIPass.h new file mode 100644 index 00000000..ccdd80d7 --- /dev/null +++ b/include/Game/GUIPass.h @@ -0,0 +1,27 @@ +#pragma once +#include "Export.h" + +#include "Render/ScriptableRenderPass.h" + +#include +#include +namespace sh::game +{ + class ImGUImpl; + + /// @brief ImGUI전용 패스 + class GUIPass : public render::ScriptableRenderPass + { + public: + SH_GAME_API GUIPass(); + + SH_GAME_API void SetImGUIContext(ImGUImpl& gui); + protected: + SH_GAME_API void Configure(const render::RenderData& renderData) override; + SH_GAME_API void Record(render::CommandBuffer& cmd, const render::IRenderContext& ctx, const render::RenderData& renderData) override; + public: + const render::RenderTexture* viewportTexture = nullptr; + private: + ImGUImpl* gui = nullptr; + }; +}//namespace \ No newline at end of file diff --git a/include/Game/GameManager.h b/include/Game/GameManager.h index 20c98896..0fb4b1ca 100644 --- a/include/Game/GameManager.h +++ b/include/Game/GameManager.h @@ -12,6 +12,8 @@ #include "Game/ImGUImpl.h" #include "Game/ComponentLoader.h" +#include "Render/RenderData.h" + #include #include #include @@ -101,6 +103,8 @@ namespace sh::game core::SVector defaultAssets; + render::RenderData guiRenderData; + bool bLoadingWorld = false; }; }//namespace \ No newline at end of file diff --git a/include/Game/GameRenderer.h b/include/Game/GameRenderer.h index c550bd1e..dd6a7ca8 100644 --- a/include/Game/GameRenderer.h +++ b/include/Game/GameRenderer.h @@ -3,25 +3,48 @@ #include "Render/ScriptableRenderer.h" -#include "Component/Render/Camera.h" - #include +#include +namespace sh::render +{ + class DepthPass; + class OpaquePass; + class SSAOPass; + class CombinePass; + class TransparentPass; +} namespace sh::game { class ImGUImpl; - + class World; + class GUIPass; + class GameRenderer : public render::ScriptableRenderer { public: - SH_GAME_API GameRenderer(render::IRenderContext& ctx, game::ImGUImpl& guictx); + SH_GAME_API GameRenderer(render::IRenderContext& ctx, game::ImGUImpl& guictx, World& world); - SH_GAME_API void Setup(const render::RenderTarget& data) override; + SH_GAME_API void Init() override; + SH_GAME_API void Setup(const render::RenderData& data) override; - SH_GAME_API void SetUICamera(const render::Camera& camera); - private: - const render::Camera* uiCamera = nullptr; + auto GetDepthPass() const -> render::DepthPass& { return *depthPass; } + auto GetOpaquePass() const -> render::ScriptableRenderPass& { return *opaquePass; } + auto GetSSAOPass() const -> render::SSAOPass& { return *ssaoPass; } + auto GetCombinePass() const -> render::CombinePass& { return *combinePass; } + auto GetTransparentPass() const -> render::TransparentPass& { return *transparentPass; } + auto GetUIPass() const -> render::TransparentPass& { return *uiPass; } + auto GetGUIPass() const -> game::GUIPass& { return *guiPass; } + protected: + render::IRenderContext& renderCtx; + World& world; + game::ImGUImpl& guiCtx; - std::unordered_map> allowedCamera; - std::unordered_map> ignoreCamera; + render::DepthPass* depthPass = nullptr; + render::ScriptableRenderPass* opaquePass = nullptr; + render::SSAOPass* ssaoPass = nullptr; + render::CombinePass* combinePass = nullptr; + render::TransparentPass* transparentPass = nullptr; + render::TransparentPass* uiPass = nullptr; + game::GUIPass* guiPass = nullptr; }; -}//namespace \ No newline at end of file +}//namespace diff --git a/include/Game/ILight.h b/include/Game/ILight.h index f0f102d2..91d39105 100644 --- a/include/Game/ILight.h +++ b/include/Game/ILight.h @@ -2,6 +2,8 @@ #include "Game/Export.h" #include "IOctreeElement.h" +#include + namespace sh::render { class Material; @@ -19,7 +21,8 @@ namespace sh::game public: virtual ~ILight() {}; virtual void SetIntensity(float intensity) = 0; + virtual auto GetIntensity() const -> float = 0; virtual auto GetLightType() const -> Type = 0; - //virtual void SetMaterialData(render::Material& mat) = 0; + virtual auto GetLightSpaceMatrix() const -> glm::mat4 = 0; }; -}//namespace \ No newline at end of file +}//namespace diff --git a/include/Game/UICamera.h b/include/Game/UICamera.h deleted file mode 100644 index 14ceb764..00000000 --- a/include/Game/UICamera.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once -#include "Export.h" - -#include "Render/Camera.h" - -namespace sh::game -{ - class UICamera : public render::Camera - { - public: - SH_GAME_API UICamera(); - protected: - SH_GAME_API void UpdateViewMatrix() override; - SH_GAME_API void UpdateProjMatrix() override; - }; -}//namespace \ No newline at end of file diff --git a/include/Game/UIPass.h b/include/Game/UIPass.h deleted file mode 100644 index 66b9d709..00000000 --- a/include/Game/UIPass.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once -#include "Export.h" - -#include "Render/ScriptableRenderPass.h" - -#include -#include -namespace sh::game -{ - class ImGUImpl; - - class UIPass : public render::ScriptableRenderPass - { - public: - SH_GAME_API UIPass(); - - SH_GAME_API void SetImGUIContext(ImGUImpl& gui); - protected: - SH_GAME_API void Configure(const render::RenderTarget& renderData) override; - SH_GAME_API auto BuildDrawList(const render::RenderTarget& renderData) -> render::DrawList override; - public: - render::RenderTexture* viewportTexture = nullptr; - private: - ImGUImpl* gui = nullptr; - }; -}//namespace \ No newline at end of file diff --git a/include/Game/World.h b/include/Game/World.h index a070ff64..1d9fb7d6 100644 --- a/include/Game/World.h +++ b/include/Game/World.h @@ -2,9 +2,7 @@ #include "Export.h" #include "ComponentModule.h" #include "Octree.h" -#include "UICamera.h" #include "GameObject.h" -#include "Component/Render/Camera.h" #include "Core/NonCopyable.h" #include "Core/SObject.h" @@ -39,12 +37,14 @@ namespace sh::render { class Renderer; class ScriptableRenderer; + class ShadowMapManager; } namespace sh::game { class Component; class ImGUImpl; + class Camera; class World : public sh::core::SObject, public sh::core::INonCopyable { @@ -58,6 +58,8 @@ namespace sh::game SH_GAME_API virtual ~World(); SH_GAME_API void OnDestroy() override; + SH_GAME_API auto Serialize() const -> core::Json override; + SH_GAME_API void Deserialize(const core::Json& json) override; SH_GAME_API virtual void Clear(); @@ -74,20 +76,13 @@ namespace sh::game /// @param name 이름 /// @return 못 찾을 시 nullptr, 찾을 시 게임 오브젝트 포인터 SH_GAME_API auto GetGameObject(std::string_view name) const -> GameObject*; - SH_GAME_API auto GetGameObjects() const -> const std::vector& { return objs; } - SH_GAME_API auto GetGameObjectPool() -> core::memory::MemoryPool&; /// @brief 게임 오브젝트가 할당된 메모리를 반환 큐에 넣는 함수. /// @param ptr 오브젝트 포인터 SH_GAME_API void PushDeAllocatedGameObject(GameObject* ptr); - SH_GAME_API void RegisterCamera(Camera* cam); - SH_GAME_API void UnRegisterCamera(Camera* cam); + SH_GAME_API void RegisterCamera(Camera& cam); + SH_GAME_API void UnRegisterCamera(Camera& cam); SH_GAME_API void SetMainCamera(Camera* cam); - SH_GAME_API auto GetMainCamera() const -> Camera*; - - SH_GAME_API auto GetPhysWorld() -> phys::PhysWorld*; - SH_GAME_API auto GetLightOctree() -> Octree&; - SH_GAME_API auto GetLightOctree() const -> const Octree&; SH_GAME_API virtual void Start(); SH_GAME_API virtual void Update(double deltaTime); @@ -100,41 +95,39 @@ namespace sh::game /// @param func 함수 SH_GAME_API void AddAfterSyncTask(const std::function& func); - SH_GAME_API auto Serialize() const->core::Json override; - SH_GAME_API void Deserialize(const core::Json& json) override; - SH_GAME_API void SaveWorldPoint(const core::Json& json, std::string_view name = "default"); SH_GAME_API void SaveWorldPoint(core::Json&& json, std::string_view name = "default"); - SH_GAME_API void LoadWorldPoint(); SH_GAME_API void LoadWorldPoint(const std::string& name); - SH_GAME_API auto GetWorldPoint() const -> const core::Json*; SH_GAME_API auto GetWorldPoint(const std::string& name) const -> const core::Json*; SH_GAME_API auto GetWorldPoint() -> core::Json*; SH_GAME_API auto GetWorldPoint(const std::string& name) -> core::Json*; - SH_GAME_API void ClearWorldPoints(); SH_GAME_API void ClearWorldPoint(const std::string& name = "default"); - SH_GAME_API auto GetUiContext() const -> ImGUImpl&; - SH_GAME_API void PublishEvent(const core::IEvent& event); SH_GAME_API void SubscribeEvent(core::ISubscriber& subscriber); SH_GAME_API void Play(); SH_GAME_API void Stop(); - SH_GAME_API auto GetCameras() const -> const core::SVector& { return cameras; } - SH_GAME_API auto IsPlaying() const -> bool; - SH_GAME_API auto IsStart() const -> bool; - SH_GAME_API auto IsLoaded() const -> bool; - SH_GAME_API auto GetUICamera() -> UICamera& { return uiCamera; } - SH_GAME_API auto GetUICamera() const -> const UICamera& { return uiCamera; } - SH_GAME_API virtual void ReallocateUUIDS(); - SH_GAME_API auto GetFixedAccumulator() const -> double { return dtAccumulator; } + auto GetUiContext() const -> ImGUImpl& { return *imgui; } + auto GetPhysWorld() -> phys::PhysWorld& { return physWorld; } + auto GetLightOctree() -> Octree& { return lightOctree; } + auto GetLightOctree() const -> const Octree& { return lightOctree; } + auto GetMainCamera() const -> Camera* { return mainCamera; } + auto GetShadowMapManager() -> render::ShadowMapManager& { return *shadowMapManager; } + auto GetShadowMapManager() const -> const render::ShadowMapManager& { return *shadowMapManager; } + auto GetGameObjects() const -> const std::vector& { return objs; } + auto GetGameObjectPool() -> core::memory::MemoryPool& { return objPool; } + auto GetCameras() const -> const std::vector& { return cameras; } + auto IsPlaying() const -> bool { return bPlaying; } + auto IsStart() const -> bool { return bOnStart; } + auto IsLoaded() const -> bool { return bLoaded; } + auto GetFixedAccumulator() const -> double { return dtAccumulator; } protected: SH_GAME_API void CleanObjs(); private: @@ -148,9 +141,10 @@ namespace sh::game core::EventBus eventBus; std::unordered_map objIdx; std::vector objs; - core::SVector cameras; + std::vector cameras; std::unique_ptr customRenderer; + std::unique_ptr shadowMapManager; private: core::GarbageCollection* gc; ImGUImpl* imgui = nullptr; @@ -162,7 +156,7 @@ namespace sh::game double dt = 0.0; double dtAccumulator = 0.0; - PROPERTY(mainCamera) + PROPERTY(mainCamera, core::PropertyOption::sobjPtr) Camera* mainCamera = nullptr; phys::PhysWorld physWorld; @@ -175,8 +169,6 @@ namespace sh::game std::unordered_map savePoints; - UICamera uiCamera; - core::EventSubscriber physEventSubscriber; bool bStartLoop = false; @@ -185,4 +177,4 @@ namespace sh::game bool bLoaded = false; bool bOnStart = false; }; -} \ No newline at end of file +}//namespace \ No newline at end of file diff --git a/include/Render/BufferFactory.h b/include/Render/BufferFactory.h index 781a4e6b..b03917e3 100644 --- a/include/Render/BufferFactory.h +++ b/include/Render/BufferFactory.h @@ -8,8 +8,9 @@ namespace sh::render { class IBuffer; - class IUniformBuffer; + class IShaderBinding; class IRenderContext; + class ComputeShader; namespace vk { class VulkanContext; @@ -17,10 +18,20 @@ namespace sh::render class BufferFactory { - private: - static auto CreateVkUniformBuffer(const vk::VulkanContext& context, std::size_t size, bool bTransferDst) -> std::unique_ptr; public: - SH_RENDER_API static auto Create(const IRenderContext& context, std::size_t size, bool bTransferDst = false) -> std::unique_ptr; - SH_RENDER_API static auto CreateUniformBuffer(const IRenderContext& context, const ShaderPass& shader, UniformStructLayout::Type type) -> std::unique_ptr; + struct CreateInfo + { + std::size_t size = 0; + bool bDynamic = false; // SSBO + bool bGPUOnly = false; + }; + public: + SH_RENDER_API static auto Create(const IRenderContext& context, const CreateInfo& info) -> std::unique_ptr; + SH_RENDER_API static auto CreateShaderBinding(const IRenderContext& context, const ShaderPass& shader, UniformStructLayout::Usage usage) -> std::unique_ptr; + SH_RENDER_API static auto CreateShaderBinding(const IRenderContext& context, const ComputeShader& shader) -> std::unique_ptr; + + SH_RENDER_API static auto GetBufferAlignment(const IRenderContext& context) -> std::size_t; + private: + static auto CreateVkUniformBuffer(const vk::VulkanContext& context, const CreateInfo& info) -> std::unique_ptr; }; }//namespace \ No newline at end of file diff --git a/include/Render/Camera.h b/include/Render/Camera.h deleted file mode 100644 index ea1148c9..00000000 --- a/include/Render/Camera.h +++ /dev/null @@ -1,108 +0,0 @@ -#pragma once -#include "Export.h" -#include "IRenderContext.h" -#include "RenderTexture.h" - -#include "Core/ISyncable.h" - -#include - -#include - -#ifdef near -#undef near -#endif -#ifdef far -#undef far -#endif - -namespace sh::render -{ - /// @brief 렌더러용 카메라. - class Camera : public core::ISyncable - { - public: - SH_RENDER_API Camera(); - - SH_RENDER_API void SetRenderTexture(RenderTexture* framebuffer); - SH_RENDER_API auto GetRenderTexture() const -> RenderTexture*; - - SH_RENDER_API void SetPriority(int priority); - SH_RENDER_API auto GetPriority() const -> int; - - SH_RENDER_API auto GetProjMatrix(core::ThreadType thr) const -> const glm::mat4&; - SH_RENDER_API auto GetViewMatrix(core::ThreadType thr) const -> const glm::mat4&; - - SH_RENDER_API void SetPos(const glm::vec3& pos); - SH_RENDER_API void SetLookPos(const glm::vec3& pos); - SH_RENDER_API void SetUpVector(const glm::vec3& up); - SH_RENDER_API void SetFovRadian(float rad); - SH_RENDER_API void SetFov(float degree); - SH_RENDER_API void SetNearPlane(float near); - SH_RENDER_API void SetFarPlane(float far); - SH_RENDER_API void SetWidth(float width); - SH_RENDER_API void SetHeight(float height); - SH_RENDER_API void SetOrthographic(bool bEnable); - SH_RENDER_API void SetCustomProjectionMatrix(const glm::mat4* matrix); - - SH_RENDER_API void SetRenderTagMask(uint32_t mask); - SH_RENDER_API void AddRenderTagMask(uint32_t tagId); - SH_RENDER_API void RemoveRenderTagMask(uint32_t tagId); - SH_RENDER_API auto GetRenderTagMask() const -> uint32_t; - /// @brief 렌더 태그가 mask에 통과 하는지 검증하는 함수. - /// @param tagId 태그 id - /// @return 통과 하면 true 아니면 false - SH_RENDER_API auto CheckRenderTag(uint32_t tagId) const -> bool; - - SH_RENDER_API void SetActive(bool bActive); - SH_RENDER_API auto GetActive() const -> bool; - - SH_RENDER_API auto GetPos(core::ThreadType thr) const -> const glm::vec3& { return pos[thr]; } - SH_RENDER_API auto GetLookPos(core::ThreadType thr) const -> const glm::vec3& { return to[thr]; } - SH_RENDER_API auto GetUpVector(core::ThreadType thr) const -> const glm::vec3& { return up[thr]; } - SH_RENDER_API auto GetFovRadian(core::ThreadType thr) const -> float { return fovRadians[thr]; } - SH_RENDER_API auto GetNearPlane(core::ThreadType thr) const -> float { return nearPlane[thr]; } - SH_RENDER_API auto GetFarPlane(core::ThreadType thr) const -> float { return farPlane[thr]; } - SH_RENDER_API auto GetWidth(core::ThreadType thr) const -> float { return width[thr]; } - SH_RENDER_API auto GetHeight(core::ThreadType thr) const -> float { return height[thr]; } - - SH_RENDER_API auto IsOrtho() const -> bool { return !bPerspective; } - protected: - SH_RENDER_API void SyncDirty() override; - SH_RENDER_API void Sync() override; - /// @brief dirtyMask |= 256; 후 SyncDirty호출 필요 - SH_RENDER_API virtual void UpdateProjMatrix(); - /// @brief dirtyMask |= 256; 후 SyncDirty호출 필요 - SH_RENDER_API virtual void UpdateViewMatrix(); - public: - struct alignas(16) BufferData - { - glm::mat4 matView; - glm::mat4 matProj; - }; - const uint32_t id; - protected: - core::SyncArray pos; - core::SyncArray to; - core::SyncArray up; - - core::SyncArray fovRadians = { 0.0f, 0.f }; - core::SyncArray nearPlane = { 0.1f, 0.1f }; - core::SyncArray farPlane = { 1000.f, 1000.f }; - core::SyncArray width = { 1.f, 1.f }; - core::SyncArray height = { 1.f, 1.f }; - core::SyncArray bufferData; - - uint32_t dirtyMask = 0; - private: - SH_RENDER_API static uint32_t nextId; - RenderTexture* renderTexture; - - int priority; - uint32_t renderTagMask = 1; - - bool bActive = true; - bool bDirty = false; - bool bPerspective = true; - }; -} //namespace \ No newline at end of file diff --git a/include/Render/CommandBuffer.h b/include/Render/CommandBuffer.h index 039e12cc..3a5d0497 100644 --- a/include/Render/CommandBuffer.h +++ b/include/Render/CommandBuffer.h @@ -1,20 +1,46 @@ #pragma once - #include "Core/NonCopyable.h" +#include "Core/Name.h" + +#include "Render/IRenderThrMethod.h" #include namespace sh::render { + class ComputeShader; class RenderTexture; class IBuffer; + class Material; + class Drawable; + class ShaderPass; + class RenderData; + struct BarrierInfo; class CommandBuffer : public core::INonCopyable { + friend struct IRenderThrMethod; public: virtual ~CommandBuffer() = default; virtual void Begin(bool bOnetime) = 0; - virtual void Blit(RenderTexture& src, int x, int y, IBuffer& dst) = 0; virtual void End() = 0; + virtual void Reset() = 0; + + virtual void Blit(RenderTexture& src, int x, int y, IBuffer& dst) = 0; + virtual void Dispatch(const ComputeShader& shader, uint32_t x, uint32_t y, uint32_t z) = 0; + virtual void SetRenderData(const RenderData& renderData, bool bClearColor = true, bool bClearDepth = true, bool bStoreColor = false, bool bStoreDepth = false) = 0; + virtual void SetViewport(uint32_t x, uint32_t y, uint32_t width, uint32_t height) = 0; + virtual void SetScissor(uint32_t x, uint32_t y, uint32_t width, uint32_t height) = 0; + virtual void DrawMeshBatch(const std::vector& drawables, core::Name passName, std::size_t viewerIdx = 0) = 0; + virtual void DrawMesh(const Drawable& drawable, core::Name passName, std::size_t viewerIdx = 0) = 0; + virtual void EmitBarrier(const std::vector& barriers) = 0; + protected: + virtual auto GetRenderCall() const -> uint32_t = 0; + }; + + template<> + struct IRenderThrMethod + { + static auto GetRenderCall(const CommandBuffer& cmd) -> uint32_t { return cmd.GetRenderCall(); } }; }//namespace \ No newline at end of file diff --git a/include/Render/ComputeShader.h b/include/Render/ComputeShader.h new file mode 100644 index 00000000..727a9cad --- /dev/null +++ b/include/Render/ComputeShader.h @@ -0,0 +1,56 @@ +#pragma once +#include "Export.h" +#include "ShaderAST.h" +#include "IBuffer.h" +#include "IShaderBinding.h" + +#include "Core/SObject.h" +#include "Core/Name.h" +#include "Core/Reflection.hpp" + +#include +#include +#include +namespace sh::render +{ + struct ComputeShaderCreateInfo; + + class IRenderContext; + class ComputeShader : public core::SObject + { + SCLASS(ComputeShader) + public: + SH_RENDER_API ComputeShader(const IRenderContext& ctx, ComputeShaderCreateInfo createInfo); + SH_RENDER_API ~ComputeShader(); + + /// @brief AST와 SPIR-V 바이너리를 직렬화 한다. + /// @return 직렬화 된 json + SH_RENDER_API auto Serialize() const -> core::Json override; + /// @brief AST와 SPIR-V 바이너리를 역직렬화 한다. + /// @param json 직렬화 된 json + SH_RENDER_API void Deserialize(const core::Json& json) override; + + SH_RENDER_API void SetFloats(const std::string& name, float* values, std::size_t count); + + SH_RENDER_API auto GetSpirv() const -> const std::vector& { return spirv; } + SH_RENDER_API auto GetShaderAST() const -> const ShaderAST::ComputeShaderNode& { return shaderNode; } + + SH_RENDER_API auto GetNumthreadsX() const -> uint32_t { return shaderNode.numthreadsX; } + SH_RENDER_API auto GetNumthreadsY() const -> uint32_t { return shaderNode.numthreadsY; } + SH_RENDER_API auto GetNumthreadsZ() const -> uint32_t { return shaderNode.numthreadsZ; } + SH_RENDER_API auto GetNode() const -> const ShaderAST::ComputeShaderNode& { return shaderNode; } + SH_RENDER_API auto GetBuffers() const -> const std::vector>& { return buffers; } + SH_RENDER_API auto GetShaderBinding() const -> IShaderBinding* { return shaderBinding.get(); } + SH_RENDER_API auto GetBuffer(const std::string& name) -> IBuffer*; + private: + auto GetBinding(const std::string& name) -> std::optional; + private: + const IRenderContext& ctx; + + ShaderAST::ComputeShaderNode shaderNode; + std::vector spirv; + + std::vector> buffers; // binding, buffer + std::unique_ptr shaderBinding; + }; +}//namespace diff --git a/include/Render/ComputeShaderCreateInfo.h b/include/Render/ComputeShaderCreateInfo.h new file mode 100644 index 00000000..bfeac71f --- /dev/null +++ b/include/Render/ComputeShaderCreateInfo.h @@ -0,0 +1,12 @@ +#pragma once +#include "ShaderAST.h" + +#include +namespace sh::render +{ + struct ComputeShaderCreateInfo + { + ShaderAST::ComputeShaderNode shaderNode; + std::vector spirv; + }; +}//namespace diff --git a/include/Render/ComputeShaderLexer.h b/include/Render/ComputeShaderLexer.h new file mode 100644 index 00000000..3ae2d5cd --- /dev/null +++ b/include/Render/ComputeShaderLexer.h @@ -0,0 +1,51 @@ +#pragma once +#include "Export.h" + +#include +#include +#include +namespace sh::render +{ + class ComputeShaderLexer + { + public: + enum class TokenType + { + Preprocessor, + Numthreads, + RBuffer, WBuffer, RWBuffer, + Const, + LBracket, // ( + RBracket, // ) + LBrace, // { + RBrace, // } + LSquareBracket, // [ + RSquareBracket, // ] + Comma, // , + String, Identifier, Literal, Number, Operator, Semicolon, + EndOfFile, + Unknown + }; + struct Token + { + TokenType type; + std::string text; + int line; + int column; + }; + public: + SH_RENDER_API static auto Lex(const std::string& source) -> std::vector; + private: + static auto IsIdentifierChar(char c) -> bool; + }; + + class ComputeShaderLexerException : public std::exception + { + public: + ComputeShaderLexerException(const std::string& err) : err(err) {} + + auto what() const noexcept -> const char* override { return err.c_str(); } + private: + std::string err; + }; +}//namespace diff --git a/include/Render/ComputeShaderParser.h b/include/Render/ComputeShaderParser.h new file mode 100644 index 00000000..0b608a36 --- /dev/null +++ b/include/Render/ComputeShaderParser.h @@ -0,0 +1,56 @@ +#pragma once +#include "Export.h" +#include "ShaderAST.h" +#include "ComputeShaderLexer.h" + +#include +#include +#include +#include + +namespace sh::render +{ + class ComputeShaderParser + { + public: + SH_RENDER_API auto Parse(const std::vector& tokens) -> ShaderAST::ComputeShaderNode; + protected: + /// @brief 현재 토큰을 반환하는 함수. + auto PeekToken() const -> const ComputeShaderLexer::Token&; + /// @brief 현재 토큰 타입 검사 함수. + auto CheckToken(ComputeShaderLexer::TokenType token) const -> bool; + /// @brief 다음 토큰으로 이동하는 함수. + auto NextToken() -> const ComputeShaderLexer::Token&; + /// @brief 직전에 소모된 토큰을 반환하는 함수. + auto PreviousToken() -> const ComputeShaderLexer::Token&; + /// @brief 해당 토큰이 존재하면 다음 토큰으로 이동하며 없다면 예외를 던진다. + void ConsumeToken(ComputeShaderLexer::TokenType token); + /// @brief 해당 토큰들 중 하나라도 존재하면 다음 토큰으로 이동하며 없다면 예외를 던진다. + void ConsumeToken(const std::initializer_list& tokens); + + auto GetCurrentTokenPosString() const -> std::string; + auto GetTokenErrorString(const std::string& msg) const -> std::string; + + auto ParseComputeShader() -> ShaderAST::ComputeShaderNode; + void ParsePreprocessor(ShaderAST::ComputeShaderNode& node); + void ParseNumthreads(ShaderAST::ComputeShaderNode& node); + void ParseBuffer(ShaderAST::ComputeShaderNode& node, ShaderAST::BufferAccess access); + void ParseDeclaration(ShaderAST::ComputeShaderNode& node, const std::string& qualifier = ""); + auto ParseFunctionBody() -> std::string; + + static auto IdentifierToVariableType(const ComputeShaderLexer::Token& token) -> ShaderAST::VariableType; + static auto VariableTypeToString(ShaderAST::VariableType type) -> std::string; + static void GenerateCode(ShaderAST::ComputeShaderNode& node); + private: + const std::vector* tokens = nullptr; + std::size_t pos = 0; + uint32_t lastBinding = 0; + }; + + class ComputeShaderParserException : public std::runtime_error + { + public: + ComputeShaderParserException(const char* err) : std::runtime_error{ err } + {} + }; +}//namespace diff --git a/include/Render/Drawable.h b/include/Render/Drawable.h index 85b6de97..72d5374d 100644 --- a/include/Render/Drawable.h +++ b/include/Render/Drawable.h @@ -34,7 +34,7 @@ namespace sh::render SH_RENDER_API void SetRenderTagId(uint32_t tagId); SH_RENDER_API void SetTopology(Mesh::Topology topology); SH_RENDER_API void SetPriority(int priority); - SH_RENDER_API void SetSubMeshIndex(int idx); + SH_RENDER_API void SetSubMeshIndex(uint32_t idx); SH_RENDER_API auto CheckAssetValid() const -> bool; @@ -46,7 +46,8 @@ namespace sh::render SH_RENDER_API auto GetRenderTagId() const -> uint32_t { return renderTag; } SH_RENDER_API auto GetTopology(core::ThreadType thr = core::ThreadType::Game) const -> Mesh::Topology { return topology[thr]; } SH_RENDER_API auto GetPriority(core::ThreadType thr = core::ThreadType::Game) const -> int { return priority[thr]; } - SH_RENDER_API auto GetSubMeshIndex() const -> int { return subMeshIndex; } + SH_RENDER_API auto GetSubMeshIndex() const -> uint32_t { return subMeshIndex; } + SH_RENDER_API auto IsSkinnedMesh() const -> bool { return bSkinned; } protected: SH_RENDER_API void SyncDirty() override; SH_RENDER_API void Sync() override; @@ -71,8 +72,9 @@ namespace sh::render }; std::array syncDatas; - int subMeshIndex = -1; + uint32_t subMeshIndex = 0; + bool bSkinned = false; bool bDirty = false; bool bMatrixDirty = false; }; diff --git a/include/Render/Formats.hpp b/include/Render/Formats.hpp index 8be04fb1..ea1b82c8 100644 --- a/include/Render/Formats.hpp +++ b/include/Render/Formats.hpp @@ -1,5 +1,8 @@ #pragma once +#include +#include + namespace sh::render { enum class TextureFormat @@ -10,22 +13,73 @@ namespace sh::render SBGR24, SBGRA32, RGB24, + RG32F, RGBA32, BGR24, BGRA32, R8, D32S8, D24S8, - D16S8 + D16S8, + D32, + D16 }; - static auto GetTextureFormatChannel(TextureFormat format) -> int + inline static auto GetTextureFormatChannel(TextureFormat format) -> int { switch (format) { + case TextureFormat::RG32F: + return 2; case TextureFormat::R8: return 1; } return 4; } -}//namespace \ No newline at end of file + + inline static auto GetTextureFormatPixelSize(TextureFormat format) -> std::size_t + { + switch (format) + { + case TextureFormat::RG32F: + return sizeof(uint16_t) * 2; + case TextureFormat::R8: + return 1; + default: + return 4; + } + } + + inline static auto IsDepthTexture(TextureFormat format) -> bool + { + if (format == TextureFormat::D32S8 || + format == TextureFormat::D24S8 || + format == TextureFormat::D16S8 || + format == TextureFormat::D32 || + format == TextureFormat::D16) + return true; + return false; + } + + inline static auto TextureFormatToString(TextureFormat format) -> const char* + { + switch (format) + { + case TextureFormat::None: return "None"; + case TextureFormat::SRGB24: return "SRGB24"; + case TextureFormat::SRGBA32: return "SRGBA32"; + case TextureFormat::SBGR24: return "SBGR24"; + case TextureFormat::SBGRA32: return "SBGRA32"; + case TextureFormat::RGB24: return "RGB24"; + case TextureFormat::RG32F: return "RG32F"; + case TextureFormat::RGBA32: return "RGBA32"; + case TextureFormat::R8: return "R8"; + case TextureFormat::D32S8: return "D32S8"; + case TextureFormat::D24S8: return "D24S8"; + case TextureFormat::D16S8: return "D16S8"; + case TextureFormat::D32: return "D32"; + case TextureFormat::D16: return "D16"; + default: return "None"; + } + } +}//namespace diff --git a/include/Render/IBuffer.h b/include/Render/IBuffer.h index 36ff0847..f4626993 100644 --- a/include/Render/IBuffer.h +++ b/include/Render/IBuffer.h @@ -1,5 +1,6 @@ #pragma once +#include namespace sh::render { class IBuffer @@ -8,7 +9,9 @@ namespace sh::render virtual ~IBuffer() = default; virtual void SetData(const void* data) = 0; - virtual auto GetData() const -> void* = 0; + virtual void SetData(const void* data, std::size_t offset, std::size_t size) = 0; + virtual auto GetData() const -> std::vector = 0; virtual auto GetSize() const -> size_t = 0; + virtual auto Resize(std::size_t size) -> bool = 0; }; }//namespace \ No newline at end of file diff --git a/include/Render/IRenderContext.h b/include/Render/IRenderContext.h index ae294e41..662ddfca 100644 --- a/include/Render/IRenderContext.h +++ b/include/Render/IRenderContext.h @@ -1,7 +1,6 @@ #pragma once #include "Export.h" #include "CommandBuffer.h" -#include "IRenderImpl.h" #include "Core/NonCopyable.h" @@ -9,6 +8,8 @@ namespace sh::render { + class RenderDataManager; + enum class RenderAPI { OpenGL, @@ -23,12 +24,15 @@ namespace sh::render virtual void Clear() = 0; virtual auto GetRenderAPIType() const -> RenderAPI = 0; - virtual auto AllocateCommandBuffer() -> CommandBuffer* = 0; + virtual auto AllocateCommandBuffer(bool bCompute) -> CommandBuffer* = 0; virtual void DeallocateCommandBuffer(CommandBuffer& cmd) = 0; + virtual void SubmitCommand(CommandBuffer& cmd) = 0; virtual void SetViewport(const glm::vec2& start, const glm::vec2& end) = 0; virtual auto GetViewportStart() const -> const glm::vec2& = 0; virtual auto GetViewportEnd() const -> const glm::vec2& = 0; - virtual auto GetRenderImpl() const -> IRenderImpl& = 0; + + virtual auto GetRenderDataManager() const -> const RenderDataManager& = 0; + virtual auto GetRenderDataManager() -> RenderDataManager& = 0; }; }//namespace \ No newline at end of file diff --git a/include/Render/IRenderImpl.h b/include/Render/IRenderImpl.h deleted file mode 100644 index 5d06543c..00000000 --- a/include/Render/IRenderImpl.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once -#include "RenderData.h" -#include "CommandBuffer.h" -#include "Core/Name.h" - -namespace sh::render -{ - class IRenderImpl - { - public: - virtual ~IRenderImpl() = default; - - virtual void EmitBarrier(CommandBuffer& cmd, const std::vector& barriers) const = 0; - virtual void RecordCommand(CommandBuffer& cmd, const core::Name& passName, const RenderTarget& renderData, const DrawList& drawList, bool bStoreImage) const = 0; - }; -}//namespace \ No newline at end of file diff --git a/include/Render/IUniformBuffer.h b/include/Render/IShaderBinding.h similarity index 63% rename from include/Render/IUniformBuffer.h rename to include/Render/IShaderBinding.h index c34af835..46f17cd8 100644 --- a/include/Render/IUniformBuffer.h +++ b/include/Render/IShaderBinding.h @@ -9,17 +9,19 @@ namespace sh::render { class IRenderContext; class ShaderPass; + class ComputeShader; class Texture; class IBuffer; - class IUniformBuffer : public core::INonCopyable + class IShaderBinding : public core::INonCopyable { public: - SH_RENDER_API virtual ~IUniformBuffer() = default; + SH_RENDER_API virtual ~IShaderBinding() = default; - SH_RENDER_API virtual void Create(const IRenderContext& context, const ShaderPass& shader, UniformStructLayout::Type type) = 0; + SH_RENDER_API virtual void Create(const IRenderContext& context, const ShaderPass& shader, UniformStructLayout::Usage usage) = 0; + SH_RENDER_API virtual void Create(const IRenderContext& context, const ComputeShader& shader) = 0; SH_RENDER_API virtual void Clear() = 0; SH_RENDER_API virtual void Link(uint32_t binding, const IBuffer& buffer, std::size_t bufferSize = 0) = 0; SH_RENDER_API virtual void Link(uint32_t binding, const Texture& texture) = 0; }; -}//namespace \ No newline at end of file +}//namespace diff --git a/include/Render/Material.h b/include/Render/Material.h index 4da6483f..7b3bfc05 100644 --- a/include/Render/Material.h +++ b/include/Render/Material.h @@ -25,6 +25,8 @@ #include #include #include +#include +#include namespace sh::render { @@ -53,6 +55,12 @@ namespace sh::render template void SetProperty(const std::string& name, const T& data); + template + void SetProperty(const std::string& name, const std::vector& data); + template + void SetProperty(const std::string& name, const std::array& data); + template + void SetPropertyArray(const std::string& name, const T* data, std::size_t count); SH_RENDER_API void SetProperty(const std::string& name, const glm::vec4& data); SH_RENDER_API void SetProperty(const std::string& name, const Texture* data); SH_RENDER_API void SetProperty(const std::string& name, Texture* data); @@ -70,8 +78,6 @@ namespace sh::render SH_RENDER_API auto Serialize() const -> core::Json override; SH_RENDER_API void Deserialize(const core::Json& json) override; - - SH_RENDER_API auto GetCachedRenderTextures() const -> const core::SHashMap& { return cachedRts; } private: void Clear(); void SetDefaultProperties(); @@ -100,7 +106,6 @@ namespace sh::render std::vector> dirtyProps; std::vector> cachedConstantData; - core::SHashMap cachedRts; core::Observer::Listener onBufferUpdateListener; @@ -124,7 +129,7 @@ namespace sh::render propertyBlock.SetProperty(name, data); - for (auto& location : propInfo->locations) + for (const Shader::PropertyInfo::Location& location : propInfo->locations) { auto it = std::find(dirtyProps.begin(), dirtyProps.end(), std::pair{ location.passPtr.Get(), location.layoutPtr}); if (it == dirtyProps.end()) @@ -134,6 +139,46 @@ namespace sh::render bPropertyDirty = true; } template + inline void Material::SetProperty(const std::string& name, const std::vector& data) + { + SetPropertyArray(name, data.data(), data.size()); + } + template + inline void Material::SetProperty(const std::string& name, const std::array& data) + { + SetPropertyArray(name, data.data(), data.size()); + } + template + inline void Material::SetPropertyArray(const std::string& name, const T* data, std::size_t count) + { + if (!core::IsValid(shader)) + return; + + const Shader::PropertyInfo* propInfo = shader->GetProperty(name); + assert(propInfo != nullptr); + if (propInfo == nullptr) + return; + + assert(propInfo->type == core::reflection::GetType()); + if (propInfo->type != core::reflection::GetType()) + return; + + assert(propInfo->locations.empty() || propInfo->locations.front().memberPtr->IsArray()); + if (!propInfo->locations.empty() && !propInfo->locations.front().memberPtr->IsArray()) + return; + + propertyBlock.SetArrayProperty(name, data, count); + + for (const Shader::PropertyInfo::Location& location : propInfo->locations) + { + auto it = std::find(dirtyProps.begin(), dirtyProps.end(), std::pair{ location.passPtr.Get(), location.layoutPtr }); + if (it == dirtyProps.end()) + dirtyProps.push_back({ location.passPtr.Get(), location.layoutPtr }); + } + + bPropertyDirty = true; + } + template inline void Material::SetConstant(const std::string& name, const T& value) { if (shader == nullptr) @@ -227,4 +272,4 @@ namespace sh::render static_assert(true); return {}; } -} \ No newline at end of file +} diff --git a/include/Render/MaterialData.h b/include/Render/MaterialData.h index 6082f3c7..5142cf34 100644 --- a/include/Render/MaterialData.h +++ b/include/Render/MaterialData.h @@ -1,8 +1,9 @@ #pragma once #include "Export.h" #include "IBuffer.h" -#include "IUniformBuffer.h" +#include "IShaderBinding.h" #include "ShaderEnum.h" +#include "IRenderThrMethod.h" #include "Core/ISyncable.h" #include "Core/SContainer.hpp" @@ -18,6 +19,7 @@ namespace sh::render class Shader; class ShaderPass; class IRenderContext; + class RenderTexture; /// @brief 셰이더에 전달 할 데이터를 가지고 있는 클래스 /// @brief 모든 변경 사항은 게임 스레드에서만 작성 시 스레드 안전하다. @@ -25,12 +27,13 @@ namespace sh::render public core::ISyncable, public core::INonCopyable { + friend struct IRenderThrMethod; private: struct PassData { const ShaderPass* pass; - std::map>> uniformData; //set, binding - std::map> uniformBuffer; // set + std::map>> buffers; //set, binding + std::map> shaderBindings; // set }; struct SyncData { @@ -41,14 +44,14 @@ namespace sh::render uint32_t binding; std::vector data; }; - struct UniformBufferSyncData + struct ShaderBindingSyncData { const ShaderPass* pass; uint32_t set; uint32_t binding; const Texture* tex = nullptr; }; - std::variant data; + std::variant data; }; public: SH_RENDER_API MaterialData() = default; @@ -56,39 +59,43 @@ namespace sh::render SH_RENDER_API void Create(const IRenderContext& context, const Shader& shader, bool bPerObject = false); SH_RENDER_API void Clear(); - /// @brief 유니폼에 데이터를 지정한다. + /// @brief 버퍼에 데이터를 지정한다. /// @param pass 패스 번호 /// @param binding 바인딩 번호 /// @param data 데이터 위치 포인터 /// @param dataSize 데이터 사이즈 - SH_RENDER_API void SetUniformData(const ShaderPass& shaderPass, UniformStructLayout::Type type, uint32_t binding, const void* data, std::size_t dataSize); - /// @brief 유니폼에 데이터를 지정한다. + SH_RENDER_API void SetBindingData(const ShaderPass& shaderPass, UniformStructLayout::Usage usage, uint32_t binding, const void* data, std::size_t dataSize); + /// @brief 버퍼에 데이터를 지정한다. /// @param pass 패스 번호 /// @param binding 바인딩 번호 /// @param data 데이터 - SH_RENDER_API void SetUniformData(const ShaderPass& shaderPass, UniformStructLayout::Type type, uint32_t binding, const std::vector& data); - /// @brief 유니폼에 데이터를 지정한다. - /// @param pass 패스 번호 - /// @param binding 바인딩 번호 - /// @param data 데이터 - SH_RENDER_API void SetUniformData(const ShaderPass& shaderPass, UniformStructLayout::Type type, uint32_t binding, std::vector&& data); + SH_RENDER_API void SetBindingData(const ShaderPass& shaderPass, UniformStructLayout::Usage usage, uint32_t binding, std::vector data); /// @brief 유니폼 텍스쳐 데이터를 지정한다. /// /// @param pass 패스 번호 /// @param binding 텍스쳐 바인딩 번호 /// @param tex 텍스쳐 - SH_RENDER_API void SetTextureData(const ShaderPass& shaderPass, UniformStructLayout::Type type, uint32_t binding, const Texture& tex); + SH_RENDER_API void SetTextureData(const ShaderPass& shaderPass, UniformStructLayout::Usage usage, uint32_t binding, const Texture& tex); - SH_RENDER_API auto GetShaderBuffer(const ShaderPass& shaderPass, UniformStructLayout::Type type, uint32_t binding) const -> IBuffer*; - SH_RENDER_API auto GetUniformBuffer(const ShaderPass& shaderPass, UniformStructLayout::Type type) const -> IUniformBuffer*; + SH_RENDER_API auto GetShaderBuffer(const ShaderPass& shaderPass, UniformStructLayout::Usage usage, uint32_t binding) const -> IBuffer*; + SH_RENDER_API auto GetShaderBinding(const ShaderPass& shaderPass, UniformStructLayout::Usage usage) const -> IShaderBinding*; SH_RENDER_API void SyncDirty() override; + public: + struct CachedRT + { + uint32_t set = 0; + uint32_t binding = 0; + core::SObjWeakPtr pass; + core::SObjWeakPtr rt; + }; protected: SH_RENDER_API void Sync() override; + auto GetCachedRTs() const -> const std::vector& { return cachedRTs; } private: void CreateBuffers(const IRenderContext& context, const Shader& shader, bool bPerObject); auto GetMaterialPassData(const ShaderPass& shaderPass) const -> const MaterialData::PassData*; void SetUniformDataAtSync(const SyncData::BufferSyncData& bufferSyncData); - void SetTextureDataAtSync(const SyncData::UniformBufferSyncData& uniformBufferSyncData); + void SetTextureDataAtSync(const SyncData::ShaderBindingSyncData& uniformBufferSyncData); private: const IRenderContext* context = nullptr; const Shader* shader = nullptr; @@ -97,9 +104,17 @@ namespace sh::render std::vector syncDatas; + std::vector cachedRTs; + bool bDirty = false; bool bClearDirty = false; bool bCreateDirty = false; bool bPerObject = false; }; + + template<> + struct IRenderThrMethod + { + static auto GetCachedRTs(const MaterialData& materialData) -> const std::vector& { return materialData.GetCachedRTs(); } + }; }//namespace \ No newline at end of file diff --git a/include/Render/MaterialPropertyBlock.h b/include/Render/MaterialPropertyBlock.h index 4d23b179..eadc1e3f 100644 --- a/include/Render/MaterialPropertyBlock.h +++ b/include/Render/MaterialPropertyBlock.h @@ -6,10 +6,17 @@ #include "Core/SContainer.hpp" #include "glm/mat4x4.hpp" +#include "glm/vec2.hpp" +#include "glm/vec3.hpp" +#include "glm/vec4.hpp" #include #include #include +#include +#include +#include +#include namespace sh::render { @@ -21,12 +28,18 @@ namespace sh::render template>> void SetProperty(const std::string& name, const T& data); + template + void SetArrayProperty(const std::string& name, const T* data, std::size_t count); SH_RENDER_API void SetProperty(const std::string& name, const Texture* data); SH_RENDER_API auto GetScalarProperty(const std::string& name) const -> std::optional; SH_RENDER_API auto GetVectorProperty(const std::string& name) const -> const glm::vec4*; SH_RENDER_API auto GetMatrixProperty(const std::string& name) const -> const glm::mat4*; SH_RENDER_API auto GetTextureProperty(const std::string& name) const -> const Texture*; + SH_RENDER_API auto GetIntArrayProperty(const std::string& name) const -> const std::vector*; + SH_RENDER_API auto GetScalarArrayProperty(const std::string& name) const -> const std::vector*; + SH_RENDER_API auto GetVectorArrayProperty(const std::string& name) const -> const std::vector*; + SH_RENDER_API auto GetMatrixArrayProperty(const std::string& name) const -> const std::vector*; SH_RENDER_API auto GetTextureProperties() const -> const std::unordered_map&; SH_RENDER_API auto Serialize() const -> core::Json override; @@ -35,6 +48,10 @@ namespace sh::render std::unordered_map scalarProperties; std::unordered_map vecProperties; std::unordered_map matProperties; + std::unordered_map> intArrayProperties; + std::unordered_map> scalarArrayProperties; + std::unordered_map> vecArrayProperties; + std::unordered_map> matArrayProperties; core::SHashMap textureProperties; }; @@ -58,4 +75,69 @@ namespace sh::render else static_assert(true); } -}//namespace \ No newline at end of file + template + inline void MaterialPropertyBlock::SetArrayProperty(const std::string& name, const T* data, std::size_t count) + { + if constexpr (std::is_arithmetic_v) + { + if constexpr (std::is_integral_v) + { + std::vector values(count); + for (std::size_t i = 0; i < count; ++i) + values[i] = static_cast(data[i]); + intArrayProperties.insert_or_assign(name, std::move(values)); + } + else + { + std::vector values(count); + for (std::size_t i = 0; i < count; ++i) + values[i] = static_cast(data[i]); + scalarArrayProperties.insert_or_assign(name, std::move(values)); + } + } + else if constexpr (std::is_same_v) + { + std::vector values(count); + for (std::size_t i = 0; i < count; ++i) + values[i] = glm::vec4{ data[i], 0.0f, 0.0f }; + vecArrayProperties.insert_or_assign(name, std::move(values)); + } + else if constexpr (std::is_same_v) + { + std::vector values(count); + for (std::size_t i = 0; i < count; ++i) + values[i] = glm::vec4{ data[i], 0.0f }; + vecArrayProperties.insert_or_assign(name, std::move(values)); + } + else if constexpr (std::is_same_v) + { + if (count == 0) + vecArrayProperties.insert_or_assign(name, std::vector{}); + else + vecArrayProperties.insert_or_assign(name, std::vector{ data, data + count }); + } + else if constexpr (std::is_same_v) + { + std::vector values(count); + for (std::size_t i = 0; i < count; ++i) + values[i] = core::Util::ConvertMat2ToMat4(data[i]); + matArrayProperties.insert_or_assign(name, std::move(values)); + } + else if constexpr (std::is_same_v) + { + std::vector values(count); + for (std::size_t i = 0; i < count; ++i) + values[i] = core::Util::ConvertMat3ToMat4(data[i]); + matArrayProperties.insert_or_assign(name, std::move(values)); + } + else if constexpr (std::is_same_v) + { + if (count == 0) + matArrayProperties.insert_or_assign(name, std::vector{}); + else + matArrayProperties.insert_or_assign(name, std::vector{ data, data + count }); + } + else + static_assert(true); + } +}//namespace diff --git a/include/Render/RenderData.h b/include/Render/RenderData.h index f7ebc89a..786f6d50 100644 --- a/include/Render/RenderData.h +++ b/include/Render/RenderData.h @@ -1,61 +1,148 @@ #pragma once #include "Mesh.h" -#include "CommandBuffer.h" +#include "Texture.h" +#include "IRenderThrMethod.h" +#include "Formats.hpp" #include "Core/SContainer.hpp" +#include "Core/Name.h" #include #include +#include #include +#include + +namespace sh +{ + template + using Ref = std::reference_wrapper; +} namespace sh::render { class Camera; class RenderTexture; class Drawable; class Material; + class CommandBuffer; + + struct RenderViewer + { + glm::mat4 viewMatrix; + glm::mat4 projMatrix; + glm::vec3 pos; + glm::vec3 to; + + glm::uvec4 viewportRect; + glm::uvec4 viewportScissor; + std::size_t offset = 0; + }; + + class RenderData + { + friend struct IRenderThrMethod; + public: + void SetRenderTarget(const RenderTexture* renderTarget) { targets.clear(); targets.push_back(renderTarget); } + void SetRenderTargets(std::initializer_list renderTargets) + { + if (renderTargets.size() == 0) + { + targets.clear(); + targets.push_back(nullptr); + } + else + targets.assign(renderTargets.begin(), renderTargets.end()); + } + void SetRenderTargets(std::vector renderTargets) + { + if (renderTargets.size() == 0) + { + targets.clear(); + targets.push_back(nullptr); + } + else + targets = std::move(renderTargets); + } + void ClearRenderTargets() { targets.clear(); targets.push_back(nullptr); } + auto GetRenderTarget(std::size_t idx) const -> const RenderTexture* { return idx < targets.size() ? targets[idx] : nullptr; } + auto GetRenderTargets() const -> const std::vector& { return targets; } + auto GetFrameIdx() const -> uint32_t { return frameIndex; } + auto GetDrawablesPtr() const -> const std::vector* { return drawables; } + public: + int priority = 0; + core::Name tag{ "Camera" }; + std::vector renderViewers; + private: + uint32_t frameIndex = 0; + std::vector targets{ nullptr }; + const std::vector* drawables = nullptr; + }; - struct RenderTarget + template<> + struct IRenderThrMethod { - uint32_t frameIndex; - const Camera* camera; - const RenderTexture* target; - const std::vector* drawables; + inline static void SetFrameIndex(RenderData& rd, uint32_t frameIdx) { rd.frameIndex = frameIdx; } + inline static void SetDrawablesPtr(RenderData& rd, const std::vector* ptr) { rd.drawables = ptr; } }; - struct DrawList + class RenderTargetLayout { - struct RenderGroup + public: + auto operator==(const RenderTargetLayout& other) const -> bool { - const Material* material; - Mesh::Topology topology; - std::vector drawables; - }; - struct RenderItem + return colorFormats == other.colorFormats && depthFormat == other.depthFormat && bUseMSAA == other.bUseMSAA; + } + auto IsDepthOnly() const -> bool { - const Material* material; - Mesh::Topology topology; - Drawable* drawable; - }; - std::variant, std::vector> renderData; - std::vector> drawCall; - - bool bClearColor = true; - bool bClearDepth = true; + for (TextureFormat format : colorFormats) + { + if (format != TextureFormat::None) + return false; + } + return depthFormat != TextureFormat::None; + } + public: + std::vector colorFormats{ TextureFormat::None }; + TextureFormat depthFormat = TextureFormat::None; + + bool bUseMSAA = false; }; - enum class ImageUsage + /// @brief 리소스(이미지/버퍼) 사용 의도. 배리어 계산의 입력으로 쓰임 + enum class ResourceUsage { Undefined, ColorAttachment, SampledRead, Present, - Src + TransferSrc, + DepthStencilAttachment, + DepthStencilSampledRead }; struct BarrierInfo { std::variant target; // uint32_t = 스왑체인 - ImageUsage lastUsage; - ImageUsage curUsage; + ResourceUsage lastUsage; + ResourceUsage curUsage; + }; +}//namespace + +namespace std +{ + template<> + struct hash + { + auto operator()(const sh::render::RenderTargetLayout& layout) const -> std::size_t + { + std::hash intHasher{}; + std::hash boolHasher{}; + + std::size_t hash = intHasher(static_cast(layout.depthFormat)); + hash = sh::core::Util::CombineHash(hash, boolHasher(layout.bUseMSAA)); + for (std::size_t i = 0; i < layout.colorFormats.size(); ++i) + hash = sh::core::Util::CombineHash(hash, intHasher(static_cast(layout.colorFormats[i]))); + return hash; + } }; -}//namespace \ No newline at end of file +}//namespace std diff --git a/include/Render/RenderDataManager.h b/include/Render/RenderDataManager.h new file mode 100644 index 00000000..26e1514a --- /dev/null +++ b/include/Render/RenderDataManager.h @@ -0,0 +1,64 @@ +#pragma once +#include "Export.h" +#include "RenderData.h" +#include "IBuffer.h" +#include "IRenderThrMethod.h" + +#include "Core/LockFreeMPSCQueue.h" +#include "Core/ArrayView.hpp" + +#include "glm/vec4.hpp" + +#include +#include +#include +#include +namespace sh::render +{ + class IRenderContext; + + /// @brief 카메라와 같은 렌더뷰어들을 관리하는 매니저. 스레드 안전하다. + class RenderDataManager + { + public: + SH_RENDER_API RenderDataManager(); + SH_RENDER_API ~RenderDataManager(); + + SH_RENDER_API void Init(const IRenderContext& ctx); + + SH_RENDER_API void PushRenderData(const RenderData& renderData); + + SH_RENDER_API auto GetBuffer() const -> const IBuffer* { return buffer.get(); } + protected: + SH_RENDER_API void ClearBuffer(); + SH_RENDER_API void ClearRenderViews(); + SH_RENDER_API void UploadToGPU(); + SH_RENDER_API auto GetRenderDatas() -> core::ArrayView; + public: + struct BufferData + { + glm::mat4 view; + glm::mat4 proj; + glm::vec4 pos; + }; + private: + friend struct IRenderThrMethod; + const IRenderContext* ctx = nullptr; + + core::LockFreeMPSCQueue> renderDataQueue; + std::vector renderDatas; + + std::unique_ptr buffer; + std::size_t alignment = 256; + std::size_t renderDatasSize = 0; + }; + + template<> + struct IRenderThrMethod + { + static void ClearBuffer(RenderDataManager& manager) { manager.ClearBuffer(); } + static void ClearRenderViews(RenderDataManager& manager) { manager.ClearRenderViews(); } + static void UploadToGPU(RenderDataManager& manager) { manager.UploadToGPU(); } + static auto GetRenderDatas(RenderDataManager& manager) -> core::ArrayView { return manager.GetRenderDatas(); } + }; +}//namespace diff --git a/include/Render/RenderPass/CombinePass.h b/include/Render/RenderPass/CombinePass.h new file mode 100644 index 00000000..ab6877dd --- /dev/null +++ b/include/Render/RenderPass/CombinePass.h @@ -0,0 +1,43 @@ +#pragma once +#include "Render/Export.h" +#include "Render/ScriptableRenderPass.h" +#include "Render/RenderData.h" + +#include "Core/Observer.hpp" +#include "Core/GCObject.h" + +namespace sh::render +{ + class Mesh; + class Drawable; + class Material; + + /// @brief aoTex 를 현재 렌더 타겟 위에 덮어쓰는 풀스크린 패스. + /// @brief "Combine" 태그가 RenderData 에 있을 때만 동작. + class CombinePass : public ScriptableRenderPass + { + public: + SH_RENDER_API CombinePass(const IRenderContext& ctx); + SH_RENDER_API ~CombinePass(); + + SH_RENDER_API void SetMaterial(Material& mat); + protected: + SH_RENDER_API void Configure(const RenderData& renderData) override; + SH_RENDER_API void Record(CommandBuffer& cmd, const IRenderContext& ctx, const RenderData& renderData) override; + private: + void EnsureDrawable(); + private: + const IRenderContext& ctx; + + struct Resource : core::GCObject + { + Mesh* plane = nullptr; + Drawable* drawable = nullptr; + Material* mat = nullptr; + + SH_RENDER_API void PushReferenceObjects(core::GarbageCollection& gc) override; + } resource; + + core::Observer::Listener onMatDestroy; + }; +}//namespace diff --git a/include/Render/RenderPass/CopyPass.h b/include/Render/RenderPass/CopyPass.h index 46366794..f542177d 100644 --- a/include/Render/RenderPass/CopyPass.h +++ b/include/Render/RenderPass/CopyPass.h @@ -8,23 +8,17 @@ namespace sh::render class RenderTexture; class IBuffer; - template<> - struct IRenderThrMethod - { - static void EnqueCopyImagePixelToBuffer(CopyPass& pass, RenderTexture& rt, int x, int y, IBuffer& buffer); - }; - class CopyPass : public ScriptableRenderPass { - friend IRenderThrMethod; + friend struct IRenderThrMethod; public: SH_RENDER_API CopyPass(); protected: - SH_RENDER_API void Configure(const RenderTarget& renderData) override; - SH_RENDER_API void Record(CommandBuffer& cmd, const IRenderContext& ctx, const RenderTarget& renderData) override; - SH_RENDER_API auto BuildDrawList(const RenderTarget& renderData) -> DrawList override; + SH_RENDER_API void Configure(const RenderData& renderData) override; + SH_RENDER_API void Record(CommandBuffer& cmd, const IRenderContext& ctx, const RenderData& renderData) override; SH_RENDER_API void EnqueCopyImagePixelToBuffer(RenderTexture& rt, int x, int y, IBuffer& buffer); + SH_RENDER_API auto IsEmpty() const -> bool { return cpyDatas.empty(); } private: struct CopyData { @@ -36,8 +30,10 @@ namespace sh::render std::vector cpyDatas; }; - inline void IRenderThrMethod::EnqueCopyImagePixelToBuffer(CopyPass& pass, RenderTexture& rt, int x, int y, IBuffer& buffer) + template<> + struct IRenderThrMethod { - pass.EnqueCopyImagePixelToBuffer(rt, x, y, buffer); - } + static void EnqueCopyImagePixelToBuffer(CopyPass& pass, RenderTexture& rt, int x, int y, IBuffer& buffer) { pass.EnqueCopyImagePixelToBuffer(rt, x, y, buffer); } + static auto IsEmpty(CopyPass& pass) -> bool { return pass.IsEmpty(); } + }; }//namespace \ No newline at end of file diff --git a/include/Render/RenderPass/DepthPass.h b/include/Render/RenderPass/DepthPass.h new file mode 100644 index 00000000..5b2f2d25 --- /dev/null +++ b/include/Render/RenderPass/DepthPass.h @@ -0,0 +1,18 @@ +#pragma once +#include "../Export.h" +#include "../ScriptableRenderPass.h" + +#include +namespace sh::render +{ + /// @brief 그림자 아틀라스에 광원의 깊이 정보를 기록하는 패스 + /// @brief RenderData의 태그가 Depth면 작동 + class DepthPass : public ScriptableRenderPass + { + public: + SH_RENDER_API DepthPass(); + + SH_RENDER_API void Configure(const RenderData& renderData) override; + SH_RENDER_API void Record(CommandBuffer& cmd, const IRenderContext& ctx, const RenderData& renderData) override; + }; +}//namespace diff --git a/include/Render/RenderPass/SSAOPass.h b/include/Render/RenderPass/SSAOPass.h new file mode 100644 index 00000000..ecc98879 --- /dev/null +++ b/include/Render/RenderPass/SSAOPass.h @@ -0,0 +1,45 @@ +#pragma once +#include "Render/Export.h" +#include "Render/ScriptableRenderPass.h" +#include "Render/RenderData.h" + +#include "Core/Observer.hpp" +#include "Core/GCObject.h" + +namespace sh::render +{ + class RenderTexture; + class Mesh; + class Drawable; + class Material; + + /// @brief depthRT 를 샘플링하여 aoRT(R8) 에 AO 값을 쓰는 풀스크린 패스. + /// @brief "SSAO" 태그가 RenderData 에 있을 때만 동작. + class SSAOPass : public ScriptableRenderPass + { + public: + SH_RENDER_API SSAOPass(const IRenderContext& ctx); + SH_RENDER_API ~SSAOPass(); + + SH_RENDER_API void SetMaterial(Material& mat); + protected: + SH_RENDER_API void Configure(const RenderData& renderData) override; + SH_RENDER_API void Record(CommandBuffer& cmd, const IRenderContext& ctx, const RenderData& renderData) override; + private: + void EnsureDrawable(); + private: + const IRenderContext& ctx; + + struct Resource : core::GCObject + { + Mesh* plane = nullptr; + Drawable* drawable = nullptr; + Material* mat = nullptr; + + SH_RENDER_API void PushReferenceObjects(core::GarbageCollection& gc) override; + } resource; + RenderData localRenderData; + + core::Observer::Listener onMatDestroy; + }; +}//namespace diff --git a/include/Render/RenderPass/TransparentPass.h b/include/Render/RenderPass/TransparentPass.h index a3ea1a56..c6058b4c 100644 --- a/include/Render/RenderPass/TransparentPass.h +++ b/include/Render/RenderPass/TransparentPass.h @@ -1,15 +1,28 @@ #pragma once -#include "../Export.h" -#include "../ScriptableRenderPass.h" +#include "Render/Export.h" +#include "Render/ScriptableRenderPass.h" +#include "Render/Mesh.h" +#include namespace sh::render { + class Material; + class Drawables; /// @brief 단순히 우선 순위 정렬기능만 추가된 패스 class TransparentPass : public ScriptableRenderPass { public: SH_RENDER_API TransparentPass(std::string_view name = "Transparent", RenderQueue renderQueue = RenderQueue::Transparent); - SH_RENDER_API auto BuildDrawList(const RenderTarget& renderData) -> DrawList override; + SH_RENDER_API void Configure(const RenderData& renderData) override; + SH_RENDER_API void Record(CommandBuffer& cmd, const IRenderContext& ctx, const RenderData& renderData) override; + private: + struct RenderItem + { + const Material* material; + Mesh::Topology topology; + const Drawable* drawable; + }; + std::vector items; }; }//namespace \ No newline at end of file diff --git a/include/Render/RenderTarget.h b/include/Render/RenderTarget.h deleted file mode 100644 index 215216d6..00000000 --- a/include/Render/RenderTarget.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once -#include "Texture.h" - -#include "Core/Util.h" - -#include -namespace sh::render -{ - struct RenderTargetLayout - { - TextureFormat format; - TextureFormat depthFormat; - bool bUseMSAA = false; - - auto operator==(const RenderTargetLayout& other) const -> bool - { - return format == other.format && depthFormat == other.depthFormat && bUseMSAA == other.bUseMSAA; - } - }; -}//namespace - -namespace std -{ - template<> - struct hash - { - auto operator()(const sh::render::RenderTargetLayout& layout) const -> std::size_t - { - std::hash intHasher{}; - std::hash boolHasher{}; - - std::size_t hash = intHasher(static_cast(layout.format)); - hash = sh::core::Util::CombineHash(hash, intHasher(static_cast(layout.depthFormat))); - hash = sh::core::Util::CombineHash(hash, boolHasher(layout.bUseMSAA)); - - return hash; - } - }; -} diff --git a/include/Render/RenderTexture.h b/include/Render/RenderTexture.h index f17ccf88..b5589ed1 100644 --- a/include/Render/RenderTexture.h +++ b/include/Render/RenderTexture.h @@ -1,9 +1,9 @@ #pragma once #include "Export.h" #include "Texture.h" -#include "RenderTarget.h" #include "RenderData.h" #include "IRenderThrMethod.h" +#include "ITextureBuffer.h" #include "glm/vec2.hpp" @@ -15,7 +15,7 @@ namespace sh::render template<> struct IRenderThrMethod { - static void ChangeUsage(RenderTexture& rt, ImageUsage newUsage); + static void ChangeUsage(RenderTexture& rt, ResourceUsage newUsage); }; class RenderTexture : public Texture @@ -23,7 +23,7 @@ namespace sh::render friend IRenderThrMethod; SCLASS(RenderTexture); public: - SH_RENDER_API RenderTexture(const RenderTargetLayout& layout); + SH_RENDER_API RenderTexture(TextureFormat colorFormat, TextureFormat depthFormat = TextureFormat::D24S8, bool bMSAA = false); SH_RENDER_API RenderTexture(RenderTexture&& other) noexcept; SH_RENDER_API ~RenderTexture(); @@ -32,34 +32,37 @@ namespace sh::render SH_RENDER_API void OnPropertyChanged(const core::reflection::Property& property) override; SH_RENDER_API void SetSize(uint32_t width, uint32_t height); - SH_RENDER_API auto GetSize() const -> glm::vec2; - SH_RENDER_API auto GetLayout() const -> const RenderTargetLayout& { return layout; } - SH_RENDER_API auto GetMSAABuffer() const -> ITextureBuffer* { return msaaBuffer.get(); } - SH_RENDER_API auto GetDepthBuffer() const -> ITextureBuffer* { return depthBuffer.get(); } - SH_RENDER_API auto GetUsage() const -> ImageUsage { return usage; } + auto GetSize() const -> glm::vec2 { return { width, height }; } + auto GetDepthFormat() const -> TextureFormat { return depthFormat; } + auto GetMSAABuffer() const -> ITextureBuffer* { return msaaBuffer.get(); } + auto GetDepthBuffer() const -> ITextureBuffer* { return IsDepthTexture() ? textureBuffer.get() : depthBuffer.get(); } + auto IsDepthTexture() const -> bool { return GetTextureFormat() == TextureFormat::None && depthFormat != TextureFormat::None; } + auto GetUsage() const -> ResourceUsage { return usage; } protected: - SH_RENDER_API void ChangeUsage(ImageUsage newUsage); + void ChangeUsage(ResourceUsage newUsage) { usage = newUsage; } private: void CreateBuffers(); + void CreateDepthBuffer(const ITextureBuffer::CreateInfo& ci); private: PROPERTY(width); uint32_t width; PROPERTY(height); uint32_t height; - RenderTargetLayout layout; + TextureFormat depthFormat = TextureFormat::D24S8; std::unique_ptr msaaBuffer; std::unique_ptr depthBuffer; - ImageUsage usage = ImageUsage::Undefined; + ResourceUsage usage = ResourceUsage::Undefined; bool bChangeSize = false; + bool bMSAA = false; }; - inline void IRenderThrMethod::ChangeUsage(RenderTexture& rt, ImageUsage newUsage) + inline void IRenderThrMethod::ChangeUsage(RenderTexture& rt, ResourceUsage newUsage) { rt.ChangeUsage(newUsage); } -}//namespace \ No newline at end of file +}//namespace diff --git a/include/Render/Renderer.h b/include/Render/Renderer.h index 228e9a25..f60bfb6f 100644 --- a/include/Render/Renderer.h +++ b/include/Render/Renderer.h @@ -1,6 +1,5 @@ #pragma once #include "Export.h" -#include "Camera.h" #include "ScriptableRenderer.h" #include "Core/ISyncable.h" @@ -38,11 +37,12 @@ namespace sh::render SH_RENDER_API Renderer(); SH_RENDER_API virtual ~Renderer(); - SH_RENDER_API void SyncDirty() override; - SH_RENDER_API void Sync() override; - - SH_RENDER_API virtual bool Init(sh::window::Window& win); + SH_RENDER_API virtual void CreateContext(const window::Window& win) = 0; + SH_RENDER_API virtual void DestroyContext() = 0; + SH_RENDER_API virtual bool Init(window::Window& win); SH_RENDER_API virtual bool Resizing() = 0; + /// @brief 렌더러 상태를 초기화 한다. + SH_RENDER_API virtual void Reset(); SH_RENDER_API virtual void Clear(); SH_RENDER_API virtual void Render(); @@ -53,12 +53,13 @@ namespace sh::render SH_RENDER_API virtual auto GetWidth() const -> uint32_t = 0; SH_RENDER_API virtual auto GetHeight() const -> uint32_t = 0; SH_RENDER_API virtual void WaitForCurrentFrame() = 0; - SH_RENDER_API virtual auto GetContext() const->IRenderContext* = 0; + SH_RENDER_API virtual auto GetContext() const -> IRenderContext* = 0; /// @brief 드로우 객체를 큐에 집어 넣는다. /// @brief 큐에 들어간 객체는 렌더 스레드 프레임 시작 시 반영된다. /// @param drawable 드로우 객체 포인터 SH_RENDER_API void PushDrawAble(Drawable* drawable); + SH_RENDER_API void PushRenderData(const RenderData& renderData); SH_RENDER_API auto GetWindow() const -> sh::window::Window&; @@ -67,56 +68,31 @@ namespace sh::render SH_RENDER_API auto IsPause() const -> bool; SH_RENDER_API void SetScriptableRenderer(ScriptableRenderer& renderer); - SH_RENDER_API auto GetScriptableRenderer() const -> ScriptableRenderer* { return renderer; } - /// @brief 카메라를 추가한다. 렌더 스레드에서 반영된다. - /// @param camera 카메라 참조 - SH_RENDER_API void AddCamera(const Camera& camera); - /// @brief 카메라를 제거한다. 렌더 스레드에서 반영된다. - /// @param camera 카메라 참조 - SH_RENDER_API void RemoveCamera(const Camera& camera); - - SH_RENDER_API auto GetDrawCall(core::ThreadType thread) const -> uint32_t; + auto GetDrawCall(core::ThreadType thread) const -> uint32_t { return drawcall[static_cast(thread)]; } /// @brief 현재 렌더러가 돌아가는 스레드의 번호를 반환한다. 한번이라도 렌더링을 한 후에 갱신된다. - SH_RENDER_API auto GetThreadId() const -> std::thread::id; + auto GetThreadId() const -> std::thread::id { return threadId; } + auto GetScriptableRenderer() const -> ScriptableRenderer* { return renderer; } protected: - virtual void OnCameraAdded(const Camera* camera) {}; - virtual void OnCameraRemoved(const Camera* camera) {}; + SH_RENDER_API void SyncDirty() override; + SH_RENDER_API void Sync() override; SH_RENDER_API void SetDrawCallCount(uint32_t drawcall); SH_RENDER_API void DrainRenderCommands(); protected: - struct CameraCompare - { - bool operator()(const Camera* left, const Camera* right) const - { - if (left->GetPriority() == right->GetPriority()) - return left->id < right->id; - return left->GetPriority() < right->GetPriority(); - } - }; struct RenderCommand { - enum class Type - { - PushDrawable, - AddCamera, - RemoveCamera - }; - Type type = Type::PushDrawable; - std::variant, const Camera*> data; + std::variant, ScriptableRenderer*> data; }; core::LockFreeMPSCQueue renderCommands; std::vector drawables; - std::set cameras; - std::vector> drawCalls; std::atomic_bool bPause; ScriptableRenderer* renderer = nullptr; private: - sh::window::Window* window; + window::Window* window; core::SyncArray drawcall; diff --git a/include/Render/ScriptableRenderPass.h b/include/Render/ScriptableRenderPass.h index 404b9709..d75e02ce 100644 --- a/include/Render/ScriptableRenderPass.h +++ b/include/Render/ScriptableRenderPass.h @@ -2,15 +2,19 @@ #include "Export.h" #include "RenderData.h" #include "IRenderThrMethod.h" +#include "Mesh.h" #include "Core/SContainer.hpp" #include +#include namespace sh::render { class IRenderContext; class CommandBuffer; class RenderTexture; + class Material; + class Drawable; enum class RenderQueue { @@ -36,10 +40,8 @@ namespace sh::render template<> struct IRenderThrMethod { - static void Configure(ScriptableRenderPass& pass, const RenderTarget& renderData); - static void Record(ScriptableRenderPass& pass, CommandBuffer& cmd, const IRenderContext& ctx, const RenderTarget& renderData); - static void EmitBarrier(ScriptableRenderPass& pass, CommandBuffer& cmd, const IRenderContext& ctx, const std::vector& barriers); - static void SetStoreImg(ScriptableRenderPass& pass, bool bStore); + static void Configure(ScriptableRenderPass& pass, const RenderData& renderData); + static void Record(ScriptableRenderPass& pass, CommandBuffer& cmd, const IRenderContext& ctx, const RenderData& renderTarget); /// @brief Configure 이후에 호출해야 정확한 렌더콜 갯수를 알 수 있음 /// @param pass 패스 /// @return 렌더콜 횟수 @@ -50,50 +52,51 @@ namespace sh::render { friend IRenderThrMethod; public: + struct RenderBatch + { + const Material* material; + Mesh::Topology topology; + bool bSkinned = false; + std::vector drawables; + }; SH_RENDER_API ScriptableRenderPass(const core::Name& passName, RenderQueue renderQueue); SH_RENDER_API virtual ~ScriptableRenderPass() = default; - SH_RENDER_API auto GetRenderTextures() const -> const std::unordered_map& { return renderTextures; } - SH_RENDER_API auto IsStoreImage() const -> bool { return bStoreImage; } + SH_RENDER_API static auto CreateRenderBatch(const core::Name& passName, const std::vector& drawables) -> std::vector; + + SH_RENDER_API auto GetRenderTextures() const -> const std::unordered_map& { return renderTextures; } protected: - SH_RENDER_API virtual void Configure(const RenderTarget& renderData); - SH_RENDER_API virtual void Record(CommandBuffer& cmd, const IRenderContext& ctx, const RenderTarget& renderData); - SH_RENDER_API virtual auto BuildDrawList(const RenderTarget& renderData) -> DrawList; + SH_RENDER_API virtual void Configure(const RenderData& renderData); + SH_RENDER_API virtual void Record(CommandBuffer& cmd, const IRenderContext& ctx, const RenderData& renderData); - SH_RENDER_API void EmitBarrier(CommandBuffer& cmd, const IRenderContext& ctx, const std::vector& barriers) const; + SH_RENDER_API void SetRenderTargetImageUsages(const RenderData& renderData); + SH_RENDER_API void SetImageUsages(const RenderData& renderData); + SH_RENDER_API void SetImageUsages(const std::vector& drawables); + SH_RENDER_API void SetImageUsages(const Material& mat); + SH_RENDER_API void SetViewportScissor(CommandBuffer& cmd, const IRenderContext& ctx, const RenderViewer& renderViewer); /// @brief Configure 이후에 호출해야 정확한 렌더콜 갯수를 알 수 있음 - SH_RENDER_API auto GetRenderCallCount() const -> uint32_t; - private: - void CollectRenderImages(const RenderTarget& renderData, const DrawList& drawList); + SH_RENDER_API auto GetRenderCallCount() const -> uint32_t { return renderCallCount; } public: const core::Name passName; const RenderQueue renderQueue; protected: - DrawList drawList; - std::unordered_map renderTextures; + std::vector renderBatches; + std::unordered_map renderTextures; private: - bool bStoreImage = true; + uint32_t renderCallCount = 0; }; - inline void IRenderThrMethod::Configure(ScriptableRenderPass& pass, const RenderTarget& renderData) + inline void IRenderThrMethod::Configure(ScriptableRenderPass& pass, const RenderData& renderData) { pass.Configure(renderData); } - inline void IRenderThrMethod::Record(ScriptableRenderPass& pass, CommandBuffer& cmd, const IRenderContext& ctx, const RenderTarget& renderData) + inline void IRenderThrMethod::Record(ScriptableRenderPass& pass, CommandBuffer& cmd, const IRenderContext& ctx, const RenderData& renderData) { pass.Record(cmd, ctx, renderData); } - inline void IRenderThrMethod::EmitBarrier(ScriptableRenderPass& pass, CommandBuffer& cmd, const IRenderContext& ctx, const std::vector& barriers) - { - pass.EmitBarrier(cmd, ctx, barriers); - } - inline void IRenderThrMethod::SetStoreImg(ScriptableRenderPass& pass, bool bStore) - { - pass.bStoreImage = bStore; - } inline auto IRenderThrMethod::GetRenderCallCount(ScriptableRenderPass& pass) -> uint32_t { return pass.GetRenderCallCount(); } -}//namespace \ No newline at end of file +}//namespace diff --git a/include/Render/ScriptableRenderer.h b/include/Render/ScriptableRenderer.h index dd1eacc1..1e28bc52 100644 --- a/include/Render/ScriptableRenderer.h +++ b/include/Render/ScriptableRenderer.h @@ -5,7 +5,6 @@ #include "ScriptableRenderPass.h" #include "IRenderThrMethod.h" #include "IBuffer.h" -#include "RenderPass/CopyPass.h" #include "Core/Util.h" #include "Core/ISyncable.h" @@ -16,11 +15,14 @@ #include namespace sh::render { + class CopyPass; + class ComputeShader; + template<> struct IRenderThrMethod { - static void Setup(ScriptableRenderer& renderer, const RenderTarget& data); - static void Execute(ScriptableRenderer& renderer, const RenderTarget& data); + static void Setup(ScriptableRenderer& renderer, const RenderData& data); + static void Execute(ScriptableRenderer& renderer, const RenderData& data); static void ExecuteTransfer(ScriptableRenderer& renderer, uint32_t imgIdx); static void EnqueRenderPass(ScriptableRenderer& renderer, ScriptableRenderPass& pass); static void CallReadbacks(ScriptableRenderer& renderer); @@ -33,29 +35,32 @@ namespace sh::render { friend IRenderThrMethod; public: - struct SubmittedCommand + struct RecordedCommand { ScriptableRenderPass& pass; CommandBuffer& cmd; }; public: SH_RENDER_API ScriptableRenderer(IRenderContext& ctx); - SH_RENDER_API virtual ~ScriptableRenderer() = default; + SH_RENDER_API virtual ~ScriptableRenderer(); + SH_RENDER_API virtual void Init() {}; SH_RENDER_API auto AddRenderPass(const core::Name& passName, RenderQueue renderQueue) -> ScriptableRenderPass&; SH_RENDER_API auto ReadRenderTextureAsync(RenderTexture& rt, int x, int y) -> std::future>; + SH_RENDER_API void Dispatch(const ComputeShader& computeShader, uint32_t x, uint32_t y, uint32_t z); - SH_RENDER_API auto GetSubmittedCommands() const -> const std::vector& { return submittedCmds; } + SH_RENDER_API auto HasPass(const core::Name& passName) const -> bool; + auto GetRecordedCommands() const -> const std::vector& { return recordedCmds; } template>, typename... Args> auto AddRenderPass(Args&&... args) -> T&; protected: SH_RENDER_API void SyncDirty() override; SH_RENDER_API void Sync() override; - SH_RENDER_API virtual void Setup(const RenderTarget& data); - SH_RENDER_API virtual void Execute(const RenderTarget& data); + SH_RENDER_API virtual void Setup(const RenderData& data); + SH_RENDER_API virtual void Execute(const RenderData& data); SH_RENDER_API void ExecuteTransfer(uint32_t imgIdx); SH_RENDER_API void CallReadbacks(); @@ -72,13 +77,12 @@ namespace sh::render std::vector> allPasses; private: - SH_RENDER_API static std::vector swapChainStates; + SH_RENDER_API static std::vector swapChainStates; std::vector activePasses; - std::vector submittedCmds; + std::vector recordedCmds; std::unique_ptr cpyPass; - struct SyncData { RenderTexture* src; @@ -109,11 +113,11 @@ namespace sh::render return *ptr; } - inline void IRenderThrMethod::Setup(ScriptableRenderer& renderer, const RenderTarget& data) + inline void IRenderThrMethod::Setup(ScriptableRenderer& renderer, const RenderData& data) { renderer.Setup(data); } - inline void IRenderThrMethod::Execute(ScriptableRenderer& renderer, const RenderTarget& data) + inline void IRenderThrMethod::Execute(ScriptableRenderer& renderer, const RenderData& data) { renderer.Execute(data); } diff --git a/include/Render/Shader.h b/include/Render/Shader.h index d62e3cd8..c14ca822 100644 --- a/include/Render/Shader.h +++ b/include/Render/Shader.h @@ -37,17 +37,8 @@ namespace sh::render std::vector> passes; }; public: - SH_RENDER_API Shader(ShaderCreateInfo&& shaderCreateInfo); + SH_RENDER_API Shader(ShaderCreateInfo shaderCreateInfo); SH_RENDER_API ~Shader(); - SH_RENDER_API auto GetShaderPasses(const core::Name& lightingPassName) const -> const std::vector>*; - SH_RENDER_API auto GetAllShaderPass() const -> const std::vector&; - - SH_RENDER_API auto GetProperties() const -> const std::unordered_map&; - SH_RENDER_API auto GetProperty(const std::string& name) const -> const PropertyInfo*; - - SH_RENDER_API auto GetShaderAST() const -> const ShaderAST::ShaderNode&; - - SH_RENDER_API auto IsUsingLight() const -> bool; SH_RENDER_API void OnDestroy() override; /// @brief AST와 셰이더 패스를 직렬화 한다. @@ -56,6 +47,17 @@ namespace sh::render /// @brief AST만 역직렬화 시킨다. /// @param json 직렬화 된 json SH_RENDER_API void Deserialize(const core::Json& json) override; + + SH_RENDER_API auto GetShaderPasses(const core::Name& lightingPassName) const -> const std::vector>*; + SH_RENDER_API auto GetAllShaderPass() const -> const std::vector& { return passDatas; } + + SH_RENDER_API auto GetProperties() const -> const std::unordered_map& { return properties; } + SH_RENDER_API auto GetProperty(const std::string& name) const -> const PropertyInfo*; + + SH_RENDER_API auto GetShaderAST() const -> const ShaderAST::ShaderNode& { return shaderNode; } + + SH_RENDER_API auto IsUsingLight() const -> bool { return bUsingLight; } + SH_RENDER_API auto IsUsingSkin() const -> bool { return bUsingSkin; } private: void Clear(); void AddShaderPasses(std::vector&& passes); @@ -73,6 +75,7 @@ namespace sh::render ShaderAST::ShaderNode shaderNode; bool bUsingLight = false; + bool bUsingSkin = false; }; template diff --git a/include/Render/ShaderAST.h b/include/Render/ShaderAST.h index 54ee444e..cff81640 100644 --- a/include/Render/ShaderAST.h +++ b/include/Render/ShaderAST.h @@ -31,7 +31,8 @@ namespace sh::render Float, Int, Sampler, - Boolean + Boolean, + Struct }; enum class VariableAttribute { @@ -39,19 +40,39 @@ namespace sh::render Local }; + struct StructNode; struct VariableNode : core::ISerializable { VariableType type = VariableType::Int; - int size = 1; + uint32_t arraySize = 1; // 배열 원소 개수 (단일 변수면 1) std::string name; std::string defaultValue; + std::string structType; VariableAttribute attribute = VariableAttribute::None; + bool bDynamicArray = false; // SSBO 끝의 가변 배열 (arraySize 무시) VariableNode() = default; - VariableNode(VariableType type, int size, std::string name) : - type(type), size(size), name(name) + VariableNode(VariableType type, uint32_t arraySize, std::string name) : + type(type), arraySize(arraySize), name(std::move(name)) { } + SH_RENDER_API auto Serialize() const -> core::Json override; + SH_RENDER_API void Deserialize(const core::Json& json) override; + + inline static auto MakeDynamicArray(VariableType type, std::string name) -> VariableNode + { + VariableNode v; + v.type = type; + v.bDynamicArray = true; + v.name = std::move(name); + return v; + } + SH_RENDER_API static auto MakeDynamicArray(StructNode& structNode, std::string name) -> VariableNode; + }; + struct StructNode : core::ISerializable + { + std::string name; + std::vector vars; SH_RENDER_API auto Serialize() const -> core::Json override; SH_RENDER_API void Deserialize(const core::Json& json) override; @@ -78,14 +99,28 @@ namespace sh::render SH_RENDER_API auto Serialize() const -> core::Json override; SH_RENDER_API void Deserialize(const core::Json& json) override; }; - struct UBONode : core::ISerializable + enum class BufferType : uint8_t { - uint32_t set; - uint32_t binding; + Uniform, // UBO (std140) + Storage, // SSBO (std430) + Sampler, + PushConstant // 푸시 상수 + }; + enum class BufferAccess : uint8_t + { + Read, // readonly + Write, // writeonly + ReadWrite // (no qualifier) + }; + struct BufferNode : core::ISerializable + { + BufferType bufferType = BufferType::Uniform; + BufferAccess access = BufferAccess::Read; + uint32_t set = 0; + uint32_t binding = 0; std::string name; - bool bSampler = false; - bool bConstant = false; std::vector vars; + bool bAnonymousInstance = false; // true이면 블록 인스턴스 이름 없이 멤버를 전역으로 노출 SH_RENDER_API auto Serialize() const -> core::Json override; SH_RENDER_API void Deserialize(const core::Json& json) override; @@ -95,14 +130,19 @@ namespace sh::render StageType type; std::vector in; std::vector out; - std::vector uniforms; + std::vector structs; + std::vector buffers; std::vector declaration; std::vector functions; std::string code; int lightingBinding = -1; + int skinBinding = -1; + int shadowMapBinding = -1; SH_RENDER_API auto Serialize() const -> core::Json override; SH_RENDER_API void Deserialize(const core::Json& json) override; + + SH_RENDER_API auto GetStructNode(const std::string& structName) const -> const StructNode*; }; struct PassNode : core::ISerializable @@ -140,5 +180,28 @@ namespace sh::render SH_RENDER_API auto Serialize() const -> core::Json override; SH_RENDER_API void Deserialize(const core::Json& json) override; }; + + struct ComputeShaderNode : core::ISerializable + { + VersionNode version; + std::string shaderName; + uint32_t numthreadsX = 1; + uint32_t numthreadsY = 1; + uint32_t numthreadsZ = 1; + std::vector buffers; + std::vector declaration; + std::vector functions; + std::string code; + + SH_RENDER_API auto Serialize() const -> core::Json override; + SH_RENDER_API void Deserialize(const core::Json& json) override; + }; + + struct alignas(16) LightBuffer + { + glm::vec4 pos; + glm::vec4 other; + glm::mat4 lightSpaceMatrix; + }; }; }//namespace \ No newline at end of file diff --git a/include/Render/ShaderLexer.h b/include/Render/ShaderLexer.h index 0b38e6cb..3e814d2c 100644 --- a/include/Render/ShaderLexer.h +++ b/include/Render/ShaderLexer.h @@ -16,8 +16,8 @@ namespace sh::render Vertex, Fragment, Layout, Uniform, In, Out, Sampler2D, Constexpr, Const, - VERTEX, UV, NORMAL, TANGENT, MVP, LIGHT, BONE_WEIGHTS, BONE_INDICES, - MATRIX_MODEL, MATRIX_VIEW, MATRIX_PROJ, + VERTEX, UV, NORMAL, TANGENT, MVP, LIGHT, BONE_WEIGHTS, BONE_INDICES, SKIN, + MATRIX_MODEL, MATRIX_VIEW, MATRIX_PROJ, CAMERA, MATRIX_SKIN, TEXTURE_SHADOW, LBracket, // ( RBracket, // ) LBrace, // { diff --git a/include/Render/ShaderParser.h b/include/Render/ShaderParser.h index 2d14a1ec..3f4e87d3 100644 --- a/include/Render/ShaderParser.h +++ b/include/Render/ShaderParser.h @@ -60,7 +60,7 @@ namespace sh::render auto ParseFunctionBody(ShaderAST::StageNode& stageNode) -> std::string; void ParseLayout(ShaderAST::StageNode& stageNode); void ParseUniform(const ShaderAST::ShaderNode& shaderNode, ShaderAST::StageNode& stageNode); - void ParseUniformBody(ShaderAST::UBONode& uboNode); + void ParseUniformBody(ShaderAST::BufferNode& uboNode); void ParseConstexpr(ShaderAST::PassNode& passNode); void Optimize(ShaderAST::ShaderNode& shaderNode); @@ -68,6 +68,7 @@ namespace sh::render auto SubstitutionFunctionToken(const ShaderLexer::Token& token) const -> std::string; private: const std::vector* tokens; + ShaderAST::ShaderNode curShaderNode; std::size_t pos; // 현재 토큰 위치 diff --git a/include/Render/ShaderPass.h b/include/Render/ShaderPass.h index 8f3158ad..ecfff94e 100644 --- a/include/Render/ShaderPass.h +++ b/include/Render/ShaderPass.h @@ -50,6 +50,13 @@ namespace sh::render SH_RENDER_API virtual void Clear() = 0; SH_RENDER_API virtual void Build() = 0; + /// @brief 셰이더 바이너리 데이터를 직렬화 한다. + /// @return 직렬화 된 json + SH_RENDER_API auto Serialize() const -> core::Json override; + /// @brief 셰이더 바이너리 데이터를 역직렬화 한다. + /// @param json 직렬화 된 json + SH_RENDER_API void Deserialize(const core::Json& json) override; + SH_RENDER_API auto HasUniformMember(const std::string& name, ShaderStage stage) const -> const UniformStructLayout*; SH_RENDER_API auto GetStencilState() const -> const StencilState& { return stencilState; } @@ -69,6 +76,10 @@ namespace sh::render /// @brief 라이트 유니폼의 바인딩 번호를 리턴한다. /// @return 라이팅을 안 쓸 시 -1 SH_RENDER_API auto GetLightingBinding() const -> int { return lightingBinding; } + /// @brief 스킨 유니폼의 바인딩 번호를 리턴한다. + /// @return 스킨을 안 쓸 시 -1 + SH_RENDER_API auto GetSkinBinding() const -> int { return skinBinding; } + SH_RENDER_API auto GetShadowMapBinding() const -> int { return shadowMapBinding; } SH_RENDER_API auto GetConstants() const -> const std::unordered_map& { return constantNameMap; } SH_RENDER_API auto GetConstantsInfo(const std::string& name) const -> const ConstantInfo*; SH_RENDER_API auto GetConstantSize() const -> std::size_t; @@ -77,16 +88,8 @@ namespace sh::render SH_RENDER_API auto GetAttribute(const std::string& name) const -> std::optional; SH_RENDER_API void StoreShaderCode(ShaderCode&& shaderCode); - - /// @brief 셰이더 바이너리 데이터를 직렬화 한다. - /// @return 직렬화 된 json - SH_RENDER_API auto Serialize() const -> core::Json override; - /// @brief 셰이더 바이너리 데이터를 역직렬화 한다. - /// @param json 직렬화 된 json - SH_RENDER_API void Deserialize(const core::Json& json) override; protected: ShaderPass(const ShaderAST::PassNode& passNode, ShaderType type); - ShaderPass(const ShaderPass& other); ShaderPass(ShaderPass&& other) noexcept; private: void AddUniformLayout(ShaderStage stage, const UniformStructLayout& layout); @@ -117,6 +120,8 @@ namespace sh::render uint8_t colorMask = 7; //0b111 std::size_t constantSize = 0; int lightingBinding = -1; + int skinBinding = -1; + int shadowMapBinding = -1; bool bZWrite = true; bool bZTest = true; bool bHasConstant = false; diff --git a/include/Render/ShadowMapManager.h b/include/Render/ShadowMapManager.h new file mode 100644 index 00000000..6ff10857 --- /dev/null +++ b/include/Render/ShadowMapManager.h @@ -0,0 +1,85 @@ +#pragma once +#include "Export.h" +#include "RenderData.h" + +#include "Core/NonCopyable.h" + +#include +#include + +#include +#include +#include +#include + +namespace sh::render +{ + class IRenderContext; + class RenderTexture; + class Renderer; + class ShelfPacker; + + /// @brief 그림자 캐스터가 구현하는 인터페이스 + class IShadowCaster + { + public: + virtual ~IShadowCaster() = default; + virtual auto GetShadowBias() const -> float = 0; + virtual auto GetShadowMapResolution() const -> uint32_t = 0; + virtual auto GetShadowViewMatrix() const -> glm::mat4 = 0; + virtual auto GetShadowProjMatrix() const -> glm::mat4 = 0; + virtual auto GetShadowPos() const -> glm::vec3 = 0; + virtual auto GetShadowLookAt() const -> glm::vec3 = 0; + }; + + /// @brief 월드 단위의 그림자 아틀라스 매니저. 스레드 안전하다. + class ShadowMapManager : public core::INonCopyable + { + public: + struct Slot + { + glm::vec2 uvOffset{ 0.f, 0.f }; + glm::vec2 uvSize{ 0.f, 0.f }; + bool valid = false; + }; + public: + SH_RENDER_API ShadowMapManager(); + SH_RENDER_API ~ShadowMapManager(); + + SH_RENDER_API void Init(IRenderContext& ctx, uint32_t atlasSize = 4096); + /// @brief 등록된 캐스터/슬롯/아틀라스를 해제한다. + SH_RENDER_API void Clear(); + + /// @brief 그림자 캐스터를 등록한다. 같은 객체의 중복 등록은 무시된다. + SH_RENDER_API void Register(IShadowCaster& caster); + SH_RENDER_API void Unregister(IShadowCaster& caster); + + /// @brief 등록된 캐스터들을 모아 RenderData를 만들어 Renderer에 푸시한다. + /// @brief 매 프레임 1회 호출하면 된다. 슬롯은 이 시점에 ShelfPacker로 새로 패킹된다. + SH_RENDER_API void Submit(Renderer& renderer); + + /// @brief 캐스터에 할당된 슬롯 정보를 반환한다. + /// @brief Submit 호출 후 유효하다. 매 프레임 슬롯 위치가 바뀔 수 있으므로 매번 조회한다. + SH_RENDER_API auto GetSlot(const IShadowCaster& caster) const -> Slot; + /// @brief 캐스터의 광원 공간 변환 행렬(proj * view)을 반환한다. + /// @brief Submit 호출 후 유효하다. + SH_RENDER_API auto GetLightSpaceMatrix(const IShadowCaster& caster) const -> glm::mat4; + + auto GetAtlas() const -> RenderTexture* { return atlas; } + auto GetAtlasSize() const -> uint32_t { return atlasSize; } + private: + void EnsureAtlas(); + private: + IRenderContext* ctx = nullptr; + uint32_t atlasSize = 4096; + RenderTexture* atlas = nullptr; + + std::unique_ptr packer; + + std::vector casters; + std::unordered_map casterSlots; + std::unordered_map casterLightSpace; + + RenderData renderData; + }; +}//namespace diff --git a/include/Render/SkinnedMesh.h b/include/Render/SkinnedMesh.h index 1ae11ef9..a1a54ff1 100644 --- a/include/Render/SkinnedMesh.h +++ b/include/Render/SkinnedMesh.h @@ -22,7 +22,6 @@ namespace sh::render static constexpr uint8_t BONE_INDEX_ID = 4; static constexpr uint8_t BONE_WEIGHT_ID = 5; - static constexpr uint32_t MAX_BONES = 128; public: SH_RENDER_API SkinnedMesh(); SH_RENDER_API SkinnedMesh(const SkinnedMesh& other); diff --git a/include/Render/Texture.h b/include/Render/Texture.h index d47b3d3c..1e471248 100644 --- a/include/Render/Texture.h +++ b/include/Render/Texture.h @@ -34,19 +34,21 @@ namespace sh::render SH_RENDER_API Texture(Texture&& other) noexcept; SH_RENDER_API virtual ~Texture(); + SH_RENDER_API void SyncDirty() override; + SH_RENDER_API void Sync() override; + SH_RENDER_API void OnPropertyChanged(const core::reflection::Property& prop) override; /// @brief 픽셀 데이터를 지정한다. /// @brief [주의] 동기화 타이밍에 텍스쳐 버퍼가 재설정됨. /// @param data 데이터 포인터 /// @param mipLevel 밉 레벨 - SH_RENDER_API void SetPixelData(const std::vector& pixels, uint32_t mipLevel = 0); - SH_RENDER_API void SetPixelData(std::vector&& pixels, uint32_t mipLevel = 0); + SH_RENDER_API void SetPixelData(std::vector pixels, uint32_t mipLevel = 0); /// @brief 픽셀 데이터를 지정한다. /// @brief [주의] 동기화 타이밍에 텍스쳐 버퍼가 재설정됨. /// @param pixels 데이터 포인터 /// @param size 데이터 사이즈 /// @param mipLevel 밉 레벨 SH_RENDER_API void SetPixelData(const uint8_t* pixels, std::size_t size, uint32_t mipLevel = 0); - SH_RENDER_API virtual auto GetPixelData() const -> const std::vector>&; + virtual auto GetPixelData() const -> const std::vector>& { return pixels; } SH_RENDER_API virtual auto GetPixelData(uint32_t mipLevel) const -> const std::vector&; SH_RENDER_API virtual void Build(const IRenderContext& context); @@ -54,36 +56,29 @@ namespace sh::render /// @brief 네이티브 텍스쳐 버퍼를 가져온다. /// @return 텍스쳐 버퍼 포인터 SH_RENDER_API auto GetTextureBuffer() const -> ITextureBuffer*; - SH_RENDER_API auto GetTextureFormat() const -> TextureFormat; - - SH_RENDER_API auto GetWidth() const -> uint32_t; - SH_RENDER_API auto GetHeight() const -> uint32_t; /// @brief 텍스쳐 포멧을 변경한다. /// @brief [주의] 동기화 타이밍에 텍스쳐 버퍼가 재설정됨. /// @param target 텍스쳐 포멧 SH_RENDER_API void ChangeTextureFormat(TextureFormat target); - SH_RENDER_API auto IsSRGB() const -> bool; - SH_RENDER_API auto GetMipLevel() const -> uint32_t; - SH_RENDER_API void SetAnisoLevel(uint32_t aniso); - SH_RENDER_API auto GetAnisoLevel() const -> uint32_t; - + SH_RENDER_API void SetGenerateMipmap(bool bGenerate); - SH_RENDER_API auto IsGenerateMipmap() const -> bool; - - SH_RENDER_API void SyncDirty() override; - SH_RENDER_API void Sync() override; SH_RENDER_API void SetSRGB(bool bSRGB); SH_RENDER_API void SetFiltering(Filtering filter); - SH_RENDER_API auto GetFiltering() const-> Filtering; - - SH_RENDER_API void OnPropertyChanged(const core::reflection::Property& prop) override; SH_RENDER_API void ExportToPNG(const std::filesystem::path& path); - SH_RENDER_API auto GetContext() const -> const IRenderContext* { return context; } + SH_RENDER_API auto GetMipLevel() const -> uint32_t; + auto GetContext() const -> const IRenderContext* { return context; } + auto GetTextureFormat() const -> TextureFormat { return format; } + auto GetWidth() const -> uint32_t { return width; } + auto GetHeight() const -> uint32_t { return height; } + auto GetFiltering() const -> Filtering { return filtering; } + auto IsSRGB() const -> bool { return bSRGB; } + auto GetAnisoLevel() const -> uint32_t { return aniso; } + auto IsGenerateMipmap() const -> bool { return bGenerateMipmap; } private: void CreateTextureBuffer(); auto CheckSRGB() const -> bool; @@ -114,4 +109,4 @@ namespace sh::render bool bGenerateMipmap = true; bool bSetDataDirty = false; }; -} \ No newline at end of file +}//namespace \ No newline at end of file diff --git a/include/Render/UniformStructLayout.h b/include/Render/UniformStructLayout.h index 32a182e6..b6e5e09b 100644 --- a/include/Render/UniformStructLayout.h +++ b/include/Render/UniformStructLayout.h @@ -1,6 +1,7 @@ #pragma once #include "Export.h" #include "ShaderEnum.h" +#include "Texture.h" #include "Core/Reflection.hpp" #include "Core/Util.h" @@ -14,76 +15,90 @@ #include namespace sh::render { - class Texture; - - /// @brief 셰이더 유니폼 구조체 레이아웃 - class UniformStructLayout : core::ISerializable + /// @brief 셰이더 유니폼/스토리지 버퍼 구조체 레이아웃 + class UniformStructLayout { public: - enum class Layout + enum class Layout : uint8_t + { + STD140, // UBO + STD430 // SSBO + }; + /// @brief 버퍼 타입 + enum class Kind : uint8_t { - STD140, STD430 + Uniform, // UBO + Storage, // SSBO + Sampler, + PushConstant // 푸시 상수 }; - enum class Type + /// @brief 용도 + enum class Usage : uint8_t { - Camera = 0, // 카메라 데이터 - Object = 1, // 객체마다 고유 - Material = 2, // 메테리얼간 공유 + Camera = 0, + Object = 1, + Material = 2, }; struct UniformMember { std::string name; - std::size_t typeHash; - uint32_t offset; - uint32_t layoutSize; - uint32_t count; - bool isArray = false; - bool isSampler = false; + std::size_t typeHash = 0; + uint32_t offset = 0; + uint32_t layoutSize = 0; // 바이트 (가변 배열이면 0) + uint32_t baseAlignment = 0; + uint32_t count = 1; // 배열 원소 개수 (단일이면 1) + bool bDynamicArray = false; // SSBO + + auto IsArray() const -> bool { return count > 1 || bDynamicArray; } }; - struct Std140LayoutInfo + struct LayoutInfo { uint32_t baseAlignment; // 오프셋의 정렬 단위 - uint32_t std140Size; // 실제 차지하는 총 크기(패딩 포함) + uint32_t bytes; // 실제 차지하는 총 크기(패딩 포함) }; - private: - std::vector members; public: - const Layout layout; - const Type type; - const ShaderStage stage; - const uint32_t binding; - const std::string name; - const bool bConstant; - private: + SH_RENDER_API UniformStructLayout(std::string name, uint32_t binding, Usage type, ShaderStage stage, Kind kind = Kind::Uniform); + SH_RENDER_API UniformStructLayout(std::string name, uint32_t binding, Usage type, ShaderStage stage, Kind kind, Layout layout); + + /// @brief 구조체의 맴버 변수를 추가. 구조체의 변수 순서대로 호출해야 한다. template - static auto GetSTD140Layout() -> Std140LayoutInfo; - public: - SH_RENDER_API UniformStructLayout(const std::string name, uint32_t binding, Type type, ShaderStage stage, bool bConstant = false, Layout layout = Layout::STD140); - /// @brief 구조체의 맴버 변수를 추가 하는 함수. 구조체의 변수 순서대로 호출 해야 한다. - /// @tparam T 타입 - /// @param name 이름 - template - void AddMember(const std::string& name); - /// @brief 구조체의 배열 맴버 변수를 추가 하는 함수. 구조체의 변수 순서대로 호출 해야 한다. - /// @tparam T 타입 - /// @param name 이름 - /// @param count 배열 원소 갯수 - template - void AddArrayMember(const std::string& name, std::size_t count); + void AddMember(const std::string& name); + /// @brief 구조체의 고정 배열 맴버를 추가. 구조체의 변수 순서대로 호출해야 한다. + /// @param count 원소 개수 (>= 1) + template + void AddArrayMember(const std::string& name, uint32_t count); + /// @brief SSBO 끝에 들어가는 가변 배열 맴버 추가. 반드시 마지막에 호출. + template + void AddDynamicArrayMember(const std::string& name); SH_RENDER_API auto GetMember(const std::string& name) const -> const UniformMember*; - SH_RENDER_API auto GetMembers() const -> const std::vector&; + SH_RENDER_API auto GetMembers() const -> const std::vector& { return members; } SH_RENDER_API auto HasMember(const std::string& name) const -> bool; - /// @brief 유니폼 구조체의 사이즈를 반환 하는 함수. - /// @return 바이트 + /// @brief 구조체의 사이즈(바이트). 가변 배열 맴버가 있으면 고정 부분까지의 크기만 반환. SH_RENDER_API auto GetSize() const -> std::size_t; + SH_RENDER_API auto GetLayout() const -> Layout { return layout; } + SH_RENDER_API auto GetKind() const -> Kind { return kind; } + SH_RENDER_API auto IsSampler() const -> bool { return kind == Kind::Sampler; } + SH_RENDER_API auto IsPushConstant() const -> bool { return kind == Kind::PushConstant; } + private: + template + static auto GetLayoutInfo() -> LayoutInfo; - SH_RENDER_API auto Serialize() const -> core::Json override; - SH_RENDER_API void Deserialize(const core::Json& json) override; + template + void AddMemberImpl(const std::string& name, uint32_t count, bool bDynamic); + public: + const Kind kind; + const Layout layout; + const Usage usage; + const ShaderStage stage; + const uint32_t binding; + const std::string name; + private: + std::vector members; }; template - inline auto UniformStructLayout::GetSTD140Layout() -> Std140LayoutInfo + inline auto UniformStructLayout::GetLayoutInfo() -> LayoutInfo { if constexpr (std::is_same_v) return { 4, 4 }; @@ -101,52 +116,82 @@ namespace sh::render return { 16, 48 }; else if constexpr (std::is_same_v) return { 16, 64 }; + else if constexpr (std::is_class_v) + return { alignof(T), sizeof(T)}; #if defined(_MSC_VER) else - static_assert(std::_Always_false, "Unknown type for GetSTD140Layout: " __FUNCSIG__); + static_assert(std::_Always_false, "Unknown type for GetLayoutInfo: " __FUNCSIG__); #elif defined(__GNUC__) || defined(__clang__) else - static_assert(core::alwaysFalse, "Unknown type for GetSTD140Layout "); + static_assert(core::alwaysFalse, "Unknown type for GetLayoutInfo"); # else else - static_assert(always_false, "Unknown type for GetSTD140Layout: Unknown compiler"); + static_assert(always_false, "Unknown type for GetLayoutInfo: Unknown compiler"); #endif } template inline void UniformStructLayout::AddMember(const std::string& name) { - AddArrayMember(name, 1); + AddMemberImpl(name, 1, false); } template - inline void UniformStructLayout::AddArrayMember(const std::string& name, std::size_t count) + inline void UniformStructLayout::AddArrayMember(const std::string& name, uint32_t count) + { + AddMemberImpl(name, count, false); + } + template + inline void UniformStructLayout::AddDynamicArrayMember(const std::string& name) + { + AddMemberImpl(name, 0, true); + } + template + inline void UniformStructLayout::AddMemberImpl(const std::string& name, uint32_t count, bool bDynamic) { if constexpr (std::is_same_v) { - UniformMember member{ name, core::reflection::GetType().hash, 0, 0, count, (count > 1), true }; + UniformMember member{}; + member.name = name; + member.typeHash = core::reflection::GetType().hash; + member.count = count; + member.bDynamicArray = bDynamic; members.push_back(std::move(member)); + return; } else { - if (layout == Layout::STD140) - { - Std140LayoutInfo info = GetSTD140Layout(); - uint32_t arrayBaseAlignment = (count > 1) ? - std::max(info.baseAlignment, 16u) : info.baseAlignment; // 배열이면 최소 16정렬 - uint32_t arrayStride = (count > 1) ? - core::Util::AlignTo(info.std140Size, arrayBaseAlignment) : info.std140Size; - uint32_t totalSize = arrayStride * static_cast(count); + LayoutInfo info = GetLayoutInfo(); + if (layout == Layout::STD140 && std::is_class_v) + info.baseAlignment = 16; + const bool isArray = (count > 1 || bDynamic); + + // STD140: 배열 원소 stride는 최소 16바이트 정렬 + // STD430: 타입 고유 alignment 사용 + const uint32_t baseAlignment = (layout == Layout::STD140 && isArray) + ? std::max(info.baseAlignment, 16u) + : info.baseAlignment; + + // 배열은 stride를 baseAlignment에 맞춤 + const uint32_t elementStride = isArray + ? core::Util::AlignTo(info.bytes, baseAlignment) + : info.bytes; - UniformMember member{ name, core::reflection::GetType().hash, 0, totalSize, count, (count > 1), false }; - if (members.empty()) - { - members.push_back(std::move(member)); - return; - } - auto& prev = members.back(); - uint32_t prevEnd = prev.offset + prev.layoutSize; - member.offset = core::Util::AlignTo(prevEnd, arrayBaseAlignment); - members.push_back(std::move(member)); + // 가변 배열은 런타임에 크기 결정 + const uint32_t totalSize = bDynamic ? 0 : (elementStride * count); + + UniformMember member{}; + member.name = name; + member.typeHash = core::reflection::GetType().hash; + member.layoutSize = totalSize; + member.baseAlignment = baseAlignment; + member.count = bDynamic ? 0 : count; + member.bDynamicArray = bDynamic; + + if (!members.empty()) + { + const UniformMember& prev = members.back(); + member.offset = core::Util::AlignTo(prev.offset + prev.layoutSize, baseAlignment); } + members.push_back(std::move(member)); } } }//namespace \ No newline at end of file diff --git a/include/Render/VulkanImpl/VulkanBuffer.h b/include/Render/VulkanImpl/VulkanBuffer.h index 0a58274b..7304a773 100644 --- a/include/Render/VulkanImpl/VulkanBuffer.h +++ b/include/Render/VulkanImpl/VulkanBuffer.h @@ -18,16 +18,20 @@ namespace sh::render::vk SH_RENDER_API auto operator=(const VulkanBuffer& other)->VulkanBuffer&; SH_RENDER_API auto operator=(VulkanBuffer&& other) noexcept -> VulkanBuffer&; + SH_RENDER_API auto Resize(std::size_t size) -> bool override; + SH_RENDER_API auto Create(size_t size, VkBufferUsageFlags usageBits, VkSharingMode sharing, VkMemoryPropertyFlags memPropFlagBits, bool persistentMapping = false) -> VkResult; SH_RENDER_API void Clean(); SH_RENDER_API void SetData(const void* data); - SH_RENDER_API auto GetData() const -> void* override; - SH_RENDER_API auto GetBuffer() const->VkBuffer; - SH_RENDER_API auto GetBufferInfo() const -> const VkBufferCreateInfo&; - SH_RENDER_API auto GetBufferMemory() const->VmaAllocation; - SH_RENDER_API auto GetSize() const->size_t; + SH_RENDER_API void SetData(const void* data, std::size_t offset, std::size_t size) override; + + SH_RENDER_API auto GetData() const -> std::vector override; + SH_RENDER_API auto GetBuffer() const -> VkBuffer { return buffer; } + SH_RENDER_API auto GetBufferInfo() const -> const VkBufferCreateInfo& { return bufferInfo; } + SH_RENDER_API auto GetBufferMemory() const -> VmaAllocation { return bufferMem; } + SH_RENDER_API auto GetSize() const -> size_t { return size; } private: - auto FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) -> uint32_t; + // auto FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) -> uint32_t; // 이제 안 씀 private: const VulkanContext& context; @@ -39,6 +43,6 @@ namespace sh::render::vk size_t size; void* dataPtr; - bool persistentMapping; // 즉시 맵핑 + bool bPersistentMapping; // 즉시 맵핑 }; -} \ No newline at end of file +}//namespace \ No newline at end of file diff --git a/include/Render/VulkanImpl/VulkanCameraBuffers.h b/include/Render/VulkanImpl/VulkanCameraBuffers.h deleted file mode 100644 index 47046e1c..00000000 --- a/include/Render/VulkanImpl/VulkanCameraBuffers.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once -#include "Render/Export.h" -#include "Render/Camera.h" -#include "VulkanContext.h" -#include "VulkanBuffer.h" - -#include "Core/Singleton.hpp" - -#include -#include -#include -#include -#include -namespace sh::render::vk -{ - class VulkanCameraBuffers : public core::Singleton - { - friend core::Singleton; - private: - const VulkanContext* context = nullptr; - - std::vector cams; - uint32_t cap = 10; - std::stack emptyidx; - - std::unique_ptr cameraData; - - std::size_t dynamicAlignment; - private: - VulkanCameraBuffers() = default; - public: - SH_RENDER_API void Init(const VulkanContext& context); - SH_RENDER_API void Clear(); - - SH_RENDER_API void AddCamera(const Camera& camera); - SH_RENDER_API void RemoveCamera(const Camera& camera); - SH_RENDER_API void UploadDataToGPU(const Camera& camera); - - SH_RENDER_API auto GetDynamicOffset(const Camera& camera) const -> std::optional; - SH_RENDER_API auto GetCameraBuffer() const -> const VulkanBuffer&; - }; -}//namespace \ No newline at end of file diff --git a/include/Render/VulkanImpl/VulkanCommandBuffer.h b/include/Render/VulkanImpl/VulkanCommandBuffer.h index e4aedd7b..601a81d2 100644 --- a/include/Render/VulkanImpl/VulkanCommandBuffer.h +++ b/include/Render/VulkanImpl/VulkanCommandBuffer.h @@ -1,12 +1,19 @@ #pragma once -#include "../Export.h" -#include "../CommandBuffer.h" +#include "Render/Export.h" +#include "Render/CommandBuffer.h" +#include "Render/RenderData.h" #include "VulkanConfig.h" #include "Core/NonCopyable.h" #include #include +namespace sh::render +{ + class Camera; + class Mesh; + struct RenderView; +} namespace sh::render::vk { class VulkanContext; @@ -42,10 +49,16 @@ namespace sh::render::vk SH_RENDER_API auto Build(const std::function& recordFn, bool bOneTimeSubmit = true) -> VkResult; SH_RENDER_API void Begin(bool bOnetime) override; SH_RENDER_API void End() override; + SH_RENDER_API void Reset() override; SH_RENDER_API void Blit(RenderTexture& src, int x, int y, IBuffer& dst) override; - - SH_RENDER_API auto ResetCommand(VkCommandBufferResetFlags flags = 0) -> VkResult; + SH_RENDER_API void Dispatch(const ComputeShader& shader, uint32_t x, uint32_t y, uint32_t z) override; + SH_RENDER_API void SetRenderData(const RenderData& renderData, bool bClearColor = true, bool bClearDepth = true, bool bStoreColor = false, bool bStoreDepth = false) override; + SH_RENDER_API void SetViewport(uint32_t x, uint32_t y, uint32_t width, uint32_t height) override; + SH_RENDER_API void SetScissor(uint32_t x, uint32_t y, uint32_t width, uint32_t height) override; + SH_RENDER_API void DrawMesh(const Drawable& drawable, core::Name passName, std::size_t viewerIdx = 0) override; + SH_RENDER_API void DrawMeshBatch(const std::vector& drawables, core::Name passName, std::size_t viewerIdx = 0) override; + SH_RENDER_API void EmitBarrier(const std::vector& barriers) override; SH_RENDER_API auto GetOrCreateFence() -> VkFence; SH_RENDER_API void DestroyFence(); @@ -58,21 +71,43 @@ namespace sh::render::vk SH_RENDER_API void AddWaitSemaphore(const WaitSemaphore& w); SH_RENDER_API void AddSignalSemaphore(const SignalSemaphore& s); - SH_RENDER_API auto GetWaitSemaphores() const -> const std::vector&; - SH_RENDER_API auto GetSignalSemaphores() const -> const std::vector&; - - SH_RENDER_API auto GetCommandBuffer() const -> VkCommandBuffer; - SH_RENDER_API auto GetCommandPool() const -> VkCommandPool; + SH_RENDER_API auto GetWaitSemaphores() const -> const std::vector& { return waitSemaphores; } + SH_RENDER_API auto GetSignalSemaphores() const -> const std::vector& { return signalSemaphores; } + SH_RENDER_API auto GetCommandBuffer() const -> VkCommandBuffer { return buffer; } + SH_RENDER_API auto GetCommandPool() const -> VkCommandPool { return cmdPool; } SH_RENDER_API void Clear(); + protected: + auto GetRenderCall() const -> uint32_t override { return renderCall; } + private: + void BindCameraSet(const Material& mat, const ShaderPass& pass, VkPipelineLayout pipelineLayout, uint32_t cameraOffset); + void BindMaterialSet(const Material& mat, const ShaderPass& pass, VkPipelineLayout pipelineLayout); + void BindObjectSet(const Drawable& drawable, const ShaderPass& pass, VkPipelineLayout pipelineLayout); + void BindMesh(const Mesh& mesh, uint32_t subMeshIdx, bool bSkinned); private: + struct RenderState + { + RenderTargetLayout layout{}; + const RenderData* renderData = nullptr; + uint32_t targetWidth = 0; + uint32_t targetHeight = 0; + + uint32_t lastPipelineIdx = 0xffffffff; + uint32_t lastPipelineGen = 0xffffffff; + bool bDepthOnly = false; + } renderState; + const VulkanContext& context; VkCommandBuffer buffer = VK_NULL_HANDLE; VkCommandPool cmdPool = VK_NULL_HANDLE; - std::vector waitSemaphores; + std::vector waitSemaphores; std::vector signalSemaphores; VkFence fence = VK_NULL_HANDLE; + + uint32_t renderCall = 0; + + bool bBeginRender = false; }; }//namespace \ No newline at end of file diff --git a/include/Render/VulkanImpl/VulkanCommandBufferPool.h b/include/Render/VulkanImpl/VulkanCommandBufferPool.h index 4e49de68..f101b055 100644 --- a/include/Render/VulkanImpl/VulkanCommandBufferPool.h +++ b/include/Render/VulkanImpl/VulkanCommandBufferPool.h @@ -17,7 +17,7 @@ namespace sh::render::vk class VulkanCommandBufferPool { public: - SH_RENDER_API VulkanCommandBufferPool(const VulkanContext& context, uint32_t graphicsQueueFamily, uint32_t transferQueueFamily); + SH_RENDER_API VulkanCommandBufferPool(const VulkanContext& context, uint32_t graphicsQueueFamily, uint32_t transferQueueFamily, uint32_t computeQueueFamily); SH_RENDER_API ~VulkanCommandBufferPool(); /// @brief 해당 스레드에서 큐 타입에 맞는 커맨드 버퍼를 할당 받는다. @@ -34,22 +34,16 @@ namespace sh::render::vk const VulkanContext& context; const uint32_t graphicsQueueFamily; const uint32_t transferQueueFamily; - + const uint32_t computeQueueFamily; enum QueueType { Graphics = 0, - Transfer = 1 + Transfer = 1, + Compute = 2 }; struct PerThreadData { - PerThreadData() = default; - PerThreadData(PerThreadData&& other) noexcept : - id(other.id) - { - for (int i = 0; i < commands.size(); ++i) - commands[i] = std::move(other.commands[i]); - } struct Command { Command() = default; @@ -68,7 +62,7 @@ namespace sh::render::vk }; std::thread::id id; - std::array commands; + std::array commands; }; struct AllocData diff --git a/include/Render/VulkanImpl/VulkanComputePipeline.h b/include/Render/VulkanImpl/VulkanComputePipeline.h new file mode 100644 index 00000000..c49a3c65 --- /dev/null +++ b/include/Render/VulkanImpl/VulkanComputePipeline.h @@ -0,0 +1,67 @@ +#pragma once +#include "../Export.h" +#include "../ShaderAST.h" +#include "VulkanConfig.h" + +#include "Core/NonCopyable.h" + +#include +#include +#include +#include +namespace sh::render +{ + class ComputeShader; +} +namespace sh::render::vk +{ + class VulkanContext; + + /// @brief 컴퓨트 셰이더 파이프라인. + /// VkShaderModule, 디스크립터셋 레이아웃, 파이프라인 레이아웃, VkPipeline을 모두 보관한다. + class VulkanComputePipeline : public core::INonCopyable + { + public: + struct SetLayout + { + uint32_t set = 0; + VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE; + std::map descriptorSetLayoutBindings; + }; + public: + SH_RENDER_API VulkanComputePipeline(const VulkanContext& context, const ComputeShader& shader); + SH_RENDER_API VulkanComputePipeline(VulkanComputePipeline&& other) noexcept; + SH_RENDER_API ~VulkanComputePipeline(); + + /// @brief 셰이더 모듈, 디스크립터/파이프라인 레이아웃, VkPipeline까지 한번에 생성한다. + SH_RENDER_API auto Build() -> VkResult; + SH_RENDER_API void Clean(); + + SH_RENDER_API auto GetPipeline() const -> VkPipeline { return pipeline; } + SH_RENDER_API auto GetPipelineLayout() const -> VkPipelineLayout { return pipelineLayout; } + SH_RENDER_API auto GetShaderModule() const -> VkShaderModule { return shaderModule; } + + SH_RENDER_API auto GetSetLayouts() const -> const std::vector& { return setlayouts; } + SH_RENDER_API auto GetDescriptorSetLayout(uint32_t set) const -> VkDescriptorSetLayout; + SH_RENDER_API auto GetDescriptorBinding(uint32_t set, uint32_t binding) const -> std::optional; + + /// @brief Numthreads (local_size) 반환. + SH_RENDER_API auto GetGroupSize() const -> std::array; + private: + auto CreateShaderModule() -> VkResult; + void AddDescriptorBinding(uint32_t set, uint32_t binding, VkDescriptorType type); + auto CreateDescriptorLayout() -> VkResult; + auto CreatePipelineLayout() -> VkResult; + auto CreatePipeline() -> VkResult; + + void CleanDescriptors(); + private: + const VulkanContext& context; + const ComputeShader* shader; + + VkShaderModule shaderModule = VK_NULL_HANDLE; + std::vector setlayouts; + VkPipelineLayout pipelineLayout = VK_NULL_HANDLE; + VkPipeline pipeline = VK_NULL_HANDLE; + }; +}//namespace diff --git a/include/Render/VulkanImpl/VulkanComputePipelineManager.h b/include/Render/VulkanImpl/VulkanComputePipelineManager.h new file mode 100644 index 00000000..d93d257e --- /dev/null +++ b/include/Render/VulkanImpl/VulkanComputePipelineManager.h @@ -0,0 +1,39 @@ +#pragma once +#include "Export.h" + +#include "Core/NonCopyable.h" +#include "Core/Observer.hpp" + +#include +#include +#include +namespace sh::core +{ + class SObject; +} +namespace sh::render +{ + class ComputeShader; +} +namespace sh::render::vk +{ + class VulkanContext; + class VulkanComputePipeline; + class VulkanComputePipelineManager : public core::INonCopyable + { + public: + SH_RENDER_API VulkanComputePipelineManager(const VulkanContext& context); + SH_RENDER_API VulkanComputePipelineManager(VulkanComputePipelineManager&& other) = delete; + + /// @brief 파이프라인을 생성하거나 가져온다. 스레드 안전하다. + /// @param computeShader 컴퓨트 셰이더 + SH_RENDER_API auto GetOrCreatePipeline(const ComputeShader& computeShader) -> VulkanComputePipeline&; + private: + const VulkanContext& ctx; + + std::shared_mutex mu; + + core::Observer::Listener onShaderDestroy; + std::map> pipelines; + }; +}//namespace \ No newline at end of file diff --git a/include/Render/VulkanImpl/VulkanContext.h b/include/Render/VulkanImpl/VulkanContext.h index 01651268..b20670e8 100644 --- a/include/Render/VulkanImpl/VulkanContext.h +++ b/include/Render/VulkanImpl/VulkanContext.h @@ -1,6 +1,7 @@ #pragma once -#include "../Export.h" -#include "../IRenderContext.h" +#include "Render/Export.h" +#include "Render/IRenderContext.h" +#include "Render/RenderDataManager.h" #include "VulkanConfig.h" #include "Core/ISyncable.h" @@ -27,8 +28,8 @@ namespace sh::render::vk class VulkanCommandBuffer; class VulkanDescriptorPool; class VulkanPipelineManager; + class VulkanComputePipelineManager; class VulkanQueueManager; - class VulkanRenderImpl; class VulkanContext : public IRenderContext { @@ -39,16 +40,17 @@ namespace sh::render::vk SH_RENDER_API void Init() override; SH_RENDER_API void Clear() override; - SH_RENDER_API auto GetRenderAPIType() const -> RenderAPI override { return RenderAPI::Vulkan; } + auto GetRenderAPIType() const -> RenderAPI override { return RenderAPI::Vulkan; } - SH_RENDER_API auto AllocateCommandBuffer() -> CommandBuffer* override; + SH_RENDER_API auto AllocateCommandBuffer(bool bCompute) -> CommandBuffer* override; SH_RENDER_API void DeallocateCommandBuffer(CommandBuffer& cmd) override; + SH_RENDER_API void SubmitCommand(CommandBuffer& cmd) override; SH_RENDER_API void SetViewport(const glm::vec2& start, const glm::vec2& end) override; - SH_RENDER_API auto GetViewportStart() const -> const glm::vec2 & override; - SH_RENDER_API auto GetViewportEnd() const -> const glm::vec2 & override; - - SH_RENDER_API auto GetRenderImpl() const -> IRenderImpl& override; + auto GetViewportStart() const -> const glm::vec2& override { return viewportStart; } + auto GetViewportEnd() const -> const glm::vec2& override { return viewportEnd; } + auto GetRenderDataManager() const -> const RenderDataManager & override { return renderDataManager; } + auto GetRenderDataManager() -> RenderDataManager& override { return renderDataManager; } SH_RENDER_API auto ReSizing() -> bool; @@ -59,22 +61,23 @@ namespace sh::render::vk /// @brief 멀티 샘플링의 샘플을 지정한다. 기기에서 지원하지 않는다면 최대 지원하는 샘플 수로 지정된다. /// @param sample 샘플 수 SH_RENDER_API void SetSampleCount(VkSampleCountFlagBits sample); - SH_RENDER_API auto GetSampleCount() const -> VkSampleCountFlagBits { return sample; } - - SH_RENDER_API auto GetInstance() const -> VkInstance { return instance; } - SH_RENDER_API auto GetGPU() const -> VkPhysicalDevice { return gpu; } - SH_RENDER_API auto GetGPUName() const -> std::string_view { return gpuProp.deviceName; } - SH_RENDER_API auto GetGPUProperty() const -> const VkPhysicalDeviceProperties& { return gpuProp; } - SH_RENDER_API auto GetDevice() const -> VkDevice { return device; } - SH_RENDER_API auto GetSwapChain() const -> VulkanSwapChain& { return *swapChain; } - SH_RENDER_API auto GetCommandBufferPool() const -> VulkanCommandBufferPool& { return *cmdPool; } - SH_RENDER_API auto GetQueueManager() const -> VulkanQueueManager& { return *queueManager; } - SH_RENDER_API auto GetDescriptorPool() const -> VulkanDescriptorPool& { return *descPool; } - SH_RENDER_API auto GetAllocator() const -> VmaAllocator { return allocator; } - SH_RENDER_API auto GetPipelineManager() const -> VulkanPipelineManager& { return *pipelineManager; } - SH_RENDER_API auto GetEmptyDescriptorSetLayout() const -> VkDescriptorSetLayout { return emptyDescLayout; } - SH_RENDER_API auto GetEmptyDescriptorSet() const -> VkDescriptorSet { return emptyDescSet; } + SH_RENDER_API auto GetMaxSampleCount() const -> VkSampleCountFlagBits; + auto GetSampleCount() const -> VkSampleCountFlagBits { return sample; } + auto GetInstance() const -> VkInstance { return instance; } + auto GetGPU() const -> VkPhysicalDevice { return gpu; } + auto GetGPUName() const -> std::string_view { return gpuProp.deviceName; } + auto GetGPUProperty() const -> const VkPhysicalDeviceProperties& { return gpuProp; } + auto GetDevice() const -> VkDevice { return device; } + auto GetSwapChain() const -> VulkanSwapChain& { return *swapChain; } + auto GetCommandBufferPool() const -> VulkanCommandBufferPool& { return *cmdPool; } + auto GetQueueManager() const -> VulkanQueueManager& { return *queueManager; } + auto GetDescriptorPool() const -> VulkanDescriptorPool& { return *descPool; } + auto GetAllocator() const -> VmaAllocator { return allocator; } + auto GetPipelineManager() const -> VulkanPipelineManager& { return *pipelineManager; } + auto GetComputePipelineManager() const -> VulkanComputePipelineManager& { return *computePipelineManager; } + auto GetEmptyDescriptorSetLayout() const -> VkDescriptorSetLayout { return emptyDescLayout; } + auto GetEmptyDescriptorSet() const -> VkDescriptorSet { return emptyDescSet; } private: void PrepareValidationLayer(); void CreateDebugInfo(); @@ -124,7 +127,7 @@ namespace sh::render::vk std::unique_ptr cmdPool; std::unique_ptr descPool; std::unique_ptr pipelineManager; - std::unique_ptr renderImpl; + std::unique_ptr computePipelineManager; VkDescriptorSetLayout emptyDescLayout; VkDescriptorSet emptyDescSet; @@ -137,8 +140,10 @@ namespace sh::render::vk mutable std::shared_mutex commandPoolMutex; mutable std::shared_mutex commandBufferMutex; + RenderDataManager renderDataManager; + bool bInit = false; bool bFindValidationLayer = false; bool bEnableValidationLayers = false; }; -} \ No newline at end of file +}//namespace \ No newline at end of file diff --git a/include/Render/VulkanImpl/VulkanUniformBuffer.h b/include/Render/VulkanImpl/VulkanDescriptorSet.h similarity index 54% rename from include/Render/VulkanImpl/VulkanUniformBuffer.h rename to include/Render/VulkanImpl/VulkanDescriptorSet.h index 99d0af75..465fcf95 100644 --- a/include/Render/VulkanImpl/VulkanUniformBuffer.h +++ b/include/Render/VulkanImpl/VulkanDescriptorSet.h @@ -1,33 +1,35 @@ #pragma once #include "../Export.h" #include "VulkanConfig.h" -#include "../IUniformBuffer.h" +#include "../IShaderBinding.h" +#include namespace sh::render::vk { class VulkanContext; /// @brief 디스크립터 셋을 추상화한 클래스 - class VulkanUniformBuffer : public IUniformBuffer + class VulkanDescriptorSet : public IShaderBinding { - private: - const VulkanContext* context; - - VkDescriptorSet descSet; - uint32_t set = 0; - - bool bDynamic = false; public: - SH_RENDER_API VulkanUniformBuffer(); - SH_RENDER_API VulkanUniformBuffer(VulkanUniformBuffer&& other) noexcept; - SH_RENDER_API ~VulkanUniformBuffer(); + SH_RENDER_API VulkanDescriptorSet(); + SH_RENDER_API VulkanDescriptorSet(VulkanDescriptorSet&& other) noexcept; + SH_RENDER_API ~VulkanDescriptorSet(); - SH_RENDER_API void Create(const IRenderContext& context, const ShaderPass& shader, UniformStructLayout::Type type) override; + SH_RENDER_API void Create(const IRenderContext& context, const ShaderPass& shader, UniformStructLayout::Usage usage) override; + SH_RENDER_API void Create(const IRenderContext& context, const ComputeShader& shader) override; SH_RENDER_API void Clear() override; SH_RENDER_API void Link(uint32_t binding, const IBuffer& buffer, std::size_t bufferSize) override; SH_RENDER_API void Link(uint32_t binding, const Texture& texture) override; - SH_RENDER_API auto GetSetNumber() const -> uint32_t; - SH_RENDER_API auto GetVkDescriptorSet() const -> VkDescriptorSet; + SH_RENDER_API auto GetSetIndex() const -> uint32_t { return set; } + SH_RENDER_API auto GetVkDescriptorSet() const -> VkDescriptorSet { return descSet; } + private: + const VulkanContext* context; + + VkDescriptorSet descSet; + uint32_t set = 0; + + std::map descriptorTypes; }; }//namespace \ No newline at end of file diff --git a/include/Render/VulkanImpl/VulkanImageBuffer.h b/include/Render/VulkanImpl/VulkanImageBuffer.h index c8355b4d..73a1d723 100644 --- a/include/Render/VulkanImpl/VulkanImageBuffer.h +++ b/include/Render/VulkanImpl/VulkanImageBuffer.h @@ -55,8 +55,7 @@ namespace sh::render::vk VkAccessFlags dstAccess); SH_RENDER_API static auto ConvertTextureFormat(TextureFormat format) -> VkFormat; private: - static auto GetChannelCount(VkFormat format) -> uint32_t; - static auto IsDepthTexture(TextureFormat format) -> bool; + static auto GetFormatPixelSize(VkFormat format) -> uint32_t; private: const VulkanContext* ctx = nullptr; @@ -71,11 +70,11 @@ namespace sh::render::vk VkSampleCountFlagBits sample = VkSampleCountFlagBits::VK_SAMPLE_COUNT_1_BIT; uint32_t width = 0; uint32_t height = 0; - uint32_t channel = 4; + uint32_t pixelSize = 4; uint32_t aniso = 0; uint32_t mipLevel = 1; bool bSwapChainImg = false; bool bRenderTarget = false; }; -} \ No newline at end of file +}//namespace \ No newline at end of file diff --git a/include/Render/VulkanImpl/VulkanPipeline.h b/include/Render/VulkanImpl/VulkanPipeline.h index 37334614..327a4fa8 100644 --- a/include/Render/VulkanImpl/VulkanPipeline.h +++ b/include/Render/VulkanImpl/VulkanPipeline.h @@ -1,8 +1,8 @@ #pragma once #include "Render/Export.h" #include "Render/ShaderEnum.h" +#include "Render/RenderData.h" #include "VulkanConfig.h" -#include "../RenderTarget.h" #include "Core/NonCopyable.h" @@ -96,4 +96,4 @@ namespace sh::render::vk bool bZWrite = true; bool bZTest = true; }; -} \ No newline at end of file +}//namespace \ No newline at end of file diff --git a/include/Render/VulkanImpl/VulkanPipelineManager.h b/include/Render/VulkanImpl/VulkanPipelineManager.h index ca91840e..a6bca01f 100644 --- a/include/Render/VulkanImpl/VulkanPipelineManager.h +++ b/include/Render/VulkanImpl/VulkanPipelineManager.h @@ -1,10 +1,10 @@ #pragma once -#include "../Export.h" +#include "Render/Export.h" +#include "Render/Mesh.h" +#include "Render/StencilState.h" +#include "Render/RenderData.h" #include "VulkanPipeline.h" #include "VulkanConfig.h" -#include "../Mesh.h" -#include "../StencilState.h" -#include "../RenderTarget.h" #include "Core/SContainer.hpp" #include "Core/Util.h" diff --git a/include/Render/VulkanImpl/VulkanQueueManager.h b/include/Render/VulkanImpl/VulkanQueueManager.h index c00f14bf..f4120bb5 100644 --- a/include/Render/VulkanImpl/VulkanQueueManager.h +++ b/include/Render/VulkanImpl/VulkanQueueManager.h @@ -15,7 +15,7 @@ namespace sh::render::vk class VulkanQueueManager : public core::INonCopyable { public: - enum class Role { Graphics, Present, Transfer }; + enum class Role { Graphics, Present, Transfer, Compute }; struct Family { @@ -29,11 +29,13 @@ namespace sh::render::vk Family graphics; Family present; Family transfer; + Family compute; // 역할별 queue index(패밀리 내부 인덱스) uint32_t graphicsQIndex = 0; uint32_t presentQIndex = 0; uint32_t transferQIndex = 0; + uint32_t computeQIndex = 0; }; public: @@ -56,12 +58,11 @@ namespace sh::render::vk auto PickGraphicsFamily() const -> std::optional; auto PickPresentFamily(VkSurfaceKHR surface, uint32_t preferFamily) const -> std::optional; auto PickTransferFamily(uint32_t avoidFamily) const -> std::optional; + auto PickComputeFamily() const -> std::optional; void ComputeRequestedCountsAndIndices(); - auto ClampRequested(uint32_t want, uint32_t avail) const ->uint32_t; auto GetMutexForRole(Role role) const -> std::mutex*; - private: const VulkanContext& ctx; @@ -71,20 +72,23 @@ namespace sh::render::vk VkQueue graphicsQueue = VK_NULL_HANDLE; VkQueue presentQueue = VK_NULL_HANDLE; VkQueue transferQueue = VK_NULL_HANDLE; + VkQueue computeQueue = VK_NULL_HANDLE; // 동일 VkQueue를 공유하면 동일 mutex를 공유하도록 포인터로 묶는다 mutable std::mutex graphicsMtx; mutable std::mutex presentMtx; mutable std::mutex transferMtx; + mutable std::mutex computeMtx; mutable std::mutex* graphicsLock = &graphicsMtx; mutable std::mutex* presentLock = &presentMtx; mutable std::mutex* transferLock = &transferMtx; + mutable std::mutex* computeLock = &computeMtx; - // BuildQueueCreateInfos에서 사용하는 우선순위 배열 (가변 길이) std::vector graphicsPriorities; std::vector presentPriorities; std::vector transferPriorities; + std::vector computePriorities; bool bFamiliesReady = false; bool bQueuesReady = false; diff --git a/include/Render/VulkanImpl/VulkanRenderImpl.h b/include/Render/VulkanImpl/VulkanRenderImpl.h deleted file mode 100644 index de1503c6..00000000 --- a/include/Render/VulkanImpl/VulkanRenderImpl.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once -#include "../Export.h" -#include "../IRenderImpl.h" -#include "../RenderTarget.h" -#include "VulkanContext.h" -#include "VulkanCameraBuffers.h" - -namespace sh::render -{ - class ShaderPass; -} - -namespace sh::render::vk -{ - class VulkanCommandBuffer; - class VulkanCameraBuffers; - - class VulkanRenderImpl : public IRenderImpl - { - public: - SH_RENDER_API VulkanRenderImpl(VulkanContext& context); - SH_RENDER_API void EmitBarrier(CommandBuffer& cmd, const std::vector& barriers) const override; - SH_RENDER_API void RecordCommand(CommandBuffer& cmd, const core::Name& passName, const RenderTarget& renderData, const DrawList& drawList, bool bStoreImage) const override; - private: - void BindCameraSet(VulkanCommandBuffer& cmd, VkPipelineLayout layout, const ShaderPass& pass, const Material& mat, uint32_t cameraOffset) const; - void BindMaterialSet(VulkanCommandBuffer& cmd, VkPipelineLayout layout, const ShaderPass& pass, const Material& mat) const; - void BindObjectSet(VulkanCommandBuffer& cmd, VkPipelineLayout layout, const ShaderPass& pass, Drawable& drawable) const; - void DrawMesh(VulkanCommandBuffer& cmd, const ShaderPass& pass, const Mesh& mesh, int subMeshIndex = -1) const; - void RenderDrawable(VulkanCommandBuffer& cmd, const core::Name& passName, const RenderTarget& renderData, const DrawList& drawList, const RenderTargetLayout& layout) const; - private: - VulkanContext& ctx; - VulkanCameraBuffers* cameraManager = nullptr; - }; -}//namespace \ No newline at end of file diff --git a/include/Render/VulkanImpl/VulkanRenderer.h b/include/Render/VulkanImpl/VulkanRenderer.h index 274a28da..be9fd7d4 100644 --- a/include/Render/VulkanImpl/VulkanRenderer.h +++ b/include/Render/VulkanImpl/VulkanRenderer.h @@ -1,5 +1,5 @@ #pragma once -#include "../Renderer.h" +#include "Render/Renderer.h" #include "VulkanConfig.h" #include "Core/NonCopyable.h" @@ -18,7 +18,6 @@ namespace sh::render::vk { class VulkanContext; - class VulkanCameraBuffers; class VulkanCommandBuffer; class VulkanRenderer : @@ -28,31 +27,26 @@ namespace sh::render::vk SH_RENDER_API VulkanRenderer(); SH_RENDER_API ~VulkanRenderer(); - SH_RENDER_API bool Init(sh::window::Window& win) override; + SH_RENDER_API void CreateContext(const window::Window& win) override; + SH_RENDER_API void DestroyContext() override; + SH_RENDER_API bool Init(window::Window& win) override; SH_RENDER_API bool Resizing() override; SH_RENDER_API void Clear() override; - SH_RENDER_API bool IsInit() const override; + SH_RENDER_API bool IsInit() const override { return isInit; } SH_RENDER_API void Render() override; SH_RENDER_API void WaitForCurrentFrame() override; - SH_RENDER_API auto GetCurrentFrame() const -> int; SH_RENDER_API auto GetWidth() const -> uint32_t override; SH_RENDER_API auto GetHeight() const -> uint32_t override; - - SH_RENDER_API auto GetTimelineSemaphore() const -> VkSemaphore; - /// @brief [원자적] 첫번째 패스의 타임라인 세마포어의 값을 가져온다. - /// @return 타임라인 세마포어 값 - SH_RENDER_API auto GetTimelineValue() const -> uint64_t; - - SH_RENDER_API auto GetContext() const -> IRenderContext* override; SH_RENDER_API void Sync() override; - protected: - void OnCameraAdded(const Camera* camera) override; - void OnCameraRemoved(const Camera* camera) override; + + SH_RENDER_API auto GetContext() const -> IRenderContext* override; + auto GetTimelineSemaphore() const -> VkSemaphore { return timelineSemaphore; } + auto GetCurrentFrame() const -> int { return currentFrame; } private: auto CreateSyncObjects() -> VkResult; void DestroySyncObjects(); @@ -63,14 +57,9 @@ namespace sh::render::vk VkSemaphore renderFinishedSemaphore = VK_NULL_HANDLE; VkSemaphore timelineSemaphore = VK_NULL_HANDLE; - uint64_t timelineValue = 0; - std::atomic timelineValueAtomic = 0; - std::vector frameFences; VkFence inFlightFence; - VulkanCameraBuffers* camManager = nullptr; - int currentFrame; bool isInit = false; diff --git a/include/Render/VulkanImpl/VulkanShaderPass.h b/include/Render/VulkanImpl/VulkanShaderPass.h index 989858bf..5f5c7b9f 100644 --- a/include/Render/VulkanImpl/VulkanShaderPass.h +++ b/include/Render/VulkanImpl/VulkanShaderPass.h @@ -1,16 +1,11 @@ #pragma once - #include "../Export.h" #include "../ShaderPass.h" -#include "VulkanConfig.h" -#include "VulkanPipeline.h" - -#include "Core/SContainer.hpp" -#include "Core/NonCopyable.h" #include #include - +#include +#include namespace sh::render::vk { class VulkanContext; @@ -23,10 +18,11 @@ namespace sh::render::vk VkShaderModule vert; VkShaderModule frag; }; - struct DescryptorSetLayoutInfo + struct SetLayout { - VkDescriptorSetLayout layout; - bool bDynamic; + uint32_t set = 0; + VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE; + std::map descriptorSetLayoutBindings; }; public: SH_RENDER_API VulkanShaderPass(const VulkanContext& context, const ShaderModules& shaderModules, const ShaderAST::PassNode& passNode); @@ -35,18 +31,20 @@ namespace sh::render::vk SH_RENDER_API void Clear() override; - SH_RENDER_API auto GetVertexShader() const -> const VkShaderModule; - SH_RENDER_API auto GetFragmentShader() const -> const VkShaderModule; - SH_RENDER_API void Build(); + SH_RENDER_API auto GetVertexShader() const -> const VkShaderModule { return vertShader; } + SH_RENDER_API auto GetFragmentShader() const -> const VkShaderModule { return fragShader; } + SH_RENDER_API auto GetPipelineLayout() const -> VkPipelineLayout { return pipelineLayout; } /// @brief 디스크립터셋 레이아웃을 반환 하는 함수 /// @param set 세트 번호 /// @return 디스크립터셋 레이아웃 포인터 - SH_RENDER_API auto GetDescriptorSetLayout(uint32_t set) const -> DescryptorSetLayoutInfo; - SH_RENDER_API auto GetSetCount() const -> uint32_t; - SH_RENDER_API auto GetPipelineLayout() const -> VkPipelineLayout; - SH_RENDER_API auto GetSpecializationMapEntry() const -> const std::vector&; + SH_RENDER_API auto GetDescriptorSetLayout(uint32_t set) const -> VkDescriptorSetLayout; + SH_RENDER_API auto GetSetCount() const -> std::size_t { return setlayouts.size(); } + SH_RENDER_API auto GetSetLayout(uint32_t set) const -> std::optional; + SH_RENDER_API auto GetSetLayouts() const -> const std::vector& { return setlayouts; } + SH_RENDER_API auto GetSpecializationMapEntry() const -> const std::vector& { return specializationEntry; } + SH_RENDER_API auto GetDescriptorBinding(uint32_t set, uint32_t binding) const -> std::optional; private: void AddDescriptorBinding(uint32_t set, uint32_t binding, VkDescriptorType type, VkShaderStageFlagBits stage); auto CreateDescriptorLayout() -> VkResult; @@ -59,8 +57,7 @@ namespace sh::render::vk VkShaderModule vertShader; VkShaderModule fragShader; - std::vector> descriptorBindings; // set, binding - std::vector descriptorSetLayout; + std::vector setlayouts; VkPipelineLayout pipelineLayout; @@ -68,4 +65,4 @@ namespace sh::render::vk bool bUseMatrixModel = false; }; -} \ No newline at end of file +}//namespace \ No newline at end of file diff --git a/include/Render/VulkanImpl/VulkanSwapChain.h b/include/Render/VulkanImpl/VulkanSwapChain.h index bc5479f2..d2c135e5 100644 --- a/include/Render/VulkanImpl/VulkanSwapChain.h +++ b/include/Render/VulkanImpl/VulkanSwapChain.h @@ -1,6 +1,6 @@ #pragma once -#include "../Export.h" -#include "../RenderTarget.h" +#include "Render/Export.h" +#include "Render/RenderData.h" #include "VulkanConfig.h" #include "VulkanImageBuffer.h" @@ -45,19 +45,19 @@ namespace sh::render::vk SH_RENDER_API auto IsSwapChainSupport(VkPhysicalDevice gpu) -> bool; - SH_RENDER_API auto GetSurface() const -> const VkSurfaceKHR { return surface; } - SH_RENDER_API auto GetSwapChain() const -> const VkSwapchainKHR { return swapChain; } - SH_RENDER_API auto GetSwapChainDetail() const -> const SwapChainSupportDetails& { return details; } - SH_RENDER_API auto GetSwapChainSize() const -> VkExtent2D { return swapChainSize; } - SH_RENDER_API auto GetSwapChainImageFormat() const -> VkFormat { return swapChainImageFormat; } - SH_RENDER_API auto GetSwapChainImages() const -> const std::vector& { return swapChainImages; } - SH_RENDER_API auto GetSwapChainImages() -> std::vector& { return swapChainImages; } - SH_RENDER_API auto GetSwapChainMSAAImages() const -> const std::vector& { return swapChainImagesMSAA; } - SH_RENDER_API auto GetSwapChainMSAAImages() -> std::vector& { return swapChainImagesMSAA; } - SH_RENDER_API auto GetSwapChainDepthImages() const -> const std::vector& { return swapChainImagesDepth; } - SH_RENDER_API auto GetSwapChainDepthImages() -> std::vector& { return swapChainImagesDepth; } - SH_RENDER_API auto GetSwapChainImageCount() const -> uint32_t { return swapChainImageCount; } - SH_RENDER_API auto GetRenderTargetLayout() const -> const RenderTargetLayout& { return rtLayout; } + auto GetSurface() const -> const VkSurfaceKHR { return surface; } + auto GetSwapChain() const -> const VkSwapchainKHR { return swapChain; } + auto GetSwapChainDetail() const -> const SwapChainSupportDetails& { return details; } + auto GetSwapChainSize() const -> VkExtent2D { return swapChainSize; } + auto GetSwapChainImageFormat() const -> VkFormat { return swapChainImageFormat; } + auto GetSwapChainImages() const -> const std::vector& { return swapChainImages; } + auto GetSwapChainImages() -> std::vector& { return swapChainImages; } + auto GetSwapChainMSAAImages() const -> const std::vector& { return swapChainImagesMSAA; } + auto GetSwapChainMSAAImages() -> std::vector& { return swapChainImagesMSAA; } + auto GetSwapChainDepthImages() const -> const std::vector& { return swapChainImagesDepth; } + auto GetSwapChainDepthImages() -> std::vector& { return swapChainImagesDepth; } + auto GetSwapChainImageCount() const -> uint32_t { return swapChainImageCount; } + auto GetRenderTargetLayout() const -> const RenderTargetLayout& { return rtLayout; } private: void QuerySwapChainDetails(VkPhysicalDevice gpu); auto SelectSurfaceFormat() -> VkSurfaceFormatKHR; @@ -79,4 +79,4 @@ namespace sh::render::vk RenderTargetLayout rtLayout; }; -} \ No newline at end of file +}//namespace \ No newline at end of file diff --git a/include/Render/VulkanImpl/VulkanTextureBuffer.h b/include/Render/VulkanImpl/VulkanTextureBuffer.h deleted file mode 100644 index cc27a82d..00000000 --- a/include/Render/VulkanImpl/VulkanTextureBuffer.h +++ /dev/null @@ -1,45 +0,0 @@ -//#pragma once -//#include "../Export.h" -//#include "../ITextureBuffer.h" -// -//#include "VulkanImageBuffer.h" -// -//#include -// -//namespace sh::render::vk -//{ -// class VulkanContext; -// class VulkanQueueManager; -// -// class VulkanTextureBuffer : public ITextureBuffer -// { -// public: -// SH_RENDER_API VulkanTextureBuffer(); -// SH_RENDER_API VulkanTextureBuffer(VulkanTextureBuffer&& other) noexcept; -// SH_RENDER_API ~VulkanTextureBuffer(); -// -// SH_RENDER_API void Create(const IRenderContext& context, const CreateInfo& info) override; -// SH_RENDER_API void Clean() override; -// -// /// @brief [게임 스레드용] 텍스쳐 버퍼에 데이터를 지정한다. -// /// @param data 데이터 -// /// @param mipLevel 밉 레벨 -// SH_RENDER_API void SetData(const void* data, uint32_t mipLevel = 1) override; -// -// SH_RENDER_API auto GetImageBuffer() const -> VulkanImageBuffer*; -// SH_RENDER_API auto GetSize() const -> std::size_t; -// private: -// void CopyBufferToImageCommand(VkCommandBuffer cmd, VkBuffer buffer, VkImage image, uint32_t width, uint32_t height, uint32_t mipLevel); -// private: -// const VulkanContext* context = nullptr; -// VulkanQueueManager* queueManager = nullptr; -// -// std::unique_ptr imgBuffer; -// -// uint32_t width = 32, height = 32, channel = 4; -// -// VkFormat format = VkFormat::VK_FORMAT_R8G8B8A8_UNORM; -// -// bool isRenderTexture; -// }; -//} \ No newline at end of file diff --git a/include/Render/VulkanImpl/VulkanVertexBuffer.h b/include/Render/VulkanImpl/VulkanVertexBuffer.h index 89cd39c1..c54b10b4 100644 --- a/include/Render/VulkanImpl/VulkanVertexBuffer.h +++ b/include/Render/VulkanImpl/VulkanVertexBuffer.h @@ -29,8 +29,8 @@ namespace sh::render::vk SH_RENDER_API static auto GetBindingDescription() -> VkVertexInputBindingDescription; SH_RENDER_API static auto GetAttributeDescriptions() -> std::vector; - SH_RENDER_API auto GetVertexBuffer() const -> const VulkanBuffer&; - SH_RENDER_API auto GetIndexBuffer() const -> const VulkanBuffer&; + SH_RENDER_API auto GetVertexBuffer() const -> const VulkanBuffer& { return vertexBuffer; } + SH_RENDER_API auto GetIndexBuffer() const -> const VulkanBuffer& { return indexBuffer; } private: void CreateVertexBuffer(const Mesh& mesh); private: diff --git a/resource/ProjectTemplate/Assets/SampleWorld.world b/resource/ProjectTemplate/Assets/SampleWorld.world new file mode 100644 index 00000000..6e8011e1 --- /dev/null +++ b/resource/ProjectTemplate/Assets/SampleWorld.world @@ -0,0 +1,396 @@ +{ + "cam": { + "Camera": { + "farPlane": 10000.0, + "fov": 60.0, + "lookPos": [ + -0.13613072037696838, + -0.5873733162879944, + -0.809805154800415 + ], + "nearPlane": 0.10000000149011612, + "priority": 0, + "projection": 0, + "renderTexture": "180635b4e4d1a98ebb0064ab47dc452a" + }, + "Component": { + "hideInspector": false, + "priority": 0 + }, + "EditorCamera": { + "distance": 9.383123397827148 + }, + "SObject": null, + "fullType": "sh::game::EditorCamera", + "name": "EditorCamera", + "type": "EditorCamera", + "uuid": "849679621b2c3878ef538c9b069b0ffc", + "version": 1 + }, + "camPos": [ + 0.19899359345436096, + 5.197760581970215, + 6.570095062255859 + ], + "editorObjs": null, + "name": "World", + "objs": [ + { + "Components": [ + { + "Component": { + "hideInspector": false, + "priority": 0 + }, + "SObject": null, + "Transform": { + "quat": [ + 0.0, + 0.0, + 0.0, + 1.0 + ], + "vPosition": [ + 0.0, + 0.0, + 0.0 + ], + "vRotation": [ + 0.0, + 0.0, + 0.0 + ], + "vScale": [ + 5.0, + 5.0, + 5.0 + ] + }, + "fullType": "sh::game::Transform", + "name": "Unknown", + "type": "Transform", + "uuid": "8467e3e80fc4f2ee568a6397bed89eb4", + "version": 1 + }, + { + "Component": { + "hideInspector": false, + "priority": 0 + }, + "MeshRenderer": { + "mats": [ + "ed351337b72281ef5f457457b65b8060" + ], + "mesh": "bbc4ef7ec45dce223297a224f8093f21", + "renderTag": 1 + }, + "SObject": null, + "fullType": "sh::game::MeshRenderer", + "name": "MeshRenderer", + "type": "MeshRenderer", + "uuid": "48b1921fcf12e85469036c4404a65f15", + "version": 1 + } + ], + "GameObject": { + "bEditorOnly": false, + "bEnable": true, + "bStatic": false, + "hideInspector": false + }, + "name": "Quad", + "type": "GameObject", + "uuid": "c84b38fb3db87ac051ac5d0b797443c1", + "version": 1 + }, + { + "Components": [ + { + "Component": { + "hideInspector": false, + "priority": 0 + }, + "SObject": null, + "Transform": { + "quat": [ + 0.0, + 0.0, + 0.0, + 1.0 + ], + "vPosition": [ + 0.0, + 0.5, + 0.0 + ], + "vRotation": [ + 0.0, + -0.0, + 0.0 + ], + "vScale": [ + 1.0, + 1.0, + 1.0 + ] + }, + "fullType": "sh::game::Transform", + "name": "Unknown", + "type": "Transform", + "uuid": "61353cbcb255ce1e1fa35b7e8aa364ad", + "version": 1 + }, + { + "Component": { + "hideInspector": false, + "priority": 0 + }, + "MeshRenderer": { + "mats": [ + "ed351337b72281ef5f457457b65b8060" + ], + "mesh": "bbc4ef7ec45dce223297a224f8093f20", + "renderTag": 1 + }, + "SObject": null, + "fullType": "sh::game::MeshRenderer", + "name": "MeshRenderer", + "type": "MeshRenderer", + "uuid": "6bf0748332a42bec3a7006ea08c3a7fb", + "version": 1 + } + ], + "GameObject": { + "bEditorOnly": false, + "bEnable": true, + "bStatic": false, + "hideInspector": false + }, + "name": "Cube", + "type": "GameObject", + "uuid": "647942f843f96152cce200a1435da773", + "version": 1 + }, + { + "Components": [ + { + "Component": { + "hideInspector": false, + "priority": 0 + }, + "SObject": null, + "Transform": { + "quat": [ + 0.0, + 0.0, + 0.0, + 1.0 + ], + "vPosition": [ + 0.0, + 0.0, + 0.0 + ], + "vRotation": [ + 0.0, + 0.0, + 0.0 + ], + "vScale": [ + 1.0, + 1.0, + 1.0 + ] + }, + "fullType": "sh::game::Transform", + "name": "Unknown", + "type": "Transform", + "uuid": "6de53e419d723c382dd4da942d436a1a", + "version": 1 + }, + { + "Component": { + "hideInspector": false, + "priority": 0 + }, + "DirectionalLight": { + "direction": [ + -1.0, + -1.0, + -1.0 + ], + "intensity": 1.0 + }, + "LightBase": { + "bCastShadow": true, + "shadowBias": 0.004999999888241291, + "shadowFarPlane": 100.0, + "shadowMapResolution": 1024, + "shadowNearPlane": 0.10000000149011612, + "shadowOrthoSize": 20.0 + }, + "SObject": null, + "fullType": "sh::game::DirectionalLight", + "name": "DirectionalLight", + "type": "DirectionalLight", + "uuid": "0db6d7a50692bfccdb72c587d78dabc7", + "version": 1 + } + ], + "GameObject": { + "bEditorOnly": false, + "bEnable": true, + "bStatic": false, + "hideInspector": false + }, + "name": "Light", + "type": "GameObject", + "uuid": "6ceb7e7f4dbbc7be3314cda5662544b5", + "version": 1 + }, + { + "Components": [ + { + "Component": { + "hideInspector": false, + "priority": 0 + }, + "SObject": null, + "Transform": { + "quat": [ + 0.0, + 0.0, + 0.0, + 1.0 + ], + "vPosition": [ + -2.0, + 1.0, + -2.0 + ], + "vRotation": [ + 0.0, + -0.0, + 0.0 + ], + "vScale": [ + 1.0, + 2.0, + 1.0 + ] + }, + "fullType": "sh::game::Transform", + "name": "Unknown", + "type": "Transform", + "uuid": "02ca20a96bfc8bdb056cdeb4708f91fa", + "version": 1 + }, + { + "Component": { + "hideInspector": false, + "priority": 0 + }, + "MeshRenderer": { + "mats": [ + "ed351337b72281ef5f457457b65b8060" + ], + "mesh": "bbc4ef7ec45dce223297a224f8093f20", + "renderTag": 1 + }, + "SObject": null, + "fullType": "sh::game::MeshRenderer", + "name": "MeshRenderer", + "type": "MeshRenderer", + "uuid": "ed3d01dc72d0cf8ac2de51961d967c87", + "version": 1 + } + ], + "GameObject": { + "bEditorOnly": false, + "bEnable": true, + "bStatic": false, + "hideInspector": false + }, + "name": "Cube", + "type": "GameObject", + "uuid": "12ff273a34e3102c74d1f52f0d17d653", + "version": 1 + }, + { + "Components": [ + { + "Component": { + "hideInspector": false, + "priority": 0 + }, + "SObject": null, + "Transform": { + "quat": [ + 0.0, + 0.0, + 0.0, + 1.0 + ], + "vPosition": [ + 4.0, + 4.0, + 4.0 + ], + "vRotation": [ + 0.0, + 0.0, + 0.0 + ], + "vScale": [ + 1.0, + 1.0, + 1.0 + ] + }, + "fullType": "sh::game::Transform", + "name": "Unknown", + "type": "Transform", + "uuid": "971f215a0d326bdec2b0f32277ccdb3d", + "version": 1 + }, + { + "Camera": { + "farPlane": 1000.0, + "fov": 60.0, + "lookPos": [ + 0.0, + 0.0, + 0.0 + ], + "nearPlane": 0.10000000149011612, + "priority": 0, + "projection": 0 + }, + "Component": { + "hideInspector": false, + "priority": 0 + }, + "SObject": null, + "fullType": "sh::game::Camera", + "name": "Camera", + "type": "Camera", + "uuid": "42dd416bc98175fdc72f01bfecc37973", + "version": 1 + } + ], + "GameObject": { + "bEditorOnly": false, + "bEnable": true, + "bStatic": false, + "hideInspector": false + }, + "name": "Camera", + "type": "GameObject", + "uuid": "30cc3381a4934bedcce6082de63ae3f4", + "version": 1 + } + ], + "type": "EditorWorld", + "uuid": "12c5133bfc598964668e5266f0f74a03", + "version": 1 +} \ No newline at end of file diff --git a/resource/ProjectTemplate/Assets/SampleWorld/SampleWorld.world.meta b/resource/ProjectTemplate/Assets/SampleWorld.world.meta similarity index 79% rename from resource/ProjectTemplate/Assets/SampleWorld/SampleWorld.world.meta rename to resource/ProjectTemplate/Assets/SampleWorld.world.meta index 3f66c60b..88c7fb58 100644 --- a/resource/ProjectTemplate/Assets/SampleWorld/SampleWorld.world.meta +++ b/resource/ProjectTemplate/Assets/SampleWorld.world.meta @@ -1,5 +1,5 @@ { - "name": "World", + "name": "SampleWorld", "typeHash": 3952108875956490026, "uuid": "c3a4ec39f722e6d2704dceb7afbdc3c8", "version": 1 diff --git a/resource/ProjectTemplate/Assets/SampleWorld/assets/DefaultMaterial.mat b/resource/ProjectTemplate/Assets/SampleWorld/DefaultMaterial.mat similarity index 82% rename from resource/ProjectTemplate/Assets/SampleWorld/assets/DefaultMaterial.mat rename to resource/ProjectTemplate/Assets/SampleWorld/DefaultMaterial.mat index 16a38e04..8c69e234 100644 --- a/resource/ProjectTemplate/Assets/SampleWorld/assets/DefaultMaterial.mat +++ b/resource/ProjectTemplate/Assets/SampleWorld/DefaultMaterial.mat @@ -1,13 +1,15 @@ { "Constant": [ + [], [] ], "Material": { "shader": "0131fbb6184d3156cf10504d1df27929" }, "MaterialPropertyBlock": { + "matrix": null, "scalar": { - "ambient": 0.019999999552965164 + "ambient": 0.0 }, "textures": { "normalTex": "05d320be6d0c3a57ec078815a45cd58b", @@ -15,7 +17,7 @@ }, "vector": null }, - "name": "NewMaterial.mat", + "name": "DefaultMaterial", "type": "Material", "uuid": "ed351337b72281ef5f457457b65b8060", "version": 1 diff --git a/resource/ProjectTemplate/Assets/SampleWorld/assets/DefaultMaterial.mat.meta b/resource/ProjectTemplate/Assets/SampleWorld/DefaultMaterial.mat.meta similarity index 77% rename from resource/ProjectTemplate/Assets/SampleWorld/assets/DefaultMaterial.mat.meta rename to resource/ProjectTemplate/Assets/SampleWorld/DefaultMaterial.mat.meta index af7fa432..18bbf877 100644 --- a/resource/ProjectTemplate/Assets/SampleWorld/assets/DefaultMaterial.mat.meta +++ b/resource/ProjectTemplate/Assets/SampleWorld/DefaultMaterial.mat.meta @@ -1,5 +1,5 @@ { - "name": "NewMaterial.mat", + "name": "DefaultMaterial", "typeHash": 14895272822536500559, "uuid": "ed351337b72281ef5f457457b65b8060", "version": 1 diff --git a/resource/ProjectTemplate/Assets/SampleWorld/SampleWorld.world b/resource/ProjectTemplate/Assets/SampleWorld/SampleWorld.world deleted file mode 100644 index 0b4250d8..00000000 --- a/resource/ProjectTemplate/Assets/SampleWorld/SampleWorld.world +++ /dev/null @@ -1,235 +0,0 @@ -{ - "cam": { - "Camera": { - "depth": 0, - "fov": 60.0, - "lookPos": [ - 0.0, - 0.0, - 0.0 - ], - "projection": 0, - "renderTexture": "180635b4e4d1a98ebb0064ab47dc452a" - }, - "Component": { - "hideInspector": false, - "priority": 0 - }, - "EditorCamera": { - "distance": 7.244863510131836 - }, - "SObject": {}, - "name": "EditorCamera", - "type": "EditorCamera", - "uuid": "49b6e267952bf7cbfe326fe9d8017299", - "version": 1 - }, - "camPos": [ - 2.2565512657165527, - 4.759199619293213, - 4.974539279937744 - ], - "editorObjs": [], - "name": "World", - "objs": [ - { - "Components": [ - { - "Component": { - "hideInspector": false, - "priority": 0 - }, - "SObject": {}, - "Transform": { - "quat": [ - 0.0, - 0.0, - 0.0, - 1.0 - ], - "vPosition": [ - 0.0, - 0.0, - 0.0 - ], - "vRotation": [ - 0.0, - 0.0, - 0.0 - ], - "vScale": [ - 5.0, - 1.0, - 5.0 - ] - }, - "name": "Unknown", - "type": "Transform", - "uuid": "b44e6e7898be2c02285711b9fdd04c4a", - "version": 1 - }, - { - "Component": { - "hideInspector": false, - "priority": 0 - }, - "MeshRenderer": { - "mat": "ed351337b72281ef5f457457b65b8060", - "mesh": "bbc4ef7ec45dce223297a224f8093f20", - "renderTag": 1 - }, - "SObject": {}, - "name": "MeshRenderer", - "type": "MeshRenderer", - "uuid": "b2a3ec6fa22673f1d505269011493b71", - "version": 1 - } - ], - "GameObject": { - "bEditorOnly": false, - "bEnable": true, - "bStatic": false, - "hideInspector": false - }, - "name": "Cube", - "type": "GameObject", - "uuid": "01aa951d8dafee78706b05d275f48b08", - "version": 1 - }, - { - "Components": [ - { - "Component": { - "hideInspector": false, - "priority": 0 - }, - "SObject": {}, - "Transform": { - "quat": [ - 0.0, - 0.0, - 0.0, - 1.0 - ], - "vPosition": [ - 0.0, - 2.0, - 0.0 - ], - "vRotation": [ - 0.0, - 0.0, - 0.0 - ], - "vScale": [ - 1.0, - 1.0, - 1.0 - ] - }, - "name": "Unknown", - "type": "Transform", - "uuid": "e606e7a9aaf0006dfcbde7cf9aa5e2b9", - "version": 1 - }, - { - "Component": { - "hideInspector": false, - "priority": 0 - }, - "PointLight": { - "intensity": 1.0, - "range": 4.0 - }, - "SObject": {}, - "name": "PointLight", - "type": "PointLight", - "uuid": "ce042c273824748a5b90874ab3cb2403", - "version": 1 - } - ], - "GameObject": { - "bEditorOnly": false, - "bEnable": true, - "bStatic": false, - "hideInspector": false - }, - "name": "Light", - "type": "GameObject", - "uuid": "034cc967fe0412b031c04155de682b19", - "version": 1 - }, - { - "Components": [ - { - "Component": { - "hideInspector": false, - "priority": 0 - }, - "SObject": {}, - "Transform": { - "quat": [ - 0.0, - 0.0, - 0.0, - 1.0 - ], - "vPosition": [ - 2.0, - 2.0, - 2.0 - ], - "vRotation": [ - 0.0, - 0.0, - 0.0 - ], - "vScale": [ - 1.0, - 1.0, - 1.0 - ] - }, - "name": "Unknown", - "type": "Transform", - "uuid": "8a7c0cadb9a86ca9690bf19cf851f221", - "version": 1 - }, - { - "Camera": { - "depth": 0, - "fov": 60.0, - "lookPos": [ - 0.0, - 0.0, - 0.0 - ], - "projection": 0 - }, - "Component": { - "hideInspector": false, - "priority": 0 - }, - "SObject": {}, - "name": "Camera", - "type": "Camera", - "uuid": "9945d707ba86250259f1d5721ef434fa", - "version": 1 - } - ], - "GameObject": { - "bEditorOnly": false, - "bEnable": true, - "bStatic": false, - "hideInspector": false - }, - "name": "Camera", - "type": "GameObject", - "uuid": "72fff2c07f59927205ef7fe7b9d65ce8", - "version": 1 - } - ], - "type": "EditorWorld", - "uuid": "c3a4ec39f722e6d2704dceb7afbdc3c8", - "version": 1 -} \ No newline at end of file diff --git a/resource/ProjectTemplate/Assets/SampleWorld/assets/Text.shader b/resource/ProjectTemplate/Assets/SampleWorld/Text.shader similarity index 100% rename from resource/ProjectTemplate/Assets/SampleWorld/assets/Text.shader rename to resource/ProjectTemplate/Assets/SampleWorld/Text.shader diff --git a/resource/ProjectTemplate/Assets/SampleWorld/assets/Text.shader.meta b/resource/ProjectTemplate/Assets/SampleWorld/Text.shader.meta similarity index 100% rename from resource/ProjectTemplate/Assets/SampleWorld/assets/Text.shader.meta rename to resource/ProjectTemplate/Assets/SampleWorld/Text.shader.meta diff --git a/resource/ProjectTemplate/Assets/SampleWorld/assets/Tiles107_2K-JPG_AmbientOcclusion.jpg b/resource/ProjectTemplate/Assets/SampleWorld/Tiles107_2K-JPG_AmbientOcclusion.jpg similarity index 100% rename from resource/ProjectTemplate/Assets/SampleWorld/assets/Tiles107_2K-JPG_AmbientOcclusion.jpg rename to resource/ProjectTemplate/Assets/SampleWorld/Tiles107_2K-JPG_AmbientOcclusion.jpg diff --git a/resource/ProjectTemplate/Assets/SampleWorld/assets/Tiles107_2K-JPG_AmbientOcclusion.jpg.meta b/resource/ProjectTemplate/Assets/SampleWorld/Tiles107_2K-JPG_AmbientOcclusion.jpg.meta similarity index 92% rename from resource/ProjectTemplate/Assets/SampleWorld/assets/Tiles107_2K-JPG_AmbientOcclusion.jpg.meta rename to resource/ProjectTemplate/Assets/SampleWorld/Tiles107_2K-JPG_AmbientOcclusion.jpg.meta index 22c5987b..c892dc01 100644 --- a/resource/ProjectTemplate/Assets/SampleWorld/assets/Tiles107_2K-JPG_AmbientOcclusion.jpg.meta +++ b/resource/ProjectTemplate/Assets/SampleWorld/Tiles107_2K-JPG_AmbientOcclusion.jpg.meta @@ -1,5 +1,5 @@ { - "metaHash": 0, + "metaHash": 15575255052746670163, "name": "Tiles107_2K-JPG_AmbientOcclusion", "obj": { "Texture": { diff --git a/resource/ProjectTemplate/Assets/SampleWorld/assets/Tiles107_2K-JPG_NormalDX.jpg b/resource/ProjectTemplate/Assets/SampleWorld/Tiles107_2K-JPG_NormalDX.jpg similarity index 100% rename from resource/ProjectTemplate/Assets/SampleWorld/assets/Tiles107_2K-JPG_NormalDX.jpg rename to resource/ProjectTemplate/Assets/SampleWorld/Tiles107_2K-JPG_NormalDX.jpg diff --git a/resource/ProjectTemplate/Assets/SampleWorld/assets/Tiles107_2K-JPG_NormalDX.jpg.meta b/resource/ProjectTemplate/Assets/SampleWorld/Tiles107_2K-JPG_NormalDX.jpg.meta similarity index 92% rename from resource/ProjectTemplate/Assets/SampleWorld/assets/Tiles107_2K-JPG_NormalDX.jpg.meta rename to resource/ProjectTemplate/Assets/SampleWorld/Tiles107_2K-JPG_NormalDX.jpg.meta index 8659d9ca..fdd2ebf7 100644 --- a/resource/ProjectTemplate/Assets/SampleWorld/assets/Tiles107_2K-JPG_NormalDX.jpg.meta +++ b/resource/ProjectTemplate/Assets/SampleWorld/Tiles107_2K-JPG_NormalDX.jpg.meta @@ -1,5 +1,5 @@ { - "metaHash": 0, + "metaHash": 17930001031590521832, "name": "Tiles107_2K-JPG_NormalDX", "obj": { "Texture": { diff --git a/resource/ProjectTemplate/Assets/SampleWorld/assets/Unlit.shader b/resource/ProjectTemplate/Assets/SampleWorld/Unlit.shader similarity index 100% rename from resource/ProjectTemplate/Assets/SampleWorld/assets/Unlit.shader rename to resource/ProjectTemplate/Assets/SampleWorld/Unlit.shader diff --git a/resource/ProjectTemplate/Assets/SampleWorld/assets/Unlit.shader.meta b/resource/ProjectTemplate/Assets/SampleWorld/Unlit.shader.meta similarity index 100% rename from resource/ProjectTemplate/Assets/SampleWorld/assets/Unlit.shader.meta rename to resource/ProjectTemplate/Assets/SampleWorld/Unlit.shader.meta diff --git a/resource/ProjectTemplate/Assets/SampleWorld/assets/default.shader b/resource/ProjectTemplate/Assets/SampleWorld/assets/default.shader deleted file mode 100644 index 63e49cbf..00000000 --- a/resource/ProjectTemplate/Assets/SampleWorld/assets/default.shader +++ /dev/null @@ -1,77 +0,0 @@ -#version 430 core - -Shader "Default Shader" -{ - Property - { - float ambient; - sampler2D tex; - sampler2D normalTex; - } - - Pass - { - LightingPass "Opaque" - - Stage Vertex - { - layout(location = 0) out vec2 fragUvs; - layout(location = 1) out vec3 fragPos; - layout(location = 2) out mat3 TBN; - void main() - { - gl_Position = MATRIX_PROJ * MATRIX_VIEW * MATRIX_MODEL * vec4(VERTEX, 1.0f); - fragUvs = UV; - fragPos = (MATRIX_MODEL * vec4(VERTEX, 1.0f)).xyz; - - mat3 normalMatrix = transpose(inverse(mat3(MATRIX_MODEL))); - - vec3 t = normalize(normalMatrix * TANGENT); - vec3 n = normalize(normalMatrix * NORMAL); - vec3 b = cross(n, t); - - TBN = mat3(t, b, n); - } - } - Stage Fragment - { - layout(location = 0) out vec4 outColor; - - layout(location = 0) in vec2 uvs; - layout(location = 1) in vec3 fragPos; - layout(location = 2) in mat3 TBN; - uniform float ambient; - uniform sampler2D tex; - uniform sampler2D normalTex; - - void main() - { - vec3 normal = texture(normalTex, uvs).xyz; - normal = normal * 2.0 - 1.0; - normal = normalize(TBN * normal); - - float diffuse = 0.0; - for (int i = 0; i < LIGHT.count; ++i) - { - int type = int(LIGHT.other[i].w); - if (type == 0) - { - vec3 toLightDir = -LIGHT.pos[i].xyz; - float intensity = LIGHT.pos[i].w; - diffuse += max(dot(normal, toLightDir), 0.0) * intensity; - } - else if (type == 1) - { - vec3 toLightVec = LIGHT.pos[i].xyz - fragPos; - vec3 toLightDir = normalize(toLightVec); - float lightDis = length(toLightVec); - float attenuation = clamp(1.0 - (lightDis / LIGHT.pos[i].w), 0.0, 1.0); - diffuse += max(dot(normal, toLightDir), 0.0) * attenuation; - } - } - outColor = texture(tex, uvs); - outColor.xyz *= diffuse + ambient; - } - } - } -} \ No newline at end of file diff --git a/resource/ProjectTemplate/Assets/SampleWorld/default.shader b/resource/ProjectTemplate/Assets/SampleWorld/default.shader new file mode 100644 index 00000000..15be92e2 --- /dev/null +++ b/resource/ProjectTemplate/Assets/SampleWorld/default.shader @@ -0,0 +1,137 @@ +#version 430 core + +Shader "Default Shader" +{ + Property + { + float ambient; + sampler2D tex; + sampler2D normalTex; + } + Pass + { + LightingPass "DepthPass" + Cull Off; + + Stage Vertex + { + void main() + { + gl_Position = MATRIX_PROJ * MATRIX_VIEW * MATRIX_MODEL * vec4(VERTEX, 1.0f); + } + } + Stage Fragment + { + void main() + { + } + } + } + Pass + { + LightingPass "Opaque" + + Stage Vertex + { + layout(location = 0) out vec2 fragUvs; + layout(location = 1) out vec3 fragPos; + layout(location = 2) out mat3 TBN; + void main() + { + fragUvs = UV; + fragPos = (MATRIX_MODEL * vec4(VERTEX, 1.0f)).xyz; + gl_Position = MATRIX_PROJ * MATRIX_VIEW * vec4(fragPos, 1.0f); + + mat3 normalMatrix = transpose(inverse(mat3(MATRIX_MODEL))); + + vec3 t = normalize(normalMatrix * TANGENT); + vec3 n = normalize(normalMatrix * NORMAL); + vec3 b = cross(n, t); + + TBN = mat3(t, b, n); + } + } + Stage Fragment + { + layout(location = 0) out vec4 outColor; + + layout(location = 0) in vec2 uvs; + layout(location = 1) in vec3 fragPos; + layout(location = 2) in mat3 TBN; + uniform float ambient; + uniform sampler2D tex; + uniform sampler2D normalTex; + + float ShadowSample(vec3 worldPos, float nDotL, vec4 shadowRect, mat4 lightMatrix) + { + const vec4 lightClip = lightMatrix * vec4(worldPos, 1.0); + const vec3 proj = lightClip.xyz / lightClip.w; + if (proj.z > 1.0 || proj.z < 0.0) + return 1.0; + + vec2 tileUV; + tileUV.x = proj.x * 0.5 + 0.5; + tileUV.y = 1.0 - (proj.y * 0.5 + 0.5); + if (tileUV.x < 0.0 || tileUV.x > 1.0 || tileUV.y < 0.0 || tileUV.y > 1.0) + return 1.0; + const vec2 uv = tileUV * shadowRect.zw + shadowRect.xy; + + const float currentDepth = proj.z; + const vec2 atlasSize = textureSize(TEXTURE_SHADOW, 0); + const vec2 texelSize = 1.0 / (atlasSize); + + const float bias = max(0.005 * (1.0 - nDotL), 0.001); + float result = 0.0; + for (int x = -1; x <= 1; ++x) + { + for (int y = -1; y <= 1; ++y) + { + float pcfDepth = texture(TEXTURE_SHADOW, uv + vec2(x, y) * texelSize).r; + result += (currentDepth - bias) > pcfDepth ? 0.0 : 1.0; + } + } + result /= 9.0; + return result; + } + + void main() + { + vec3 normal = texture(normalTex, uvs).xyz; + normal = normal * 2.0 - 1.0; + normal = normalize(TBN * normal); + + float diffuse = 0.0; + for (int i = 0; i < LIGHT.count; ++i) + { + int type = int(LIGHT.lights[i].other.w); + if (type == 0) + { + vec3 toLightDir = normalize(-LIGHT.lights[i].pos.xyz); + const float intensity = LIGHT.lights[i].pos.w; + const float nDotL = max(dot(normal, toLightDir), 0.0); + if (nDotL > 0.0) + { + const float shadow = ShadowSample(fragPos, nDotL, LIGHT.lights[i].shadowRect, LIGHT.lights[i].lightSpaceMatrix); + diffuse += nDotL * intensity * shadow; + } + } + else if (type == 1) + { + vec3 toLightVec = LIGHT.lights[i].pos.xyz - fragPos; + vec3 toLightDir = normalize(toLightVec); + float lightDis = length(toLightVec); + float attenuation = clamp(1.0 - (lightDis / LIGHT.lights[i].pos.w), 0.0, 1.0); + const float nDotL = max(dot(normal, toLightDir), 0.0); + if (nDotL > 0.0) + { + const float shadow = ShadowSample(fragPos, nDotL, LIGHT.lights[i].shadowRect, LIGHT.lights[i].lightSpaceMatrix); + diffuse += nDotL * attenuation * shadow; + } + } + } + outColor = texture(tex, uvs); + outColor.xyz *= diffuse + ambient; + } + } + } +} \ No newline at end of file diff --git a/resource/ProjectTemplate/Assets/SampleWorld/assets/default.shader.meta b/resource/ProjectTemplate/Assets/SampleWorld/default.shader.meta similarity index 100% rename from resource/ProjectTemplate/Assets/SampleWorld/assets/default.shader.meta rename to resource/ProjectTemplate/Assets/SampleWorld/default.shader.meta diff --git a/resource/ProjectTemplate/Assets/SampleWorld/skinned.shader b/resource/ProjectTemplate/Assets/SampleWorld/skinned.shader new file mode 100644 index 00000000..99c0793e --- /dev/null +++ b/resource/ProjectTemplate/Assets/SampleWorld/skinned.shader @@ -0,0 +1,71 @@ +#version 430 core + +Shader "Skinned Shader2" +{ + Property + { + float ambient; + sampler2D tex; + } + + Pass + { + LightingPass "Opaque" + + Stage Vertex + { + layout(location = 0) out vec2 fragUvs; + layout(location = 1) out vec3 fragPos; + layout(location = 2) out vec3 fragNormal; + + void main() + { + vec4 skinnedPos = MATRIX_SKIN * vec4(VERTEX, 1.0); + vec3 skinnedNormal = mat3(MATRIX_SKIN) * NORMAL; + + gl_Position = MATRIX_PROJ * MATRIX_VIEW * MATRIX_MODEL * skinnedPos; + + fragUvs = UV; + fragPos = (MATRIX_MODEL * skinnedPos).xyz; + fragNormal = normalize(mat3(transpose(inverse(MATRIX_MODEL))) * skinnedNormal); + } + } + Stage Fragment + { + layout(location = 0) out vec4 outColor; + + layout(location = 0) in vec2 uvs; + layout(location = 1) in vec3 fragPos; + layout(location = 2) in vec3 fragNormal; + + uniform float ambient; + uniform sampler2D tex; + + void main() + { + float diffuse = 0.0; + + for (int i = 0; i < LIGHT.count; ++i) + { + int type = int(LIGHT.lights[i].other.w); + if (type == 0) + { + vec3 toLightDir = -LIGHT.lights[i].pos.xyz; + float intensity = LIGHT.lights[i].pos.w; + diffuse += max(dot(fragNormal, toLightDir), 0.0) * intensity; + } + else if (type == 1) + { + vec3 toLightVec = LIGHT.lights[i].pos.xyz - fragPos; + vec3 toLightDir = normalize(toLightVec); + float lightDis = length(toLightVec); + float attenuation = clamp(1.0 - (lightDis / LIGHT.lights[i].pos.w), 0.0, 1.0); + diffuse += max(dot(fragNormal, toLightDir), 0.0) * attenuation; + } + } + outColor = texture(tex, uvs); + outColor.xyz *= diffuse + ambient; + } + } + } +} diff --git a/resource/ProjectTemplate/Assets/SampleWorld/skinned.shader.meta b/resource/ProjectTemplate/Assets/SampleWorld/skinned.shader.meta new file mode 100644 index 00000000..331b7cbf --- /dev/null +++ b/resource/ProjectTemplate/Assets/SampleWorld/skinned.shader.meta @@ -0,0 +1,6 @@ +{ + "name": "skinned", + "typeHash": 11175581975934587683, + "uuid": "fb1961bc1fad585cac9526892b097a86", + "version": 1 +} \ No newline at end of file diff --git a/resource/shaders/SSAO.shader b/resource/shaders/SSAO.shader new file mode 100644 index 00000000..c07167f8 --- /dev/null +++ b/resource/shaders/SSAO.shader @@ -0,0 +1,156 @@ +#version 430 core + +Shader "SSAO Shader" +{ + Property + { + vec3 kernel[64]; + sampler2D normalTex; + sampler2D depthTex; + sampler2D noiseTex; + sampler2D aoTex; + } + + Pass + { + LightingPass "SSAOPass" + Cull Back; + + Stage Vertex + { + layout(location = 0) out vec2 fragUV; + void main() + { + fragUV = UV; + gl_Position = vec4(VERTEX, 1.0f); + } + } + Stage Fragment + { + layout(location = 0) in vec2 uvs; + + layout(location = 0) out vec4 outColor; + + uniform vec3 kernel[64]; + uniform sampler2D depthTex; + uniform sampler2D normalTex; + uniform sampler2D noiseTex; + + vec3 ReconstructViewPos(vec2 uv, float depth) + { + vec4 clipPos = vec4(uv.x * 2.0 - 1.0, (1.0 - uv.y) * 2.0 - 1.0, depth, 1.0); + vec4 viewPos = inverse(MATRIX_PROJ) * clipPos; + viewPos /= viewPos.w; + return viewPos.xyz; + } + + void main() + { + float depth = texture(depthTex, uvs).r; + + if (depth >= 1.0) + { + outColor = vec4(1.0); + return; + } + vec3 fragPos = ReconstructViewPos(uvs, depth); + vec3 normal = texture(normalTex, uvs).xyz; + normal = normalize(normal * 2.0 - 1.0); + + const vec2 noiseScale = vec2(1024.0/4.0, 768.0/4.0); + vec3 randomVec = normalize(vec3(texture(noiseTex, uvs * noiseScale).rg, 0.0)); + + vec3 tangent = normalize(randomVec - normal * dot(randomVec, normal)); + vec3 bitangent = cross(normal, tangent); + mat3 TBN = mat3(tangent, bitangent, normal); + + float occlusion = 0.0; + + for (int i = 0; i < 64; ++i) + { + const float radius = 0.5f; + vec3 samplePos = fragPos + TBN * kernel[i] * radius; + + vec4 offset = MATRIX_PROJ * vec4(samplePos, 1.0); + offset.xyz /= offset.w; + offset.xy = vec2(offset.x * 0.5 + 0.5, 0.5 - offset.y * 0.5); + + if (offset.x < 0.0 || offset.x > 1.0 || + offset.y < 0.0 || offset.y > 1.0) + { + continue; + } + + float sampleDepth = texture(depthTex, offset.xy).r; + vec3 sampleViewPos = ReconstructViewPos(offset.xy, sampleDepth); + + float rangeCheck = smoothstep( + 0.0, + 1.0, + radius / abs(fragPos.z - sampleViewPos.z) + ); + + const float bias = 0.025f; + occlusion += (sampleViewPos.z >= samplePos.z + bias ? 1.0 : 0.0) * rangeCheck; + } + + const float strength = 1.5f; + occlusion = 1.0 - (occlusion / 64.0); + occlusion = pow(occlusion, strength); + + outColor = vec4(vec3(occlusion), 1.0); + } + } + } + Pass + { + LightingPass "CombinePass" + Cull Off; + ZWrite Off; + ZTest Off; + + Stage Vertex + { + layout(location = 0) out vec2 fragUV; + void main() + { + fragUV = UV; + gl_Position = vec4(VERTEX, 1.0); + } + } + Stage Fragment + { + layout(location = 0) in vec2 uvs; + + layout(location = 0) out vec4 outColor; + + uniform sampler2D aoTex; + + float BlurAO(vec2 uv) + { + vec2 texelSize = 1.0 / vec2(textureSize(aoTex, 0)); + float result = 0.0; + + for (int x = -2; x < 2; ++x) + { + for (int y = -2; y < 2; ++y) + { + vec2 offset = vec2(float(x), float(y)) * texelSize; + result += texture(aoTex, uv + offset).r; + } + } + + return result / 16.0; + } + + void main() + { + float ao = BlurAO(uvs); + float shadow = 1.0 - ao; + if (shadow <= 0.001) + discard; + outColor = vec4(0.0, 0.0, 0.0, shadow); + } + } + } +} diff --git a/src/Core/Name.cpp b/src/Core/Name.cpp index 6288b019..bba4fd2a 100644 --- a/src/Core/Name.cpp +++ b/src/Core/Name.cpp @@ -1,5 +1,4 @@ #include "Name.h" -#include "Util.h" #include namespace sh::core @@ -60,31 +59,6 @@ namespace sh::core #endif return *this; } - SH_CORE_API auto Name::operator==(const Name& other) const -> bool - { - return hash == other.hash; - } - SH_CORE_API auto Name::operator!=(const Name& other) const -> bool - { - return hash != other.hash; - } - SH_CORE_API auto Name::operator==(const std::string_view str) const -> bool - { - return hash == core::Util::ConstexprHash(str); - } - SH_CORE_API auto Name::operator!=(const std::string_view str) const -> bool - { - return hash != core::Util::ConstexprHash(str); - } - SH_CORE_API auto Name::operator==(std::size_t hash) const -> bool - { - return this->hash == hash; - } - SH_CORE_API auto Name::operator!=(std::size_t hash) const -> bool - { - return this->hash != hash; - } - SH_CORE_API Name::operator const std::string& () const { return ToString(); diff --git a/src/Editor/AssetDatabase.cpp b/src/Editor/AssetDatabase.cpp index f51685a5..a283d7d5 100644 --- a/src/Editor/AssetDatabase.cpp +++ b/src/Editor/AssetDatabase.cpp @@ -30,6 +30,7 @@ #include "Game/Asset/TextLoader.h" #include "Game/Asset/FontLoader.h" #include "Game/Asset/ScriptableObjectLoader.h" +#include "Game/Asset/ComputeShaderLoader.h" #include "Game/Asset/TextureAsset.h" #include "Game/Asset/ModelAsset.h" @@ -43,6 +44,7 @@ #include "Game/Asset/TextAsset.h" #include "Game/Asset/FontAsset.h" #include "Game/Asset/ScriptableObjectAsset.h" +#include "Game/Asset/ComputeShaderAsset.h" #include "Sound/SoundClip.h" @@ -79,13 +81,17 @@ namespace sh::editor assert(ctx.GetRenderAPIType() == render::RenderAPI::Vulkan); // TODO static render::vk::VulkanShaderPassBuilder passBuilder{ static_cast(ctx) }; - auto shaderLoader = std::make_unique(&passBuilder); + std::unique_ptr shaderLoader = std::make_unique(&passBuilder); shaderLoader->SetCachePath(projectPath / "temp"); + std::unique_ptr computeShaderLoader = std::make_unique(ctx); + computeShaderLoader->SetCachePath(projectPath / "temp"); + assetLoaders.Clear(); assetLoaders.RegisterLoader(AssetExtensions::Type::Model, std::make_unique(ctx), 2, true); assetLoaders.RegisterLoader(AssetExtensions::Type::Mesh, std::make_unique(ctx), 2, true); assetLoaders.RegisterLoader(AssetExtensions::Type::Texture, std::make_unique(ctx), 2, true); + assetLoaders.RegisterLoader(AssetExtensions::Type::ComputeShader, std::move(computeShaderLoader), 2, false); assetLoaders.RegisterLoader(AssetExtensions::Type::Shader, std::move(shaderLoader), 2, false); assetLoaders.RegisterLoader(AssetExtensions::Type::Binary, std::make_unique(), 2, false); assetLoaders.RegisterLoader(AssetExtensions::Type::Sound, std::make_unique(), 2, false); @@ -439,13 +445,15 @@ namespace sh::editor assetType = game::SoundAsset::ASSET_NAME; else if (obj.GetType().IsChildOf(game::ScriptableObject::GetStaticType())) assetType = game::ScriptableObjectAsset::ASSET_NAME; + else if (obj.GetType() == render::ComputeShader::GetStaticType()) + assetType = game::ComputeShaderAsset::ASSET_NAME; else { SH_ERROR_FORMAT("Failed to export asset (type: {})", obj.GetType().name.ToString()); return false; } - auto asset = core::Factory::GetInstance()->Create(assetType); + std::unique_ptr asset = core::Factory::GetInstance()->Create(assetType); if (asset == nullptr) { SH_ERROR_FORMAT("Asset type({}) is invalid", assetType); diff --git a/src/Editor/AssetExtensions.cpp b/src/Editor/AssetExtensions.cpp index 16bf5d10..3f5e52b5 100644 --- a/src/Editor/AssetExtensions.cpp +++ b/src/Editor/AssetExtensions.cpp @@ -18,7 +18,8 @@ namespace sh::editor {".ttf", Type::Binary}, {".ogg", Type::Sound}, {".wav", Type::Sound}, - {".srpo", Type::ScriptableObject} + {".srpo", Type::ScriptableObject}, + {".compute", Type::ComputeShader} }; SH_EDITOR_API void AssetExtensions::AddExtension(const std::string& ext, Type type) @@ -60,6 +61,8 @@ namespace sh::editor return "Sound"; case Type::ScriptableObject: return "ScriptableObject"; + case Type::ComputeShader: + return "ComputeShader"; } return "None"; } diff --git a/src/Editor/BuildSystem.cpp b/src/Editor/BuildSystem.cpp index cadb6a93..fc2d6c7b 100644 --- a/src/Editor/BuildSystem.cpp +++ b/src/Editor/BuildSystem.cpp @@ -85,6 +85,96 @@ namespace sh::editor bundle.SaveBundle(outputPath / "assets.bundle"); ExportGameManager(outputPath / "gameManager.bin"); + + CopyRuntimeBinaries(outputPath); + } + + void BuildSystem::CopyRuntimeBinaries(const std::filesystem::path& outputPath) + { + const std::filesystem::path engineDir = core::FileSystem::GetExecutableDirectory(); + +#if _WIN32 + constexpr const char* exeExt = ".exe"; + constexpr const char* libExt = ".dll"; + constexpr const char* libPrefix = ""; +#else + constexpr const char* exeExt = ""; + constexpr const char* libExt = ".so"; + constexpr const char* libPrefix = "lib"; +#endif + const std::vector executables = { + "ShellGame" + }; + const std::vector libraries = { + "ShellEngineCore", + "ShellEngineWindow", + "ShellEngineRender", + "ShellEnginePhysics", + "ShellEngineGame", + "ShellEngineNetwork", + "ShellEngineSound" + }; +#if _WIN32 + const std::vector extraFiles = { + "OpenAL32.dll" + }; +#else + const std::vector extraFiles = { + "libopenal.so" + }; +#endif + const std::vector directories = { + "fonts" + }; + + std::error_code ec; + if (!std::filesystem::exists(outputPath)) + std::filesystem::create_directories(outputPath, ec); + + auto copyIfMissingFn = [&](const std::filesystem::path& src, const std::filesystem::path& dst) + { + if (!std::filesystem::exists(src)) + { + SH_WARN_FORMAT("Runtime binary not found in engine path: {}", src.u8string()); + return; + } + std::error_code copyEc; + std::filesystem::copy_file(src, dst, std::filesystem::copy_options::overwrite_existing, copyEc); + if (copyEc) + SH_ERROR_FORMAT("Failed to copy {} -> {}: {}", src.u8string(), dst.u8string(), copyEc.message()); + }; + + for (const std::string& name : executables) + { + const std::string fileName = name + exeExt; + copyIfMissingFn(engineDir / fileName, outputPath / fileName); + } + for (const std::string& name : libraries) + { + const std::string fileName = libPrefix + name + libExt; + copyIfMissingFn(engineDir / fileName, outputPath / fileName); + } + for (const std::string& fileName : extraFiles) + { + copyIfMissingFn(engineDir / fileName, outputPath / fileName); + } + for (const std::string& dirName : directories) + { + const std::filesystem::path src = engineDir / dirName; + const std::filesystem::path dst = outputPath / dirName; + if (!std::filesystem::exists(src)) + { + SH_WARN_FORMAT("Runtime directory not found in engine path: {}", src.u8string()); + continue; + } + std::error_code copyEc; + std::filesystem::copy(src, dst, + std::filesystem::copy_options::recursive | + std::filesystem::copy_options::overwrite_existing, + copyEc); + if (copyEc) + SH_ERROR_FORMAT("Failed to copy directory {} -> {}: {}", src.u8string(), dst.u8string(), copyEc.message()); + } } void BuildSystem::ExtractUUIDs(std::unordered_set& set, const core::Json& worldJson) { @@ -129,6 +219,8 @@ namespace sh::editor bundle.AddAsset(lineShaderAsset, true); game::ShaderAsset uiTextShaderAsset{ *editorResource->GetShader("UITextShader") }; bundle.AddAsset(uiTextShaderAsset, true); + game::ShaderAsset ssaoShaderAsset{ *editorResource->GetShader("SSAOShader") }; + bundle.AddAsset(ssaoShaderAsset, true); game::MaterialAsset errorMatAsset{ *editorResource->GetMaterial("ErrorMaterial") }; bundle.AddAsset(errorMatAsset, true); diff --git a/src/Editor/Component/EditorUI.cpp b/src/Editor/Component/EditorUI.cpp index 91ece94e..af2fddbc 100644 --- a/src/Editor/Component/EditorUI.cpp +++ b/src/Editor/Component/EditorUI.cpp @@ -12,7 +12,7 @@ #include "Game/Asset/FontGenerator.h" #include "Game/Input.h" -#include "Render/VulkanImpl/VulkanTextureBuffer.h" +#include "Render/Renderer.h" #include "Render/RenderTexture.h" #include diff --git a/src/Editor/EditorOutlinePass.cpp b/src/Editor/EditorOutlinePass.cpp index a0fd13f4..fdadb815 100644 --- a/src/Editor/EditorOutlinePass.cpp +++ b/src/Editor/EditorOutlinePass.cpp @@ -3,7 +3,7 @@ #include "Core/GarbageCollection.h" #include "Render/RenderTexture.h" - +#include "Render/CommandBuffer.h" namespace sh::editor { EditorOutlinePass::EditorOutlinePass() : @@ -15,26 +15,34 @@ namespace sh::editor } SH_EDITOR_API void EditorOutlinePass::SetOutTexture(render::RenderTexture& tex) { - output = &tex; + rd.SetRenderTarget(&tex); + rd.renderViewers.resize(1); } - SH_EDITOR_API void EditorOutlinePass::Configure(const render::RenderTarget& renderData) + SH_EDITOR_API void EditorOutlinePass::Configure(const render::RenderData& renderData) { - render::RenderTarget rd; - rd.camera = renderData.camera; - rd.frameIndex = renderData.frameIndex; - rd.target = output.Get(); - rd.drawables = renderData.drawables; - - ScriptableRenderPass::Configure(rd); + renderTextures.clear(); + renderTextures[rd.GetRenderTargets()[0]] = render::ResourceUsage::ColorAttachment; + if (renderData.GetDrawablesPtr() != nullptr) + { + SetImageUsages(*renderData.GetDrawablesPtr()); + renderBatches = CreateRenderBatch(passName, *renderData.GetDrawablesPtr()); + } } - SH_EDITOR_API void EditorOutlinePass::Record(render::CommandBuffer& cmd, const render::IRenderContext& ctx, const render::RenderTarget& renderData) + SH_EDITOR_API void EditorOutlinePass::Record(render::CommandBuffer& cmd, const render::IRenderContext& ctx, const render::RenderData& renderData) { - render::RenderTarget rd; - rd.camera = renderData.camera; - rd.frameIndex = renderData.frameIndex; - rd.target = output.Get(); + if (renderData.renderViewers.empty() || renderData.GetDrawablesPtr() == nullptr) + return; + + rd.renderViewers[0] = renderData.renderViewers.front(); + render::IRenderThrMethod::SetDrawablesPtr(rd, renderData.GetDrawablesPtr()); + + const render::RenderTexture& mainTaret = *rd.GetRenderTargets()[0]; + cmd.SetRenderData(rd, true, true, true, true); + cmd.SetViewport(0, 0, mainTaret.GetSize().x, mainTaret.GetSize().y); + cmd.SetScissor(0, 0, mainTaret.GetSize().x, mainTaret.GetSize().y); - ScriptableRenderPass::Record(cmd, ctx, rd); + for (const RenderBatch& batch : renderBatches) + cmd.DrawMeshBatch(batch.drawables, passName); } -}//namespace \ No newline at end of file +}//namespace diff --git a/src/Editor/EditorPostOutlinePass.cpp b/src/Editor/EditorPostOutlinePass.cpp index 726bcd18..0ba7113c 100644 --- a/src/Editor/EditorPostOutlinePass.cpp +++ b/src/Editor/EditorPostOutlinePass.cpp @@ -33,10 +33,14 @@ namespace sh::editor } SH_EDITOR_API void EditorPostOutlinePass::SetOutlineMaterial(render::Material& mat) { + if (this->mat == &mat || mat.IsPendingKill()) + return; this->mat = &mat; + if (drawable != nullptr) + drawable->SetMaterial(mat); } - SH_EDITOR_API void EditorPostOutlinePass::Configure(const render::RenderTarget& renderData) + SH_EDITOR_API void EditorPostOutlinePass::Configure(const render::RenderData& renderData) { if (drawable == nullptr && mat != nullptr) { @@ -45,24 +49,24 @@ namespace sh::editor core::GarbageCollection::GetInstance()->SetRootSet(drawable); } - ScriptableRenderPass::Configure(renderData); - } - SH_EDITOR_API auto EditorPostOutlinePass::BuildDrawList(const render::RenderTarget& renderData) -> render::DrawList - { - render::DrawList list{}; - list.renderData = std::vector{}; - list.bClearColor = false; - - if (!core::IsValid(drawable) || !drawable->CheckAssetValid() || drawable->GetMaterial()->GetShader()->GetShaderPasses(passName) == nullptr) - return list; - - render::DrawList::RenderItem renderItem; - renderItem.material = mat; - renderItem.topology = drawable->GetTopology(core::ThreadType::Render); - renderItem.drawable = drawable; - std::get<1>(list.renderData).push_back(renderItem); + renderTextures.clear(); + SetRenderTargetImageUsages(renderData); - return list; + if (mat != nullptr) + SetImageUsages(*mat); + } + SH_EDITOR_API void EditorPostOutlinePass::Record(render::CommandBuffer& cmd, const render::IRenderContext& ctx, const render::RenderData& renderData) + { + if (drawable == nullptr || !core::IsValid(drawable->GetMesh()) || !core::IsValid(drawable->GetMaterial())) + return; + cmd.SetRenderData(renderData, false, false, true, false); + std::size_t viewerIdx = 0; + for (const render::RenderViewer& viewer : renderData.renderViewers) + { + SetViewportScissor(cmd, ctx, viewer); + cmd.DrawMesh(*drawable, passName, viewerIdx); + ++viewerIdx; + } } -}//namespace \ No newline at end of file +}//namespace diff --git a/src/Editor/EditorRenderer.cpp b/src/Editor/EditorRenderer.cpp index c85826c7..907b5abb 100644 --- a/src/Editor/EditorRenderer.cpp +++ b/src/Editor/EditorRenderer.cpp @@ -1,76 +1,39 @@ #include "EditorRenderer.h" -#include "Game/UIPass.h" - +#include "Game/GUIPass.h" #include "Render/RenderPass/TransparentPass.h" #include namespace sh::editor { - EditorRenderer::EditorRenderer(render::IRenderContext& ctx, game::ImGUImpl& guictx) : - ScriptableRenderer(ctx) - { - AddRenderPass(core::Name{ "EditorPicking" }, render::RenderQueue::Picking); - outlinePass = &AddRenderPass(); - AddRenderPass(core::Name{ "Opaque" }, render::RenderQueue::Opaque); - AddRenderPass(); - AddRenderPass("UI", render::RenderQueue::Transparent); - postOutlinePass = &AddRenderPass(ctx); - uiPass = &AddRenderPass(); - uiPass->SetImGUIContext(guictx); - } - SH_EDITOR_API void EditorRenderer::SetImGUICamera(const render::Camera& camera) - { - ImGUICamera = &camera; - allowedCamera["ImGUI"].push_back(&camera); - ignoreCamera["Opaque"].push_back(&camera); - ignoreCamera["Transparent"].push_back(&camera); - ignoreCamera["UI"].push_back(&camera); - } - SH_EDITOR_API void EditorRenderer::SetEditorCamera(const render::Camera& camera) + EditorRenderer::EditorRenderer(render::IRenderContext& ctx, game::ImGUImpl& guictx, game::World& world) : + GameRenderer(ctx, guictx, world) { - editorCamera = &camera; - allowedCamera[outlinePass->passName].push_back(&camera); - allowedCamera[postOutlinePass->passName].push_back(&camera); - - uiPass->viewportTexture = editorCamera->GetRenderTexture(); } - SH_EDITOR_API void EditorRenderer::SetPickingCamera(const render::Camera& camera) + SH_EDITOR_API void EditorRenderer::Init() { - pickingCamera = &camera; - allowedCamera["EditorPicking"].push_back(&camera); - ignoreCamera["Opaque"].push_back(&camera); - ignoreCamera["Transparent"].push_back(&camera); - ignoreCamera["ImGUI"].push_back(&camera); - ignoreCamera["UI"].push_back(&camera); + GameRenderer::Init(); + pickingPass = &AddRenderPass(core::Name{ "EditorPicking" }, render::RenderQueue::Picking); + outlinePass = &AddRenderPass(); + postOutlinePass = &AddRenderPass(ctx); } - - SH_EDITOR_API void EditorRenderer::Setup(const render::RenderTarget& data) + SH_EDITOR_API void EditorRenderer::Setup(const render::RenderData& data) { - // allowedCamera에 등록된 패스는 등록된 카메라에서만 실행됨 - // ignoreCamera에 등록된 패스는 등록된 카메라에서는 실행 안 함 - for (auto& pass : allPasses) + if (data.tag == "EditorCamera") { - auto allowedIt = allowedCamera.find(pass->passName.ToString()); - if (allowedIt != allowedCamera.end()) - { - const auto& allowedCams = allowedIt->second; - bool bAllowed = std::find(allowedCams.begin(), allowedCams.end(), data.camera) != allowedCams.end(); - if (!bAllowed) - continue; - } - - auto it = ignoreCamera.find(pass->passName.ToString()); - if (it == ignoreCamera.end()) - { - EnqueRenderPass(*pass); - continue; - } - const auto& ignoreCams = it->second; - bool bIgnore = std::find(ignoreCams.begin(), ignoreCams.end(), data.camera) != ignoreCams.end(); - if (bIgnore) - continue; - EnqueRenderPass(*pass); + guiPass->viewportTexture = data.GetRenderTargets()[0]; + EnqueRenderPass(*outlinePass); + EnqueRenderPass(*opaquePass); + EnqueRenderPass(*transparentPass); + EnqueRenderPass(*uiPass); + EnqueRenderPass(*postOutlinePass); + return; + } + if (data.tag == "PickingCamera") + { + EnqueRenderPass(*pickingPass); + return; } + GameRenderer::Setup(data); } -}//namespace \ No newline at end of file +}//namespace diff --git a/src/Editor/EditorResource.cpp b/src/Editor/EditorResource.cpp index 29cab40d..040d5ab8 100644 --- a/src/Editor/EditorResource.cpp +++ b/src/Editor/EditorResource.cpp @@ -4,6 +4,7 @@ #include "Core/AssetExporter.h" #include "Core/GarbageCollection.h" +#include "Render/Renderer.h" #include "Render/VulkanImpl/VulkanContext.h" #include "Render/VulkanImpl/VulkanShaderPassBuilder.h" #include "Render/Material.h" @@ -19,7 +20,7 @@ namespace sh::editor { EditorResource::~EditorResource() { - for (auto icon : icons) + for (game::GUITexture* icon : icons) icon->Destroy(); } SH_EDITOR_API void EditorResource::LoadAllAssets(Project& project) @@ -81,45 +82,42 @@ namespace sh::editor for (auto icon : icons) icon->SetName("icon"); - auto lineShader = shaders.insert_or_assign("Line", static_cast(shaderLoader.Load("shaders/line.shader"))).first->second; - auto errorShader = shaders.insert_or_assign("ErrorShader", static_cast(shaderLoader.Load("shaders/error.shader"))).first->second; - auto gridShader = shaders.insert_or_assign("GridShader", static_cast(shaderLoader.Load("shaders/grid.shader"))).first->second; - auto pickingShader = shaders.insert_or_assign("EditorPickingShader", static_cast(shaderLoader.Load("shaders/EditorPicking.shader"))).first->second; - auto outlineShader = shaders.insert_or_assign("OutlineShader", static_cast(shaderLoader.Load("shaders/outline.shader"))).first->second; - auto triangleShader = shaders.insert_or_assign("TriangleShader", static_cast(shaderLoader.Load("shaders/triangle.shader"))).first->second; - auto outlinePreShader = shaders.insert_or_assign("OutlinePreShader", static_cast(shaderLoader.Load("shaders/EditorOutlinePre.shader"))).first->second; - auto outlinePostShader = shaders.insert_or_assign("OutlinePostShader", static_cast(shaderLoader.Load("shaders/EditorOutlinePost.shader"))).first->second; - auto uiTextShader = shaders.insert_or_assign("UITextShader", static_cast(shaderLoader.Load("shaders/UIText.shader"))).first->second; - - auto blackTex = textures.insert_or_assign("BlackTexture", static_cast(texLoader.Load("textures/black.png"))).first->second; + render::Shader* const lineShader = shaders.insert_or_assign("Line", static_cast(shaderLoader.Load("shaders/line.shader"))).first->second; + render::Shader* const errorShader = shaders.insert_or_assign("ErrorShader", static_cast(shaderLoader.Load("shaders/error.shader"))).first->second; + render::Shader* const gridShader = shaders.insert_or_assign("GridShader", static_cast(shaderLoader.Load("shaders/grid.shader"))).first->second; + render::Shader* const pickingShader = shaders.insert_or_assign("EditorPickingShader", static_cast(shaderLoader.Load("shaders/EditorPicking.shader"))).first->second; + render::Shader* const outlineShader = shaders.insert_or_assign("OutlineShader", static_cast(shaderLoader.Load("shaders/outline.shader"))).first->second; + render::Shader* const triangleShader = shaders.insert_or_assign("TriangleShader", static_cast(shaderLoader.Load("shaders/triangle.shader"))).first->second; + render::Shader* const outlinePreShader = shaders.insert_or_assign("OutlinePreShader", static_cast(shaderLoader.Load("shaders/EditorOutlinePre.shader"))).first->second; + render::Shader* const outlinePostShader = shaders.insert_or_assign("OutlinePostShader", static_cast(shaderLoader.Load("shaders/EditorOutlinePost.shader"))).first->second; + render::Shader* const uiTextShader = shaders.insert_or_assign("UITextShader", static_cast(shaderLoader.Load("shaders/UIText.shader"))).first->second; + render::Shader* const ssaoShader = shaders.insert_or_assign("SSAOShader", static_cast(shaderLoader.Load("shaders/SSAO.shader"))).first->second; + ssaoShader->SetUUID(core::UUID{ "bbc4ef7ec45dce223297a224f8093f24" }); + + render::Texture* const blackTex = textures.insert_or_assign("BlackTexture", static_cast(texLoader.Load("textures/black.png"))).first->second; blackTex->Build(*renderer.GetContext()); blackTex->SetUUID(core::UUID{ "bbc4ef7ec45dce223297a224f8093f18" }); - auto errorMat = materials.insert_or_assign("ErrorMaterial", core::SObject::Create(errorShader)).first->second; - auto lineMat = materials.insert_or_assign("LineMaterial", core::SObject::Create(lineShader)).first->second; - auto gridMat = materials.insert_or_assign("GridMaterial", core::SObject::Create(gridShader)).first->second; - auto pickingMat = materials.insert_or_assign("PickingMaterial", core::SObject::Create(pickingShader)).first->second; - auto triMat = materials.insert_or_assign("TriangleMaterial", core::SObject::Create(triangleShader)).first->second; - auto outlinePreMat = materials.insert_or_assign("OutlinePreMaterial", core::SObject::Create(outlinePreShader)).first->second; - auto outlinePostMat = materials.insert_or_assign("OutlinePostMaterial", core::SObject::Create(outlinePostShader)).first->second; - auto uiTextMat = materials.insert_or_assign("UITextMaterial", core::SObject::Create(uiTextShader)).first->second; + render::Material* const errorMat = materials.insert_or_assign("ErrorMaterial", core::SObject::Create(errorShader)).first->second; + render::Material* const lineMat = materials.insert_or_assign("LineMaterial", core::SObject::Create(lineShader)).first->second; + render::Material* const gridMat = materials.insert_or_assign("GridMaterial", core::SObject::Create(gridShader)).first->second; + render::Material* const pickingMat = materials.insert_or_assign("PickingMaterial", core::SObject::Create(pickingShader)).first->second; + render::Material* const triMat = materials.insert_or_assign("TriangleMaterial", core::SObject::Create(triangleShader)).first->second; + render::Material* const outlinePreMat = materials.insert_or_assign("OutlinePreMaterial", core::SObject::Create(outlinePreShader)).first->second; + render::Material* const outlinePostMat = materials.insert_or_assign("OutlinePostMaterial", core::SObject::Create(outlinePostShader)).first->second; + render::Material* const uiTextMat = materials.insert_or_assign("UITextMaterial", core::SObject::Create(uiTextShader)).first->second; - auto sphereModel = models.insert_or_assign("SphereModel", static_cast(modelLoader.Load("model/Sphere.obj"))).first->second; + render::Model* const sphereModel = models.insert_or_assign("SphereModel", static_cast(modelLoader.Load("model/Sphere.obj"))).first->second; sphereModel->SetUUID(core::UUID{ "bbc4ef7ec45dce223297a224f8093f15" }); sphereModel->GetMeshes()[0]->SetUUID(core::UUID{ "bbc4ef7ec45dce223297a224f8093f19" }); - auto cubeModel = models.insert_or_assign("CubeModel", static_cast(modelLoader.Load("model/cube.obj"))).first->second; + render::Model* const cubeModel = models.insert_or_assign("CubeModel", static_cast(modelLoader.Load("model/cube.obj"))).first->second; cubeModel->SetUUID(core::UUID{ "bbc4ef7ec45dce223297a224f8093f16" }); cubeModel->GetMeshes()[0]->SetUUID(core::UUID{ "bbc4ef7ec45dce223297a224f8093f20" }); - auto planeModel = models.insert_or_assign("PlaneModel", static_cast(modelLoader.Load("model/Plane.glb"))).first->second; + render::Model* const planeModel = models.insert_or_assign("PlaneModel", static_cast(modelLoader.Load("model/Plane.glb"))).first->second; planeModel->SetUUID(core::UUID{ "bbc4ef7ec45dce223297a224f8093f17" }); planeModel->GetMeshes()[0]->SetUUID(core::UUID{ "bbc4ef7ec45dce223297a224f8093f21" }); - render::RenderTargetLayout rt{}; - rt.format = render::TextureFormat::R8; - rt.depthFormat = render::TextureFormat::D24S8; - rt.bUseMSAA = false; - - render::RenderTexture* outlineTexture = core::SObject::Create(rt); + render::RenderTexture* outlineTexture = core::SObject::Create(render::TextureFormat::R8, render::TextureFormat::D24S8, false); outlineTexture->SetSize(1024, 1024); outlineTexture->Build(*renderer.GetContext()); textures.insert_or_assign("OutlineTexture", outlineTexture); diff --git a/src/Editor/EditorWorld.cpp b/src/Editor/EditorWorld.cpp index 34148413..4e40dc94 100644 --- a/src/Editor/EditorWorld.cpp +++ b/src/Editor/EditorWorld.cpp @@ -9,8 +9,11 @@ #include "Core/Name.h" +#include "Render/Renderer.h" #include "Render/Mesh/Grid.h" #include "Render/Model.h" +#include "Render/ShadowMapManager.h" +#include "Render/RenderPass/SSAOPass.h" #include "Editor/Component/EditorControl.h" @@ -21,6 +24,7 @@ #include "Game/Component/Render/PickingRenderer.h" #include "Game/Component/Render/LineRenderer.h" #include "Game/WorldEvents.hpp" +#include "Game/GUIPass.h" namespace sh::editor { @@ -80,60 +84,51 @@ namespace sh::editor SH_EDITOR_API void EditorWorld::SetupRenderer() { - customRenderer = std::make_unique(*renderer.GetContext(), GetUiContext()); + customRenderer = std::make_unique(*renderer.GetContext(), GetUiContext(), *this); + EditorRenderer& editorRenderer = static_cast(*customRenderer); + editorRenderer.Init(); renderer.SetScriptableRenderer(*customRenderer); - auto& editorRenderer = static_cast(*customRenderer); - - auto& uiCamera = GetUICamera(); - uiCamera.SetPriority(1000); - renderer.AddCamera(uiCamera); - - editorRenderer.SetImGUICamera(uiCamera); + if (shadowMapManager != nullptr) + shadowMapManager->Init(*renderer.GetContext()); } SH_EDITOR_API void EditorWorld::InitResource() { Super::InitResource(); - auto* editorRenderer = static_cast(customRenderer.get()); + EditorRenderer* const editorRenderer = static_cast(customRenderer.get()); // editorRenderer가 없으면 addtive로 추가된 월드임 if (editorRenderer != nullptr) { - auto viewportPtr = static_cast(core::SObjectManager::GetInstance()->GetSObject(core::UUID{ "180635b4e4d1a98ebb0064ab47dc452a" })); + render::RenderTexture* const viewportPtr = + static_cast(core::SObjectManager::GetInstance()->GetSObject(core::UUID{ "180635b4e4d1a98ebb0064ab47dc452a" })); if (!core::IsValid(viewportPtr)) { if (viewportPtr != nullptr && viewportPtr->IsPendingKill()) viewportPtr->SetUUID(core::UUID::Generate()); - render::RenderTargetLayout rt{}; - rt.format = render::TextureFormat::SRGBA32; - rt.depthFormat = render::TextureFormat::D24S8; - rt.bUseMSAA = true; - - viewportTexture = core::SObject::Create(rt); + viewportTexture = core::SObject::Create(render::TextureFormat::SRGBA32, render::TextureFormat::D24S8, true); viewportTexture->SetUUID(core::UUID{ "180635b4e4d1a98ebb0064ab47dc452a" }); viewportTexture->Build(*renderer.GetContext()); } else viewportTexture = viewportPtr; + editorRenderer->GetGUIPass().viewportTexture = viewportTexture; - game::GameObject* camObj = AddGameObject("EditorCamera"); + game::GameObject* const camObj = AddGameObject("EditorCamera"); camObj->transform->SetPosition({ 2.f, 2.f, 2.f }); camObj->hideInspector = true; camObj->bNotSave = true; editorCamera = camObj->AddComponent(); editorCamera->SetRenderTexture(viewportTexture); - editorCamera->GetNative().SetActive(true); - editorRenderer->SetEditorCamera(editorCamera->GetNative()); + editorCamera->SetTag(core::Name{ "EditorCamera" }); - auto PickingCamObj = AddGameObject("PickingCamera"); + game::GameObject* const PickingCamObj = AddGameObject("PickingCamera"); PickingCamObj->bNotSave = true; PickingCamObj->transform->SetParent(camObj->transform); PickingCamObj->transform->SetPosition({ 0.f, 0.f, 0.f }); pickingCamera = PickingCamObj->AddComponent(); pickingCamera->SetFollowCamera(editorCamera); - pickingCamera->GetNative().SetActive(true); - - editorRenderer->SetPickingCamera(pickingCamera->GetNative()); + pickingCamera->SetTag(core::Name{ "PickingCamera" }); auto outlineTexture = EditorResource::GetInstance()->GetTexture("OutlineTexture"); assert(outlineTexture); diff --git a/src/Editor/UI/AssetExplorer.cpp b/src/Editor/UI/AssetExplorer.cpp index 328f1037..b5e2cd42 100644 --- a/src/Editor/UI/AssetExplorer.cpp +++ b/src/Editor/UI/AssetExplorer.cpp @@ -1,6 +1,8 @@ #include "UI/AssetExplorer.h" #include "EditorWorld.h" +#include "Render/Renderer.h" + #include "Game/ImGUImpl.h" #include "Window/Window.h" diff --git a/src/Editor/UI/ProjectExplorer.cpp b/src/Editor/UI/ProjectExplorer.cpp index bfeecc4f..ea35856e 100644 --- a/src/Editor/UI/ProjectExplorer.cpp +++ b/src/Editor/UI/ProjectExplorer.cpp @@ -8,6 +8,7 @@ #include "Core/FileSystem.h" +#include "Render/Renderer.h" #include "Render/Font.h" #include "Game/GUITexture.h" diff --git a/src/Editor/UI/Viewport.cpp b/src/Editor/UI/Viewport.cpp index 8f894d76..789e1662 100644 --- a/src/Editor/UI/Viewport.cpp +++ b/src/Editor/UI/Viewport.cpp @@ -14,6 +14,7 @@ #include "Game/Component/Render/PickingRenderer.h" #include "Game/GameManager.h" +#include "Render/Renderer.h" #include "Render/RenderTexture.h" #include "External/imgui/ImGuizmo.h" @@ -261,9 +262,7 @@ namespace sh::editor std::sort(cams.begin(), cams.end(), [](const game::Camera* left, const game::Camera* right) { - if (left->GetDepth() == right->GetDepth()) - return left->GetNative().id < right->GetNative().id; - return left->GetDepth() < right->GetDepth(); + return left->GetPriority() < right->GetPriority(); } ); diff --git a/src/EngineInit.cpp b/src/EngineInit.cpp index 806e3290..cfd43fe2 100644 --- a/src/EngineInit.cpp +++ b/src/EngineInit.cpp @@ -45,6 +45,7 @@ #include "Game/Asset/FontLoader.h" #include "Game/Asset/SoundLoader.h" #include "Game/Asset/ScriptableObjectLoader.h" +#include "Game/Asset/ComputeShaderLoader.h" #include "Game/Asset/TextureAsset.h" #include "Game/Asset/MaterialAsset.h" @@ -58,6 +59,7 @@ #include "Game/Asset/FontAsset.h" #include "Game/Asset/SoundAsset.h" #include "Game/Asset/ScriptableObjectAsset.h" +#include "Game/Asset/ComputeShaderAsset.h" #endif namespace sh @@ -164,6 +166,7 @@ namespace sh SH_INFO("Renderer initialization"); renderer = std::make_unique(); + renderer->CreateContext(*window); renderer->Init(*window); renderer->GetContext()->SetViewport({ 150.f, 0.f }, { window->GetWidth() - 150.f, window->GetHeight() - 180}); @@ -236,6 +239,7 @@ namespace sh assetLoaderFactory->RegisterLoader(game::FontAsset::ASSET_NAME, std::make_unique()); assetLoaderFactory->RegisterLoader(game::SoundAsset::ASSET_NAME, std::make_unique()); assetLoaderFactory->RegisterLoader(game::ScriptableObjectAsset::ASSET_NAME, std::make_unique()); + assetLoaderFactory->RegisterLoader(game::ComputeShaderAsset::ASSET_NAME, std::make_unique(*renderer->GetContext())); core::AssetResolverRegistry::SetResolver( [this](const core::UUID& uuid) -> core::SObject* diff --git a/src/Game/Asset/ComputeShaderAsset.cpp b/src/Game/Asset/ComputeShaderAsset.cpp new file mode 100644 index 00000000..66d23ce5 --- /dev/null +++ b/src/Game/Asset/ComputeShaderAsset.cpp @@ -0,0 +1,36 @@ +#include "Asset/ComputeShaderAsset.h" + +namespace sh::game +{ + ComputeShaderAsset::ComputeShaderAsset() : + Asset(ASSET_NAME) + { + } + ComputeShaderAsset::ComputeShaderAsset(const render::ComputeShader& shader) : + Asset(ASSET_NAME), + shaderPtr(static_cast(&shader)) + { + assetUUID = shaderPtr->GetUUID(); + } + SH_GAME_API void ComputeShaderAsset::SetAsset(const core::SObject& obj) + { + if (obj.GetType() != render::ComputeShader::GetStaticType()) + return; + + shaderPtr = static_cast(&obj); + assetUUID = shaderPtr->GetUUID(); + } + SH_GAME_API void ComputeShaderAsset::SetAssetData() const + { + if (!shaderPtr.IsValid()) + return; + + const core::Json shaderObjJson = shaderPtr->Serialize(); + data = core::Json::to_cbor(shaderObjJson); + } + SH_GAME_API auto ComputeShaderAsset::ParseAssetData() -> bool + { + shaderObjJson = core::Json::from_cbor(data); + return true; + } +}//namespace diff --git a/src/Game/Asset/ComputeShaderLoader.cpp b/src/Game/Asset/ComputeShaderLoader.cpp new file mode 100644 index 00000000..0113c6b0 --- /dev/null +++ b/src/Game/Asset/ComputeShaderLoader.cpp @@ -0,0 +1,162 @@ +#include "Asset/ComputeShaderLoader.h" +#include "Asset/ComputeShaderAsset.h" + +#include "Core/FileSystem.h" +#include "Core/Logger.h" +#include "Core/ExecuteProcess.h" + +#include "Render/ComputeShader.h" +#include "Render/ComputeShaderCreateInfo.h" +#include "Render/ComputeShaderLexer.h" +#include "Render/ComputeShaderParser.h" + +#include + +#include +#include +#include +namespace sh::game +{ + static auto ReplaceSpaceString(const std::string& str) -> std::string + { + std::string result; + result.reserve(str.size()); + for (char c : str) + { + if (std::isalnum(static_cast(c))) + result.push_back(c); + else + result.push_back('_'); + } + return result; + } + + ComputeShaderLoader::ComputeShaderLoader(const render::IRenderContext& ctx) : + ctx(ctx) + { + cachePath = std::filesystem::current_path() / "cache"; + if (!std::filesystem::exists(cachePath)) + std::filesystem::create_directory(cachePath); + } + ComputeShaderLoader::~ComputeShaderLoader() = default; + + SH_GAME_API auto ComputeShaderLoader::Load(const std::filesystem::path& path) const -> core::SObject* + { + std::optional srcOpt = core::FileSystem::LoadText(path); + if (!srcOpt.has_value()) + { + SH_ERROR_FORMAT("Can't load file: {}", path.string()); + return nullptr; + } + if (!std::filesystem::exists(cachePath)) + std::filesystem::create_directory(cachePath); + + render::ComputeShaderLexer lexer{}; + render::ComputeShaderParser parser{}; + + render::ComputeShaderCreateInfo csCI{}; + try + { + csCI.shaderNode = parser.Parse(lexer.Lex(srcOpt.value())); + } + catch (const render::ComputeShaderParserException& e) + { + SH_ERROR_FORMAT("Compute shader parsing error: {}", e.what()); + return nullptr; + } + catch (const render::ComputeShaderLexerException& e) + { + SH_ERROR_FORMAT("Compute shader lexing error: {}", e.what()); + return nullptr; + } + + // 셰이더 이름이 비어있으면 파일명을 사용 + if (csCI.shaderNode.shaderName.empty()) + csCI.shaderNode.shaderName = path.stem().string(); + + // GLSL 파일 작성 + const std::string fileName = ReplaceSpaceString(csCI.shaderNode.shaderName) + ".comp"; + const std::filesystem::path glslPath = cachePath / fileName; + if (!core::FileSystem::SaveText(csCI.shaderNode.code, glslPath)) + { + SH_ERROR_FORMAT("Can't write compute shader source: {}", glslPath.string()); + return nullptr; + } + + // SPIR-V 컴파일 + const std::filesystem::path spirvPath = cachePath / (fileName + ".spv"); + + std::string output; + std::string compiler = "glslc"; +#if _WIN32 + std::vector args = { + "-fshader-stage=compute", + fmt::format("\"{}\"", glslPath.u8string()), + "-o", + fmt::format("\"{}\"", spirvPath.u8string()) + }; + compiler += ".exe"; +#else + std::vector args = { + "-fshader-stage=compute", + fmt::format("{}", glslPath.u8string()), + "-o", + fmt::format("{}", spirvPath.u8string()) + }; +#endif + if (!core::ExecuteProcess::Execute(compiler, args, output)) + { + SH_ERROR_FORMAT("Compute shader compile error: {}", output); + return nullptr; + } + + // SPIR-V 로드 + auto spirvOpt = core::FileSystem::LoadBinary(spirvPath); + if (!spirvOpt.has_value()) + { + SH_ERROR_FORMAT("Compute shader spirv load error: {}", spirvPath.string()); + return nullptr; + } + csCI.spirv = std::move(spirvOpt.value()); + + render::ComputeShader* const shader = core::SObject::Create(ctx, std::move(csCI)); + return shader; + } + + SH_GAME_API auto ComputeShaderLoader::Load(const core::Asset& asset) const -> core::SObject* + { + if (std::strcmp(asset.GetType(), ASSET_NAME) != 0) + { + SH_ERROR_FORMAT("Asset({}) is not a compute shader!", asset.GetAssetUUID().ToString()); + return nullptr; + } + const game::ComputeShaderAsset& csAsset = static_cast(asset); + const core::Json& shaderObjJson = csAsset.GetShaderObjectJson(); + + if (!shaderObjJson.contains("computeShader")) + return nullptr; + + const core::Json& csJson = shaderObjJson["computeShader"]; + + render::ComputeShaderCreateInfo csCI{}; + if (!csJson.contains("AST")) + return nullptr; + + csCI.shaderNode.Deserialize(csJson["AST"]); + if (csJson.contains("spirv")) + csCI.spirv = csJson["spirv"].get>(); + + if (core::SObject* const oldObj = core::SObjectManager::GetInstance()->GetSObject(asset.GetAssetUUID()); oldObj != nullptr) + { + render::ComputeShader* const oldShaderPtr = core::reflection::Cast(oldObj); + if (oldShaderPtr == nullptr) + return nullptr; + oldShaderPtr->Deserialize(shaderObjJson); + return oldShaderPtr; + } + render::ComputeShader* const shader = core::SObject::Create(ctx, std::move(csCI)); + shader->Deserialize(shaderObjJson); + + return shader; + } +}//namespace diff --git a/src/Game/Asset/ModelLoader.cpp b/src/Game/Asset/ModelLoader.cpp index fc6ae933..7362b0b0 100644 --- a/src/Game/Asset/ModelLoader.cpp +++ b/src/Game/Asset/ModelLoader.cpp @@ -23,6 +23,7 @@ #include #include #include +#include namespace sh::game { SH_GAME_API auto ModelLoader::LoadObj(const std::filesystem::path& path) const -> render::Model* @@ -417,9 +418,7 @@ namespace sh::game glm::vec2 deltaUV0 = uv1 - uv0; glm::vec2 deltaUV1 = uv2 - uv0; - const float delta = deltaUV0.x * deltaUV1.y - deltaUV1.x * deltaUV0.y; - if (delta < 1e-6) - return glm::vec3{ 0.f }; + const float delta = std::max(deltaUV0.x * deltaUV1.y - deltaUV1.x * deltaUV0.y, std::numeric_limits::epsilon()); float f = 1.0f / delta; diff --git a/src/Game/Asset/ShaderAsset.cpp b/src/Game/Asset/ShaderAsset.cpp index 8a06fd2a..69245570 100644 --- a/src/Game/Asset/ShaderAsset.cpp +++ b/src/Game/Asset/ShaderAsset.cpp @@ -2,19 +2,6 @@ namespace sh::game { - SH_GAME_API void ShaderAsset::SetAssetData() const - { - if (!shaderPtr.IsValid()) - return; - - const core::Json shaderObjJson = shaderPtr->Serialize(); - data = core::Json::to_cbor(shaderObjJson); - } - SH_GAME_API auto ShaderAsset::ParseAssetData() -> bool - { - shaderObjJson = core::Json::from_cbor(data); - return true; - } ShaderAsset::ShaderAsset() : Asset(ASSET_NAME) { @@ -37,4 +24,17 @@ namespace sh::game { return shaderObjJson; } -}//namespace \ No newline at end of file + SH_GAME_API void ShaderAsset::SetAssetData() const + { + if (!shaderPtr.IsValid()) + return; + + const core::Json shaderObjJson = shaderPtr->Serialize(); + data = core::Json::to_cbor(shaderObjJson); + } + SH_GAME_API auto ShaderAsset::ParseAssetData() -> bool + { + shaderObjJson = core::Json::from_cbor(data); + return true; + } +}//namespace1 \ No newline at end of file diff --git a/src/Game/Asset/ShaderLoader.cpp b/src/Game/Asset/ShaderLoader.cpp index 25673aa1..cbdbc6fd 100644 --- a/src/Game/Asset/ShaderLoader.cpp +++ b/src/Game/Asset/ShaderLoader.cpp @@ -61,16 +61,16 @@ namespace sh::game SH_ERROR_FORMAT("Shader parsing error: {}", e.what()); return nullptr; } - for (auto& passNode : shaderCI.shaderNode.passes) + for (const render::ShaderAST::PassNode& passNode : shaderCI.shaderNode.passes) { - auto shaderPaths = render::ShaderGenerator::GenerateShaderFile(shaderCI.shaderNode.shaderName, passNode, cachePath); + std::vector shaderPaths = render::ShaderGenerator::GenerateShaderFile(shaderCI.shaderNode.shaderName, passNode, cachePath); - for (auto& shaderPath : shaderPaths) + for (const std::filesystem::path& shaderPath : shaderPaths) { std::string name = shaderPath.filename().string() + ".spv"; std::filesystem::path spirvPath = (shaderPath.parent_path() / name); - auto stageType = render::ShaderPassBuilder::shaderType::Vertex; + render::ShaderPassBuilder::shaderType stageType = render::ShaderPassBuilder::shaderType::Vertex; if (shaderPath.extension() == ".frag") stageType = render::ShaderPassBuilder::shaderType::Fragment; @@ -90,7 +90,7 @@ namespace sh::game return nullptr; } // spir-v 불러오기 - auto spirv = core::FileSystem::LoadBinary(spirvPath); + std::optional> spirv = core::FileSystem::LoadBinary(spirvPath); if (!spirv.has_value()) { SH_ERROR_FORMAT("Temp shader load error: {}", spirvPath.string()); @@ -100,14 +100,14 @@ namespace sh::game } // 패스 생성 - render::ShaderPass* shaderPass = passBuilder->Build(passNode); + render::ShaderPass* const shaderPass = passBuilder->Build(passNode); if (shaderPass == nullptr) return nullptr; shaderCI.passes.push_back(shaderPass); } // 셰이더 생성 - auto shader = core::SObject::Create(std::move(shaderCI)); + render::Shader* const shader = core::SObject::Create(std::move(shaderCI)); return shader; } @@ -118,7 +118,7 @@ namespace sh::game SH_ERROR_FORMAT("Asset({}) is not a shader!", asset.GetAssetUUID().ToString()); return nullptr; } - const auto& shaderAsset = static_cast(asset); + const game::ShaderAsset& shaderAsset = static_cast(asset); const core::Json& shaderObjJson = shaderAsset.GetShaderObjectJson(); if (!shaderObjJson.contains("shader")) @@ -137,7 +137,7 @@ namespace sh::game for (int i = 0; i < shaderCI.shaderNode.passes.size(); ++i) { - const auto& passNode = shaderCI.shaderNode.passes[i]; + const render::ShaderAST::PassNode& passNode = shaderCI.shaderNode.passes[i]; const core::Json& shaderPassObjJson = *(shaderJson["passes"].begin() + i); const core::Json& shaderPassJson = shaderPassObjJson["shaderPass"]; @@ -146,15 +146,13 @@ namespace sh::game if (shaderPassJson.contains("fragShaderData")) passBuilder->SetData(render::ShaderPassBuilder::shaderType::Fragment, shaderPassJson["fragShaderData"].get>()); - render::ShaderPass* shaderPass = passBuilder->Build(passNode); + render::ShaderPass* const shaderPass = passBuilder->Build(passNode); if (shaderPass == nullptr) return nullptr; - shaderPass->Deserialize(shaderPassObjJson); - shaderCI.passes.push_back(shaderPass); } - auto shader = core::SObject::Create(std::move(shaderCI)); - shader->Deserialize(shaderObjJson); + render::Shader* const shader = core::SObject::Create(std::move(shaderCI)); + shader->SetUUID(asset.GetAssetUUID()); return shader; } diff --git a/src/Game/Component/Phys/BoxCollider.cpp b/src/Game/Component/Phys/BoxCollider.cpp index cfa95568..07cfa7d5 100644 --- a/src/Game/Component/Phys/BoxCollider.cpp +++ b/src/Game/Component/Phys/BoxCollider.cpp @@ -18,7 +18,7 @@ namespace sh::game { impl = std::make_unique(); - auto ctx = reinterpret_cast(world.GetPhysWorld()->GetContext()); + auto ctx = reinterpret_cast(world.GetPhysWorld().GetContext()); Vec3 halfSize = size / 2.0f; impl->shape = ctx->createBoxShape(reactphysics3d::Vector3{ halfSize.x, halfSize.y, halfSize.z }); @@ -88,7 +88,7 @@ namespace sh::game if (impl->shape != nullptr) { SH_INFO("DestroyShape()"); - auto ctx = reinterpret_cast(world.GetPhysWorld()->GetContext()); + auto ctx = reinterpret_cast(world.GetPhysWorld().GetContext()); ctx->destroyBoxShape(impl->shape); impl->shape = nullptr; } diff --git a/src/Game/Component/Phys/CapsuleCollider.cpp b/src/Game/Component/Phys/CapsuleCollider.cpp index 8c35c1fc..d74434dc 100644 --- a/src/Game/Component/Phys/CapsuleCollider.cpp +++ b/src/Game/Component/Phys/CapsuleCollider.cpp @@ -5,6 +5,7 @@ #include "Game/GameObject.h" #include "Game/World.h" +#include "Render/Renderer.h" #include "Render/Mesh/CapsuleMesh.h" #include "reactphysics3d/reactphysics3d.h" @@ -20,7 +21,7 @@ namespace sh::game { impl = std::make_unique(); - auto ctx = reinterpret_cast(world.GetPhysWorld()->GetContext()); + auto ctx = reinterpret_cast(world.GetPhysWorld().GetContext()); impl->shape = ctx->createCapsuleShape(radius, height); if(IsEditor()) @@ -119,7 +120,7 @@ namespace sh::game if (impl->shape != nullptr) { SH_INFO("DestroyShape()"); - auto ctx = reinterpret_cast(world.GetPhysWorld()->GetContext()); + auto ctx = reinterpret_cast(world.GetPhysWorld().GetContext()); ctx->destroyCapsuleShape(impl->shape); impl->shape = nullptr; } diff --git a/src/Game/Component/Phys/ConvexCollider.cpp b/src/Game/Component/Phys/ConvexCollider.cpp index 27f493e7..847865a8 100644 --- a/src/Game/Component/Phys/ConvexCollider.cpp +++ b/src/Game/Component/Phys/ConvexCollider.cpp @@ -90,7 +90,7 @@ namespace sh::game return; lastMesh = mesh; - auto ctx = reinterpret_cast(world.GetPhysWorld()->GetContext()); + auto ctx = reinterpret_cast(world.GetPhysWorld().GetContext()); const auto& meshVerts = mesh->GetVertex(); const reactphysics3d::VertexArray verts{ meshVerts.data(), sizeof(render::Mesh::Vertex), static_cast(meshVerts.size()), reactphysics3d::VertexArray::DataType::VERTEX_FLOAT_TYPE }; @@ -115,7 +115,7 @@ namespace sh::game SH_GAME_API void ConvexCollider::DestroyShape() { - auto ctx = reinterpret_cast(world.GetPhysWorld()->GetContext()); + auto ctx = reinterpret_cast(world.GetPhysWorld().GetContext()); if (impl->shape != nullptr) ctx->destroyConvexMeshShape(impl->shape); if (impl->convexMesh != nullptr) diff --git a/src/Game/Component/Phys/RigidBody.cpp b/src/Game/Component/Phys/RigidBody.cpp index 747a2bdb..6d31d7f1 100644 --- a/src/Game/Component/Phys/RigidBody.cpp +++ b/src/Game/Component/Phys/RigidBody.cpp @@ -21,8 +21,8 @@ namespace sh::game SH_GAME_API RigidBody::RigidBody(GameObject& owner) : Component(owner) { - auto ctx = reinterpret_cast(gameObject.world.GetPhysWorld()->GetContext()); - auto world = reinterpret_cast(gameObject.world.GetPhysWorld()->GetNative()); + auto ctx = reinterpret_cast(gameObject.world.GetPhysWorld().GetContext()); + auto world = reinterpret_cast(gameObject.world.GetPhysWorld().GetNative()); reactphysics3d::Vector3 physPos{ gameObject.transform->position.x, gameObject.transform->position.y, gameObject.transform->position.z }; auto& quat = gameObject.transform->GetQuat(); @@ -80,7 +80,7 @@ namespace sh::game { nativeMap.erase(impl->rigidbody); - auto physWorld = reinterpret_cast(gameObject.world.GetPhysWorld()->GetNative()); + auto physWorld = reinterpret_cast(gameObject.world.GetPhysWorld().GetNative()); physWorld->destroyRigidBody(impl->rigidbody); impl->rigidbody = nullptr; @@ -289,7 +289,7 @@ namespace sh::game if (this == &other) return true; - auto world = reinterpret_cast(gameObject.world.GetPhysWorld()->GetNative()); + auto world = reinterpret_cast(gameObject.world.GetPhysWorld().GetNative()); return world->testOverlap(impl->rigidbody, other.impl->rigidbody); } SH_GAME_API auto RigidBody::GetRigidBodyUsingHandle(RigidBodyHandle handle) -> RigidBody* diff --git a/src/Game/Component/Phys/SphereCollider.cpp b/src/Game/Component/Phys/SphereCollider.cpp index 663fe743..ea1e2f2f 100644 --- a/src/Game/Component/Phys/SphereCollider.cpp +++ b/src/Game/Component/Phys/SphereCollider.cpp @@ -18,7 +18,7 @@ namespace sh::game { impl = std::make_unique(); - auto ctx = reinterpret_cast(world.GetPhysWorld()->GetContext()); + auto ctx = reinterpret_cast(world.GetPhysWorld().GetContext()); impl->shape = ctx->createSphereShape(radius); if (IsEditor()) { @@ -88,7 +88,7 @@ namespace sh::game if (impl->shape != nullptr) { SH_INFO("DestroyShape()"); - auto ctx = reinterpret_cast(world.GetPhysWorld()->GetContext()); + auto ctx = reinterpret_cast(world.GetPhysWorld().GetContext()); ctx->destroySphereShape(impl->shape); impl->shape = nullptr; } diff --git a/src/Game/Component/Render/Camera.cpp b/src/Game/Component/Render/Camera.cpp index 796e7eb7..c44b089a 100644 --- a/src/Game/Component/Render/Camera.cpp +++ b/src/Game/Component/Render/Camera.cpp @@ -1,50 +1,57 @@ #include "Component/Render/Camera.h" #include "World.h" +#include "Render/Renderer.h" #include "Render/IRenderContext.h" +#include "Render/RenderData.h" #include "Physics/Ray.h" #include "glm/gtc/matrix_transform.hpp" #include - namespace sh::game { Camera::Camera(GameObject& owner) : Component(owner), - - fov(60.f), fovx(0.f), - camera(), depth(0), - renderTexture(nullptr) { - SetFov(60); - owner.world.RegisterCamera(this); - camera.SetNearPlane(0.1f); - camera.SetFarPlane(1000.f); + fovRadians = glm::radians(fov); + owner.world.RegisterCamera(*this); + renderData.renderViewers.resize(1); canPlayInEditor = true; } - Camera::~Camera() - { - } + Camera::~Camera() = default; SH_GAME_API void Camera::BeginUpdate() { + render::RenderViewer& viewer = renderData.renderViewers[0]; if (renderTexture == nullptr) { - glm::vec2 size = gameObject.world.renderer.GetContext()->GetViewportEnd() - gameObject.world.renderer.GetContext()->GetViewportStart(); - camera.SetWidth(size.x); - camera.SetHeight(size.y); + const glm::vec2 size = gameObject.world.renderer.GetContext()->GetViewportEnd() - gameObject.world.renderer.GetContext()->GetViewportStart(); + width = size.x; + height = size.y; + viewer.viewportRect = { 0, 0, size.x, size.y }; + viewer.viewportScissor = { 0, 0, size.x, size.y }; + renderData.ClearRenderTargets(); } else { - glm::vec2 size = renderTexture->GetSize(); - camera.SetWidth(size.x); - camera.SetHeight(size.y); + const glm::vec2 size = renderTexture->GetSize(); + width = size.x; + height = size.y; + viewer.viewportRect = { 0, 0, size.x, size.y }; + viewer.viewportScissor = { 0, 0, size.x, size.y }; + + renderData.SetRenderTarget(renderTexture); } - camera.SetPos(gameObject.transform->GetWorldPosition()); - camera.SetLookPos(lookPos); + gameObject.transform->UpdateMatrix(); + viewer.pos = gameObject.transform->GetWorldPosition(); + viewer.to = lookPos; + UpdateViewMatrix(); + UpdateProjMatrix(); + + world.renderer.PushRenderData(renderData); } SH_GAME_API void Camera::OnDestroy() { @@ -52,93 +59,37 @@ namespace sh::game { gameObject.world.SetMainCamera(nullptr); } - gameObject.world.UnRegisterCamera(this); + gameObject.world.UnRegisterCamera(*this); Super::OnDestroy(); } - SH_GAME_API void Camera::SetActive(bool b) - { - Super::SetActive(b); - camera.SetActive(b); - } - - SH_GAME_API auto Camera::GetProjMatrix() const -> const glm::mat4& - { - return camera.GetProjMatrix(core::ThreadType::Game); - } - - SH_GAME_API auto Camera::GetViewMatrix() const -> const glm::mat4& - { - return camera.GetViewMatrix(core::ThreadType::Game); - } - - SH_GAME_API void Camera::SetDepth(int depth) - { - this->depth = depth; - camera.SetPriority(depth); - } - SH_GAME_API void Camera::SetFov(float degree) - { - const float aspect = camera.GetWidth(core::ThreadType::Game) / camera.GetHeight(core::ThreadType::Game); - fov = degree; - fovx = glm::degrees(glm::atan(glm::tan(glm::radians(fov) * 0.5f) * aspect) * 2.f); - - camera.SetFov(degree); - } - - SH_GAME_API void Camera::SetRenderTexture(render::RenderTexture* renderTexture) - { - this->renderTexture = renderTexture; - camera.SetRenderTexture(renderTexture); - } - SH_GAME_API auto Camera::GetRenderTexture() const -> render::RenderTexture* - { - return renderTexture; - } - - SH_GAME_API auto Camera::GetNative() -> render::Camera& - { - return camera; - } - - SH_GAME_API auto Camera::GetNative() const -> const render::Camera& - { - return camera; - } - SH_GAME_API auto Camera::ScreenPointToRay(const Vec2& mousePos) const -> phys::Ray { - float w = camera.GetWidth(core::ThreadType::Game); - float h = camera.GetHeight(core::ThreadType::Game); - float aspect = w / h; + const float aspect = width / height; - float ndcX = 2.f * mousePos.x / w - 1.0f; - float ndcY = 1.0f - 2.f * mousePos.y / h; + const float ndcX = 2.f * mousePos.x / width - 1.0f; + const float ndcY = 1.0f - 2.f * mousePos.y / height; glm::vec4 camCoord{ 0.f, 0.f, 0.f, 1.f }; camCoord.x = aspect * ndcX; camCoord.y = ndcY; - camCoord.z = -1.f / glm::tan(camera.GetFovRadian(core::ThreadType::Game) / 2.f); + camCoord.z = -1.f / glm::tan(fovRadians / 2.f); - glm::vec3 worldCoord{ glm::inverse(camera.GetViewMatrix(core::ThreadType::Game)) * camCoord }; + glm::vec3 worldCoord{ glm::inverse(GetViewMatrix()) * camCoord }; glm::vec3 dir = glm::normalize(worldCoord - glm::vec3{ gameObject.transform->position }); return phys::Ray(gameObject.transform->position, dir); } - SH_GAME_API auto Camera::ScreenPointToRayOrtho(const Vec2& mousePos) const -> phys::Ray { - float w = camera.GetWidth(core::ThreadType::Game); - float h = camera.GetHeight(core::ThreadType::Game); - - float ndcX = 2.f * (mousePos.x / w) - 1.f; - float ndcY = 1.f - 2.f * (mousePos.y / h); + const float ndcX = 2.f * (mousePos.x / width) - 1.f; + const float ndcY = 1.f - 2.f * (mousePos.y / height); // Vulkan z_ndc = 0 ~ 1 glm::vec4 ndcNear(ndcX, ndcY, 0.f, 1.f); glm::vec4 ndcFar(ndcX, ndcY, 1.f, 1.f); - glm::mat4 invViewProj = glm::inverse(camera.GetProjMatrix(core::ThreadType::Game) * camera.GetViewMatrix(core::ThreadType::Game)); + glm::mat4 invViewProj = glm::inverse(GetProjMatrix() * GetViewMatrix()); glm::vec4 worldNear4 = invViewProj * ndcNear; glm::vec4 worldFar4 = invViewProj * ndcFar; @@ -150,78 +101,29 @@ namespace sh::game return phys::Ray(worldNear, dir); } - - SH_GAME_API void Camera::SetLookPos(const Vec3& pos) - { - lookPos = pos; - } - SH_GAME_API auto Camera::GetLookPos() const -> const Vec3& - { - return lookPos; - } - SH_GAME_API void Camera::SetUpVector(const Vec3& up) - { - camera.SetUpVector(up); - } - SH_GAME_API auto Camera::GetUpVector() const -> Vec3 - { - return camera.GetUpVector(core::ThreadType::Game); - } - SH_GAME_API void Camera::SetWidth(float width) - { - camera.SetWidth(width); - } - SH_GAME_API auto Camera::GetWidth() const -> float - { - return camera.GetWidth(core::ThreadType::Game); - } - SH_GAME_API void Camera::SetHeight(float height) - { - camera.SetHeight(height); - } - SH_GAME_API auto Camera::GetHeight() const -> float - { - return camera.GetHeight(core::ThreadType::Game); - } - - SH_GAME_API void Camera::SetProjection(Projection proj) - { - projection = proj; - if (proj == Projection::Perspective) - camera.SetOrthographic(false); - else - camera.SetOrthographic(true); - } - - SH_GAME_API auto Camera::GetProjection() const -> Projection + SH_GAME_API void Camera::SetFov(float degree) { - return projection; + const float aspect = width / height; + fov = degree; + fovx = glm::degrees(glm::atan(glm::tan(glm::radians(fov) * 0.5f) * aspect) * 2.f); + fovRadians = glm::radians(fov); } - SH_GAME_API void Camera::SetNearPlane(float near) - { - camera.SetNearPlane(near); - } - SH_GAME_API auto Camera::GetNearPlane() const -> float - { - return camera.GetNearPlane(core::ThreadType::Game); - } - SH_GAME_API void Camera::SetFarPlane(float far) + SH_GAME_API void Camera::UpdateViewMatrix() { - camera.SetFarPlane(far); + renderData.renderViewers.front().viewMatrix = glm::lookAt(glm::vec3{ gameObject.transform->GetWorldPosition() }, glm::vec3{ lookPos }, glm::vec3{ up }); } - SH_GAME_API auto Camera::GetFarPlane() const -> float + SH_GAME_API void Camera::UpdateProjMatrix() { - return camera.GetFarPlane(core::ThreadType::Game); - } - - SH_GAME_API void Camera::OnPropertyChanged(const core::reflection::Property& prop) - { - if (prop.GetName() == core::Util::ConstexprHash("depth")) - SetDepth(depth); - else if (prop.GetName() == core::Util::ConstexprHash("fov")) - SetFov(fov); - else if (prop.GetName() == core::Util::ConstexprHash("projection")) - SetProjection(projection); + if (projection == Projection::Perspective) + { + renderData.renderViewers.front().projMatrix = glm::perspectiveFovRH_ZO(fovRadians, width, height, nearPlane, farPlane); + } + else + { + const float dis = glm::length(glm::vec3{ lookPos } - glm::vec3{ gameObject.transform->GetWorldPosition() }) / 2.0f; + const float aspect = width / height; + renderData.renderViewers.front().projMatrix = glm::orthoRH_ZO(-dis * aspect, dis * aspect, -dis, dis, nearPlane, farPlane); + } } -} \ No newline at end of file +}//namespace diff --git a/src/Game/Component/Render/DirectionalLight.cpp b/src/Game/Component/Render/DirectionalLight.cpp index b91a7eac..2fded976 100644 --- a/src/Game/Component/Render/DirectionalLight.cpp +++ b/src/Game/Component/Render/DirectionalLight.cpp @@ -2,50 +2,67 @@ #include "World.h" +#include "Core/SObject.h" + +#include "Render/Renderer.h" +#include "Render/ShadowMapManager.h" +#include "Render/RenderTexture.h" + +#include + namespace sh::game { - SH_GAME_API DirectionalLight::DirectionalLight(GameObject& owner) : - Component(owner) - { - world.GetLightOctree().Insert(*this); - canPlayInEditor = true; - } - SH_GAME_API DirectionalLight::~DirectionalLight() + DirectionalLight::DirectionalLight(GameObject& owner) : + LightBase(owner) { } - - SH_GAME_API void DirectionalLight::OnDestroy() - { - world.GetLightOctree().Erase(*this); - } + DirectionalLight::~DirectionalLight() = default; SH_GAME_API bool DirectionalLight::Intersect(const render::AABB& aabb) const { + // 항상 통과 return true; } SH_GAME_API void DirectionalLight::SetIntensity(float intensity) { this->intensity = intensity; } - SH_GAME_API auto DirectionalLight::GetIntensity() const -> float - { - return intensity; - } SH_GAME_API void DirectionalLight::SetDirection(const Vec3& dir) { direction = dir; } - SH_GAME_API auto DirectionalLight::GetDirection() const -> const Vec3& + SH_GAME_API auto DirectionalLight::GetLightSpaceMatrix() const -> glm::mat4 { - return direction; + return GetShadowProjMatrix() * GetShadowViewMatrix(); } - SH_GAME_API auto DirectionalLight::GetPos() const -> const Vec3& + + SH_GAME_API auto DirectionalLight::GetShadowViewMatrix() const -> glm::mat4 { - return gameObject.transform->GetWorldPosition(); + const glm::vec3 dir = glm::normalize(glm::vec3{ direction.x, direction.y, direction.z }); + const glm::vec3 sceneCenter{ 0.f, 0.f, 0.f }; + const float distance = GetShadowFarPlane() * 0.5f; + const glm::vec3 lightPos = sceneCenter - dir * distance; + const glm::vec3 worldUp = (glm::abs(dir.y) > 0.99f) ? glm::vec3{ 0.f, 0.f, 1.f } : glm::vec3{ 0.f, 1.f, 0.f }; + return glm::lookAt(lightPos, lightPos + dir, worldUp); } - - SH_GAME_API auto DirectionalLight::GetLightType() const -> ILight::Type + SH_GAME_API auto DirectionalLight::GetShadowProjMatrix() const -> glm::mat4 { - return ILight::Type::Directional; + const float ortho = GetShadowOrthoSize(); + return glm::orthoRH_ZO(-ortho, ortho, -ortho, ortho, GetShadowNearPlane(), GetShadowFarPlane()); + } + SH_GAME_API auto DirectionalLight::GetShadowPos() const -> glm::vec3 + { + const glm::vec3 dir = glm::normalize(glm::vec3{ direction.x, direction.y, direction.z }); + const glm::vec3 sceneCenter{ 0.f, 0.f, 0.f }; + const float distance = GetShadowFarPlane() * 0.5f; + return sceneCenter - dir * distance; + } + SH_GAME_API auto DirectionalLight::GetShadowLookAt() const -> glm::vec3 + { + return glm::vec3{ 0.f, 0.f, 0.f }; + } + SH_GAME_API auto DirectionalLight::GetPos() const -> const Vec3& + { + return gameObject.transform->GetWorldPosition(); } -}//namespace \ No newline at end of file +}//namespace diff --git a/src/Game/Component/Render/EditorCamera.cpp b/src/Game/Component/Render/EditorCamera.cpp index 2b7d915e..359ebb19 100644 --- a/src/Game/Component/Render/EditorCamera.cpp +++ b/src/Game/Component/Render/EditorCamera.cpp @@ -16,7 +16,6 @@ namespace sh::game lastLookPos(lookPos), middleDragRight(), middleDragUp(), rotationSpeed(0.2f), moveSpeed(0.02f) { - auto& type = GetStaticType(); } SH_GAME_API void EditorCamera::Start() @@ -26,7 +25,7 @@ namespace sh::game const float pitch = glm::degrees(asin(forward.y)); xdir = pitch; ydir = yaw; - camera.SetFarPlane(10000.0f); + SetFarPlane(10'000.f); } SH_GAME_API void EditorCamera::BeginUpdate() @@ -210,9 +209,9 @@ namespace sh::game glm::vec3 v = gameObject.transform->GetWorldPosition() - lookPos; //glm::vec3 up = glm::cross(glm::vec3{ 1.0f, 0.f, 0.f }, glm::normalize(v)); - Vec3 up = camera.GetUpVector(core::ThreadType::Game); + Vec3 up = GetUpVector(); up.y = (xdir >= 90 && xdir <= 270) ? -1 : 1; - camera.SetUpVector(up); + SetUpVector(up); } void EditorCamera::UpdateCameraPosition() diff --git a/src/Game/Component/Render/LightBase.cpp b/src/Game/Component/Render/LightBase.cpp new file mode 100644 index 00000000..101f9016 --- /dev/null +++ b/src/Game/Component/Render/LightBase.cpp @@ -0,0 +1,88 @@ +#include "Game/Component/Render/LightBase.h" +#include "Game/World.h" + +namespace sh::game +{ + LightBase::LightBase(GameObject& owner) : + Component(owner) + { + canPlayInEditor = true; + } + SH_GAME_API void LightBase::Awake() + { + Super::Awake(); + if (bCastShadow) + RegisterToShadowManager(); + world.GetLightOctree().Insert(*this); + } + SH_GAME_API void LightBase::OnDestroy() + { + UnregisterFromShadowManager(); + world.GetLightOctree().Erase(*this); + Super::OnDestroy(); + } + SH_GAME_API void LightBase::OnEnable() + { + if (bCastShadow) + RegisterToShadowManager(); + world.GetLightOctree().Insert(*this); + } + SH_GAME_API void LightBase::OnDisable() + { + UnregisterFromShadowManager(); + world.GetLightOctree().Erase(*this); + } + SH_GAME_API void LightBase::OnPropertyChanged(const core::reflection::Property& prop) + { + Super::OnPropertyChanged(prop); + if (prop.GetName() == "bCastShadow") + { + if (bCastShadow) + RegisterToShadowManager(); + else + UnregisterFromShadowManager(); + } + } + SH_GAME_API void LightBase::SetCastShadow(bool _bCastShadow) + { + if (bCastShadow == _bCastShadow) + return; + bCastShadow = _bCastShadow; + if (bCastShadow) + RegisterToShadowManager(); + else + UnregisterFromShadowManager(); + } + SH_GAME_API auto LightBase::GetShadowMap() const -> render::RenderTexture* + { + if (!bCastShadow) + return nullptr; + return world.GetShadowMapManager().GetAtlas(); + } + SH_GAME_API auto LightBase::GetShadowSlot() const -> render::ShadowMapManager::Slot + { + if (!bCastShadow) + return render::ShadowMapManager::Slot{}; + return world.GetShadowMapManager().GetSlot(*this); + } + SH_GAME_API void LightBase::UpdateLightOctree() + { + world.GetLightOctree().Erase(*this); + world.GetLightOctree().Insert(*this); + } + SH_GAME_API void LightBase::RegisterToShadowManager() + { + if (bRegistered) + return; + world.GetShadowMapManager().Register(*this); + bRegistered = true; + } + + SH_GAME_API void LightBase::UnregisterFromShadowManager() + { + if (!bRegistered) + return; + world.GetShadowMapManager().Unregister(*this); + bRegistered = false; + } +}//namespace \ No newline at end of file diff --git a/src/Game/Component/Render/LineRenderer.cpp b/src/Game/Component/Render/LineRenderer.cpp index 865151c1..5ea34f97 100644 --- a/src/Game/Component/Render/LineRenderer.cpp +++ b/src/Game/Component/Render/LineRenderer.cpp @@ -1,6 +1,7 @@ #include "Component/Render/LineRenderer.h" #include "Component/Render/Camera.h" +#include "Render/Renderer.h" #include "Render/Mesh.h" #include "World.h" diff --git a/src/Game/Component/Render/MeshRenderer.cpp b/src/Game/Component/Render/MeshRenderer.cpp index f6f9f52a..75944915 100644 --- a/src/Game/Component/Render/MeshRenderer.cpp +++ b/src/Game/Component/Render/MeshRenderer.cpp @@ -4,6 +4,8 @@ #include "World.h" +#include "Render/Renderer.h" + #include #include @@ -108,14 +110,15 @@ namespace sh::game } else if (prop.GetName() == core::Util::ConstexprHash("mats")) { - CreateDrawable(true); + for (std::size_t i = 0; i < mats.size(); ++i) + SetMaterial(i, mats[i]); } else if (prop.GetName() == core::Util::ConstexprHash("renderTag")) { - for (auto* d : drawables) + for (render::Drawable* drawable : drawables) { - if (d != nullptr) - d->SetRenderTagId(renderTag); + if (drawable != nullptr) + drawable->SetRenderTagId(renderTag); } } } @@ -227,7 +230,7 @@ namespace sh::game for (auto& [passPtr, layoutPtr] : locations) { - if (layoutPtr->type == render::UniformStructLayout::Type::Material) + if (layoutPtr->usage == render::UniformStructLayout::Usage::Material) continue; std::vector data(layoutPtr->GetSize()); @@ -278,12 +281,12 @@ namespace sh::game { auto var = block.GetTextureProperty(member.name); if (core::IsValid(var)) - drawable->GetMaterialData().SetTextureData(*passPtr, layoutPtr->type, layoutPtr->binding, *var); + drawable->GetMaterialData().SetTextureData(*passPtr, layoutPtr->usage, layoutPtr->binding, *var); else { render::Texture* const texPtr = static_cast(core::SObject::GetSObjectUsingResolver(blackTexUUID)); if (texPtr != nullptr) - drawable->GetMaterialData().SetTextureData(*passPtr, layoutPtr->type, layoutPtr->binding, *texPtr); + drawable->GetMaterialData().SetTextureData(*passPtr, layoutPtr->usage, layoutPtr->binding, *texPtr); else SH_ERROR("Can't get default texture!"); } @@ -291,7 +294,7 @@ namespace sh::game } } if (!isSampler) - drawable->GetMaterialData().SetUniformData(*passPtr, layoutPtr->type, layoutPtr->binding, std::move(data)); + drawable->GetMaterialData().SetBindingData(*passPtr, layoutPtr->usage, layoutPtr->binding, std::move(data)); } } } @@ -377,7 +380,7 @@ namespace sh::game { for (auto& location : propInfo.locations) { - if (location.layoutPtr->type != render::UniformStructLayout::Type::Object) + if (location.layoutPtr->usage != render::UniformStructLayout::Usage::Object) continue; auto& locs = localUniformLocationsList[i]; auto it = std::find(locs.begin(), locs.end(), std::pair{ location.passPtr.Get(), location.layoutPtr }); @@ -425,13 +428,18 @@ namespace sh::game } UpdatePropertyBlockData(); } - void MeshRenderer::FillLightStruct(render::Drawable& drawable, render::Shader& shader) const + void MeshRenderer::FillLightStruct(render::Drawable& drawable, render::Shader& shader) { - Light lightStruct{}; - auto lights = world.GetLightOctree().Query(worldAABB); - int idx = 0; - for (int i = 0; i < std::min((int)lights.size(), 10); ++i) + const std::vector& lights = world.GetLightOctree().Query(worldAABB); + + lightDatas.clear(); + lightDatas.resize(sizeof(int) * 4 + lights.size() * sizeof(Light), 0); // int 3개는 패딩 + const int count = static_cast(lights.size()); + std::memcpy(lightDatas.data(), &count, sizeof(int)); + std::size_t offset = sizeof(int) * 4; + for (int i = 0; i < count; ++i) { + Light lightStruct{}; const ILight* light = static_cast(lights[i]); if (light->GetLightType() == ILight::Type::Point) { @@ -439,9 +447,8 @@ namespace sh::game if (!core::IsValid(pointLight)) continue; const Vec3& pos = pointLight->gameObject.transform->GetWorldPosition(); - lightStruct.lightPos[idx] = { pos.x, pos.y, pos.z, 0.f }; - lightStruct.lightPos[idx].w = pointLight->GetRadius(); - lightStruct.other[idx].w = 1; + lightStruct.pos = { pos.x, pos.y, pos.z, pointLight->GetRadius() }; + lightStruct.other.w = 1; } else if (light->GetLightType() == ILight::Type::Directional) { @@ -449,20 +456,25 @@ namespace sh::game if (!core::IsValid(dirLight)) continue; const Vec3& dir = dirLight->GetDirection(); - lightStruct.lightPos[idx] = { dir.x, dir.y, dir.z, dirLight->GetIntensity() }; - lightStruct.other[idx].w = 0; + lightStruct.pos = { dir.x, dir.y, dir.z, dirLight->GetIntensity() }; + lightStruct.other.w = 0; + render::ShadowMapManager::Slot slot = dirLight->GetShadowSlot(); + lightStruct.shadow = { slot.uvOffset.x,slot.uvOffset.y,slot.uvSize.x, slot.uvSize.y }; } - ++idx; + lightStruct.lightSpaceMatrix = light->GetLightSpaceMatrix(); + + std::memcpy(lightDatas.data() + offset, &lightStruct, sizeof(Light)); + offset += sizeof(Light); } - lightStruct.lightCount = idx; - for (auto& lightingPass : shader.GetAllShaderPass()) + for (const render::Shader::LightingPassData& lightingPassData : shader.GetAllShaderPass()) { - for (render::ShaderPass& pass : lightingPass.passes) + for (const render::ShaderPass& pass : lightingPassData.passes) { if (pass.IsPendingKill() || pass.GetLightingBinding() == -1) continue; - - drawable.GetMaterialData().SetUniformData(pass, render::UniformStructLayout::Type::Object, pass.GetLightingBinding(), &lightStruct, sizeof(Light)); + drawable.GetMaterialData().SetBindingData(pass, render::UniformStructLayout::Usage::Object, pass.GetLightingBinding(), lightDatas.data(), lightDatas.size()); + if (pass.GetShadowMapBinding() != -1 && world.GetShadowMapManager().GetAtlas() != nullptr) + drawable.GetMaterialData().SetTextureData(pass, render::UniformStructLayout::Usage::Object, pass.GetShadowMapBinding(), *world.GetShadowMapManager().GetAtlas()); } } } diff --git a/src/Game/Component/Render/PickingCamera.cpp b/src/Game/Component/Render/PickingCamera.cpp index 1cd37850..9344dc8f 100644 --- a/src/Game/Component/Render/PickingCamera.cpp +++ b/src/Game/Component/Render/PickingCamera.cpp @@ -3,6 +3,8 @@ #include "Game/World.h" #include "Game/GameObject.h" +#include "Render/Renderer.h" + #include namespace sh::game { @@ -15,16 +17,11 @@ namespace sh::game PickingCamera::PickingCamera(GameObject& owner) : Camera(owner) { - render::RenderTargetLayout rt{}; - rt.format = render::TextureFormat::RGBA32; - rt.depthFormat = render::TextureFormat::D24S8; - rt.bUseMSAA = false; + pixels.resize(4); - renderTex = core::SObject::Create(rt); + renderTex = core::SObject::Create(render::TextureFormat::RGBA32, render::TextureFormat::D24S8, false); renderTex->SetSize(1024, 768); renderTex->Build(*world.renderer.GetContext()); - camera.SetWidth(1024); - camera.SetHeight(768); renderTex->SetName("PickingFramebuffer"); SetRenderTexture(renderTex); @@ -46,8 +43,11 @@ namespace sh::game Super::BeginUpdate(); if (!pickingCallback.Empty() && !bRequestRead) { - bufferFuture = world.renderer.GetScriptableRenderer()->ReadRenderTextureAsync(*renderTex, x, y); - bRequestRead = true; + if (world.renderer.GetScriptableRenderer() != nullptr) + { + bufferFuture = world.renderer.GetScriptableRenderer()->ReadRenderTextureAsync(*renderTex, x, y); + bRequestRead = true; + } } if (bufferFuture.valid()) @@ -55,7 +55,7 @@ namespace sh::game auto status = bufferFuture.wait_for(std::chrono::milliseconds(0)); if (status == std::future_status::ready) { - pixels = reinterpret_cast(bufferFuture.get()->GetData()); + pixels = bufferFuture.get()->GetData(); SH_INFO_FORMAT("{}, {}, {}, {}", pixels[0], pixels[1], pixels[2], pixels[3]); pickingCallback.Notify({ pixels[0], pixels[1], pixels[2], pixels[3] }); @@ -73,8 +73,6 @@ namespace sh::game SH_GAME_API void PickingCamera::SetTextureSize(const Vec2& size) { - camera.SetWidth(size.x); - camera.SetHeight(size.y); renderTex->SetSize(size.x, size.y); } diff --git a/src/Game/Component/Render/PointLight.cpp b/src/Game/Component/Render/PointLight.cpp index e73354c9..14395a4f 100644 --- a/src/Game/Component/Render/PointLight.cpp +++ b/src/Game/Component/Render/PointLight.cpp @@ -5,21 +5,13 @@ namespace sh::game { SH_GAME_API PointLight::PointLight(GameObject& owner) : - Component(owner) + LightBase(owner) { - world.GetLightOctree().Insert(*this); - canPlayInEditor = true; } SH_GAME_API PointLight::~PointLight() { } - SH_GAME_API void PointLight::Destroy() - { - Super::Destroy(); - world.GetLightOctree().Erase(*this); - } - SH_GAME_API void PointLight::BeginUpdate() { if (lastPos != gameObject.transform->position) @@ -27,8 +19,7 @@ namespace sh::game if (bUpdateOctree) { - world.GetLightOctree().Erase(*this); - world.GetLightOctree().Insert(*this); + UpdateLightOctree(); } } @@ -83,30 +74,39 @@ namespace sh::game this->range = radius; bUpdateOctree = true; } - SH_GAME_API void PointLight::SetIntensity(float intensity) + SH_GAME_API auto PointLight::GetPos() const -> const Vec3& { - this->intensity = intensity; + return gameObject.transform->GetWorldPosition(); } - SH_GAME_API auto PointLight::GetRadius() const -> float + + SH_GAME_API auto PointLight::GetLightSpaceMatrix() const -> glm::mat4 { - return range; + return glm::mat4{ 1.f }; } - SH_GAME_API auto PointLight::GetIntensity() const -> float + + SH_GAME_API auto PointLight::GetShadowViewMatrix() const -> glm::mat4 { - return intensity; + return glm::mat4{ 1.f }; } - SH_GAME_API auto PointLight::GetPos() const -> const Vec3& + + SH_GAME_API auto PointLight::GetShadowProjMatrix() const -> glm::mat4 { - return gameObject.transform->GetWorldPosition(); + return glm::mat4{ 1.f }; + } + + SH_GAME_API auto PointLight::GetShadowPos() const -> glm::vec3 + { + return glm::vec3{ 0.f }; } - SH_GAME_API auto PointLight::GetLightType() const -> ILight::Type + SH_GAME_API auto PointLight::GetShadowLookAt() const -> glm::vec3 { - return ILight::Type::Point; + return glm::vec3{ 0.f }; } SH_GAME_API void PointLight::OnPropertyChanged(const core::reflection::Property& prop) { + Super::OnPropertyChanged(prop); if (prop.GetName() == "range") { world.GetLightOctree().Erase(*this); diff --git a/src/Game/Component/Render/SSAOComponent.cpp b/src/Game/Component/Render/SSAOComponent.cpp new file mode 100644 index 00000000..c9202d24 --- /dev/null +++ b/src/Game/Component/Render/SSAOComponent.cpp @@ -0,0 +1,199 @@ +#include "Component/Render/SSAOComponent.h" +#include "Component/Render/Camera.h" +#include "World.h" +#include "GameObject.h" +#include "GameRenderer.h" + +#include "Core/SObject.h" +#include "Core/GarbageCollection.h" +#include "Core/Util.h" + +#include "Render/Renderer.h" +#include "Render/IRenderContext.h" +#include "Render/Material.h" +#include "Render/RenderTexture.h" +#include "Render/RenderPass/CombinePass.h" +#include "Render/RenderPass/SSAOPass.h" + +#include +#include + +#include +#include +namespace sh::game +{ + std::array SSAOComponent::kernel{}; + bool SSAOComponent::bKernelInit = false; + + SSAOComponent::SSAOComponent(GameObject& owner) : + Component(owner) + { + canPlayInEditor = true; + + depthRenderData.tag = core::Name{ "Depth" }; + ssaoRenderData.tag = core::Name{ "SSAO" }; + combineRenderData.tag = core::Name{ "Combine" }; + depthRenderData.priority = 2; + ssaoRenderData.priority = 1; + combineRenderData.priority = -1; + combineRenderData.ClearRenderTargets(); + } + SSAOComponent::~SSAOComponent() = default; + + SH_GAME_API void SSAOComponent::Awake() + { + camera = gameObject.GetComponent(); + if (camera == nullptr) + SH_ERROR("SSAOComponent requires a Camera component on the same GameObject"); + CreateKernel(); + + mat = core::SObject::Create(static_cast(core::SObject::GetSObjectUsingResolver(core::UUID{ "bbc4ef7ec45dce223297a224f8093f24" }))); + mat->Build(*world.renderer.GetContext()); + SetMaterial(mat); + } + SH_GAME_API void SSAOComponent::OnDestroy() + { + if (depthRT != nullptr) + depthRT->Destroy(); + if (normalRT != nullptr) + normalRT->Destroy(); + if (aoRT != nullptr) + aoRT->Destroy(); + if (noiseTex != nullptr) + noiseTex->Destroy(); + if (mat != nullptr) + mat->Destroy(); + + Super::OnDestroy(); + } + SH_GAME_API void SSAOComponent::BeginUpdate() + { + if (camera == nullptr) + return; + + depthRenderData.renderViewers = ssaoRenderData.renderViewers = combineRenderData.renderViewers = camera->GetRenderData().renderViewers; + combineRenderData.SetRenderTargets(camera->GetRenderData().GetRenderTargets()); + EnsureRenderTextures(); + UpdateMaterialUniforms(); + + world.renderer.PushRenderData(depthRenderData); + world.renderer.PushRenderData(ssaoRenderData); + world.renderer.PushRenderData(combineRenderData); + } + SH_GAME_API void SSAOComponent::SetMaterial(render::Material* _mat) + { + mat = _mat; + if (mat == nullptr) + return; + + GameRenderer* const renderer = dynamic_cast(world.renderer.GetScriptableRenderer()); + if (renderer == nullptr) + return; + + render::SSAOPass& ssaoPass = renderer->GetSSAOPass(); + ssaoPass.SetMaterial(*mat); + + render::CombinePass& combinePass = renderer->GetCombinePass(); + combinePass.SetMaterial(*mat); + } + + void SSAOComponent::EnsureRenderTextures() + { + uint32_t w = static_cast(camera->GetWidth()); + uint32_t h = static_cast(camera->GetHeight()); + if (w == 0 || h == 0) + return; + + render::IRenderContext* ctx = world.renderer.GetContext(); + if (ctx == nullptr) + return; + + core::GarbageCollection& gc = *core::GarbageCollection::GetInstance(); + + if (depthRT == nullptr) + { + depthRT = core::SObject::Create(render::TextureFormat::None, render::TextureFormat::D32, false); + depthRT->SetSize(w, h); + depthRT->Build(*ctx); + } + if (normalRT == nullptr) + { + normalRT = core::SObject::Create(render::TextureFormat::RGBA32, render::TextureFormat::None, false); + normalRT->SetSize(w, h); + normalRT->Build(*ctx); + } + depthRenderData.SetRenderTargets({ depthRT, normalRT }); + + if (aoRT == nullptr) + { + aoRT = core::SObject::Create(render::TextureFormat::R8, render::TextureFormat::None, false); + aoRT->SetSize(w, h); + aoRT->Build(*ctx); + + ssaoRenderData.SetRenderTarget(aoRT); + } + + if (noiseTex == nullptr) + { + noiseTex = core::SObject::Create(render::TextureFormat::RG32F, 4, 4, false); + std::array noise{}; // 4x4x2 channels, R16G16_SFLOAT + for (int i = 0; i < 32; ++i) + noise[i] = glm::packHalf1x16(core::Util::RandomRange(-1.f, 1.f)); + noiseTex->SetPixelData(reinterpret_cast(noise.data()), sizeof(uint16_t) * noise.size()); + noiseTex->SetAnisoLevel(0); + noiseTex->SetFiltering(render::Texture::Filtering::Box); + noiseTex->Build(*ctx); + } + + if (w != cachedWidth || h != cachedHeight) + { + depthRT->SetSize(w, h); + normalRT->SetSize(w, h); + aoRT->SetSize(w, h); + cachedWidth = w; + cachedHeight = h; + } + } + void SSAOComponent::UpdateMaterialUniforms() + { + if (!core::IsValid(mat)) + return; + + const glm::mat4& proj = camera->GetProjMatrix(); + const glm::mat4 invProj = glm::inverse(proj); + + mat->SetProperty("kernel", kernel); + mat->SetProperty("depthTex", depthRT); + mat->SetProperty("normalTex", normalRT); + mat->SetProperty("noiseTex", noiseTex); + mat->SetProperty("aoTex", aoRT); + mat->UpdateUniformBuffers(); + //material->SetProperty("invProj", invProj); + //material->SetProperty("proj", proj); + //material->SetProperty("radius", radius); + //material->SetProperty("bias", bias); + //material->SetProperty("power", power); + //material->SetProperty("depthTex", static_cast(depthRT)); + } + void SSAOComponent::CreateKernel() + { + if (bKernelInit) + return; + bKernelInit = true; + + for (int i = 0; i < 64; ++i) + { + glm::vec3 sample( + core::Util::RandomRange(-1.f, 1.f), + core::Util::RandomRange(-1.f, 1.f), + core::Util::RandomRange(0.f, 1.f) + ); + sample = glm::normalize(sample); + sample *= core::Util::RandomRange(0.f, 1.f); + float scale = i / 64.f; + scale = glm::mix(0.1f, 1.0f, scale * scale); + sample *= scale; + kernel[i] = sample; + } + } +}//namespace diff --git a/src/Game/Component/Render/SkinnedMeshRenderer.cpp b/src/Game/Component/Render/SkinnedMeshRenderer.cpp index d0e7bbca..1f11faab 100644 --- a/src/Game/Component/Render/SkinnedMeshRenderer.cpp +++ b/src/Game/Component/Render/SkinnedMeshRenderer.cpp @@ -1,6 +1,8 @@ #include "Component/Render/SkinnedMeshRenderer.h" #include "Game/Component/Transform.h" +#include "Core/ThreadPool.h" + #include "Render/UniformStructLayout.h" #include "Render/Shader.h" @@ -11,7 +13,6 @@ namespace sh::game SkinnedMeshRenderer::SkinnedMeshRenderer(GameObject& owner) : MeshRenderer(owner) { - finalBoneMatrices.fill(glm::mat4{ 1.0f }); } SkinnedMeshRenderer::~SkinnedMeshRenderer() = default; @@ -23,18 +24,21 @@ namespace sh::game inverseBindMatrices = skinnedMesh->GetInverseBindMatrices(); } + SH_GAME_API void SkinnedMeshRenderer::OnPropertyChanged(const core::reflection::Property& prop) + { + Super::OnPropertyChanged(prop); + if (prop.GetName() == core::Util::ConstexprHash("bones")) + InitIBM(); + } + SH_GAME_API void SkinnedMeshRenderer::SetSkinnedMesh(render::SkinnedMesh* mesh) { - finalBoneMatrices.fill(glm::mat4{ 1.0f }); SetMesh(mesh); } SH_GAME_API void SkinnedMeshRenderer::SetBones(std::vector boneTransforms) { bones = std::move(boneTransforms); - - const render::SkinnedMesh* const skinnedMesh = static_cast(GetMesh()); - if (core::IsValid(skinnedMesh)) - inverseBindMatrices = skinnedMesh->GetInverseBindMatrices(); + InitIBM(); } SH_GAME_API void SkinnedMeshRenderer::UpdateDrawable() @@ -50,19 +54,52 @@ namespace sh::game if (jointCount == 0) return; - for (std::size_t i = 0; i < jointCount; ++i) + if (finalBoneMatrices.size() < bones.size()) + finalBoneMatrices.resize(bones.size()); + + const auto calcMatricesFn = + [this](std::size_t start, std::size_t end) + { + for (std::size_t i = start; i < end; ++i) + { + if (!core::IsValid(bones[i])) + continue; + finalBoneMatrices[i] = bones[i]->localToWorldMatrix * inverseBindMatrices[i]; + } + }; + + if (jointCount > 100) { - if (!core::IsValid(bones[i])) - continue; - finalBoneMatrices[i] = bones[i]->localToWorldMatrix * inverseBindMatrices[i]; + static core::ThreadPool& threadPool = *core::ThreadPool::GetInstance(); + const std::size_t threadCount = 4; + std::vector> futures(threadCount); + const std::size_t perTask = jointCount / threadCount; + const std::size_t rest = jointCount % threadCount; + + std::size_t start = 0; + std::size_t futureIdx = 0; + for (int i = 0; i < rest; ++i) + { + futures[futureIdx++] = threadPool.AddTask(calcMatricesFn, start, start + perTask + 1); + start = start + perTask + 1; + } + for (int i = 0; i < threadCount - rest; ++i) + { + futures[futureIdx++] = threadPool.AddTask(calcMatricesFn, start, start + perTask); + start = start + perTask; + } + for (std::future& future : futures) + future.get(); } + else + calcMatricesFn(0, jointCount); } void SkinnedMeshRenderer::UploadBoneMatrices() { if (drawables.empty()) return; - const std::size_t dataSize = render::SkinnedMesh::MAX_BONES * sizeof(glm::mat4); + const std::size_t dataSize = bones.size() * sizeof(glm::mat4); for (std::size_t i = 0; i < drawables.size(); ++i) { @@ -79,14 +116,22 @@ namespace sh::game { if (pass.IsPendingKill()) continue; - drawable->GetMaterialData().SetUniformData( + drawable->GetMaterialData().SetBindingData( pass, - render::UniformStructLayout::Type::Object, - 0, + render::UniformStructLayout::Usage::Object, + pass.GetSkinBinding(), finalBoneMatrices.data(), dataSize); } } } } + void SkinnedMeshRenderer::InitIBM() + { + finalBoneMatrices.resize(bones.size()); + + const render::SkinnedMesh* const skinnedMesh = static_cast(GetMesh()); + if (core::IsValid(skinnedMesh)) + inverseBindMatrices = skinnedMesh->GetInverseBindMatrices(); + } }//namespace diff --git a/src/Game/Component/Render/TextRenderer.cpp b/src/Game/Component/Render/TextRenderer.cpp index c1198505..a886c046 100644 --- a/src/Game/Component/Render/TextRenderer.cpp +++ b/src/Game/Component/Render/TextRenderer.cpp @@ -3,6 +3,8 @@ #include "Core/Util.h" +#include "Render/Renderer.h" + #include "Render/Model.h" namespace sh::game { diff --git a/src/Game/Component/TestComputeShader.cpp b/src/Game/Component/TestComputeShader.cpp new file mode 100644 index 00000000..d58d26d1 --- /dev/null +++ b/src/Game/Component/TestComputeShader.cpp @@ -0,0 +1,53 @@ +#include "Component/TestComputeShader.h" + +#include "Game/World.h" +#include "Game/Input.h" + +#include "Render/Renderer.h" +#include "Render/ScriptableRenderer.h" +#include "Render/ComputeShader.h" + +#include "Core/Logger.h" + +namespace sh::game +{ + TestComputeShader::TestComputeShader(GameObject& owner) : + Component(owner) + { + } + + SH_GAME_API void TestComputeShader::Update() + { + if (!Input::GetKeyPressed(Input::KeyCode::C)) + return; + + if (shader == nullptr) + { + SH_ERROR("TestComputeShader: shader is null"); + return; + } + + render::ScriptableRenderer* const scriptableRenderer = world.renderer.GetScriptableRenderer(); + if (scriptableRenderer == nullptr) + { + SH_ERROR("TestComputeShader: ScriptableRenderer is null"); + return; + } + std::vector a(10'000, 0); + std::vector b(10'000, 0); + std::vector c(10'000, 0); + for (int i = 0; i < 10'000; ++i) + { + a[i] = i; + b[i] = 10'000 - i; + } + shader->SetFloats("bufferA", a.data(), a.size()); + shader->SetFloats("bufferB", b.data(), b.size()); + shader->SetFloats("bufferC", c.data(), c.size()); + const uint32_t workGroup = std::ceil(10'000.f / 128.f); + SH_INFO_FORMAT("Dispatch ComputeShader [{} x {} x {}]", workGroup, 1, 1); + scriptableRenderer->Dispatch(*shader, workGroup, 1, 1); + std::vector buffer = shader->GetBuffer("bufferC")->GetData(); + std::memcpy(c.data(), buffer.data(), buffer.size()); + } +}//namespace diff --git a/src/Game/GUIPass.cpp b/src/Game/GUIPass.cpp new file mode 100644 index 00000000..2bfe2bac --- /dev/null +++ b/src/Game/GUIPass.cpp @@ -0,0 +1,36 @@ +#include "GUIPass.h" +#include "ImGUImpl.h" + +#include "Render/IRenderContext.h" +#include "Render/VulkanImpl/VulkanCommandBuffer.h" + +#include "External/imgui/backends/imgui_impl_vulkan.h" + +namespace sh::game +{ + GUIPass::GUIPass() : + render::ScriptableRenderPass(core::Name{ "ImGUI" }, render::RenderQueue::UI) + { + } + SH_GAME_API void GUIPass::SetImGUIContext(ImGUImpl& gui) + { + this->gui = &gui; + } + SH_GAME_API void GUIPass::Configure(const render::RenderData& renderData) + { + renderTextures.clear(); + SetRenderTargetImageUsages(renderData); + if (viewportTexture != nullptr) + renderTextures[viewportTexture] = render::ResourceUsage::SampledRead; + } + + SH_GAME_API void GUIPass::Record(render::CommandBuffer& cmd, const render::IRenderContext& ctx, const render::RenderData& renderData) + { + cmd.SetRenderData(renderData, false, true, true, false); + for (const render::RenderViewer& viewer : renderData.renderViewers) + { + SetViewportScissor(cmd, ctx, viewer); + ImGui_ImplVulkan_RenderDrawData(&gui->GetDrawData(), static_cast(cmd).GetCommandBuffer()); + } + } +}//namespace diff --git a/src/Game/GameManager.cpp b/src/Game/GameManager.cpp index 8862e47b..bf058275 100644 --- a/src/Game/GameManager.cpp +++ b/src/Game/GameManager.cpp @@ -3,6 +3,7 @@ #include "GameObject.h" #include "ComponentModule.h" #include "ImGUImpl.h" +#include "Component/Render/Camera.h" #include "AssetLoaderFactory.h" #include "Asset/WorldAsset.h" @@ -17,6 +18,8 @@ #include "Core/ModuleLoader.h" #include "Core/ThreadSyncManager.h" +#include "Render/Renderer.h" + #include "Sound/SoundSystem.h" #include @@ -30,6 +33,17 @@ namespace sh::game immortalWorld = core::SObject::Create(renderer, gui); immortalWorld->SetName("immortalWorld"); core::GarbageCollection::GetInstance()->SetRootSet(immortalWorld); + + render::RenderViewer uiViewer{}; + uiViewer.projMatrix = + glm::orthoRH_ZO(0.f, 13.66f, 0.0f, 7.68f, 0.01f, 1000.f); + uiViewer.viewMatrix = glm::mat4{ 1.f }; + uiViewer.viewportRect = { 0, 0, renderer.GetWidth(), renderer.GetHeight() }; + uiViewer.viewportScissor = uiViewer.viewportRect; + + guiRenderData.priority = -100; + guiRenderData.renderViewers.push_back(uiViewer); + guiRenderData.tag = core::Name{ "ImGUI" }; } SH_GAME_API auto GameManager::GetRenderer() const -> render::Renderer& { @@ -79,6 +93,7 @@ namespace sh::game worldPtr->Update(dt); immortalWorld->Update(dt); gui->End(); + renderer->PushRenderData(guiRenderData); UpdateSoundListener(); @@ -123,7 +138,7 @@ namespace sh::game worldPtr->Clear(); worlds.clear(); - renderer->Clear(); + renderer->Reset(); gui->ClearDrawData(); loadingSingleWorld->SetupRenderer(); diff --git a/src/Game/GameRenderer.cpp b/src/Game/GameRenderer.cpp index 0fee30a0..0ea21804 100644 --- a/src/Game/GameRenderer.cpp +++ b/src/Game/GameRenderer.cpp @@ -1,49 +1,59 @@ #include "GameRenderer.h" -#include "UIPass.h" +#include "GUIPass.h" +#include "World.h" +#include "Render/RenderPass/DepthPass.h" #include "Render/RenderPass/TransparentPass.h" +#include "Render/RenderPass/SSAOPass.h" +#include "Render/RenderPass/CombinePass.h" + +#include namespace sh::game { - GameRenderer::GameRenderer(render::IRenderContext& ctx, game::ImGUImpl& guictx) : - ScriptableRenderer(ctx) + GameRenderer::GameRenderer(render::IRenderContext& ctx, game::ImGUImpl& guictx, World& world) : + ScriptableRenderer(ctx), + renderCtx(ctx), + world(world), + guiCtx(guictx) { - AddRenderPass(core::Name{ "Opaque" }, render::RenderQueue::Opaque); - AddRenderPass(); - AddRenderPass("UI", render::RenderQueue::UI); - AddRenderPass().SetImGUIContext(guictx); } - SH_GAME_API void GameRenderer::Setup(const render::RenderTarget& data) + SH_GAME_API void GameRenderer::Init() { - for (auto& pass : allPasses) - { - auto allowedIt = allowedCamera.find(pass->passName.ToString()); - if (allowedIt != allowedCamera.end()) - { - const auto& allowedCams = allowedIt->second; - bool bAllowed = std::find(allowedCams.begin(), allowedCams.end(), data.camera) != allowedCams.end(); - if (!bAllowed) - continue; - } - - auto it = ignoreCamera.find(pass->passName.ToString()); - if (it == ignoreCamera.end()) - { - EnqueRenderPass(*pass); - continue; - } - const auto& ignoreCams = it->second; - bool bIgnore = std::find(ignoreCams.begin(), ignoreCams.end(), data.camera) != ignoreCams.end(); - if (bIgnore) - continue; - EnqueRenderPass(*pass); - } + depthPass = &AddRenderPass(); + opaquePass = &AddRenderPass(core::Name{ "Opaque" }, render::RenderQueue::Opaque); + ssaoPass = &AddRenderPass(renderCtx); + combinePass = &AddRenderPass(renderCtx); + transparentPass = &AddRenderPass(); + uiPass = &AddRenderPass("UI", render::RenderQueue::Transparent); + guiPass = &AddRenderPass(); + guiPass->SetImGUIContext(guiCtx); } - SH_GAME_API void GameRenderer::SetUICamera(const render::Camera& camera) + SH_GAME_API void GameRenderer::Setup(const render::RenderData& data) { - uiCamera = &camera; - allowedCamera["UI"].push_back(&camera); - allowedCamera["ImGUI"].push_back(&camera); - ignoreCamera["Opaque"].push_back(&camera); - ignoreCamera["Transparent"].push_back(&camera); + if (data.tag == "Depth") + { + EnqueRenderPass(*depthPass); + return; + } + if (data.tag == "SSAO") + { + EnqueRenderPass(*ssaoPass); + return; + } + if (data.tag == "Combine") + { + EnqueRenderPass(*combinePass); + return; + } + if (data.tag == "ImGUI") + { + if (guiPass != nullptr) + EnqueRenderPass(*guiPass); + return; + } + // 그 외 카메라 + EnqueRenderPass(*opaquePass); + EnqueRenderPass(*transparentPass); + EnqueRenderPass(*uiPass); } -}//namespace \ No newline at end of file +}//namespace diff --git a/src/Game/UICamera.cpp b/src/Game/UICamera.cpp deleted file mode 100644 index 052349a2..00000000 --- a/src/Game/UICamera.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "UICamera.h" - -#include "glm/gtc/matrix_transform.hpp" -namespace sh::game -{ - UICamera::UICamera() - { - SetOrthographic(true); - pos[core::ThreadType::Render] = pos[core::ThreadType::Game] = { 0.f, 0.f, 0.0f }; - to[core::ThreadType::Render] = to[core::ThreadType::Game] = { 0.f, 0.f, -1.0f }; - - bufferData[core::ThreadType::Game].matProj = - glm::orthoRH_ZO(0.f, 13.66f, 0.0f, 7.68f, 0.01f, 1000.f); - bufferData[core::ThreadType::Game].matView = glm::mat4{ 1.f }; - bufferData[core::ThreadType::Render] = bufferData[core::ThreadType::Game]; - } - SH_GAME_API void UICamera::UpdateViewMatrix() - { - } - SH_GAME_API void UICamera::UpdateProjMatrix() - { - bufferData[core::ThreadType::Game].matProj = - glm::orthoRH_ZO(0.f, width[core::ThreadType::Game], 0.0f, height[core::ThreadType::Game], nearPlane[core::ThreadType::Game], farPlane[core::ThreadType::Game]); - dirtyMask |= 256; - SyncDirty(); - } -}//namepsace \ No newline at end of file diff --git a/src/Game/UIPass.cpp b/src/Game/UIPass.cpp deleted file mode 100644 index 112e7779..00000000 --- a/src/Game/UIPass.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "UIPass.h" -#include "ImGUImpl.h" - -#include "Render/IRenderContext.h" -#include "Render/VulkanImpl/VulkanCommandBuffer.h" - -#include "External/imgui/backends/imgui_impl_vulkan.h" - -namespace sh::game -{ - UIPass::UIPass() : - render::ScriptableRenderPass(core::Name{ "ImGUI" }, render::RenderQueue::UI) - { - } - SH_GAME_API void UIPass::SetImGUIContext(ImGUImpl& gui) - { - this->gui = &gui; - } - SH_GAME_API void UIPass::Configure(const render::RenderTarget& renderData) - { - ScriptableRenderPass::Configure(renderData); - if (viewportTexture != nullptr) - renderTextures[viewportTexture] = render::ImageUsage::SampledRead; - } - SH_GAME_API auto UIPass::BuildDrawList(const render::RenderTarget& renderData) -> render::DrawList - { - render::DrawList list{}; - - list.drawCall.push_back( - [&](render::CommandBuffer& cmd) - { - ImGui_ImplVulkan_RenderDrawData(&gui->GetDrawData(), static_cast(cmd).GetCommandBuffer()); - } - ); - list.bClearColor = false; - - return list; - } -}//namespace \ No newline at end of file diff --git a/src/Game/World.cpp b/src/Game/World.cpp index eaa26ed8..bfabdb74 100644 --- a/src/Game/World.cpp +++ b/src/Game/World.cpp @@ -13,6 +13,9 @@ #include "Core/SObjectManager.h" #include "Core/Asset.h" +#include "Render/Renderer.h" +#include "Render/ShadowMapManager.h" + #include #include @@ -27,6 +30,8 @@ namespace sh::game SetName("World"); gc = core::GarbageCollection::GetInstance(); + shadowMapManager = std::make_unique(); + physEventSubscriber.SetCallback( [](const phys::PhysicsEvent& evt) { @@ -107,9 +112,140 @@ namespace sh::game Clear(); Super::OnDestroy(); } + SH_GAME_API auto World::Serialize() const -> core::Json + { + if (IsLoaded()) + { + core::Json mainJson = Super::Serialize(); + core::Json& objsJson = mainJson["objs"]; + for (auto obj : objs) + { + if (obj->bNotSave || !core::IsValid(obj)) + continue; + objsJson.push_back(obj->Serialize()); + } + return mainJson; + } + const core::Json* worldPointPtr = GetWorldPoint(); + if (worldPointPtr == nullptr || worldPointPtr->empty() || worldPointPtr->is_discarded()) + return core::Json(); + + return *worldPointPtr; + } + SH_GAME_API void World::Deserialize(const core::Json& json) + { + bLoaded = true; + Super::Deserialize(json); + + if (!json.contains("objs")) + { + bLoaded = false; + return; + } + + bool hasCustomRenderer = customRenderer != nullptr; + CleanObjs(); + if (hasCustomRenderer) + SetupRenderer(); + InitResource(); + + core::SObjectManager* objManager = core::SObjectManager::GetInstance(); + // 유효한 참조를 위해 두번 로드하는 과정을 거친다. + // 생성만 하는 과정 + for (const auto& objJson : json["objs"]) + { + const std::string& name = objJson["name"].get_ref(); + core::UUID objuuid{ objJson["uuid"].get_ref() }; + auto sobj = objManager->GetSObject(objuuid); + GameObject* gameObj = nullptr; + if (core::IsValid(sobj)) // 이미 월드 상에 존재 할 경우 + { + gameObj = static_cast(sobj); + // 모든 컴포넌트 제거 + for (int i = 0; i < gameObj->GetComponents().size(); ++i) + { + Component* component = gameObj->GetComponents()[i]; + if (component == nullptr) + continue; + component->SetUUID(core::UUID::Generate()); + component->Destroy(); + } + } + else + { + if (sobj != nullptr) // pending kill + sobj->SetUUID(core::UUID::Generate()); + gameObj = this->AddGameObject(name); + gameObj->SetUUID(objuuid); + } + + for (auto& compJson : objJson["Components"]) + { + std::string name{ compJson["name"].get() }; + std::string type{ compJson["type"].get() }; + core::UUID uuid{ compJson["uuid"].get() }; + if (type == "Transform") // 트랜스폼은 게임오브젝트 생성 시 이미 만들어져있다. + { + if (gameObj->transform->GetUUID() != uuid) + { + // 실패 했다면 이미 해당 트랜스폼이 존재하는 상태 + if (!gameObj->transform->SetUUID(uuid)) + { + auto obj = core::SObjectManager::GetInstance()->GetSObject(uuid); + // 보류 상태라면 UUID 변경 + if (obj != nullptr && obj->IsPendingKill()) + { + obj->SetUUID(core::UUID::Generate()); + gameObj->transform->SetUUID(uuid); + } + } + } + continue; + } + + auto compType = ComponentModule::GetInstance()->GetComponent(name); + if (compType == nullptr) + { + SH_ERROR_FORMAT("Not found component - {}", type); + continue; + } + Component* component = compType->Create(*gameObj); + // 실패 했다면 이미 해당 컴포넌트가 존재하는 상태 (PendingKill상태 일 수도 있음) + if (!component->SetUUID(core::UUID{ uuid })) + { + auto obj = core::SObjectManager::GetInstance()->GetSObject(uuid); + // 보류 상태라면 UUID 변경 + if (obj != nullptr && obj->IsPendingKill()) + { + obj->SetUUID(core::UUID::Generate()); + component->SetUUID(core::UUID{ uuid }); + } + } + gameObj->AddComponent(component); + } + } + // 역 직렬화 + for (const auto& objJson : json["objs"]) + { + GameObject* obj = static_cast(objManager->GetSObject(core::UUID{ objJson["uuid"].get() })); + obj->Deserialize(objJson); + } + for (auto obj : objs) + { + if (!obj->activeSelf) + obj->PropagateEnable(); + } + for (auto& obj : addedObjs) + { + if (!obj->activeSelf) + obj->PropagateEnable(); + } + } SH_GAME_API void World::Clear() { + if (shadowMapManager != nullptr) + shadowMapManager->Clear(); CleanObjs(); while (!deallocatedObjs.empty()) @@ -125,15 +261,11 @@ namespace sh::game } SH_GAME_API void World::SetupRenderer() { - uiCamera.SetPriority(1000); - if (customRenderer == nullptr) - { - renderer.AddCamera(uiCamera); - customRenderer = std::make_unique(*renderer.GetContext(), GetUiContext()); - renderer.SetScriptableRenderer(*customRenderer); - } - - static_cast(customRenderer.get())->SetUICamera(uiCamera); + customRenderer = std::make_unique(*renderer.GetContext(), GetUiContext(), *this); + customRenderer->Init(); + renderer.SetScriptableRenderer(*customRenderer); + if (shadowMapManager != nullptr) + shadowMapManager->Init(*renderer.GetContext()); } SH_GAME_API void World::InitResource() { @@ -228,17 +360,10 @@ namespace sh::game return nullptr; } - - SH_GAME_API auto World::GetGameObjectPool() -> core::memory::MemoryPool& - { - return objPool; - } - SH_GAME_API void World::PushDeAllocatedGameObject(GameObject* ptr) { deallocatedObjs.push(ptr); } - SH_GAME_API void World::Start() { bOnStart = true; @@ -246,6 +371,7 @@ namespace sh::game SH_GAME_API void World::Update(double deltaTime) { dt = deltaTime; + if (!bStartLoop) bStartLoop = true; @@ -329,6 +455,8 @@ namespace sh::game continue; obj->LateUpdate(); } + if (shadowMapManager != nullptr) + shadowMapManager->Submit(renderer); } SH_GAME_API void World::BeforeSync() @@ -349,28 +477,22 @@ namespace sh::game } } - SH_GAME_API void World::RegisterCamera(Camera* cam) + SH_GAME_API void World::RegisterCamera(Camera& cam) { - if (!core::IsValid(cam)) + if (cam.IsPendingKill()) return; for (int i = 0; i < cameras.size(); ++i) { - if (cameras[i] == cam) + if (cameras[i] == &cam) return; } - cameras.push_back(cam); - renderer.AddCamera(cam->GetNative()); - - eventBus.Publish(events::CameraEvent{ *cam, events::CameraEvent::Type::Added }); + cameras.push_back(&cam); + eventBus.Publish(events::CameraEvent{ cam, events::CameraEvent::Type::Added }); } - SH_GAME_API void World::UnRegisterCamera(Camera* cam) + SH_GAME_API void World::UnRegisterCamera(Camera& cam) { - if (cam == nullptr) - return; - cameras.erase(std::remove(cameras.begin(), cameras.end(), cam), cameras.end()); // O(n) - renderer.RemoveCamera(cam->GetNative()); - - eventBus.Publish(events::CameraEvent{ *cam, events::CameraEvent::Type::Removed }); + cameras.erase(std::remove(cameras.begin(), cameras.end(), &cam), cameras.end()); // O(n) + eventBus.Publish(events::CameraEvent{ cam, events::CameraEvent::Type::Removed }); } SH_GAME_API void World::SetMainCamera(Camera* cam) { @@ -378,24 +500,6 @@ namespace sh::game return; mainCamera = cam; } - SH_GAME_API auto World::GetMainCamera() const -> Camera* - { - return mainCamera; - } - - SH_GAME_API auto World::GetPhysWorld() -> phys::PhysWorld* - { - return &physWorld; - } - SH_GAME_API auto World::GetLightOctree() -> Octree& - { - return lightOctree; - } - SH_GAME_API auto World::GetLightOctree() const -> const Octree& - { - return lightOctree; - } - SH_GAME_API void World::AddBeforeSyncTask(const std::function& func) { beforeSyncTasks.push(func); @@ -405,137 +509,6 @@ namespace sh::game { afterSyncTasks.push(func); } - - SH_GAME_API auto World::Serialize() const -> core::Json - { - if (IsLoaded()) - { - core::Json mainJson = Super::Serialize(); - core::Json& objsJson = mainJson["objs"]; - for (auto obj : objs) - { - if (obj->bNotSave || !core::IsValid(obj)) - continue; - objsJson.push_back(obj->Serialize()); - } - return mainJson; - } - const core::Json* worldPointPtr = GetWorldPoint(); - if (worldPointPtr == nullptr || worldPointPtr->empty() || worldPointPtr->is_discarded()) - return core::Json(); - - return *worldPointPtr; - } - SH_GAME_API void World::Deserialize(const core::Json& json) - { - bLoaded = true; - Super::Deserialize(json); - - if (!json.contains("objs")) - { - bLoaded = false; - return; - } - - bool hasCustomRenderer = customRenderer != nullptr; - CleanObjs(); - if (hasCustomRenderer) - SetupRenderer(); - InitResource(); - - core::SObjectManager* objManager = core::SObjectManager::GetInstance(); - // 유효한 참조를 위해 두번 로드하는 과정을 거친다. - // 생성만 하는 과정 - for (const auto& objJson : json["objs"]) - { - const std::string& name = objJson["name"].get_ref(); - core::UUID objuuid{ objJson["uuid"].get_ref() }; - - auto sobj = objManager->GetSObject(objuuid); - GameObject* gameObj = nullptr; - if (core::IsValid(sobj)) // 이미 월드 상에 존재 할 경우 - { - gameObj = static_cast(sobj); - // 모든 컴포넌트 제거 - for (int i = 0; i < gameObj->GetComponents().size(); ++i) - { - Component* component = gameObj->GetComponents()[i]; - if (component == nullptr) - continue; - component->SetUUID(core::UUID::Generate()); - component->Destroy(); - } - } - else - { - if (sobj != nullptr) // pending kill - sobj->SetUUID(core::UUID::Generate()); - gameObj = this->AddGameObject(name); - gameObj->SetUUID(objuuid); - } - - for (auto& compJson : objJson["Components"]) - { - std::string name{ compJson["name"].get() }; - std::string type{ compJson["type"].get() }; - core::UUID uuid{ compJson["uuid"].get() }; - if (type == "Transform") // 트랜스폼은 게임오브젝트 생성 시 이미 만들어져있다. - { - if (gameObj->transform->GetUUID() != uuid) - { - // 실패 했다면 이미 해당 트랜스폼이 존재하는 상태 - if (!gameObj->transform->SetUUID(uuid)) - { - auto obj = core::SObjectManager::GetInstance()->GetSObject(uuid); - // 보류 상태라면 UUID 변경 - if (obj != nullptr && obj->IsPendingKill()) - { - obj->SetUUID(core::UUID::Generate()); - gameObj->transform->SetUUID(uuid); - } - } - } - continue; - } - - auto compType = ComponentModule::GetInstance()->GetComponent(name); - if (compType == nullptr) - { - SH_ERROR_FORMAT("Not found component - {}", type); - continue; - } - Component* component = compType->Create(*gameObj); - // 실패 했다면 이미 해당 컴포넌트가 존재하는 상태 (PendingKill상태 일 수도 있음) - if (!component->SetUUID(core::UUID{ uuid })) - { - auto obj = core::SObjectManager::GetInstance()->GetSObject(uuid); - // 보류 상태라면 UUID 변경 - if (obj != nullptr && obj->IsPendingKill()) - { - obj->SetUUID(core::UUID::Generate()); - component->SetUUID(core::UUID{ uuid }); - } - } - gameObj->AddComponent(component); - } - } - // 역 직렬화 - for (const auto& objJson : json["objs"]) - { - GameObject* obj = static_cast(objManager->GetSObject(core::UUID{ objJson["uuid"].get() })); - obj->Deserialize(objJson); - } - for (auto obj : objs) - { - if (!obj->activeSelf) - obj->PropagateEnable(); - } - for (auto& obj : addedObjs) - { - if (!obj->activeSelf) - obj->PropagateEnable(); - } - } SH_GAME_API void World::SaveWorldPoint(const core::Json& json, std::string_view name) { savePoints[std::string{ name }] = json; @@ -557,6 +530,7 @@ namespace sh::game AddAfterSyncTask( [this, savePoint = it->second]() { + renderer.Reset(); Deserialize(savePoint); } ); @@ -591,10 +565,6 @@ namespace sh::game { savePoints.erase(name); } - SH_GAME_API auto World::GetUiContext() const -> ImGUImpl& - { - return *imgui; - } SH_GAME_API void World::PublishEvent(const core::IEvent& event) { eventBus.Publish(event); @@ -619,18 +589,6 @@ namespace sh::game bOnStart = false; eventBus.Publish(events::WorldEvent{ events::WorldEvent::Type::Stop }); } - SH_GAME_API auto World::IsPlaying() const -> bool - { - return bPlaying; - } - SH_GAME_API auto World::IsStart() const -> bool - { - return bOnStart; - } - SH_GAME_API auto World::IsLoaded() const -> bool - { - return bLoaded; - } SH_GAME_API void World::ReallocateUUIDS() { auto changeObjUUIDfn = diff --git a/src/Render/BufferFactory.cpp b/src/Render/BufferFactory.cpp index d8fd1307..f4852e11 100644 --- a/src/Render/BufferFactory.cpp +++ b/src/Render/BufferFactory.cpp @@ -3,7 +3,7 @@ #include "VulkanContext.h" #include "VulkanImpl/VulkanBuffer.h" -#include "VulkanImpl/VulkanUniformBuffer.h" +#include "VulkanImpl/VulkanDescriptorSet.h" #include "Core/Logger.h" @@ -11,18 +11,20 @@ namespace sh::render { - auto BufferFactory::CreateVkUniformBuffer(const vk::VulkanContext& context, std::size_t size, bool bTransferDst) -> std::unique_ptr + auto BufferFactory::CreateVkUniformBuffer(const vk::VulkanContext& context, const CreateInfo& info) -> std::unique_ptr { std::unique_ptr buffer = std::make_unique(context); - VkBufferUsageFlags usage = VkBufferUsageFlagBits::VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; - if (bTransferDst) - usage |= VkBufferUsageFlagBits::VK_BUFFER_USAGE_TRANSFER_DST_BIT; + VkBufferUsageFlags usage = + info.bDynamic ? VkBufferUsageFlagBits::VK_BUFFER_USAGE_STORAGE_BUFFER_BIT : VkBufferUsageFlagBits::VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; - auto result = buffer->Create(size, usage, - VkSharingMode::VK_SHARING_MODE_EXCLUSIVE, - VkMemoryPropertyFlagBits::VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VkMemoryPropertyFlagBits::VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, - true); + usage |= VkBufferUsageFlagBits::VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + usage |= VkBufferUsageFlagBits::VK_BUFFER_USAGE_TRANSFER_DST_BIT; + + const VkMemoryPropertyFlags memFlag = info.bGPUOnly ? VkMemoryPropertyFlagBits::VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT : + VkMemoryPropertyFlagBits::VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VkMemoryPropertyFlagBits::VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + + const VkResult result = buffer->Create(info.size, usage, VkSharingMode::VK_SHARING_MODE_EXCLUSIVE,memFlag, true); assert(result == VkResult::VK_SUCCESS); if (result != VkResult::VK_SUCCESS) { @@ -32,24 +34,44 @@ namespace sh::render return buffer; } - auto BufferFactory::Create(const IRenderContext& context, std::size_t size, bool bTransferDst) -> std::unique_ptr + auto BufferFactory::Create(const IRenderContext& context, const CreateInfo& info) -> std::unique_ptr { assert(context.GetRenderAPIType() == RenderAPI::Vulkan); if (context.GetRenderAPIType() == RenderAPI::Vulkan) - return CreateVkUniformBuffer(static_cast(context), size, bTransferDst); + return CreateVkUniformBuffer(static_cast(context), info); return nullptr; } - auto BufferFactory::CreateUniformBuffer(const IRenderContext& context, const ShaderPass& shader, UniformStructLayout::Type type) -> std::unique_ptr + auto BufferFactory::CreateShaderBinding(const IRenderContext& context, const ShaderPass& shader, UniformStructLayout::Usage usage) -> std::unique_ptr + { + if (context.GetRenderAPIType() == RenderAPI::Vulkan) + { + std::unique_ptr ptr = std::make_unique(); + ptr->Create(context, shader, usage); + return ptr; + } + else + assert(false); + return nullptr; + } + SH_RENDER_API auto BufferFactory::CreateShaderBinding(const IRenderContext& context, const ComputeShader& shader) -> std::unique_ptr { if (context.GetRenderAPIType() == RenderAPI::Vulkan) { - auto ptr = std::make_unique(); - ptr->Create(context, shader, type); + std::unique_ptr ptr = std::make_unique(); + ptr->Create(context, shader); return ptr; } else assert(false); return nullptr; } + SH_RENDER_API auto BufferFactory::GetBufferAlignment(const IRenderContext& context) -> std::size_t + { + if (context.GetRenderAPIType() == RenderAPI::Vulkan) + { + return static_cast(static_cast(context).GetGPUProperty().limits.minUniformBufferOffsetAlignment); + } + return 256u; + } }//namespace \ No newline at end of file diff --git a/src/Render/CMakeLists.txt b/src/Render/CMakeLists.txt index 332478c7..4f831b71 100644 --- a/src/Render/CMakeLists.txt +++ b/src/Render/CMakeLists.txt @@ -1,6 +1,6 @@ message("Build ShellEngine Renderer") file(GLOB_RECURSE SRC CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) -file(GLOB_RECURSE HEADERS CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/include/Render/*.{h,hpp}) +file(GLOB_RECURSE HEADERS CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/include/Render/*.h ${CMAKE_SOURCE_DIR}/include/Render/*.hpp) add_library(ShellEngineRender SHARED ${SRC} ${HEADERS}) add_library(ShellEngine::Render ALIAS ShellEngineRender) diff --git a/src/Render/Camera.cpp b/src/Render/Camera.cpp deleted file mode 100644 index a850ae44..00000000 --- a/src/Render/Camera.cpp +++ /dev/null @@ -1,207 +0,0 @@ -#include "Camera.h" - -#include "Core/ThreadSyncManager.h" - -#include "glm/gtc/matrix_transform.hpp" - -#include - -namespace sh::render -{ - uint32_t Camera::nextId = 0; - - SH_RENDER_API void Camera::SyncDirty() - { - if (bDirty) - return; - bDirty = true; - - core::ThreadSyncManager::PushSyncable(*this); - } - SH_RENDER_API void Camera::Sync() - { - if (dirtyMask & 1) - std::swap(pos[core::ThreadType::Render], pos[core::ThreadType::Game]); - if (dirtyMask & 2) - std::swap(to[core::ThreadType::Render], to[core::ThreadType::Game]); - if (dirtyMask & 4) - std::swap(up[core::ThreadType::Render], up[core::ThreadType::Game]); - if (dirtyMask & 8) - std::swap(fovRadians[core::ThreadType::Render], fovRadians[core::ThreadType::Game]); - if (dirtyMask & 16) - std::swap(nearPlane[core::ThreadType::Render], nearPlane[core::ThreadType::Game]); - if (dirtyMask & 32) - std::swap(farPlane[core::ThreadType::Render], farPlane[core::ThreadType::Game]); - if (dirtyMask & 64) - std::swap(width[core::ThreadType::Render], width[core::ThreadType::Game]); - if (dirtyMask & 128) - std::swap(height[core::ThreadType::Render], height[core::ThreadType::Game]); - if (dirtyMask & 256) - std::swap(bufferData[core::ThreadType::Render], bufferData[core::ThreadType::Game]); - dirtyMask = 0; - bDirty = false; - } - Camera::Camera() : - id(nextId++), - renderTexture(nullptr), priority(0) - { - fovRadians[core::ThreadType::Game] = glm::radians(60.0f); - pos[core::ThreadType::Game] = glm::vec3(0.0f, 0.0f, 0.0f); - to[core::ThreadType::Game] = glm::vec3(0.0f, 0.0f, 1.0f); - up[core::ThreadType::Game] = glm::vec3(0.0f, 1.0f, 0.0f); - - fovRadians[core::ThreadType::Render] = glm::radians(60.0f); - pos[core::ThreadType::Render] = glm::vec3(0.0f, 0.0f, 0.0f); - to[core::ThreadType::Render] = glm::vec3(0.0f, 0.0f, 1.0f); - up[core::ThreadType::Render] = glm::vec3(0.0f, 1.0f, 0.0f); - } - SH_RENDER_API void Camera::SetRenderTexture(RenderTexture* framebuffer) - { - this->renderTexture = framebuffer; - } - SH_RENDER_API auto Camera::GetRenderTexture() const -> RenderTexture* - { - return renderTexture; - } - - SH_RENDER_API void Camera::SetPriority(int priority) - { - this->priority = priority; - } - SH_RENDER_API auto Camera::GetPriority() const -> int - { - return priority; - } - SH_RENDER_API auto Camera::GetProjMatrix(core::ThreadType thr) const -> const glm::mat4& - { - return bufferData[thr].matProj; - } - SH_RENDER_API auto Camera::GetViewMatrix(core::ThreadType thr) const -> const glm::mat4& - { - return bufferData[thr].matView; - } - SH_RENDER_API void Camera::SetPos(const glm::vec3& pos) - { - this->pos[core::ThreadType::Game] = pos; - dirtyMask |= 1; - UpdateViewMatrix(); - SyncDirty(); - } - SH_RENDER_API void Camera::SetLookPos(const glm::vec3& pos) - { - this->to[core::ThreadType::Game] = pos; - dirtyMask |= 2; - UpdateViewMatrix(); - SyncDirty(); - } - SH_RENDER_API void Camera::SetUpVector(const glm::vec3& up) - { - this->up[core::ThreadType::Game] = up; - dirtyMask |= 4; - UpdateViewMatrix(); - SyncDirty(); - } - SH_RENDER_API void sh::render::Camera::SetFovRadian(float rad) - { - fovRadians[core::ThreadType::Game] = rad; - dirtyMask |= 8; - UpdateProjMatrix(); - SyncDirty(); - } - SH_RENDER_API void Camera::SetFov(float degree) - { - fovRadians[core::ThreadType::Game] = glm::radians(degree); - dirtyMask |= 8; - UpdateProjMatrix(); - SyncDirty(); - } - SH_RENDER_API void Camera::SetNearPlane(float near) - { - nearPlane[core::ThreadType::Game] = near; - dirtyMask |= 16; - UpdateProjMatrix(); - SyncDirty(); - } - SH_RENDER_API void Camera::SetFarPlane(float far) - { - farPlane[core::ThreadType::Game] = far; - dirtyMask |= 32; - UpdateProjMatrix(); - SyncDirty(); - } - SH_RENDER_API void Camera::SetWidth(float width) - { - this->width[core::ThreadType::Game] = width; - dirtyMask |= 64; - UpdateProjMatrix(); - SyncDirty(); - } - SH_RENDER_API void Camera::SetHeight(float height) - { - this->height[core::ThreadType::Game] = height; - dirtyMask |= 128; - UpdateProjMatrix(); - SyncDirty(); - } - SH_RENDER_API void Camera::SetOrthographic(bool bEnable) - { - bPerspective = !bEnable; - } - SH_RENDER_API void Camera::SetRenderTagMask(uint32_t mask) - { - renderTagMask = mask; - } - SH_RENDER_API void Camera::AddRenderTagMask(uint32_t tagId) - { - renderTagMask |= tagId; - } - SH_RENDER_API void Camera::RemoveRenderTagMask(uint32_t tagId) - { - assert(renderTagMask & tagId); - renderTagMask ^= tagId; - } - SH_RENDER_API auto Camera::GetRenderTagMask() const -> uint32_t - { - return renderTagMask; - } - SH_RENDER_API auto Camera::CheckRenderTag(uint32_t tagId) const -> bool - { - return renderTagMask & tagId; - } - SH_RENDER_API void Camera::SetActive(bool bActive) - { - this->bActive = bActive; - } - SH_RENDER_API auto Camera::GetActive() const -> bool - { - return bActive; - } - void Camera::UpdateProjMatrix() - { - if (bPerspective) - { - bufferData[core::ThreadType::Game].matProj = - glm::perspectiveFovRH_ZO( - fovRadians[core::ThreadType::Game], - width[core::ThreadType::Game], - height[core::ThreadType::Game], - nearPlane[core::ThreadType::Game], - farPlane[core::ThreadType::Game]); - } - else - { - const float dis = glm::length(to[core::ThreadType::Game] - pos[core::ThreadType::Game]) / 2.0f; - const float aspect = width[core::ThreadType::Game] / height[core::ThreadType::Game]; - bufferData[core::ThreadType::Game].matProj = - glm::orthoRH_ZO(-dis * aspect, dis * aspect, -dis, dis, nearPlane[core::ThreadType::Game], farPlane[core::ThreadType::Game]); - } - dirtyMask |= 256; - SyncDirty(); - } - void Camera::UpdateViewMatrix() - { - bufferData[core::ThreadType::Game].matView = glm::lookAt(pos[core::ThreadType::Game], to[core::ThreadType::Game], up[core::ThreadType::Game]); - dirtyMask |= 256; - SyncDirty(); - } -}//namespace render \ No newline at end of file diff --git a/src/Render/ComputeShader.cpp b/src/Render/ComputeShader.cpp new file mode 100644 index 00000000..c3a5780c --- /dev/null +++ b/src/Render/ComputeShader.cpp @@ -0,0 +1,111 @@ +#include "ComputeShader.h" +#include "ComputeShaderCreateInfo.h" +#include "BufferFactory.h" + +namespace sh::render +{ + ComputeShader::ComputeShader(const IRenderContext& ctx, ComputeShaderCreateInfo createInfo) : + ctx(ctx) + { + SetName(createInfo.shaderNode.shaderName); + + shaderNode = std::move(createInfo.shaderNode); + spirv = std::move(createInfo.spirv); + + // 코드 텍스트는 보존할 필요 없음 + shaderNode.code.clear(); + shaderNode.functions.clear(); + shaderNode.declaration.clear(); + } + ComputeShader::~ComputeShader() = default; + + SH_RENDER_API auto ComputeShader::Serialize() const -> core::Json + { + core::Json mainJson = Super::Serialize(); + + core::Json& json = mainJson["computeShader"]; + json["AST"] = shaderNode.Serialize(); + json["spirv"] = spirv; + + return mainJson; + } + + SH_RENDER_API void ComputeShader::Deserialize(const core::Json& json) + { + Super::Deserialize(json); + + if (!json.contains("computeShader")) + return; + + const core::Json& csJson = json["computeShader"]; + + if (csJson.contains("AST")) + shaderNode.Deserialize(csJson["AST"]); + if (csJson.contains("spirv")) + spirv = csJson["spirv"].get>(); + } + + SH_RENDER_API void ComputeShader::SetFloats(const std::string& name, float* values, std::size_t count) + { + uint32_t binding = 0; + bool bFind = false; + for (const ShaderAST::BufferNode& bufferNode : shaderNode.buffers) + { + if (bufferNode.name == name) + { + if (!bufferNode.vars.empty() && bufferNode.vars[0].type == ShaderAST::VariableType::Float) + { + binding = bufferNode.binding; + bFind = true; + break; + } + } + } + if (!bFind) + return; + + if (buffers.size() <= binding) + buffers.resize(binding + 1); + if (buffers[binding] == nullptr) + { + BufferFactory::CreateInfo ci{}; + ci.bDynamic = true; + ci.size = sizeof(float) * count; + buffers[binding] = BufferFactory::Create(ctx, ci); + buffers[binding]->SetData(values); + + if (shaderBinding == nullptr) + shaderBinding = BufferFactory::CreateShaderBinding(ctx, *this); + shaderBinding->Link(binding, *buffers[binding]); + } + else + { + buffers[binding]->Resize(sizeof(float) * count); + buffers[binding]->SetData(values); + + if (shaderBinding == nullptr) + shaderBinding = BufferFactory::CreateShaderBinding(ctx, *this); + shaderBinding->Link(binding, *buffers[binding]); + } + } + + SH_RENDER_API auto ComputeShader::GetBuffer(const std::string& name) -> IBuffer* + { + std::optional binding = GetBinding(name); + if (!binding.has_value()) + return nullptr; + if (*binding >= buffers.size()) + return nullptr; + return buffers[*binding].get(); + } + + auto ComputeShader::GetBinding(const std::string& name) -> std::optional + { + for (const ShaderAST::BufferNode& bufferNode : shaderNode.buffers) + { + if (bufferNode.name == name) + return bufferNode.binding; + } + return {}; + } +}//namespace diff --git a/src/Render/ComputeShaderLexer.cpp b/src/Render/ComputeShaderLexer.cpp new file mode 100644 index 00000000..19f9572a --- /dev/null +++ b/src/Render/ComputeShaderLexer.cpp @@ -0,0 +1,270 @@ +#include "ComputeShaderLexer.h" + +#include + +#include +#include + +namespace sh::render +{ + SH_RENDER_API auto ComputeShaderLexer::Lex(const std::string& source) -> std::vector + { + std::vector tokens; + + int line = 1; + int column = 1; + const std::size_t length = source.size(); + std::size_t i = 0; + + auto AddToken = [&](TokenType t, const std::string& txt) + { + tokens.push_back({ t, txt, line, column }); + }; + + while (i < length) + { + char c = source[i]; + if (c == '\n') + { + ++i; + ++line; + column = 0; + continue; + } + if (c == '/') // 주석 + { + if (i + 1 < length) + { + if (source[i + 1] == '/') // // + { + i += 2; + while (true) + { + if (i >= length) + break; + if (source[i] == '\n') + { + ++i; + column = 0; + ++line; + break; + } + ++i; + } + continue; + } + else if (source[i + 1] == '*') // /**/ + { + i += 2; + while (true) + { + if (i >= length || i + 1 >= length) + break; + if (source[i] == '*' && source[i + 1] == '/') + { + i += 2; + break; + } + if (source[i] == '\n') + { + column = 0; + ++line; + } + if (source[i] == '\r' || source[i] == '\t') + ++column; + ++i; + } + continue; + } + } + } + if (c == ' ' || c == '\t' || c == '\r') + { + ++i; + ++column; + continue; + } + + if (c == '{') + { + AddToken(TokenType::LBrace, "{"); + ++i; + ++column; + continue; + } + if (c == '}') + { + AddToken(TokenType::RBrace, "}"); + ++i; + ++column; + continue; + } + if (c == '(') + { + AddToken(TokenType::LBracket, "("); + ++i; + ++column; + continue; + } + if (c == ')') + { + AddToken(TokenType::RBracket, ")"); + ++i; + ++column; + continue; + } + if (c == '[') + { + AddToken(TokenType::LSquareBracket, "["); + ++i; + ++column; + continue; + } + if (c == ']') + { + AddToken(TokenType::RSquareBracket, "]"); + ++i; + ++column; + continue; + } + if (c == ',') + { + AddToken(TokenType::Comma, ","); + ++i; + ++column; + continue; + } + // 두 글자 합성 연산자가 가능한 연산자들 + auto LexCompoundOp = [&](char single, std::initializer_list followers) + { + std::string op{ single }; + ++i; + ++column; + if (i < source.size()) + { + for (char f : followers) + { + if (source[i] == f) + { + op.push_back(source[i]); + ++i; + ++column; + break; + } + } + } + AddToken(TokenType::Operator, op); + }; + if (c == '=') { LexCompoundOp('=', { '=' }); continue; } + if (c == '+') { LexCompoundOp('+', { '+', '=' }); continue; } + if (c == '-') { LexCompoundOp('-', { '-', '=' }); continue; } + if (c == '*') { LexCompoundOp('*', { '=' }); continue; } + if (c == '/') { LexCompoundOp('/', { '=' }); continue; } + if (c == '<') { LexCompoundOp('<', { '=', '<' }); continue; } + if (c == '>') { LexCompoundOp('>', { '=', '>' }); continue; } + if (c == '!') { LexCompoundOp('!', { '=' }); continue; } + if (c == '&') { LexCompoundOp('&', { '&', '=' }); continue; } + if (c == '|') { LexCompoundOp('|', { '|', '=' }); continue; } + if (c == '%') { LexCompoundOp('%', { '=' }); continue; } + if (c == '^') { LexCompoundOp('^', { '=' }); continue; } + + if (c == ';') + { + AddToken(TokenType::Semicolon, ";"); + ++i; + ++column; + continue; + } + + if (c == '\"') + { + ++i; + ++column; + std::string strValue; + bool closed = false; + while (i < length) + { + char c2 = source[i]; + if (c2 == '\"') + { + closed = true; + ++i; + ++column; + break; + } + strValue.push_back(c2); + ++i; + ++column; + } + if (!closed) + throw ComputeShaderLexerException{ fmt::format("\" not closed! (line: {})", line) }; + AddToken(TokenType::String, strValue); + continue; + } + // 숫자 + if (std::isdigit(static_cast(c))) + { + std::string number; + while (i < length && (std::isdigit(static_cast(source[i])) || source[i] == '.')) + { + number.push_back(source[i]); + ++i; + ++column; + } + AddToken(TokenType::Number, number); + if (i < length && source[i] == 'f') + { + ++i; + ++column; + AddToken(TokenType::Literal, "f"); + } + continue; + } + // 식별자 또는 키워드 + if (std::isalpha(static_cast(c)) || c == '_') + { + std::string ident; + while (i < length && IsIdentifierChar(source[i])) + { + ident.push_back(source[i]); + ++i; + ++column; + } + static const std::unordered_map keywordMap = + { + {"Numthreads", TokenType::Numthreads}, + {"RBuffer", TokenType::RBuffer}, + {"WBuffer", TokenType::WBuffer}, + {"RWBuffer", TokenType::RWBuffer}, + {"const", TokenType::Const} + }; + auto it = keywordMap.find(ident); + if (it == keywordMap.end()) + AddToken(TokenType::Identifier, ident); + else + AddToken(it->second, ident); + continue; + } + + if (c == '#') + { + AddToken(TokenType::Preprocessor, "#"); + ++i; + ++column; + continue; + } + + AddToken(TokenType::Unknown, std::string{ c }); + ++i; + ++column; + } + + AddToken(TokenType::EndOfFile, ""); + return tokens; + } + + auto ComputeShaderLexer::IsIdentifierChar(char c) -> bool + { + return std::isalnum(static_cast(c)) || c == '_'; + } +}//namespace diff --git a/src/Render/ComputeShaderParser.cpp b/src/Render/ComputeShaderParser.cpp new file mode 100644 index 00000000..433297d4 --- /dev/null +++ b/src/Render/ComputeShaderParser.cpp @@ -0,0 +1,356 @@ +#include "ComputeShaderParser.h" + +#include + +#include + +namespace sh::render +{ + auto ComputeShaderParser::PeekToken() const -> const ComputeShaderLexer::Token& + { + return (*tokens)[pos]; + } + auto ComputeShaderParser::CheckToken(ComputeShaderLexer::TokenType token) const -> bool + { + return PeekToken().type == token; + } + auto ComputeShaderParser::NextToken() -> const ComputeShaderLexer::Token& + { + if (pos < tokens->size()) + return (*tokens)[pos++]; + return (*tokens)[pos]; + } + auto ComputeShaderParser::PreviousToken() -> const ComputeShaderLexer::Token& + { + return (*tokens)[pos - 1]; + } + void ComputeShaderParser::ConsumeToken(ComputeShaderLexer::TokenType token) + { + if (CheckToken(token)) + NextToken(); + else + throw ComputeShaderParserException{ GetTokenErrorString(fmt::format("Unexpected token '{}'", PeekToken().text)).c_str() }; + } + void ComputeShaderParser::ConsumeToken(const std::initializer_list& tokens) + { + for (ComputeShaderLexer::TokenType token : tokens) + { + if (CheckToken(token)) + { + NextToken(); + return; + } + } + throw ComputeShaderParserException{ GetTokenErrorString(fmt::format("Unexpected token '{}'", PeekToken().text)).c_str() }; + } + + auto ComputeShaderParser::GetCurrentTokenPosString() const -> std::string + { + const ComputeShaderLexer::Token& tk = PeekToken(); + return fmt::format("(line: {}, col: {})", tk.line, tk.column); + } + auto ComputeShaderParser::GetTokenErrorString(const std::string& msg) const -> std::string + { + const ComputeShaderLexer::Token& tk = PeekToken(); + return fmt::format("{} (line: {}, col: {})", msg, tk.line, tk.column); + } + + auto ComputeShaderParser::ParseComputeShader() -> ShaderAST::ComputeShaderNode + { + ShaderAST::ComputeShaderNode node; + + while (CheckToken(ComputeShaderLexer::TokenType::Preprocessor)) + { + NextToken(); + ParsePreprocessor(node); + } + + while (!CheckToken(ComputeShaderLexer::TokenType::EndOfFile)) + { + if (CheckToken(ComputeShaderLexer::TokenType::Numthreads)) + ParseNumthreads(node); + else if (CheckToken(ComputeShaderLexer::TokenType::RBuffer)) + ParseBuffer(node, ShaderAST::BufferAccess::Read); + else if (CheckToken(ComputeShaderLexer::TokenType::WBuffer)) + ParseBuffer(node, ShaderAST::BufferAccess::Write); + else if (CheckToken(ComputeShaderLexer::TokenType::RWBuffer)) + ParseBuffer(node, ShaderAST::BufferAccess::ReadWrite); + else if (CheckToken(ComputeShaderLexer::TokenType::Const)) + { + NextToken(); + ParseDeclaration(node, "const"); + } + else if (CheckToken(ComputeShaderLexer::TokenType::Identifier)) + ParseDeclaration(node); + else + NextToken(); + } + + return node; + } + + void ComputeShaderParser::ParsePreprocessor(ShaderAST::ComputeShaderNode& node) + { + ConsumeToken(ComputeShaderLexer::TokenType::Identifier); + if (PreviousToken().text == "version") + { + ShaderAST::VersionNode versionNode{}; + ConsumeToken(ComputeShaderLexer::TokenType::Number); + versionNode.versionNumber = std::stoi(PreviousToken().text); + ConsumeToken(ComputeShaderLexer::TokenType::Identifier); + versionNode.profile = PreviousToken().text; + node.version = versionNode; + return; + } + throw ComputeShaderParserException{ + fmt::format("Unknown preprocessor identifier {} {}", PreviousToken().text, GetCurrentTokenPosString()).c_str() + }; + } + + void ComputeShaderParser::ParseNumthreads(ShaderAST::ComputeShaderNode& node) + { + // Numthreads(x, y, z); + ConsumeToken(ComputeShaderLexer::TokenType::Numthreads); + ConsumeToken(ComputeShaderLexer::TokenType::LBracket); + + ConsumeToken(ComputeShaderLexer::TokenType::Number); + node.numthreadsX = static_cast(std::stoi(PreviousToken().text)); + ConsumeToken(ComputeShaderLexer::TokenType::Comma); + ConsumeToken(ComputeShaderLexer::TokenType::Number); + node.numthreadsY = static_cast(std::stoi(PreviousToken().text)); + ConsumeToken(ComputeShaderLexer::TokenType::Comma); + ConsumeToken(ComputeShaderLexer::TokenType::Number); + node.numthreadsZ = static_cast(std::stoi(PreviousToken().text)); + + ConsumeToken(ComputeShaderLexer::TokenType::RBracket); + ConsumeToken(ComputeShaderLexer::TokenType::Semicolon); + } + + void ComputeShaderParser::ParseBuffer(ShaderAST::ComputeShaderNode& node, ShaderAST::BufferAccess access) + { + // (R|W|RW)Buffer name; + NextToken(); // RBuffer / WBuffer / RWBuffer + + // '<' + if (!CheckToken(ComputeShaderLexer::TokenType::Operator) || PeekToken().text != "<") + throw ComputeShaderParserException{ GetTokenErrorString("Expected '<' for buffer template").c_str() }; + NextToken(); + + // 원소 타입 + ConsumeToken(ComputeShaderLexer::TokenType::Identifier); + ShaderAST::VariableType elementType = IdentifierToVariableType(PreviousToken()); + + // '>' + if (!CheckToken(ComputeShaderLexer::TokenType::Operator) || PeekToken().text != ">") + throw ComputeShaderParserException{ GetTokenErrorString("Expected '>' for buffer template").c_str() }; + NextToken(); + + // 버퍼 이름 + ConsumeToken(ComputeShaderLexer::TokenType::Identifier); + std::string bufferName = PreviousToken().text; + + ConsumeToken(ComputeShaderLexer::TokenType::Semicolon); + + ShaderAST::BufferNode buffer{}; + buffer.bufferType = ShaderAST::BufferType::Storage; + buffer.access = access; + buffer.bAnonymousInstance = true; + buffer.set = 0; + buffer.binding = lastBinding++; + buffer.name = bufferName; + buffer.vars.push_back(ShaderAST::VariableNode::MakeDynamicArray(elementType, bufferName)); + + node.buffers.push_back(std::move(buffer)); + } + + void ComputeShaderParser::ParseDeclaration(ShaderAST::ComputeShaderNode& node, const std::string& qualifier) + { + // + // ::= ';' | '=' ';' | '(' ')' + ConsumeToken(ComputeShaderLexer::TokenType::Identifier); + const std::string type = PreviousToken().text; + ConsumeToken(ComputeShaderLexer::TokenType::Identifier); + const std::string name = PreviousToken().text; + bool array = false; + std::size_t arraySize = 0; + if (CheckToken(ComputeShaderLexer::TokenType::LSquareBracket)) + { + NextToken(); + ConsumeToken(ComputeShaderLexer::TokenType::Number); + arraySize = std::stoi(PreviousToken().text); + ConsumeToken(ComputeShaderLexer::TokenType::RSquareBracket); + array = true; + } + ConsumeToken({ ComputeShaderLexer::TokenType::Semicolon, + ComputeShaderLexer::TokenType::Operator, + ComputeShaderLexer::TokenType::LBracket }); + auto& prevToken = PreviousToken(); + if (prevToken.type == ComputeShaderLexer::TokenType::Semicolon) // 변수 선언 + { + if (!array) + node.declaration.push_back(fmt::format("{} {} {};", qualifier, type, name)); + else + node.declaration.push_back(fmt::format("{} {} {}[{}];", qualifier, type, name, arraySize)); + } + else if (prevToken.type == ComputeShaderLexer::TokenType::Operator) // 변수 선언+초기화 + { + if (prevToken.text != "=") + throw ComputeShaderParserException{ GetTokenErrorString("Expected '='").c_str() }; + std::string expr; + while (!CheckToken(ComputeShaderLexer::TokenType::Semicolon)) + { + if (PeekToken().type == ComputeShaderLexer::TokenType::Literal) + expr.pop_back(); + expr += PeekToken().text; + expr += " "; + NextToken(); + } + NextToken(); // ; + if (!array) + node.declaration.push_back(fmt::format("{} {} {} = {};", qualifier, type, name, std::move(expr))); + else + node.declaration.push_back(fmt::format("{} {} {}[{}] = {};", qualifier, type, name, arraySize, std::move(expr))); + } + else if (prevToken.type == ComputeShaderLexer::TokenType::LBracket) // 함수 정의 + { + std::string parameters; + while (!CheckToken(ComputeShaderLexer::TokenType::RBracket)) + { + if (PeekToken().type == ComputeShaderLexer::TokenType::Literal) + parameters.pop_back(); + parameters += PeekToken().text; + parameters += " "; + NextToken(); + } + ConsumeToken(ComputeShaderLexer::TokenType::RBracket); + ConsumeToken(ComputeShaderLexer::TokenType::LBrace); + std::string body = ParseFunctionBody(); + ConsumeToken(ComputeShaderLexer::TokenType::RBrace); + node.functions.push_back(fmt::format("{} {} {}({}) {{ {} }}", qualifier, type, name, std::move(parameters), std::move(body))); + } + } + + auto ComputeShaderParser::ParseFunctionBody() -> std::string + { + std::string code; + int nested = 1; + while (PeekToken().type != ComputeShaderLexer::TokenType::EndOfFile) + { + auto& token = PeekToken(); + if (token.type == ComputeShaderLexer::TokenType::RBrace) + { + if (--nested == 0) + break; + } + else if (token.type == ComputeShaderLexer::TokenType::LBrace) + ++nested; + else if (token.type == ComputeShaderLexer::TokenType::Literal) + { + if (!code.empty()) + code.pop_back(); + } + code += token.text + " "; + NextToken(); + } + return code; + } + + auto ComputeShaderParser::IdentifierToVariableType(const ComputeShaderLexer::Token& token) -> ShaderAST::VariableType + { + const std::string& type = token.text; + if (type == "vec4") return ShaderAST::VariableType::Vec4; + if (type == "vec3") return ShaderAST::VariableType::Vec3; + if (type == "vec2") return ShaderAST::VariableType::Vec2; + if (type == "mat4") return ShaderAST::VariableType::Mat4; + if (type == "mat3") return ShaderAST::VariableType::Mat3; + if (type == "mat2") return ShaderAST::VariableType::Mat2; + if (type == "ivec4") return ShaderAST::VariableType::IVec4; + if (type == "int") return ShaderAST::VariableType::Int; + if (type == "float") return ShaderAST::VariableType::Float; + if (type == "bool") return ShaderAST::VariableType::Boolean; + else return ShaderAST::VariableType::Struct; + } + auto ComputeShaderParser::VariableTypeToString(ShaderAST::VariableType type) -> std::string + { + switch (type) + { + case ShaderAST::VariableType::Mat4: return "mat4"; + case ShaderAST::VariableType::Mat3: return "mat3"; + case ShaderAST::VariableType::Mat2: return "mat2"; + case ShaderAST::VariableType::Vec4: return "vec4"; + case ShaderAST::VariableType::Vec3: return "vec3"; + case ShaderAST::VariableType::Vec2: return "vec2"; + case ShaderAST::VariableType::IVec4: return "ivec4"; + case ShaderAST::VariableType::Float: return "float"; + case ShaderAST::VariableType::Int: return "int"; + case ShaderAST::VariableType::Sampler: return "sampler2D"; + case ShaderAST::VariableType::Boolean: return "bool"; + default: return "unknown"; + } + } + + void ComputeShaderParser::GenerateCode(ShaderAST::ComputeShaderNode& node) + { + std::string code = fmt::format("#version {} {}\n", node.version.versionNumber, node.version.profile); + code += fmt::format("layout(local_size_x = {}, local_size_y = {}, local_size_z = {}) in;\n", + node.numthreadsX, node.numthreadsY, node.numthreadsZ); + + for (auto& buf : node.buffers) + { + std::string members; + for (ShaderAST::VariableNode& v : buf.vars) + { + if (v.bDynamicArray) + members += fmt::format(" {} {}[];\n", VariableTypeToString(v.type), v.name); + else if (v.arraySize == 1) + members += fmt::format(" {} {};\n", VariableTypeToString(v.type), v.name); + else + members += fmt::format(" {} {}[{}];\n", VariableTypeToString(v.type), v.name, v.arraySize); + } + + const char* accessQualifier = ""; + switch (buf.access) + { + case ShaderAST::BufferAccess::Read: accessQualifier = "readonly "; break; + case ShaderAST::BufferAccess::Write: accessQualifier = "writeonly "; break; + case ShaderAST::BufferAccess::ReadWrite: accessQualifier = ""; break; + } + + if (buf.bufferType == ShaderAST::BufferType::Storage) + { + if (buf.bAnonymousInstance) + { + code += fmt::format("layout(std430, binding = {}) {}buffer BUFFER_{} {{\n{}}};\n", + buf.binding, accessQualifier, buf.name, members); + } + else + { + code += fmt::format("layout(std430, set = {}, binding = {}) {}buffer UNIFORM_{} {{\n{}}} {};\n", + buf.set, buf.binding, accessQualifier, buf.name, members, buf.name); + } + } + else if (buf.bufferType == ShaderAST::BufferType::Uniform) + { + code += fmt::format("layout(set = {}, binding = {}) uniform UNIFORM_{} {{\n{}}} {};\n", + buf.set, buf.binding, buf.name, members, buf.name); + } + } + for (auto& decl : node.declaration) + code += decl + '\n'; + for (auto& fn : node.functions) + code += fn + '\n'; + + node.code = std::move(code); + } + + SH_RENDER_API auto ComputeShaderParser::Parse(const std::vector& tokens) -> ShaderAST::ComputeShaderNode + { + this->tokens = &tokens; + pos = 0; + lastBinding = 0; + + ShaderAST::ComputeShaderNode node = ParseComputeShader(); + GenerateCode(node); + return node; + } +}//namespace diff --git a/src/Render/Drawable.cpp b/src/Render/Drawable.cpp index e219aed3..85a1a3d4 100644 --- a/src/Render/Drawable.cpp +++ b/src/Render/Drawable.cpp @@ -1,9 +1,10 @@ #include "Drawable.h" +#include "SkinnedMesh.h" #include "Core/ThreadSyncManager.h" #include - +#include namespace sh::render { Drawable::Drawable(const Material& mat, const Mesh& mesh) : @@ -17,16 +18,23 @@ namespace sh::render priority[core::ThreadType::Game] = 0; priority[core::ThreadType::Render] = 0; + + if (mesh.GetType().IsChildOf(SkinnedMesh::GetStaticType())) + bSkinned = true; } Drawable::Drawable(Drawable&& other) noexcept : mat(other.mat), mesh(other.mesh), modelMatrix(other.modelMatrix), materialData(std::move(other.materialData)), renderTag(other.renderTag), - priority(other.priority) + priority(other.priority), + topology(other.topology), + subMeshIndex(other.subMeshIndex), + syncDatas(other.syncDatas), + bSkinned(other.bSkinned), + bDirty(other.bDirty), + bMatrixDirty(other.bMatrixDirty) { other.bDirty = false; - topology[core::ThreadType::Game] = other.topology[core::ThreadType::Game]; - topology[core::ThreadType::Render] = other.topology[core::ThreadType::Render]; } Drawable::~Drawable() { @@ -91,14 +99,14 @@ namespace sh::render SyncDirty(); } } - SH_RENDER_API void Drawable::SetSubMeshIndex(int idx) + SH_RENDER_API void Drawable::SetSubMeshIndex(uint32_t idx) { subMeshIndex = idx; } SH_RENDER_API auto Drawable::CheckAssetValid() const -> bool { - return core::IsValid(mat) && core::IsValid(mesh); + return core::IsValid(mesh) && core::IsValid(mat) && core::IsValid(mesh); } SH_RENDER_API void Drawable::SyncDirty() @@ -114,24 +122,27 @@ namespace sh::render { for (auto& syncData : syncDatas) { - std::size_t index = syncData.changed.index(); - if (index == 0) + if (std::holds_alternative(syncData.changed)) + { continue; - else if (index == 1) // 메테리얼이 변경됨 + } + else if (std::holds_alternative(syncData.changed)) // 메테리얼이 변경됨 { - this->mat = std::get<1>(syncData.changed); + mat = std::get(syncData.changed); } - else if (index == 2) + else if (std::holds_alternative(syncData.changed)) { - this->mesh = std::get<2>(syncData.changed); + mesh = std::get(syncData.changed); + if (mesh->GetType().IsChildOf(SkinnedMesh::GetStaticType())) + bSkinned = true; } - else if (index == 3) + else if (std::holds_alternative(syncData.changed)) { - topology[core::ThreadType::Render] = std::get<3>(syncData.changed); + topology[core::ThreadType::Render] = std::get(syncData.changed); } - else if (index == 4) + else if (std::holds_alternative(syncData.changed)) { - priority[core::ThreadType::Render] = std::get<4>(syncData.changed); + priority[core::ThreadType::Render] = std::get(syncData.changed); } syncData.changed = std::monostate{}; } diff --git a/src/Render/Material.cpp b/src/Render/Material.cpp index 70b37556..079592eb 100644 --- a/src/Render/Material.cpp +++ b/src/Render/Material.cpp @@ -6,6 +6,8 @@ #include "Core/SContainer.hpp" #include "Core/Logger.h" +#include + namespace sh::render { SH_RENDER_API Material::Material() : @@ -154,88 +156,166 @@ namespace sh::render for (auto& [pass, uniformLayout] : dirtyProps) { - if (uniformLayout->type == UniformStructLayout::Type::Object) + if (uniformLayout->usage == UniformStructLayout::Usage::Object) continue; std::vector data(uniformLayout->GetSize(), 0); bool isSampler = false; for (const UniformStructLayout::UniformMember& member : uniformLayout->GetMembers()) { + const auto writeArrayFn = [&](const auto* values, auto convertFn) + { + if (values == nullptr || member.count == 0) + return; + + const uint32_t stride = member.layoutSize / member.count; + const uint32_t count = std::min(member.count, static_cast(values->size())); + for (uint32_t i = 0; i < count; ++i) + { + auto converted = convertFn((*values)[i]); + SetData(converted, data, member.offset + stride * i); + } + }; + if (member.typeHash == core::reflection::GetType().hash) { - auto var = propertyBlock.GetScalarProperty(member.name); - if (var.has_value()) - SetData(static_cast(var.value()), data, member.offset); + if (member.IsArray()) + { + writeArrayFn(propertyBlock.GetIntArrayProperty(member.name), + [](int value) { return value; }); + } else - SetData(0, data, member.offset); + { + auto var = propertyBlock.GetScalarProperty(member.name); + if (var.has_value()) + SetData(static_cast(var.value()), data, member.offset); + else + SetData(0, data, member.offset); + } } else if (member.typeHash == core::reflection::GetType().hash) { - auto var = propertyBlock.GetScalarProperty(member.name); - if (var.has_value()) - SetData(var.value(), data, member.offset); + if (member.IsArray()) + { + writeArrayFn(propertyBlock.GetScalarArrayProperty(member.name), + [](float value) { return value; }); + } else - SetData(0.0f, data, member.offset); + { + auto var = propertyBlock.GetScalarProperty(member.name); + if (var.has_value()) + SetData(var.value(), data, member.offset); + else + SetData(0.0f, data, member.offset); + } } else if (member.typeHash == core::reflection::GetType().hash) { - auto var = propertyBlock.GetVectorProperty(member.name); - if (var) - SetData(glm::vec2{ var->x, var->y }, data, member.offset); + if (member.IsArray()) + { + writeArrayFn(propertyBlock.GetVectorArrayProperty(member.name), + [](const glm::vec4& value) { return glm::vec2{ value.x, value.y }; }); + } else - SetData(glm::vec2{ 0.f }, data, member.offset); + { + auto var = propertyBlock.GetVectorProperty(member.name); + if (var) + SetData(glm::vec2{ var->x, var->y }, data, member.offset); + else + SetData(glm::vec2{ 0.f }, data, member.offset); + } } else if (member.typeHash == core::reflection::GetType().hash) { - auto var = propertyBlock.GetVectorProperty(member.name); - if (var) - SetData(glm::vec3{ var->x, var->y, var->z }, data, member.offset); + if (member.IsArray()) + { + writeArrayFn(propertyBlock.GetVectorArrayProperty(member.name), + [](const glm::vec4& value) { return glm::vec3{ value.x, value.y, value.z }; }); + } else - SetData(glm::vec3{ 0.f }, data, member.offset); + { + auto var = propertyBlock.GetVectorProperty(member.name); + if (var) + SetData(glm::vec3{ var->x, var->y, var->z }, data, member.offset); + else + SetData(glm::vec3{ 0.f }, data, member.offset); + } } else if (member.typeHash == core::reflection::GetType().hash) { - auto var = propertyBlock.GetVectorProperty(member.name); - if (var) - SetData(*var, data, member.offset); + if (member.IsArray()) + { + writeArrayFn(propertyBlock.GetVectorArrayProperty(member.name), + [](const glm::vec4& value) { return value; }); + } else - SetData(glm::vec4{ 0.f }, data, member.offset); + { + auto var = propertyBlock.GetVectorProperty(member.name); + if (var) + SetData(*var, data, member.offset); + else + SetData(glm::vec4{ 0.f }, data, member.offset); + } } else if (member.typeHash == core::reflection::GetType().hash) { - auto var = propertyBlock.GetMatrixProperty(member.name); - if (var) - SetData(core::Util::ConvertMat4ToMat2(*var), data, member.offset); + if (member.IsArray()) + { + writeArrayFn(propertyBlock.GetMatrixArrayProperty(member.name), + [](const glm::mat4& value) { return core::Util::ConvertMat4ToMat2(value); }); + } else - SetData(glm::mat2{ 1.f }, data, member.offset); + { + auto var = propertyBlock.GetMatrixProperty(member.name); + if (var) + SetData(core::Util::ConvertMat4ToMat2(*var), data, member.offset); + else + SetData(glm::mat2{ 1.f }, data, member.offset); + } } else if (member.typeHash == core::reflection::GetType().hash) { - auto var = propertyBlock.GetMatrixProperty(member.name); - if (var) - SetData(core::Util::ConvertMat4ToMat3(*var), data, member.offset); + if (member.IsArray()) + { + writeArrayFn(propertyBlock.GetMatrixArrayProperty(member.name), + [](const glm::mat4& value) { return core::Util::ConvertMat4ToMat3(value); }); + } else - SetData(glm::mat3{ 1.f }, data, member.offset); + { + auto var = propertyBlock.GetMatrixProperty(member.name); + if (var) + SetData(core::Util::ConvertMat4ToMat3(*var), data, member.offset); + else + SetData(glm::mat3{ 1.f }, data, member.offset); + } } else if (member.typeHash == core::reflection::GetType().hash) { - auto var = propertyBlock.GetMatrixProperty(member.name); - if (var) - SetData(*var, data, member.offset); + if (member.IsArray()) + { + writeArrayFn(propertyBlock.GetMatrixArrayProperty(member.name), + [](const glm::mat4& value) { return value; }); + } else - SetData(glm::mat2{ 1.f }, data, member.offset); + { + auto var = propertyBlock.GetMatrixProperty(member.name); + if (var) + SetData(*var, data, member.offset); + else + SetData(glm::mat4{ 1.f }, data, member.offset); + } } else if (member.typeHash == core::reflection::GetType().hash) { auto var = propertyBlock.GetTextureProperty(member.name); if (core::IsValid(var)) - materialData->SetTextureData(*pass, uniformLayout->type, uniformLayout->binding, *var); + materialData->SetTextureData(*pass, uniformLayout->usage, uniformLayout->binding, *var); isSampler = true; } } if (!isSampler) - materialData->SetUniformData(*pass, uniformLayout->type, uniformLayout->binding, std::move(data)); + materialData->SetBindingData(*pass, uniformLayout->usage, uniformLayout->binding, std::move(data)); } dirtyProps.clear(); bPropertyDirty = false; @@ -269,9 +349,6 @@ namespace sh::render } bPropertyDirty = true; - - if (data->GetType() == RenderTexture::GetStaticType()) - cachedRts[name] = static_cast(data); } SH_RENDER_API void Material::SetProperty(const std::string& name, const glm::vec4& data) { @@ -383,6 +460,132 @@ namespace sh::render } } } + if (propertyJson.contains("matrix")) + { + const auto& matJson = propertyJson["matrix"]; + for (const auto& [name, value] : matJson.items()) + { + if (!value.is_array() || value.size() != 4) + continue; + + glm::mat4 mat{ 0.f }; + for (int i = 0; i < 4; ++i) + { + const core::Json& col = value[i]; + if (!col.is_array() || col.size() != 4) + continue; + mat[i] = { col[0], col[1], col[2], col[3] }; + } + SetProperty(name, mat); + } + } + if (propertyJson.contains("scalarArrays")) + { + const auto& scalarArrayJson = propertyJson["scalarArrays"]; + for (const auto& [name, value] : scalarArrayJson.items()) + { + if (!value.is_array()) + continue; + + std::vector values = value.get>(); + SetProperty(name, values); + } + } + if (propertyJson.contains("intArrays")) + { + const auto& intArrayJson = propertyJson["intArrays"]; + for (const auto& [name, value] : intArrayJson.items()) + { + if (!value.is_array()) + continue; + + std::vector values = value.get>(); + SetProperty(name, values); + } + } + if (propertyJson.contains("vectorArrays")) + { + const auto& vectorArrayJson = propertyJson["vectorArrays"]; + for (const auto& [name, value] : vectorArrayJson.items()) + { + if (!value.is_array()) + continue; + + std::vector values{}; + for (const core::Json& vecJson : value) + { + if (!vecJson.is_array() || vecJson.size() != 4) + continue; + values.push_back(glm::vec4{ + vecJson[0].get(), + vecJson[1].get(), + vecJson[2].get(), + vecJson[3].get() + }); + } + + const Shader::PropertyInfo* propInfo = core::IsValid(shader) ? shader->GetProperty(name) : nullptr; + if (propInfo != nullptr && propInfo->type == core::reflection::GetType()) + { + std::vector vec2Values(values.size()); + for (std::size_t i = 0; i < values.size(); ++i) + vec2Values[i] = glm::vec2{ values[i].x, values[i].y }; + SetProperty(name, vec2Values); + } + else if (propInfo != nullptr && propInfo->type == core::reflection::GetType()) + { + std::vector vec3Values(values.size()); + for (std::size_t i = 0; i < values.size(); ++i) + vec3Values[i] = glm::vec3{ values[i].x, values[i].y, values[i].z }; + SetProperty(name, vec3Values); + } + else + SetProperty(name, values); + } + } + if (propertyJson.contains("matrixArrays")) + { + const auto& matrixArrayJson = propertyJson["matrixArrays"]; + for (const auto& [name, value] : matrixArrayJson.items()) + { + if (!value.is_array()) + continue; + + std::vector values{}; + for (const core::Json& matJson : value) + { + if (!matJson.is_array() || matJson.size() != 4) + continue; + glm::mat4 mat{ 0.f }; + for (int i = 0; i < 4; ++i) + { + const core::Json& col = matJson[i]; + if (!col.is_array() || col.size() != 4) + continue; + mat[i] = { col[0], col[1], col[2], col[3] }; + } + values.push_back(mat); + } + + const Shader::PropertyInfo* propInfo = core::IsValid(shader) ? shader->GetProperty(name) : nullptr; + if (propInfo != nullptr && propInfo->type == core::reflection::GetType()) + { + std::vector mat2Values(values.size()); + for (std::size_t i = 0; i < values.size(); ++i) + mat2Values[i] = core::Util::ConvertMat4ToMat2(values[i]); + SetProperty(name, mat2Values); + } + else if (propInfo != nullptr && propInfo->type == core::reflection::GetType()) + { + std::vector mat3Values(values.size()); + for (std::size_t i = 0; i < values.size(); ++i) + mat3Values[i] = core::Util::ConvertMat4ToMat3(values[i]); + SetProperty(name, mat3Values); + } + else + SetProperty(name, values); + } + } if (propertyJson.contains("textures")) { const auto& texJson = propertyJson["textures"]; @@ -412,4 +615,4 @@ namespace sh::render } } } -}//namespace \ No newline at end of file +}//namespace diff --git a/src/Render/MaterialData.cpp b/src/Render/MaterialData.cpp index 97c6bc8b..aef336d0 100644 --- a/src/Render/MaterialData.cpp +++ b/src/Render/MaterialData.cpp @@ -3,12 +3,13 @@ #include "ShaderPass.h" #include "BufferFactory.h" #include "IRenderContext.h" - -#include "VulkanCameraBuffers.h" +#include "RenderTexture.h" #include "Core/ThreadSyncManager.h" #include "Core/Logger.h" +#include "Render/RenderDataManager.h" + namespace sh::render { MaterialData::MaterialData(MaterialData&& other) noexcept : @@ -62,24 +63,24 @@ namespace sh::render { if (std::holds_alternative(syncData.data)) // 버퍼 { - auto& bufferData = std::get<0>(syncData.data); + const SyncData::BufferSyncData& bufferData = std::get(syncData.data); SetUniformDataAtSync(bufferData); } else // 텍스쳐 { - auto& uniformBufferData = std::get<1>(syncData.data); - SetTextureDataAtSync(uniformBufferData); + auto& shaderBindingsData = std::get<1>(syncData.data); + SetTextureDataAtSync(shaderBindingsData); } } syncDatas.clear(); bDirty = false; } - SH_RENDER_API void MaterialData::SetUniformData(const ShaderPass& shaderPass, UniformStructLayout::Type type, uint32_t binding, const void* data, std::size_t dataSize) + SH_RENDER_API void MaterialData::SetBindingData(const ShaderPass& shaderPass, UniformStructLayout::Usage usage, uint32_t binding, const void* data, std::size_t dataSize) { SyncData::BufferSyncData syncData{}; syncData.pass = &shaderPass; - syncData.set = static_cast(type); + syncData.set = static_cast(usage); syncData.binding = binding; syncData.data.resize(dataSize); std::memcpy(syncData.data.data(), data, syncData.data.size()); @@ -87,33 +88,22 @@ namespace sh::render syncDatas.push_back(SyncData{ std::move(syncData) }); SyncDirty(); } - SH_RENDER_API void MaterialData::SetUniformData(const ShaderPass& shaderPass, UniformStructLayout::Type type, uint32_t binding, const std::vector& data) + SH_RENDER_API void MaterialData::SetBindingData(const ShaderPass& shaderPass, UniformStructLayout::Usage usage, uint32_t binding, std::vector data) { SyncData::BufferSyncData syncData{}; syncData.pass = &shaderPass; - syncData.set = static_cast(type); - syncData.binding = binding; - syncData.data = data; - - syncDatas.push_back(SyncData{ std::move(syncData) }); - SyncDirty(); - } - SH_RENDER_API void MaterialData::SetUniformData(const ShaderPass& shaderPass, UniformStructLayout::Type type, uint32_t binding, std::vector&& data) - { - SyncData::BufferSyncData syncData{}; - syncData.pass = &shaderPass; - syncData.set = static_cast(type); + syncData.set = static_cast(usage); syncData.binding = binding; syncData.data = std::move(data); syncDatas.push_back(SyncData{ std::move(syncData) }); SyncDirty(); } - SH_RENDER_API void MaterialData::SetTextureData(const ShaderPass& shaderPass, UniformStructLayout::Type type, uint32_t binding, const Texture& tex) + SH_RENDER_API void MaterialData::SetTextureData(const ShaderPass& shaderPass, UniformStructLayout::Usage usage, uint32_t binding, const Texture& tex) { - SyncData::UniformBufferSyncData syncData{}; + SyncData::ShaderBindingSyncData syncData{}; syncData.pass = &shaderPass; - syncData.set = static_cast(type); + syncData.set = static_cast(usage); syncData.binding = binding; syncData.tex = &tex; @@ -121,15 +111,15 @@ namespace sh::render SyncDirty(); } - SH_RENDER_API auto MaterialData::GetShaderBuffer(const ShaderPass& shaderPass, UniformStructLayout::Type type, uint32_t binding) const -> IBuffer* + SH_RENDER_API auto MaterialData::GetShaderBuffer(const ShaderPass& shaderPass, UniformStructLayout::Usage usage, uint32_t binding) const -> IBuffer* { const PassData* passData = GetMaterialPassData(shaderPass); if (passData == nullptr) return nullptr; - uint32_t set = static_cast(type); - auto itSet = passData->uniformData.find(set); - if (itSet == passData->uniformData.end()) + uint32_t set = static_cast(usage); + auto itSet = passData->buffers.find(set); + if (itSet == passData->buffers.end()) return nullptr; const auto& buffers = itSet->second; @@ -138,15 +128,15 @@ namespace sh::render return buffers[binding] ? buffers[binding].get() : nullptr; } - SH_RENDER_API auto MaterialData::GetUniformBuffer(const ShaderPass& shaderPass, UniformStructLayout::Type type) const -> IUniformBuffer* + SH_RENDER_API auto MaterialData::GetShaderBinding(const ShaderPass& shaderPass, UniformStructLayout::Usage usage) const -> IShaderBinding* { const PassData* passData = GetMaterialPassData(shaderPass); if (passData == nullptr) return nullptr; - uint32_t set = static_cast(type); - auto it = passData->uniformBuffer.find(set); - if (it == passData->uniformBuffer.end()) + uint32_t set = static_cast(usage); + auto it = passData->shaderBindings.find(set); + if (it == passData->shaderBindings.end()) return nullptr; return it->second.get(); @@ -162,9 +152,9 @@ namespace sh::render void MaterialData::CreateBuffers(const IRenderContext& context, const Shader& shader, bool bPerObject) { - std::vector usages = { UniformStructLayout::Type::Camera, UniformStructLayout::Type::Material }; + std::vector usages{ UniformStructLayout::Usage::Camera, UniformStructLayout::Usage::Material }; if (bPerObject) - usages = { UniformStructLayout::Type::Object }; + usages = { UniformStructLayout::Usage::Object }; for (auto& [passName, shaderPasses] : shader.GetAllShaderPass()) { @@ -173,65 +163,68 @@ namespace sh::render PassData passData{}; uint32_t maxSet = 0; std::vector sets; - // 유니폼 데이터 버퍼 (텍스쳐외 모든 GPU에 저장할 데이터) - auto layoutsPerPass = { shaderPass.GetVertexUniforms(), shaderPass.GetFragmentUniforms() }; - for (const std::vector& layouts : layoutsPerPass) + // 버퍼 (텍스쳐외 모든 GPU에 저장할 데이터) + for (const std::vector* layouts : { &shaderPass.GetVertexUniforms(), &shaderPass.GetFragmentUniforms() }) { - for (const UniformStructLayout& uniformLayout : layouts) + for (const UniformStructLayout& uniformLayout : *layouts) { - if (uniformLayout.bConstant) + if (uniformLayout.IsPushConstant()) continue; - if (std::find(usages.begin(), usages.end(), uniformLayout.type) == usages.end()) + + if (std::find(usages.begin(), usages.end(), uniformLayout.usage) == usages.end()) continue; - uint32_t set = static_cast(uniformLayout.type); + const uint32_t set = static_cast(uniformLayout.usage); + maxSet = (maxSet < set) ? set : maxSet; if (std::find(sets.begin(), sets.end(), set) == sets.end()) sets.push_back(set); - auto& bindingBuffers = passData.uniformData[set]; + std::vector>& bindingBuffers = passData.buffers[set]; if (bindingBuffers.size() <= uniformLayout.binding) bindingBuffers.resize(uniformLayout.binding + 1); if (set == 0) // 카메라 데이터는 다른 곳에서 관리한다. continue; - bindingBuffers[uniformLayout.binding] = BufferFactory::Create(context, uniformLayout.GetSize()); + + BufferFactory::CreateInfo info{}; + info.size = uniformLayout.GetSize(); + info.bDynamic = (uniformLayout.GetKind() == UniformStructLayout::Kind::Storage); + if (info.bDynamic && info.size == 0) + info.size = 1; // 실제 데이터 업로드 시 Resize됨 + bindingBuffers[uniformLayout.binding] = BufferFactory::Create(context, info); } } for (const UniformStructLayout& layout : shaderPass.GetSamplerUniforms()) { - uint32_t set = static_cast(layout.type); + uint32_t set = static_cast(layout.usage); maxSet = (maxSet < set) ? set : maxSet; if (std::find(sets.begin(), sets.end(), set) == sets.end()) sets.push_back(set); - auto& bindingBuffers = passData.uniformData[set]; + std::vector>& bindingBuffers = passData.buffers[set]; if (bindingBuffers.size() <= layout.binding) bindingBuffers.resize(layout.binding + 1); } // 유니폼 버퍼 (GPU로 데이터 전송 역할) for (uint32_t set : sets) { - passData.uniformBuffer[set] = BufferFactory::CreateUniformBuffer(context, shaderPass, static_cast(set)); - auto it = passData.uniformData.find(set); - if (it == passData.uniformData.end()) + passData.shaderBindings[set] = BufferFactory::CreateShaderBinding(context, shaderPass, static_cast(set)); + auto it = passData.buffers.find(set); + if (it == passData.buffers.end()) continue; - auto& bufferVec = it->second; + const std::vector>& bufferVec = it->second; for (uint32_t binding = 0; binding < bufferVec.size(); ++binding) { if (set != 0) // 카메라 데이터는 다른 곳에서 관리한다. { if (bufferVec[binding] == nullptr) continue; - passData.uniformBuffer[set]->Link(binding, *bufferVec[binding].get()); + passData.shaderBindings[set]->Link(binding, *bufferVec[binding].get()); } else { - // 카메라 데이터 - if (context.GetRenderAPIType() == RenderAPI::Vulkan) - { - passData.uniformBuffer[set]->Link(binding, vk::VulkanCameraBuffers::GetInstance()->GetCameraBuffer(), 128); - } + passData.shaderBindings[set]->Link(binding, *context.GetRenderDataManager().GetBuffer(), sizeof(RenderDataManager::BufferData)); } } } @@ -258,40 +251,52 @@ namespace sh::render return; const uint32_t set = bufferSyncData.set; - auto itSet = passData->uniformData.find(set); - if (itSet == passData->uniformData.end()) + auto itSet = passData->buffers.find(set); + if (itSet == passData->buffers.end()) return; const uint32_t binding = bufferSyncData.binding; - auto& bufferVec = itSet->second; + const std::vector>& bufferVec = itSet->second; if (bufferSyncData.binding >= bufferVec.size() || bufferVec[bufferSyncData.binding] == nullptr) return; if (bufferVec[binding]->GetSize() != bufferSyncData.data.size()) { + for (const std::vector* layouts : { &bufferSyncData.pass->GetVertexUniforms(), &bufferSyncData.pass->GetFragmentUniforms() }) + { + for (const UniformStructLayout& layout : *layouts) + { + if (static_cast(layout.usage) != set || layout.binding != binding || layout.GetKind() != UniformStructLayout::Kind::Storage) + continue; + bufferVec[binding]->Resize(bufferSyncData.data.size()); + passData->shaderBindings.at(set)->Link(binding, *bufferVec[binding].get()); + bufferVec[binding]->SetData(bufferSyncData.data.data()); + return; + } + } SH_ERROR_FORMAT("Buffer size is different! expected: {}, current: {}", bufferVec[binding]->GetSize(), bufferSyncData.data.size()); return; } bufferVec[binding]->SetData(bufferSyncData.data.data()); } - void MaterialData::SetTextureDataAtSync(const SyncData::UniformBufferSyncData& uniformBufferSyncData) + void MaterialData::SetTextureDataAtSync(const SyncData::ShaderBindingSyncData& shaderBindingsSyncData) { - const ShaderPass* shaderPass = uniformBufferSyncData.pass; - if (!core::IsValid(shaderPass) || !core::IsValid(uniformBufferSyncData.tex)) + const ShaderPass* shaderPass = shaderBindingsSyncData.pass; + if (!core::IsValid(shaderPass) || !core::IsValid(shaderBindingsSyncData.tex)) return; const PassData* passData = GetMaterialPassData(*shaderPass); if (passData == nullptr) return; - const uint32_t set = uniformBufferSyncData.set; - const uint32_t binding = uniformBufferSyncData.binding; + const uint32_t set = shaderBindingsSyncData.set; + const uint32_t binding = shaderBindingsSyncData.binding; bool bFind = false; for (auto& layout : shaderPass->GetSamplerUniforms()) { - if (static_cast(layout.type) == set && layout.binding == binding) + if (static_cast(layout.usage) == set && layout.binding == binding) { bFind = true; break; @@ -300,11 +305,28 @@ namespace sh::render if (!bFind) return; - auto itUb = passData->uniformBuffer.find(set); - if (itUb == passData->uniformBuffer.end() || itUb->second == nullptr) + auto itUb = passData->shaderBindings.find(set); + if (itUb == passData->shaderBindings.end() || itUb->second == nullptr) return; - IUniformBuffer& uniformBuffer = *itUb->second.get(); - uniformBuffer.Link(binding, *uniformBufferSyncData.tex); + IShaderBinding& shaderBindings = *itUb->second.get(); + shaderBindings.Link(binding, *shaderBindingsSyncData.tex); + + auto it = std::find_if(cachedRTs.begin(), cachedRTs.end(), + [&](const CachedRT& cached) + { + return cached.set == set && cached.binding == binding && cached.pass == shaderPass; + }); + + if (shaderBindingsSyncData.tex->GetType().IsChildOf(RenderTexture::GetStaticType())) + { + const RenderTexture* const rt = static_cast(shaderBindingsSyncData.tex); + if (it == cachedRTs.end()) + cachedRTs.push_back(CachedRT{ set, binding, shaderPass, rt }); + else + it->rt = rt; + } + else if (it != cachedRTs.end()) + cachedRTs.erase(it); } }//namespace \ No newline at end of file diff --git a/src/Render/MaterialPropertyBlock.cpp b/src/Render/MaterialPropertyBlock.cpp index f73025b6..deb3574a 100644 --- a/src/Render/MaterialPropertyBlock.cpp +++ b/src/Render/MaterialPropertyBlock.cpp @@ -8,6 +8,10 @@ namespace sh::render scalarProperties.clear(); vecProperties.clear(); matProperties.clear(); + intArrayProperties.clear(); + scalarArrayProperties.clear(); + vecArrayProperties.clear(); + matArrayProperties.clear(); textureProperties.clear(); } SH_RENDER_API void MaterialPropertyBlock::SetProperty(const std::string& name, const Texture* data) @@ -42,6 +46,34 @@ namespace sh::render return nullptr; return it->second; } + SH_RENDER_API auto MaterialPropertyBlock::GetIntArrayProperty(const std::string& name) const -> const std::vector* + { + auto it = intArrayProperties.find(name); + if (it == intArrayProperties.end()) + return nullptr; + return &it->second; + } + SH_RENDER_API auto MaterialPropertyBlock::GetScalarArrayProperty(const std::string& name) const -> const std::vector* + { + auto it = scalarArrayProperties.find(name); + if (it == scalarArrayProperties.end()) + return nullptr; + return &it->second; + } + SH_RENDER_API auto MaterialPropertyBlock::GetVectorArrayProperty(const std::string& name) const -> const std::vector* + { + auto it = vecArrayProperties.find(name); + if (it == vecArrayProperties.end()) + return nullptr; + return &it->second; + } + SH_RENDER_API auto MaterialPropertyBlock::GetMatrixArrayProperty(const std::string& name) const -> const std::vector* + { + auto it = matArrayProperties.find(name); + if (it == matArrayProperties.end()) + return nullptr; + return &it->second; + } SH_RENDER_API auto MaterialPropertyBlock::GetTextureProperties() const -> const std::unordered_map& { return textureProperties; @@ -68,6 +100,37 @@ namespace sh::render } propertiesJson["matrix"] = std::move(matrixJson); + core::Json intArrayJson{}; + for (auto& [name, values] : intArrayProperties) + intArrayJson[name] = values; + propertiesJson["intArrays"] = std::move(intArrayJson); + + core::Json scalarArrayJson{}; + for (auto& [name, values] : scalarArrayProperties) + scalarArrayJson[name] = values; + propertiesJson["scalarArrays"] = std::move(scalarArrayJson); + + core::Json vectorArrayJson{}; + for (auto& [name, values] : vecArrayProperties) + { + for (const glm::vec4& value : values) + vectorArrayJson[name].push_back({ value.x, value.y, value.z, value.w }); + } + propertiesJson["vectorArrays"] = std::move(vectorArrayJson); + + core::Json matrixArrayJson{}; + for (auto& [name, values] : matArrayProperties) + { + for (const glm::mat4& value : values) + { + core::Json matJson{}; + for (int i = 0; i < 4; ++i) + matJson.push_back({ value[i].x, value[i].y, value[i].z, value[i].w }); + matrixArrayJson[name].push_back(std::move(matJson)); + } + } + propertiesJson["matrixArrays"] = std::move(matrixArrayJson); + core::Json texJson{}; for (auto& [name, value] : textureProperties) texJson[name] = value->GetUUID().ToString(); @@ -120,6 +183,77 @@ namespace sh::render SetProperty(name, mat); } } + if (json.contains("scalarArrays")) + { + const auto& scalarArrayJson = json["scalarArrays"]; + for (const auto& [name, value] : scalarArrayJson.items()) + { + if (value.is_array()) + { + std::vector values = value.get>(); + SetArrayProperty(name, values.data(), values.size()); + } + } + } + if (json.contains("intArrays")) + { + const auto& intArrayJson = json["intArrays"]; + for (const auto& [name, value] : intArrayJson.items()) + { + if (value.is_array()) + { + std::vector values = value.get>(); + SetArrayProperty(name, values.data(), values.size()); + } + } + } + if (json.contains("vectorArrays")) + { + const auto& vectorArrayJson = json["vectorArrays"]; + for (const auto& [name, value] : vectorArrayJson.items()) + { + if (!value.is_array()) + continue; + std::vector values{}; + for (const core::Json& vecJson : value) + { + if (!vecJson.is_array() || vecJson.size() != 4) + continue; + values.push_back(glm::vec4{ + vecJson[0].get(), + vecJson[1].get(), + vecJson[2].get(), + vecJson[3].get() + }); + } + SetArrayProperty(name, values.data(), values.size()); + } + } + if (json.contains("matrixArrays")) + { + const auto& matrixArrayJson = json["matrixArrays"]; + for (const auto& [name, value] : matrixArrayJson.items()) + { + if (!value.is_array()) + continue; + std::vector values{}; + for (const core::Json& matJson : value) + { + if (!matJson.is_array() || matJson.size() != 4) + continue; + glm::mat4 mat{ 0.f }; + for (int i = 0; i < 4; ++i) + { + const core::Json& col = matJson[i]; + if (!col.is_array() || col.size() != 4) + continue; + mat[i] = { col[0], col[1], col[2], col[3] }; + } + values.push_back(mat); + } + SetArrayProperty(name, values.data(), values.size()); + } + } if (json.contains("textures")) { const auto& texJson = json["textures"]; @@ -134,4 +268,4 @@ namespace sh::render } } } -}//namespace \ No newline at end of file +}//namespace diff --git a/src/Render/RenderDataManager.cpp b/src/Render/RenderDataManager.cpp new file mode 100644 index 00000000..63063255 --- /dev/null +++ b/src/Render/RenderDataManager.cpp @@ -0,0 +1,84 @@ +#include "RenderDataManager.h" +#include "BufferFactory.h" + +#include "Core/Util.h" +namespace sh::render +{ + RenderDataManager::RenderDataManager() = default; + RenderDataManager::~RenderDataManager() = default; + + SH_RENDER_API void RenderDataManager::Init(const IRenderContext& ctx) + { + if (this->ctx != nullptr) + return; + + this->ctx = &ctx; + alignment = core::Util::AlignTo(sizeof(BufferData), BufferFactory::GetBufferAlignment(ctx));; + + const std::size_t cap = 100; + + BufferFactory::CreateInfo info{}; + info.size = cap * alignment; + info.bDynamic = false; + info.bGPUOnly = false; + + buffer = BufferFactory::Create(ctx, info); + } + SH_RENDER_API void RenderDataManager::PushRenderData(const RenderData& renderTarget) + { + renderDataQueue.Push(renderTarget); + } + SH_RENDER_API void RenderDataManager::ClearBuffer() + { + buffer.reset(); + } + SH_RENDER_API void RenderDataManager::ClearRenderViews() + { + renderDataQueue.Clear(); + renderDatas.clear(); + } + SH_RENDER_API void RenderDataManager::UploadToGPU() + { + std::size_t offset = 0; + std::size_t i = 0; + renderDatasSize = 0; + renderDataQueue.Drain( + [&](Ref& renderTarget) + { + if (offset + sizeof(BufferData) > buffer->GetSize()) + return; + + if (renderDatas.size() <= i) // 메모) 힙 할당 최소화 하기 위해 이렇게 + renderDatas.resize(i + 1); + + renderDatas[i] = renderTarget.get(); + + for (RenderViewer& viewer : renderDatas[i].renderViewers) + { + BufferData data; + data.view = viewer.viewMatrix; + data.proj = viewer.projMatrix; + data.pos = glm::vec4{ viewer.pos, 1.0f }; + + buffer->SetData(&data, offset, sizeof(BufferData)); + + viewer.offset = offset; + offset += alignment; + } + ++i; + } + ); + renderDatasSize = i; + std::sort(renderDatas.begin(), renderDatas.begin() + renderDatasSize, + [](const RenderData& left, const RenderData& right) + { + return left.priority > right.priority; + } + ); + } + + SH_RENDER_API auto RenderDataManager::GetRenderDatas() -> core::ArrayView + { + return core::ArrayView{renderDatas.data(), renderDatasSize}; + } +}//namespace diff --git a/src/Render/RenderPass/CombinePass.cpp b/src/Render/RenderPass/CombinePass.cpp new file mode 100644 index 00000000..c47c883c --- /dev/null +++ b/src/Render/RenderPass/CombinePass.cpp @@ -0,0 +1,103 @@ +#include "RenderPass/CombinePass.h" +#include "CommandBuffer.h" +#include "Drawable.h" +#include "IRenderContext.h" +#include "Material.h" +#include "Mesh.h" + +#include "Core/GarbageCollection.h" +#include "Core/SObject.h" + +namespace sh::render +{ + CombinePass::CombinePass(const IRenderContext& ctx) : + ScriptableRenderPass(core::Name{ "CombinePass" }, RenderQueue::AfterRendering), + ctx(ctx) + { + resource.plane = core::SObject::Create(); + + resource.plane->SetVertex({ + {{-1.0f, -1.0f, 0.0f}, {0.0f, 1.0f}, {0.f, 0.f, 0.f}}, + {{ 1.0f, -1.0f, 0.0f}, {1.0f, 1.0f}, {0.f, 0.f, 0.f}}, + {{ 1.0f, 1.0f, 0.0f}, {1.0f, 0.0f}, {0.f, 0.f, 0.f}}, + {{-1.0f, 1.0f, 0.0f}, {0.0f, 0.0f}, {0.f, 0.f, 0.f}}, + }); + resource.plane->SetIndices({ 0, 1, 2, 2, 3, 0 }); + resource.plane->Build(ctx); + + onMatDestroy.SetCallback( + [this](const core::SObject* objPtr) + { + resource.mat = nullptr; + if (resource.drawable != nullptr) + resource.drawable->Destroy(); + resource.drawable = nullptr; + } + ); + } + CombinePass::~CombinePass() + { + core::GarbageCollection& gc = *core::GarbageCollection::GetInstance(); + gc.ForceDelete(resource.plane); + gc.ForceDelete(resource.drawable); + resource.plane = nullptr; + resource.drawable = nullptr; + resource.mat = nullptr; + } + + SH_RENDER_API void CombinePass::SetMaterial(Material& mat) + { + if (resource.mat == &mat || mat.IsPendingKill()) + return; + resource.mat = &mat; + resource.mat->onDestroy.Register(onMatDestroy); + if (resource.drawable != nullptr) + resource.drawable->SetMaterial(*resource.mat); + } + + void CombinePass::EnsureDrawable() + { + if (resource.drawable != nullptr || resource.mat == nullptr) + return; + resource.drawable = core::SObject::Create(*resource.mat, *resource.plane); + resource.drawable->Build(ctx); + } + + SH_RENDER_API void CombinePass::Configure(const RenderData& renderData) + { + renderTextures.clear(); + + if (!core::IsValid(resource.mat)) + return; + + EnsureDrawable(); + if (resource.drawable == nullptr || !core::IsValid(resource.drawable->GetMesh()) || !core::IsValid(resource.drawable->GetMaterial())) + return; + + SetRenderTargetImageUsages(renderData); + SetImageUsages(*resource.mat); + } + + SH_RENDER_API void CombinePass::Record(CommandBuffer& cmd, const IRenderContext& ctx, const RenderData& renderData) + { + if (!core::IsValid(resource.mat) || !core::IsValid(resource.drawable)) + return; + + cmd.SetRenderData(renderData, false, false, true, false); + + std::size_t viewerIdx = 0; + for (const RenderViewer& viewer : renderData.renderViewers) + { + SetViewportScissor(cmd, ctx, viewer); + cmd.DrawMesh(*resource.drawable, passName, viewerIdx); + ++viewerIdx; + } + } + + SH_RENDER_API void CombinePass::Resource::PushReferenceObjects(core::GarbageCollection& gc) + { + gc.PushReferenceObject(plane); + gc.PushReferenceObject(drawable); + gc.PushReferenceObject(mat); + } +}//namespace diff --git a/src/Render/RenderPass/CopyPass.cpp b/src/Render/RenderPass/CopyPass.cpp index 2fb2f188..3ce8276a 100644 --- a/src/Render/RenderPass/CopyPass.cpp +++ b/src/Render/RenderPass/CopyPass.cpp @@ -1,5 +1,6 @@ #include "RenderPass/CopyPass.h" #include "RenderTexture.h" +#include "CommandBuffer.h" namespace sh::render { @@ -8,43 +9,24 @@ namespace sh::render { } - SH_RENDER_API void CopyPass::Configure(const RenderTarget& renderData) + SH_RENDER_API void CopyPass::Configure(const RenderData& renderData) { if (cpyDatas.empty()) return; - drawList = BuildDrawList(renderData); - renderTextures.clear(); - for (auto& cpyData : cpyDatas) - renderTextures[cpyData.src] = ImageUsage::Src; + for (const CopyData& cpyData : cpyDatas) + renderTextures[cpyData.src] = ResourceUsage::TransferSrc; } - SH_RENDER_API void CopyPass::Record(CommandBuffer& cmd, const IRenderContext& ctx, const RenderTarget& renderData) + SH_RENDER_API void CopyPass::Record(CommandBuffer& cmd, const IRenderContext& ctx, const RenderData& renderData) { if (cpyDatas.empty()) return; - RenderTarget data{}; - data.frameIndex = 0; - data.camera = nullptr; - data.target = nullptr; - data.drawables = nullptr; - - ScriptableRenderPass::Record(cmd, ctx, data); + for (const CopyData& cpyData : cpyDatas) + cmd.Blit(*cpyData.src, cpyData.x, cpyData.y, *cpyData.dst); cpyDatas.clear(); } - SH_RENDER_API auto CopyPass::BuildDrawList(const RenderTarget& renderData) -> DrawList - { - DrawList list{}; - list.drawCall.push_back( - [cpyDatas = cpyDatas](CommandBuffer& cmd) - { - for (auto& cpyData : cpyDatas) - cmd.Blit(*cpyData.src, cpyData.x, cpyData.y, *cpyData.dst); - } - ); - return list; - } SH_RENDER_API void CopyPass::EnqueCopyImagePixelToBuffer(RenderTexture& rt, int x, int y, IBuffer& buffer) { diff --git a/src/Render/RenderPass/DepthPass.cpp b/src/Render/RenderPass/DepthPass.cpp new file mode 100644 index 00000000..e0b2e1ef --- /dev/null +++ b/src/Render/RenderPass/DepthPass.cpp @@ -0,0 +1,32 @@ +#include "RenderPass/DepthPass.h" +#include "CommandBuffer.h" +#include "IRenderContext.h" + +namespace sh::render +{ + DepthPass::DepthPass() : + ScriptableRenderPass(core::Name{ "DepthPass" }, RenderQueue::BeforeRendering) + { + } + + SH_RENDER_API void DepthPass::Configure(const RenderData& renderData) + { + ScriptableRenderPass::Configure(renderData); + } + SH_RENDER_API void DepthPass::Record(CommandBuffer& cmd, const IRenderContext& ctx, const RenderData& renderData) + { + cmd.SetRenderData(renderData, true, true, true, true); + + if (renderData.GetDrawablesPtr() == nullptr) + return; + + std::size_t viewerIdx = 0; + for (const RenderViewer& viewer : renderData.renderViewers) + { + SetViewportScissor(cmd, ctx, viewer); + for (const RenderBatch& batch : renderBatches) + cmd.DrawMeshBatch(batch.drawables, passName, viewerIdx); + ++viewerIdx; + } + } +}//namespace diff --git a/src/Render/RenderPass/SSAOPass.cpp b/src/Render/RenderPass/SSAOPass.cpp new file mode 100644 index 00000000..b8d6d2e1 --- /dev/null +++ b/src/Render/RenderPass/SSAOPass.cpp @@ -0,0 +1,105 @@ +#include "RenderPass/SSAOPass.h" +#include "RenderTexture.h" +#include "CommandBuffer.h" +#include "Mesh.h" +#include "Material.h" +#include "Drawable.h" +#include "IRenderContext.h" + +#include "Core/SObject.h" +#include "Core/GarbageCollection.h" + +namespace sh::render +{ + SSAOPass::SSAOPass(const IRenderContext& ctx) : + ScriptableRenderPass(core::Name{ "SSAOPass" }, RenderQueue::AfterOpaque), + ctx(ctx) + { + resource.plane = core::SObject::Create(); + + resource.plane->SetVertex({ + {{-1.0f, -1.0f, 0.0f}, {0.0f, 1.0f}, {0.f, 0.f, 0.f}}, + {{ 1.0f, -1.0f, 0.0f}, {1.0f, 1.0f}, {0.f, 0.f, 0.f}}, + {{ 1.0f, 1.0f, 0.0f}, {1.0f, 0.0f}, {0.f, 0.f, 0.f}}, + {{-1.0f, 1.0f, 0.0f}, {0.0f, 0.0f}, {0.f, 0.f, 0.f}}, + }); + resource.plane->SetIndices({ 0, 1, 2, 2, 3, 0 }); + resource.plane->Build(ctx); + + onMatDestroy.SetCallback( + [this](const core::SObject* objPtr) + { + resource.mat = nullptr; + if (resource.drawable != nullptr) + resource.drawable->Destroy(); + resource.drawable = nullptr; + } + ); + } + SSAOPass::~SSAOPass() + { + core::GarbageCollection& gc = *core::GarbageCollection::GetInstance(); + gc.ForceDelete(resource.plane); + gc.ForceDelete(resource.drawable); + resource.plane = nullptr; + resource.drawable = nullptr; + resource.mat = nullptr; + } + + SH_RENDER_API void SSAOPass::SetMaterial(Material& mat) + { + if (resource.mat == &mat || mat.IsPendingKill()) + return; + resource.mat = &mat; + resource.mat->onDestroy.Register(onMatDestroy); + if (resource.drawable != nullptr) + resource.drawable->SetMaterial(*resource.mat); + } + + void SSAOPass::EnsureDrawable() + { + if (resource.drawable != nullptr || resource.mat == nullptr) + return; + resource.drawable = core::SObject::Create(*resource.mat, *resource.plane); + resource.drawable->Build(ctx); + } + + SH_RENDER_API void SSAOPass::Configure(const RenderData& renderData) + { + renderTextures.clear(); + + if (!core::IsValid(resource.mat)) + return; + + EnsureDrawable(); + if (resource.drawable == nullptr || !core::IsValid(resource.drawable->GetMesh()) || !core::IsValid(resource.drawable->GetMaterial())) + return; + + SetRenderTargetImageUsages(renderData); + SetImageUsages(*resource.mat); + } + SH_RENDER_API void SSAOPass::Record(CommandBuffer& cmd, const IRenderContext& ctx, const RenderData& renderData) + { + if (!core::IsValid(resource.mat) || !core::IsValid(resource.drawable)) + return; + localRenderData.SetRenderTargets(renderData.GetRenderTargets()); + localRenderData.renderViewers = renderData.renderViewers; + + cmd.SetRenderData(renderData, true, false, true, false); + + std::size_t viewerIdx = 0; + for (const RenderViewer& viewer : localRenderData.renderViewers) + { + SetViewportScissor(cmd, ctx, viewer); + cmd.DrawMesh(*resource.drawable, passName, viewerIdx); + ++viewerIdx; + } + } + + SH_RENDER_API void SSAOPass::Resource::PushReferenceObjects(core::GarbageCollection& gc) + { + gc.PushReferenceObject(plane); + gc.PushReferenceObject(drawable); + gc.PushReferenceObject(mat); + } +}//namespace diff --git a/src/Render/RenderPass/TransparentPass.cpp b/src/Render/RenderPass/TransparentPass.cpp index 834990fb..4fb05ad7 100644 --- a/src/Render/RenderPass/TransparentPass.cpp +++ b/src/Render/RenderPass/TransparentPass.cpp @@ -1,6 +1,6 @@ #include "RenderPass/TransparentPass.h" -#include "Camera.h" #include "Drawable.h" +#include "CommandBuffer.h" #define GLM_ENABLE_EXPERIMENTAL #include "glm/gtx/norm.hpp" @@ -12,41 +12,50 @@ namespace sh::render ScriptableRenderPass(core::Name(name), renderQueue) { } - SH_RENDER_API auto TransparentPass::BuildDrawList(const RenderTarget& renderData) -> DrawList + + SH_RENDER_API void TransparentPass::Configure(const RenderData& renderData) + { + SetImageUsages(renderData); + } + SH_RENDER_API void TransparentPass::Record(CommandBuffer& cmd, const IRenderContext& ctx, const RenderData& renderTarget) { - DrawList list{}; - list.renderData = std::vector{}; - list.bClearColor = false; - list.bClearDepth = false; - if (renderData.drawables == nullptr) - return list; + cmd.SetRenderData(renderTarget, false, false, true, false); - for (auto drawable : *renderData.drawables) - { - if (!core::IsValid(drawable) || !drawable->CheckAssetValid() || drawable->GetMaterial()->GetShader()->GetShaderPasses(passName) == nullptr) - continue; + if (renderTarget.GetDrawablesPtr() == nullptr) + return; - DrawList::RenderItem item{}; + items.clear(); + for (const Drawable* drawable : *renderTarget.GetDrawablesPtr()) + { + RenderItem item{}; item.material = drawable->GetMaterial(); item.topology = drawable->GetTopology(core::ThreadType::Render); item.drawable = drawable; - - std::get>(list.renderData).push_back(item); + items.push_back(item); } - const auto& camPos = renderData.camera->GetPos(core::ThreadType::Render); - const auto& camTo = renderData.camera->GetLookPos(core::ThreadType::Render); - const glm::vec3 to = glm::normalize(camTo - camPos); - std::stable_sort(std::get<1>(list.renderData).begin(), std::get<1>(list.renderData).end(), - [&camPos, &to](const DrawList::RenderItem& left, const DrawList::RenderItem& right) -> bool - { - const glm::vec3 posLeft = left.drawable->GetModelMatrix(core::ThreadType::Render)[3]; - const glm::vec3 posRight = right.drawable->GetModelMatrix(core::ThreadType::Render)[3]; - const float leftLen = glm::dot(to, posLeft - camPos); - const float rightLen = glm::dot(to, posRight - camPos); - return leftLen > rightLen; - } - ); - return list; + std::size_t idx = 0; + for (const RenderViewer& viewer : renderTarget.renderViewers) + { + cmd.SetViewport(viewer.viewportRect.x, viewer.viewportRect.y, viewer.viewportRect.z, viewer.viewportRect.w); + cmd.SetScissor(viewer.viewportScissor.x, viewer.viewportScissor.y, viewer.viewportScissor.z, viewer.viewportScissor.w); + const glm::vec3& camPos = viewer.pos; + const glm::vec3& camTo = viewer.to; + const glm::vec3 to = glm::normalize(camTo - camPos); + std::stable_sort(items.begin(), items.end(), + [&camPos, &to](const RenderItem& left, const RenderItem& right) -> bool + { + const glm::vec3 posLeft = left.drawable->GetModelMatrix(core::ThreadType::Render)[3]; + const glm::vec3 posRight = right.drawable->GetModelMatrix(core::ThreadType::Render)[3]; + const float leftLen = glm::dot(to, posLeft - camPos); + const float rightLen = glm::dot(to, posRight - camPos); + return leftLen > rightLen; + } + ); + + for (const RenderItem& item : items) + cmd.DrawMesh(*item.drawable, passName, idx); + ++idx; + } } }//namespace \ No newline at end of file diff --git a/src/Render/RenderTarget.cpp b/src/Render/RenderTarget.cpp deleted file mode 100644 index 16919920..00000000 --- a/src/Render/RenderTarget.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "RenderTarget.h" \ No newline at end of file diff --git a/src/Render/RenderTexture.cpp b/src/Render/RenderTexture.cpp index d721eee2..c4fa99d3 100644 --- a/src/Render/RenderTexture.cpp +++ b/src/Render/RenderTexture.cpp @@ -2,13 +2,16 @@ #include "VulkanImageBuffer.h" #include "IRenderContext.h" +#include "Core/Logger.h" + namespace sh::render { - RenderTexture::RenderTexture(const RenderTargetLayout& layout) : - Texture(layout.format, 1024, 1024, false), + RenderTexture::RenderTexture(TextureFormat colorFormat, TextureFormat depthFormat, bool bMSAA) : + Texture(colorFormat, 1024, 1024, false), width(1024), height(1024), - layout(layout) + depthFormat(depthFormat), + bMSAA(bMSAA) { SetAnisoLevel(0); } @@ -16,12 +19,11 @@ namespace sh::render Texture(std::move(other)), width(other.width), height(other.height), - layout(other.layout) - { - } - RenderTexture::~RenderTexture() + depthFormat(other.depthFormat), + bMSAA(other.bMSAA) { } + RenderTexture::~RenderTexture() = default; SH_RENDER_API void RenderTexture::Build(const IRenderContext& context) { @@ -59,36 +61,34 @@ namespace sh::render bChangeSize = true; SyncDirty(); } - SH_RENDER_API auto RenderTexture::GetSize() const -> glm::vec2 - { - return { width, height }; - } - SH_RENDER_API void RenderTexture::ChangeUsage(ImageUsage newUsage) - { - usage = newUsage; - } - void RenderTexture::CreateBuffers() { assert(context->GetRenderAPIType() == RenderAPI::Vulkan); - if (textureBuffer == nullptr) - { - if (context->GetRenderAPIType() == RenderAPI::Vulkan) - textureBuffer = std::make_unique(); - } + ITextureBuffer::CreateInfo ci{}; ci.width = width; ci.height = height; - ci.format = GetTextureFormat(); + ci.format = IsDepthTexture() ? depthFormat : GetTextureFormat(); ci.aniso = 0; ci.filtering = 1; ci.mipLevel = 1; ci.bMSAAImg = false; ci.bRenderTarget = true; + usage = ResourceUsage::Undefined; + + if (IsDepthTexture()) + { + CreateDepthBuffer(ci); + return; + } + + if (textureBuffer == nullptr) + if (context->GetRenderAPIType() == RenderAPI::Vulkan) + textureBuffer = std::make_unique(); textureBuffer->Create(*context, ci); - if (layout.bUseMSAA) + if (bMSAA) { ci.bMSAAImg = true; if (msaaBuffer == nullptr) @@ -98,20 +98,38 @@ namespace sh::render } msaaBuffer->Create(*context, ci); } - - if (layout.depthFormat == TextureFormat::D32S8 || - layout.depthFormat == TextureFormat::D24S8 || - layout.depthFormat == TextureFormat::D16S8) + if (depthFormat != TextureFormat::None) + { + ci.format = depthFormat; + CreateDepthBuffer(ci); + } + } + void RenderTexture::CreateDepthBuffer(const ITextureBuffer::CreateInfo& ci) + { + const bool bValidDepthFormat = + depthFormat == TextureFormat::D32 || + depthFormat == TextureFormat::D16 || + depthFormat == TextureFormat::D32S8 || + depthFormat == TextureFormat::D24S8 || + depthFormat == TextureFormat::D16S8; + if (!bValidDepthFormat) + { + SH_ERROR_FORMAT("Invalid depth foramt!: {}", TextureFormatToString(ci.format)); + return; + } + if (IsDepthTexture()) + { + if (textureBuffer == nullptr) + if (context->GetRenderAPIType() == RenderAPI::Vulkan) + textureBuffer = std::make_unique(); + textureBuffer->Create(*context, ci); + } + else { if (depthBuffer == nullptr) - { if (context->GetRenderAPIType() == RenderAPI::Vulkan) depthBuffer = std::make_unique(); - } - ci.format = layout.depthFormat; depthBuffer->Create(*context, ci); } - - usage = ImageUsage::Undefined; } }//namespace \ No newline at end of file diff --git a/src/Render/Renderer.cpp b/src/Render/Renderer.cpp index f79cdb70..900cd05d 100644 --- a/src/Render/Renderer.cpp +++ b/src/Render/Renderer.cpp @@ -1,5 +1,6 @@ #include "Renderer.h" #include "Drawable.h" +#include "RenderDataManager.h" #include "Core/ThreadSyncManager.h" @@ -12,9 +13,7 @@ namespace sh::render drawcall({ 0, 0 }) { } - Renderer::~Renderer() - { - } + Renderer::~Renderer() = default; SH_RENDER_API void Renderer::SyncDirty() { @@ -36,18 +35,27 @@ namespace sh::render SH_RENDER_API bool Renderer::Init(sh::window::Window& win) { window = &win; + IRenderContext* ctx = GetContext(); + assert(ctx != nullptr); + if (ctx == nullptr) + { + SH_ERROR("Create context first!"); + return false; + } return true; } - SH_RENDER_API void Renderer::Clear() + SH_RENDER_API void Renderer::Reset() { renderer = nullptr; renderCommands.Clear(); drawables.clear(); drawcall = core::SyncArray{}; - cameras.clear(); - - drawCalls.clear(); + IRenderThrMethod::ClearRenderViews(GetContext()->GetRenderDataManager()); + } + SH_RENDER_API void Renderer::Clear() + { + Reset(); } SH_RENDER_API void Renderer::Render() @@ -58,6 +66,7 @@ namespace sh::render drawables.clear(); DrainRenderCommands(); + IRenderThrMethod::UploadToGPU(GetContext()->GetRenderDataManager()); } SH_RENDER_API void Renderer::Pause(bool b) { @@ -68,13 +77,12 @@ namespace sh::render { if (!core::IsValid(drawable)) return; - - RenderCommand cmd{}; - cmd.type = RenderCommand::Type::PushDrawable; - cmd.data = drawable; - renderCommands.Push(std::move(cmd)); + renderCommands.Push(RenderCommand{ drawable }); + } + SH_RENDER_API void Renderer::PushRenderData(const RenderData& renderData) + { + GetContext()->GetRenderDataManager().PushRenderData(renderData); } - SH_RENDER_API auto Renderer::GetWindow() const -> sh::window::Window& { assert(window); @@ -88,32 +96,8 @@ namespace sh::render SH_RENDER_API void Renderer::SetScriptableRenderer(ScriptableRenderer& renderer) { - this->renderer = &renderer; - } - SH_RENDER_API void Renderer::AddCamera(const Camera& camera) - { - RenderCommand cmd{}; - cmd.type = RenderCommand::Type::AddCamera; - cmd.data = &camera; - renderCommands.Push(std::move(cmd)); + renderCommands.Push(RenderCommand{ &renderer }); } - SH_RENDER_API void Renderer::RemoveCamera(const Camera& camera) - { - RenderCommand cmd{}; - cmd.type = RenderCommand::Type::RemoveCamera; - cmd.data = &camera; - renderCommands.Push(std::move(cmd)); - } - - SH_RENDER_API auto Renderer::GetDrawCall(core::ThreadType thread) const -> uint32_t - { - return drawcall[static_cast(thread)]; - } - SH_RENDER_API auto Renderer::GetThreadId() const -> std::thread::id - { - return threadId; - } - SH_RENDER_API void Renderer::SetDrawCallCount(uint32_t drawcall) { this->drawcall[static_cast(core::ThreadType::Render)] = drawcall; @@ -126,35 +110,19 @@ namespace sh::render renderCommands.Drain( [this](RenderCommand& cmd) { - switch (cmd.type) - { - case RenderCommand::Type::PushDrawable: + if (std::holds_alternative>(cmd.data)) { - Drawable* drawable = std::get<0>(cmd.data).Get(); + Drawable* const drawable = std::get>(cmd.data).Get(); if (!core::IsValid(drawable) || !drawable->CheckAssetValid()) return; drawables.push_back(drawable); - break; - } - case RenderCommand::Type::AddCamera: - { - const Camera* camera = std::get<1>(cmd.data); - if (camera == nullptr) - return; - auto [it, inserted] = cameras.insert(camera); - if (inserted) - OnCameraAdded(camera); - break; } - case RenderCommand::Type::RemoveCamera: + else { - const Camera* camera = std::get<1>(cmd.data); - if (camera == nullptr) + ScriptableRenderer* sr = std::get(cmd.data); + if (sr == nullptr) return; - if (cameras.erase(camera) != 0) - OnCameraRemoved(camera); - break; - } + renderer = sr; } } ); diff --git a/src/Render/ScriptableRenderPass.cpp b/src/Render/ScriptableRenderPass.cpp index 28b526e2..2707a220 100644 --- a/src/Render/ScriptableRenderPass.cpp +++ b/src/Render/ScriptableRenderPass.cpp @@ -2,8 +2,10 @@ #include "IRenderContext.h" #include "Drawable.h" #include "RenderTexture.h" +#include "MaterialData.h" #include "Core/Logger.h" +#include "Core/Util.h" namespace sh::render { @@ -12,112 +14,146 @@ namespace sh::render renderQueue(renderQueue) { } - SH_RENDER_API void ScriptableRenderPass::Configure(const RenderTarget& renderData) + SH_RENDER_API auto ScriptableRenderPass::CreateRenderBatch(const core::Name& passName, const std::vector& drawables) -> std::vector { - drawList = BuildDrawList(renderData); - CollectRenderImages(renderData, drawList); - } - SH_RENDER_API void ScriptableRenderPass::Record(CommandBuffer& cmd, const IRenderContext& ctx, const RenderTarget& renderData) - { - ctx.GetRenderImpl().RecordCommand(cmd, passName, renderData, drawList, bStoreImage); - } - SH_RENDER_API auto ScriptableRenderPass::BuildDrawList(const RenderTarget& renderData) -> DrawList - { - DrawList list{}; - list.renderData = std::vector{}; - if (renderData.drawables == nullptr) - return list; + std::vector groups; + struct GroupKey + { + const Material* mat; + Mesh::Topology topology; + bool bSkinned; - for (auto drawable : *renderData.drawables) + bool operator==(const GroupKey& other) const noexcept { return mat == other.mat && topology == other.topology && bSkinned == other.bSkinned; } + }; + struct GroupKeyHash { - if (!core::IsValid(drawable) || !drawable->CheckAssetValid() || drawable->GetMaterial()->GetShader()->GetShaderPasses(passName) == nullptr) - continue; + std::size_t operator()(const GroupKey& k) const noexcept + { + const std::size_t hash0 = core::Util::CombineHash(std::hash{}(k.mat), std::hash{}(static_cast(k.topology))); + const std::size_t hash1 = core::Util::CombineHash(hash0, std::hash{}(k.bSkinned)); + return hash1; + } + }; + std::unordered_map groupIndex; + groupIndex.reserve(drawables.size()); - const Material* mat = drawable->GetMaterial(); - Mesh::Topology topology = drawable->GetTopology(core::ThreadType::Render); + std::map materialFilter; + for (const Drawable* drawable : drawables) + { + if (!core::IsValid(drawable) || !drawable->CheckAssetValid()) + continue; - DrawList::RenderGroup* renderGroup = nullptr; - for (auto& group : std::get<0>(list.renderData)) + const Material* const mat = drawable->GetMaterial(); + if (auto it = materialFilter.find(mat); it == materialFilter.end()) { - if (group.material == mat && group.topology == topology) + if (mat->GetShader()->GetShaderPasses(passName) == nullptr) { - renderGroup = &group; - break; + materialFilter.insert({ mat, false }); + continue; } + materialFilter.insert({ mat, true }); } - if (renderGroup == nullptr) + else + { + if (!it->second) + continue; + } + + Mesh::Topology topology = drawable->GetTopology(core::ThreadType::Render); + const bool bSkinned = drawable->IsSkinnedMesh(); + + GroupKey key{ mat, topology, bSkinned }; + auto it = groupIndex.find(key); + if (it == groupIndex.end()) { - DrawList::RenderGroup group{}; + RenderBatch group{}; group.material = mat; group.topology = topology; + group.bSkinned = bSkinned; group.drawables.push_back(drawable); - std::get<0>(list.renderData).push_back(std::move(group)); + groupIndex.emplace(key, groups.size()); + groups.push_back(std::move(group)); } else { - renderGroup->drawables.push_back(drawable); + groups[it->second].drawables.push_back(drawable); } } - return list; + return groups; } - SH_RENDER_API void ScriptableRenderPass::EmitBarrier(CommandBuffer& cmd, const IRenderContext& ctx, const std::vector& barriers) const + SH_RENDER_API void ScriptableRenderPass::Configure(const RenderData& renderData) { - ctx.GetRenderImpl().EmitBarrier(cmd, barriers); - } - SH_RENDER_API auto ScriptableRenderPass::GetRenderCallCount() const -> uint32_t - { - uint32_t renderCallCount = drawList.drawCall.size(); - if (drawList.renderData.index() == 0) + if (renderData.GetDrawablesPtr() != nullptr) { - const auto& renderGroups = std::get<0>(drawList.renderData); - for (const auto& renderGroup : renderGroups) - { - renderCallCount += renderGroup.drawables.size(); - } + renderBatches = CreateRenderBatch(passName, *renderData.GetDrawablesPtr()); } - else + SetImageUsages(renderData); + } + SH_RENDER_API void ScriptableRenderPass::Record(CommandBuffer& cmd, const IRenderContext& ctx, const RenderData& renderData) + { + cmd.SetRenderData(renderData, true, true, true, true); + if (renderData.GetDrawablesPtr() == nullptr) + return; + std::size_t viewerIdx = 0; + for (const RenderViewer& viewer : renderData.renderViewers) { - const auto& renderItems = std::get<1>(drawList.renderData); - renderCallCount += renderItems.size(); + SetViewportScissor(cmd, ctx, viewer); + for (const RenderBatch& batch : renderBatches) + cmd.DrawMeshBatch(batch.drawables, passName, viewerIdx); + ++viewerIdx; } - - return renderCallCount; + //ctx.GetRenderImpl().RecordCommand(cmd, passName, renderTarget, drawList, bStoreImage); } - - void ScriptableRenderPass::CollectRenderImages(const RenderTarget& renderData, const DrawList& drawList) + SH_RENDER_API void ScriptableRenderPass::SetImageUsages(const RenderData& renderData) { renderTextures.clear(); - renderTextures[renderData.target] = ImageUsage::ColorAttachment; - const auto collectFn = - [this](const Material* mat) + SetRenderTargetImageUsages(renderData); + + if (renderData.GetDrawablesPtr() != nullptr) + SetImageUsages(*renderData.GetDrawablesPtr()); + } + SH_RENDER_API void ScriptableRenderPass::SetRenderTargetImageUsages(const RenderData& renderData) + { + for (const RenderTexture* target : renderData.GetRenderTargets()) + { + if (target == nullptr) { - for (auto& [name, rt] : mat->GetCachedRenderTextures()) - { - auto it = renderTextures.find(rt); - if (it != renderTextures.end()) - { - if (it->second == ImageUsage::ColorAttachment) - { - SH_ERROR_FORMAT("RenderTexture {} is used as ColorAttachment", it->first->GetName().ToString()); - continue; - } - } - else - renderTextures[rt] = ImageUsage::SampledRead; - } - }; + renderTextures[nullptr] = ResourceUsage::ColorAttachment; + continue; + } - if (drawList.renderData.index() == 0) + renderTextures[target] = target->IsDepthTexture() ? ResourceUsage::DepthStencilAttachment : ResourceUsage::ColorAttachment; + } + } + SH_RENDER_API void ScriptableRenderPass::SetImageUsages(const std::vector& drawables) + { + for (const Drawable* drawable : drawables) { - for (const auto& group : std::get<0>(drawList.renderData)) - collectFn(group.material); + if (drawable == nullptr || !drawable->CheckAssetValid()) + continue; + + for (const MaterialData::CachedRT& cachedRT : IRenderThrMethod::GetCachedRTs(drawable->GetMaterialData())) + { + if (cachedRT.rt.IsValid() && cachedRT.pass->GetLightingPassName() == passName) + renderTextures[cachedRT.rt.Get()] = cachedRT.rt->IsDepthTexture() ? ResourceUsage::DepthStencilSampledRead : ResourceUsage::SampledRead; + } + const Material* const mat = drawable->GetMaterial(); + SetImageUsages(*mat); } - else + } + SH_RENDER_API void ScriptableRenderPass::SetImageUsages(const Material& mat) + { + for (const MaterialData::CachedRT& cachedRT : IRenderThrMethod::GetCachedRTs(mat.GetMaterialData())) { - for (const auto& item : std::get<1>(drawList.renderData)) - collectFn(item.material); + if (cachedRT.rt.IsValid() && cachedRT.pass->GetLightingPassName() == passName) + renderTextures[cachedRT.rt.Get()] = cachedRT.rt->IsDepthTexture() ? ResourceUsage::DepthStencilSampledRead : ResourceUsage::SampledRead; } } -}//namespace \ No newline at end of file + SH_RENDER_API void ScriptableRenderPass::SetViewportScissor(CommandBuffer& cmd, const IRenderContext& ctx, const RenderViewer& renderViewer) + { + cmd.SetViewport(renderViewer.viewportRect.x, renderViewer.viewportRect.y, renderViewer.viewportRect.z, renderViewer.viewportRect.w); + cmd.SetScissor(renderViewer.viewportScissor.x, renderViewer.viewportScissor.y, renderViewer.viewportScissor.z, renderViewer.viewportScissor.w); + } +}//namespace diff --git a/src/Render/ScriptableRenderer.cpp b/src/Render/ScriptableRenderer.cpp index 06d70e79..33e7d247 100644 --- a/src/Render/ScriptableRenderer.cpp +++ b/src/Render/ScriptableRenderer.cpp @@ -1,20 +1,24 @@ #include "ScriptableRenderer.h" #include "RenderTexture.h" #include "BufferFactory.h" +#include "ComputeShader.h" +#include "RenderPass/CopyPass.h" #include "Core/ThreadPool.h" #include "Core/ThreadSyncManager.h" +#include "Core/Logger.h" #include namespace sh::render { - std::vector ScriptableRenderer::swapChainStates{}; + std::vector ScriptableRenderer::swapChainStates{}; ScriptableRenderer::ScriptableRenderer(IRenderContext& ctx) : ctx(ctx) { cpyPass = std::make_unique(); } + ScriptableRenderer::~ScriptableRenderer() = default; SH_RENDER_API auto ScriptableRenderer::AddRenderPass(const core::Name& passName, RenderQueue renderQueue) -> ScriptableRenderPass& { @@ -33,6 +37,31 @@ namespace sh::render return syncDatas.emplace_back(std::move(data)).promise.get_future(); } + SH_RENDER_API void ScriptableRenderer::Dispatch(const ComputeShader& computeShader, uint32_t x, uint32_t y, uint32_t z) + { + CommandBuffer* const cmdPtr = ctx.AllocateCommandBuffer(true); + if (cmdPtr == nullptr) + { + SH_ERROR("Failed to allocate command buffer!"); + return; + } + cmdPtr->Begin(true); + cmdPtr->Dispatch(computeShader, x, y, z); + cmdPtr->End(); + ctx.SubmitCommand(*cmdPtr); + ctx.DeallocateCommandBuffer(*cmdPtr); + } + + SH_RENDER_API auto ScriptableRenderer::HasPass(const core::Name& passName) const -> bool + { + for (const std::unique_ptr& pass : allPasses) + { + if (pass->passName == passName) + return true; + } + return false; + } + SH_RENDER_API void ScriptableRenderer::SyncDirty() { if (bSyncDirty) @@ -45,7 +74,9 @@ namespace sh::render { for (auto& data : syncDatas) { - std::unique_ptr buffer = BufferFactory::Create(ctx, sizeof(uint8_t) * 4, true); + BufferFactory::CreateInfo ci{}; + ci.size = sizeof(uint8_t) * 4; + std::unique_ptr buffer = BufferFactory::Create(ctx, ci); IRenderThrMethod::EnqueCopyImagePixelToBuffer(*cpyPass, *data.src, data.x, data.y, *buffer.get()); pendingReadbacks.push_back(PendingReadback{ std::move(buffer), std::move(data.promise) }); @@ -54,12 +85,12 @@ namespace sh::render bSyncDirty = false; } - SH_RENDER_API void ScriptableRenderer::Setup(const RenderTarget& data) + SH_RENDER_API void ScriptableRenderer::Setup(const RenderData& data) { - for (auto& pass : allPasses) + for (const std::unique_ptr& pass : allPasses) activePasses.push_back(pass.get()); } - SH_RENDER_API void ScriptableRenderer::Execute(const RenderTarget& data) + SH_RENDER_API void ScriptableRenderer::Execute(const RenderData& data) { renderCallCount = 0; std::stable_sort(activePasses.begin(), activePasses.end(), @@ -69,7 +100,7 @@ namespace sh::render } ); - for (auto& pass : activePasses) + for (ScriptableRenderPass* pass : activePasses) { IRenderThrMethod::Configure(*pass, data); renderCallCount += IRenderThrMethod::GetRenderCallCount(*pass); @@ -80,7 +111,7 @@ namespace sh::render for (int i = 0; i < activePasses.size(); ++i) { - barriers[i] = BuildBarrierInfo(*activePasses[i], data.frameIndex); + barriers[i] = BuildBarrierInfo(*activePasses[i], data.GetFrameIdx()); } std::vector>> futurePasses; @@ -92,9 +123,9 @@ namespace sh::render futurePasses.push_back(core::ThreadPool::GetInstance()->AddTask( [idx, pass, &ctx = ctx, &data, &barriers]() -> std::pair { - CommandBuffer* cmd = ctx.AllocateCommandBuffer(); + CommandBuffer* cmd = ctx.AllocateCommandBuffer(false); cmd->Begin(true); - IRenderThrMethod::EmitBarrier(*pass, *cmd, ctx, barriers[idx]); + cmd->EmitBarrier(barriers[idx]); IRenderThrMethod::Record(*pass, *cmd, ctx, data); cmd->End(); return { pass, cmd }; @@ -107,39 +138,41 @@ namespace sh::render { auto [pass, cmd] = futurePass.get(); - submittedCmds.push_back({ *pass, *cmd }); + recordedCmds.push_back({ *pass, *cmd }); } activePasses.clear(); } SH_RENDER_API void ScriptableRenderer::ExecuteTransfer(uint32_t imgIdx) { - RenderTarget target{}; - target.camera = nullptr; - target.target = nullptr; - target.drawables = nullptr; - target.frameIndex = imgIdx; + RenderData data{}; + IRenderThrMethod::SetFrameIndex(data, imgIdx); + IRenderThrMethod::SetDrawablesPtr(data, nullptr); - IRenderThrMethod::Configure(*cpyPass, target); + IRenderThrMethod::Configure(*cpyPass, data); std::vector preBarriers = BuildBarrierInfo(*cpyPass, imgIdx); - std::vector postBarriers = { BarrierInfo{imgIdx, swapChainStates[imgIdx], ImageUsage::Present} }; - swapChainStates[imgIdx] = ImageUsage::Present; - - RenderTarget rt{}; - rt.frameIndex = imgIdx; - rt.camera = nullptr; - rt.target = nullptr; - rt.drawables = nullptr; + std::vector postBarriers; + if (swapChainStates.size() > imgIdx) + { + postBarriers = { BarrierInfo{imgIdx, swapChainStates[imgIdx], ResourceUsage::Present} }; + swapChainStates[imgIdx] = ResourceUsage::Present; + } + else + { + swapChainStates.resize(imgIdx + 1, ResourceUsage::Undefined); + postBarriers = { BarrierInfo{imgIdx, ResourceUsage::Undefined, ResourceUsage::Present} }; + swapChainStates[imgIdx] = ResourceUsage::Present; + } - CommandBuffer* cmd = ctx.AllocateCommandBuffer(); + CommandBuffer* cmd = ctx.AllocateCommandBuffer(false); cmd->Begin(true); - IRenderThrMethod::EmitBarrier(*cpyPass, *cmd, ctx, preBarriers); - IRenderThrMethod::Record(*cpyPass, *cmd, ctx, rt); - IRenderThrMethod::EmitBarrier(*cpyPass, *cmd, ctx, postBarriers); + cmd->EmitBarrier(preBarriers); + IRenderThrMethod::Record(*cpyPass, *cmd, ctx, data); + cmd->EmitBarrier(postBarriers); cmd->End(); - submittedCmds.push_back({ *cpyPass, *cmd}); + recordedCmds.push_back({ *cpyPass, *cmd}); } SH_RENDER_API void ScriptableRenderer::CallReadbacks() { @@ -154,15 +187,15 @@ namespace sh::render } SH_RENDER_API void ScriptableRenderer::ResetSubmittedCommands(IRenderContext& ctx) { - for (auto& submittedCmd : submittedCmds) - ctx.DeallocateCommandBuffer(submittedCmd.cmd); - submittedCmds.clear(); + for (RecordedCommand& recordedCmd : recordedCmds) + ctx.DeallocateCommandBuffer(recordedCmd.cmd); + recordedCmds.clear(); } SH_RENDER_API void ScriptableRenderer::ResetSwapChainStates() { for (auto& state : swapChainStates) - state = ImageUsage::Undefined; + state = ResourceUsage::Undefined; } auto ScriptableRenderer::BuildBarrierInfo(ScriptableRenderPass& pass, uint32_t imgIdx) -> std::vector @@ -177,7 +210,7 @@ namespace sh::render { barrierInfo.target = imgIdx; if (swapChainStates.size() <= imgIdx) - swapChainStates.resize(imgIdx + 1, ImageUsage::Undefined); + swapChainStates.resize(imgIdx + 1, ResourceUsage::Undefined); barrierInfo.lastUsage = swapChainStates[imgIdx]; swapChainStates[imgIdx] = usage; diff --git a/src/Render/Shader.cpp b/src/Render/Shader.cpp index 6e22c7c2..9577edbc 100644 --- a/src/Render/Shader.cpp +++ b/src/Render/Shader.cpp @@ -1,17 +1,15 @@ #include "Shader.h" #include "ShaderCreateInfo.h" -#include - namespace sh::render { - Shader::Shader(ShaderCreateInfo&& shaderCreateInfo) + Shader::Shader(ShaderCreateInfo shaderCreateInfo) { SetName(shaderCreateInfo.shaderNode.shaderName); AddShaderPasses(std::move(shaderCreateInfo.passes)); - for (auto& prop : shaderCreateInfo.shaderNode.properties) + for (const ShaderAST::VariableNode& prop : shaderCreateInfo.shaderNode.properties) { const bool bLocal = prop.attribute == ShaderAST::VariableAttribute::Local ? true : false; switch (prop.type) @@ -41,40 +39,8 @@ namespace sh::render } } } - Shader::~Shader() - { - } - SH_RENDER_API auto Shader::GetShaderPasses(const core::Name& lightingPassName) const -> const std::vector>* - { - const LightingPassData* lightingPassData = GetLightingPass(lightingPassName); - if (lightingPassData == nullptr) - return nullptr; - return &lightingPassData->passes; - } - SH_RENDER_API auto Shader::GetAllShaderPass() const -> const std::vector& - { - return passDatas; - } - SH_RENDER_API auto Shader::GetProperties() const -> const std::unordered_map& - { - return properties; - } - SH_RENDER_API auto Shader::GetProperty(const std::string& name) const -> const PropertyInfo* - { - auto it = properties.find(name); - if (it == properties.end()) - return nullptr; - return &it->second; - } + Shader::~Shader() = default; - SH_RENDER_API auto Shader::GetShaderAST() const -> const ShaderAST::ShaderNode& - { - return shaderNode; - } - SH_RENDER_API auto Shader::IsUsingLight() const -> bool - { - return bUsingLight; - } SH_RENDER_API void Shader::OnDestroy() { for (ShaderPass* shaderPass : passes) @@ -85,20 +51,16 @@ namespace sh::render { core::Json mainJson = Super::Serialize(); - mainJson["shader"] = core::Json{}; core::Json& json = mainJson["shader"]; json["AST"] = shaderNode.Serialize(); - - std::set uniquePass; - for (const auto& [name, passVec] : passDatas) + core::Json& passesJson = json["passes"]; + for (const ShaderPass* pass : passes) { - for (ShaderPass& pass : passVec) - uniquePass.insert(&pass); + if (pass == nullptr) + continue; + passesJson.push_back(pass->Serialize()); } - for (const ShaderPass* pass : uniquePass) - json["passes"].push_back(pass->Serialize()); - return mainJson; } SH_RENDER_API void Shader::Deserialize(const core::Json& json) @@ -109,9 +71,33 @@ namespace sh::render return; const core::Json& shaderJson = json["shader"]; - if (shaderJson.contains("AST")) shaderNode.Deserialize(shaderJson["AST"]); + if (shaderJson.contains("passes")) + { + std::size_t idx = 0; + for (const core::Json& passJson : shaderJson["passes"]) + { + if (passes.size() >= idx) + break; + passes[idx++]->Deserialize(passJson); + } + } + } + + SH_RENDER_API auto Shader::GetShaderPasses(const core::Name& lightingPassName) const -> const std::vector>* + { + const LightingPassData* lightingPassData = GetLightingPass(lightingPassName); + if (lightingPassData == nullptr) + return nullptr; + return &lightingPassData->passes; + } + SH_RENDER_API auto Shader::GetProperty(const std::string& name) const -> const PropertyInfo* + { + auto it = properties.find(name); + if (it == properties.end()) + return nullptr; + return &it->second; } void Shader::Clear() @@ -125,15 +111,20 @@ namespace sh::render this->passes = std::move(passes); for (ShaderPass* pass : this->passes) { + if (pass == nullptr) + continue; if (pass->GetLightingBinding() != -1) bUsingLight = true; - const core::Name& lightingPassName = this->passes.back()->GetLightingPassName(); + if (pass->GetSkinBinding() != -1) + bUsingSkin = true; + + const core::Name& lightingPassName = pass->GetLightingPassName(); - LightingPassData* lightingPassData = GetLightingPass(lightingPassName); + LightingPassData* const lightingPassData = GetLightingPass(lightingPassName); if (lightingPassData == nullptr) { std::vector> v{}; - v.push_back(*this->passes.back()); + v.push_back(*pass); LightingPassData passData{ lightingPassName }; passData.passes = std::move(v); diff --git a/src/Render/ShaderAST.cpp b/src/Render/ShaderAST.cpp index 2013940c..10452ec0 100644 --- a/src/Render/ShaderAST.cpp +++ b/src/Render/ShaderAST.cpp @@ -2,28 +2,69 @@ namespace sh::render { - auto ShaderAST::VariableNode::Serialize() const -> core::Json + SH_RENDER_API auto ShaderAST::VariableNode::Serialize() const -> core::Json { core::Json json; json["type"] = static_cast(type); - json["size"] = size; + json["structType"] = structType; + json["arraySize"] = arraySize; json["name"] = name; json["default"] = defaultValue; json["attribute"] = static_cast(attribute); + json["dynamicArray"] = bDynamicArray; return json; } - - void ShaderAST::VariableNode::Deserialize(const core::Json& json) + SH_RENDER_API void ShaderAST::VariableNode::Deserialize(const core::Json& json) { type = static_cast(json.at("type").get()); - size = json.at("size").get(); + if (json.contains("structType")) + structType = json["structType"].get_ref(); + if (json.contains("arraySize")) + { + arraySize = json.at("arraySize").get(); + bDynamicArray = json.value("dynamicArray", false); + } name = json.at("name").get(); if (json.contains("default")) defaultValue = json["default"].get(); attribute = static_cast(json.at("attribute").get()); } + SH_RENDER_API auto ShaderAST::VariableNode::MakeDynamicArray(StructNode& structNode, std::string name) -> VariableNode + { + VariableNode v; + v.type = VariableType::Struct; + v.structType = structNode.name; + v.bDynamicArray = true; + v.name = std::move(name); + return v; + } - auto ShaderAST::LayoutNode::Serialize() const -> core::Json + SH_RENDER_API auto ShaderAST::StructNode::Serialize() const -> core::Json + { + core::Json json; + json["name"] = name; + core::Json& varsJson = json["vars"]; + for (const VariableNode& varNode : vars) + varsJson.push_back(varNode.Serialize()); + return json; + } + SH_RENDER_API void ShaderAST::StructNode::Deserialize(const core::Json& json) + { + if (json.contains("name")) + name = json["name"].get_ref(); + if (json.contains("vars")) + { + const core::Json& varsJson = json["vars"]; + vars.reserve(varsJson.size()); + for (const core::Json& varJson : varsJson) + { + VariableNode varNode; + varNode.Deserialize(varJson); + vars.push_back(std::move(varNode)); + } + } + } + SH_RENDER_API auto ShaderAST::LayoutNode::Serialize() const -> core::Json { core::Json json; json["binding"] = binding; @@ -31,21 +72,20 @@ namespace sh::render return json; } - void ShaderAST::LayoutNode::Deserialize(const core::Json& json) + SH_RENDER_API void ShaderAST::LayoutNode::Deserialize(const core::Json& json) { binding = json.at("binding").get(); var.Deserialize(json.at("var")); } - - auto ShaderAST::UBONode::Serialize() const -> core::Json + SH_RENDER_API auto ShaderAST::BufferNode::Serialize() const -> core::Json { core::Json json; + json["bufferType"] = static_cast(bufferType); + json["access"] = static_cast(access); + json["anonymous"] = bAnonymousInstance; json["set"] = set; json["binding"] = binding; json["name"] = name; - json["sampler"] = bSampler; - json["constant"] = bConstant; - core::Json varArray = core::Json::array(); for (const auto& v : vars) { @@ -54,14 +94,18 @@ namespace sh::render json["vars"] = std::move(varArray); return json; } - - void ShaderAST::UBONode::Deserialize(const core::Json& json) + SH_RENDER_API void ShaderAST::BufferNode::Deserialize(const core::Json& json) { set = json.at("set").get(); binding = json.at("binding").get(); name = json.at("name").get(); - bSampler = json.at("sampler").get(); - bConstant = json.at("constant").get(); + + if (json.contains("bufferType")) + bufferType = static_cast(json.at("bufferType").get()); + if (json.contains("access")) + access = static_cast(json.at("access").get()); + if (json.contains("anonymous")) + bAnonymousInstance = json.at("anonymous").get(); vars.clear(); for (const auto& v : json.at("vars")) @@ -71,12 +115,13 @@ namespace sh::render vars.push_back(std::move(var)); } } - - auto ShaderAST::StageNode::Serialize() const -> core::Json + SH_RENDER_API auto ShaderAST::StageNode::Serialize() const -> core::Json { core::Json json; json["type"] = static_cast(type); json["lightingBinding"] = lightingBinding; + json["skinBinding"] = skinBinding; + json["shadowMapBinding"] = shadowMapBinding; json["code"] = code; // in @@ -91,11 +136,16 @@ namespace sh::render outArray.push_back(o.Serialize()); json["out"] = std::move(outArray); + // structs + core::Json& structsJson = json["structs"]; + for (const StructNode& structNode : structs) + structsJson.push_back(structNode.Serialize()); + // uniforms - core::Json uboArray = core::Json::array(); - for (const auto& u : uniforms) - uboArray.push_back(u.Serialize()); - json["uniforms"] = std::move(uboArray); + core::Json bufferArray = core::Json::array(); + for (const auto& u : buffers) + bufferArray.push_back(u.Serialize()); + json["buffers"] = std::move(bufferArray); // declaration json["declaration"] = declaration; @@ -106,47 +156,81 @@ namespace sh::render return json; } - void ShaderAST::StageNode::Deserialize(const core::Json& json) + SH_RENDER_API void ShaderAST::StageNode::Deserialize(const core::Json& json) { type = static_cast(json.at("type").get()); lightingBinding = json.value("lightingBinding", -1); + skinBinding = json.value("skinBinding", -1); + shadowMapBinding = json.value("shadowMapBinding", -1); code = json.at("code").get(); // in in.clear(); - for (const auto& i : json.at("in")) + out.clear(); + if (json.contains("in")) { - LayoutNode l; - l.Deserialize(i); - in.push_back(std::move(l)); + for (const auto& i : json.at("in")) + { + LayoutNode l; + l.Deserialize(i); + in.push_back(std::move(l)); + } } // out out.clear(); - for (const auto& o : json.at("out")) + if (json.contains("out")) { - LayoutNode l; - l.Deserialize(o); - out.push_back(std::move(l)); + for (const auto& o : json.at("out")) + { + LayoutNode l; + l.Deserialize(o); + out.push_back(std::move(l)); + } + } + + // structs + structs.clear(); + if (json.contains("structs")) + { + for (const core::Json& structJson : json.at("structs")) + { + StructNode structNode; + structNode.Deserialize(structJson); + structs.push_back(std::move(structNode)); + } } // uniforms - uniforms.clear(); - for (const auto& u : json.at("uniforms")) + buffers.clear(); + if (json.contains("buffers")) { - UBONode ubo; - ubo.Deserialize(u); - uniforms.push_back(std::move(ubo)); + for (const auto& u : json.at("buffers")) + { + BufferNode ubo; + ubo.Deserialize(u); + buffers.push_back(std::move(ubo)); + } } // declaration - declaration = json.at("declaration").get>(); + if (json.contains("declaration")) + declaration = json.at("declaration").get>(); // functions - functions = json.at("functions").get>(); + if (json.contains("functions")) + functions = json.at("functions").get>(); } - - auto ShaderAST::PassNode::Serialize() const -> core::Json + SH_RENDER_API auto ShaderAST::StageNode::GetStructNode(const std::string& structName) const -> const StructNode* + { + for (const StructNode& structNode : structs) + { + if (structNode.name == structName) + return &structNode; + } + return nullptr; + } + SH_RENDER_API auto ShaderAST::PassNode::Serialize() const -> core::Json { core::Json json; json["name"] = name; @@ -172,8 +256,7 @@ namespace sh::render return json; } - - void ShaderAST::PassNode::Deserialize(const core::Json& json) + SH_RENDER_API void ShaderAST::PassNode::Deserialize(const core::Json& json) { name = json.at("name").get(); lightingPass = json.at("lightingPass").get(); @@ -206,22 +289,19 @@ namespace sh::render stages.push_back(std::move(stage)); } } - - auto ShaderAST::VersionNode::Serialize() const -> core::Json + SH_RENDER_API auto ShaderAST::VersionNode::Serialize() const -> core::Json { core::Json json; json["versionNumber"] = versionNumber; json["profile"] = profile; return json; } - - void ShaderAST::VersionNode::Deserialize(const core::Json& json) + SH_RENDER_API void ShaderAST::VersionNode::Deserialize(const core::Json& json) { versionNumber = json.at("versionNumber").get(); profile = json.at("profile").get(); } - - auto ShaderAST::ShaderNode::Serialize() const -> core::Json + SH_RENDER_API auto ShaderAST::ShaderNode::Serialize() const -> core::Json { core::Json json; json["version"] = version.Serialize(); @@ -241,8 +321,7 @@ namespace sh::render return json; } - - void ShaderAST::ShaderNode::Deserialize(const core::Json& json) + SH_RENDER_API void ShaderAST::ShaderNode::Deserialize(const core::Json& json) { version.Deserialize(json.at("version")); shaderName = json.at("shaderName").get(); @@ -263,5 +342,42 @@ namespace sh::render passes.push_back(std::move(pass)); } } + SH_RENDER_API auto ShaderAST::ComputeShaderNode::Serialize() const -> core::Json + { + core::Json json; + json["version"] = version.Serialize(); + json["shaderName"] = shaderName; + json["numthreadsX"] = numthreadsX; + json["numthreadsY"] = numthreadsY; + json["numthreadsZ"] = numthreadsZ; + json["code"] = code; + + core::Json bufferArray = core::Json::array(); + for (const auto& b : buffers) + bufferArray.push_back(b.Serialize()); + json["buffers"] = std::move(bufferArray); + + json["declaration"] = declaration; + json["functions"] = functions; + return json; + } + SH_RENDER_API void ShaderAST::ComputeShaderNode::Deserialize(const core::Json& json) + { + version.Deserialize(json.at("version")); + shaderName = json.at("shaderName").get(); + numthreadsX = json.value("numthreadsX", 1u); + numthreadsY = json.value("numthreadsY", 1u); + numthreadsZ = json.value("numthreadsZ", 1u); + code = json.at("code").get(); + buffers.clear(); + for (const auto& b : json.at("buffers")) + { + BufferNode buf; + buf.Deserialize(b); + buffers.push_back(std::move(buf)); + } + declaration = json.at("declaration").get>(); + functions = json.at("functions").get>(); + } }//namespace \ No newline at end of file diff --git a/src/Render/ShaderLexer.cpp b/src/Render/ShaderLexer.cpp index 4f5540fb..a165f0d2 100644 --- a/src/Render/ShaderLexer.cpp +++ b/src/Render/ShaderLexer.cpp @@ -127,91 +127,39 @@ namespace sh::render ++column; continue; } - if (c == '=') - { - ++i; - ++column; - if (i + 1 < source.size()) - { - if (source[i] == '=') - { - AddToken(TokenType::Operator, "=="); - ++i; - ++column; - continue; - } - } - AddToken(TokenType::Operator, "="); - continue; - } - if (c == '+') - { - ++i; - ++column; - if (i < source.size()) - { - if (source[i] == '+' || source[i] == '=') - { - AddToken(TokenType::Operator, std::string{ c } + source[i]); - ++i; - ++column; - continue; - } - } - AddToken(TokenType::Operator, std::string{ c }); - continue; - } - if (c == '-') - { - ++i; - ++column; - if (i < source.size()) + // 두 글자 합성 연산자가 가능한 연산자들 + auto LexCompoundOp = [&](char single, std::initializer_list followers) { - if (source[i] == '-' || source[i] == '=') - { - AddToken(TokenType::Operator, std::string{ c } + source[i]); - ++i; - ++column; - continue; - } - } - AddToken(TokenType::Operator, std::string{ c }); - continue; - } - if (c == '*') - { - ++i; - ++column; - if (i < source.size()) - { - if (source[i] == '=') - { - AddToken(TokenType::Operator, std::string{ c } + source[i]); - ++i; - ++column; - continue; - } - } - AddToken(TokenType::Operator, std::string{ c }); - continue; - } - if (c == '/') - { - ++i; - ++column; - if (i < source.size()) - { - if (source[i] == '=') + std::string op{ single }; + ++i; + ++column; + if (i < source.size()) { - AddToken(TokenType::Operator, std::string{ c } + source[i]); - ++i; - ++column; - continue; + for (char f : followers) + { + if (source[i] == f) + { + op.push_back(source[i]); + ++i; + ++column; + break; + } + } } - } - AddToken(TokenType::Operator, std::string{ c }); - continue; - } + AddToken(TokenType::Operator, op); + }; + if (c == '=') { LexCompoundOp('=', { '=' }); continue; } + if (c == '+') { LexCompoundOp('+', { '+', '=' }); continue; } + if (c == '-') { LexCompoundOp('-', { '-', '=' }); continue; } + if (c == '*') { LexCompoundOp('*', { '=' }); continue; } + if (c == '/') { LexCompoundOp('/', { '=' }); continue; } + if (c == '<') { LexCompoundOp('<', { '=', '<' }); continue; } + if (c == '>') { LexCompoundOp('>', { '=', '>' }); continue; } + if (c == '!') { LexCompoundOp('!', { '=' }); continue; } + if (c == '&') { LexCompoundOp('&', { '&', '=' }); continue; } + if (c == '|') { LexCompoundOp('|', { '|', '=' }); continue; } + if (c == '%') { LexCompoundOp('%', { '=' }); continue; } + if (c == '^') { LexCompoundOp('^', { '=' }); continue; } if (c == ';') { @@ -308,8 +256,12 @@ namespace sh::render {"MATRIX_MODEL", TokenType::MATRIX_MODEL}, {"MATRIX_VIEW", TokenType::MATRIX_VIEW}, {"MATRIX_PROJ", TokenType::MATRIX_PROJ}, + {"CAMERA", TokenType::CAMERA}, {"BONE_WEIGHTS", TokenType::BONE_WEIGHTS}, {"BONE_INDICES", TokenType::BONE_INDICES}, + {"SKIN", TokenType::SKIN}, + {"MATRIX_SKIN", TokenType::MATRIX_SKIN}, + {"TEXTURE_SHADOW", TokenType::TEXTURE_SHADOW} }; auto it = keywordMap.find(ident); if (it == keywordMap.end()) @@ -340,4 +292,4 @@ namespace sh::render { return std::isalnum(static_cast(c)) || c == '_'; } -}//namespace \ No newline at end of file +}//namespace diff --git a/src/Render/ShaderParser.cpp b/src/Render/ShaderParser.cpp index 977b693c..1ec8679b 100644 --- a/src/Render/ShaderParser.cpp +++ b/src/Render/ShaderParser.cpp @@ -9,6 +9,7 @@ #include #include #include +#include namespace sh::render { @@ -151,19 +152,19 @@ namespace sh::render auto ShaderParser::ParseShader() -> ShaderAST::ShaderNode { - ShaderAST::ShaderNode shaderNode; + curShaderNode = ShaderAST::ShaderNode{}; while (CheckToken(ShaderLexer::TokenType::Preprocessor)) { NextToken(); - ParsePreprocessor(shaderNode); + ParsePreprocessor(curShaderNode); } ConsumeToken(ShaderLexer::TokenType::Shader); if (auto& token = PeekToken(); token.type == ShaderLexer::TokenType::String) { - shaderNode.shaderName = token.text; + curShaderNode.shaderName = token.text; NextToken(); } else @@ -173,15 +174,15 @@ namespace sh::render // 프로퍼티가 있으면 파싱 (없을 수도 있음) if (CheckToken(ShaderLexer::TokenType::Property)) - ParseProperty(shaderNode); + ParseProperty(curShaderNode); while (!CheckToken(ShaderLexer::TokenType::RBrace) && !CheckToken(ShaderLexer::TokenType::EndOfFile)) { - shaderNode.passes.push_back(ParsePass(shaderNode)); + curShaderNode.passes.push_back(ParsePass(curShaderNode)); } ConsumeToken(ShaderLexer::TokenType::RBrace); - return shaderNode; + return curShaderNode; } void ShaderParser::ParsePreprocessor(ShaderAST::ShaderNode& shaderNode) { @@ -237,7 +238,7 @@ namespace sh::render if (PreviousToken().type == ShaderLexer::TokenType::LSquareBracket) { ConsumeToken(ShaderLexer::TokenType::Number); - varNode.size = std::stoi(PreviousToken().text); + varNode.arraySize = static_cast(std::stoi(PreviousToken().text)); ConsumeToken(ShaderLexer::TokenType::RSquareBracket); ConsumeToken(ShaderLexer::TokenType::Semicolon); } @@ -590,8 +591,8 @@ namespace sh::render // uniforms 벡터 수정 후 UBO 이터레이터를 갱신하는 헬퍼 auto refreshUboIt = [&]() { - return std::find_if(stageNode.uniforms.begin(), stageNode.uniforms.end(), - [](const ShaderAST::UBONode& ubo) + return std::find_if(stageNode.buffers.begin(), stageNode.buffers.end(), + [](const ShaderAST::BufferNode& ubo) { return ubo.name == "UBO"; } @@ -599,7 +600,7 @@ namespace sh::render }; // 버텍스 입력 어트리뷰트를 한 번만 등록하는 헬퍼 - auto ensureInput = [&](bool& bRegistered, const char* name, int binding, ShaderAST::VariableType type) + auto registerAttributeFn = [&](bool& bRegistered, const char* name, int binding, ShaderAST::VariableType type) { if (bRegistered) return; @@ -615,14 +616,13 @@ namespace sh::render layoutNode.binding = binding; layoutNode.var.name = name; layoutNode.var.type = type; - layoutNode.var.size = 1; + layoutNode.var.arraySize = 1; stageNode.in.push_back(std::move(layoutNode)); } bRegistered = true; }; auto uboit = refreshUboIt(); - std::string code{}; int nested = 1; bool usingVertex = false; @@ -634,6 +634,45 @@ namespace sh::render bool usingMatrixModel = false; bool usingCamera = false; bool usingLIGHT = false; + bool usingSKIN = false; + bool usingMATRIX_SKIN = false; + bool usingTEXTURE_SHADOW = false; + auto registerCameraFn = [&]() + { + if (usingCamera) + return; + + auto it = std::find_if(stageNode.buffers.begin(), stageNode.buffers.end(), + [](const ShaderAST::BufferNode& ubo) { return ubo.name == "CAMERA"; }); + if (it == stageNode.buffers.end()) + { + ShaderAST::BufferNode uboNode{}; + uboNode.bufferType = ShaderAST::BufferType::Uniform; + uboNode.name = "CAMERA"; + uboNode.set = static_cast(UniformStructLayout::Usage::Camera); + uboNode.binding = 0; + uboNode.vars.push_back(ShaderAST::VariableNode{ ShaderAST::VariableType::Mat4, 1, "view" }); + uboNode.vars.push_back(ShaderAST::VariableNode{ ShaderAST::VariableType::Mat4, 1, "proj" }); + uboNode.vars.push_back(ShaderAST::VariableNode{ ShaderAST::VariableType::Vec3, 1, "pos" }); + stageNode.buffers.push_back(std::move(uboNode)); + } + else + { + auto hasVar = [&](std::string_view name) + { + return std::find_if(it->vars.begin(), it->vars.end(), + [&](const ShaderAST::VariableNode& var) { return var.name == name; }) != it->vars.end(); + }; + if (!hasVar("view")) + it->vars.push_back(ShaderAST::VariableNode{ ShaderAST::VariableType::Mat4, 1, "view" }); + if (!hasVar("proj")) + it->vars.push_back(ShaderAST::VariableNode{ ShaderAST::VariableType::Mat4, 1, "proj" }); + if (!hasVar("pos")) + it->vars.push_back(ShaderAST::VariableNode{ ShaderAST::VariableType::Vec3, 1, "pos" }); + } + usingCamera = true; + uboit = refreshUboIt(); + }; while (nested != 0 || PeekToken().type != ShaderLexer::TokenType::EndOfFile) { @@ -651,54 +690,38 @@ namespace sh::render code.pop_back(); } else if (CheckToken(ShaderLexer::TokenType::VERTEX)) - ensureInput(usingVertex, "VERTEX", Mesh::VERTEX_ID, ShaderAST::VariableType::Vec3); + registerAttributeFn(usingVertex, "VERTEX", Mesh::VERTEX_ID, ShaderAST::VariableType::Vec3); else if (CheckToken(ShaderLexer::TokenType::UV)) - ensureInput(usingUV, "UV", Mesh::UV_ID, ShaderAST::VariableType::Vec2); + registerAttributeFn(usingUV, "UV", Mesh::UV_ID, ShaderAST::VariableType::Vec2); else if (CheckToken(ShaderLexer::TokenType::NORMAL)) - ensureInput(usingNormal, "NORMAL", Mesh::NORMAL_ID, ShaderAST::VariableType::Vec3); + registerAttributeFn(usingNormal, "NORMAL", Mesh::NORMAL_ID, ShaderAST::VariableType::Vec3); else if (CheckToken(ShaderLexer::TokenType::TANGENT)) - ensureInput(usingTangent, "TANGENT", Mesh::TANGENT_ID, ShaderAST::VariableType::Vec3); + registerAttributeFn(usingTangent, "TANGENT", Mesh::TANGENT_ID, ShaderAST::VariableType::Vec3); else if (CheckToken(ShaderLexer::TokenType::BONE_WEIGHTS)) - ensureInput(usingBoneWeights, "BONE_WEIGHTS", SkinnedMesh::BONE_WEIGHT_ID, ShaderAST::VariableType::Vec4); + registerAttributeFn(usingBoneWeights, "BONE_WEIGHTS", SkinnedMesh::BONE_WEIGHT_ID, ShaderAST::VariableType::Vec4); else if (CheckToken(ShaderLexer::TokenType::BONE_INDICES)) - ensureInput(usingBoneIndices, "BONE_INDICES", SkinnedMesh::BONE_INDEX_ID, ShaderAST::VariableType::IVec4); - else if (CheckToken(ShaderLexer::TokenType::MATRIX_VIEW) || CheckToken(ShaderLexer::TokenType::MATRIX_PROJ)) + registerAttributeFn(usingBoneIndices, "BONE_INDICES", SkinnedMesh::BONE_INDEX_ID, ShaderAST::VariableType::IVec4); + else if (CheckToken(ShaderLexer::TokenType::MATRIX_VIEW) || + CheckToken(ShaderLexer::TokenType::MATRIX_PROJ) || + CheckToken(ShaderLexer::TokenType::CAMERA)) { - if (!usingCamera) - { - auto it = std::find_if(stageNode.uniforms.begin(), stageNode.uniforms.end(), - [](const ShaderAST::UBONode& ubo) { return ubo.name == "CAMERA"; }); - if (it == stageNode.uniforms.end()) - { - ShaderAST::UBONode uboNode{}; - uboNode.name = "CAMERA"; - uboNode.set = static_cast(UniformStructLayout::Type::Camera); - uboNode.binding = 0; - uboNode.bSampler = false; - uboNode.vars.push_back(ShaderAST::VariableNode{ ShaderAST::VariableType::Mat4, 1, "view" }); - uboNode.vars.push_back(ShaderAST::VariableNode{ ShaderAST::VariableType::Mat4, 1, "proj" }); - stageNode.uniforms.push_back(std::move(uboNode)); - uboit = refreshUboIt(); - } - usingCamera = true; - } + registerCameraFn(); } else if (CheckToken(ShaderLexer::TokenType::MATRIX_MODEL)) { if (!usingMatrixModel) { - auto it = std::find_if(stageNode.uniforms.begin(), stageNode.uniforms.end(), - [](const ShaderAST::UBONode& ubo) { return ubo.name == "CONSTANTS"; }); - if (it == stageNode.uniforms.end()) + auto it = std::find_if(stageNode.buffers.begin(), stageNode.buffers.end(), + [](const ShaderAST::BufferNode& ubo) { return ubo.name == "CONSTANTS"; }); + if (it == stageNode.buffers.end()) { - ShaderAST::UBONode uboNode{}; + ShaderAST::BufferNode uboNode{}; + uboNode.bufferType = ShaderAST::BufferType::PushConstant; uboNode.name = "CONSTANTS"; - uboNode.set = static_cast(UniformStructLayout::Type::Object); // 의미 없음 + uboNode.set = static_cast(UniformStructLayout::Usage::Object); // 의미 없음 uboNode.binding = 0; // 의미 없음 - uboNode.bConstant = true; - uboNode.bSampler = false; uboNode.vars.push_back(ShaderAST::VariableNode{ ShaderAST::VariableType::Mat4, 1, "model" }); - stageNode.uniforms.push_back(std::move(uboNode)); + stageNode.buffers.push_back(std::move(uboNode)); uboit = refreshUboIt(); } usingMatrixModel = true; @@ -708,28 +731,85 @@ namespace sh::render { if (!usingLIGHT) { - auto it = std::find_if(stageNode.uniforms.begin(), stageNode.uniforms.end(), - [](const ShaderAST::UBONode& ubo) { return ubo.name == "LIGHT"; }); - if (it == stageNode.uniforms.end()) + auto it = std::find_if(stageNode.buffers.begin(), stageNode.buffers.end(), + [](const ShaderAST::BufferNode& ubo) { return ubo.name == "LIGHT"; }); + if (it == stageNode.buffers.end()) { - ShaderAST::UBONode uboNode{}; - uboNode.name = "LIGHT"; - uboNode.set = static_cast(UniformStructLayout::Type::Object); - uboNode.binding = lastObjectUniformBinding++; - uboNode.bSampler = false; - uboNode.vars.push_back(ShaderAST::VariableNode{ ShaderAST::VariableType::Int, 1, "count" }); - uboNode.vars.push_back(ShaderAST::VariableNode{ ShaderAST::VariableType::Vec4, 10, "pos" }); - uboNode.vars.push_back(ShaderAST::VariableNode{ ShaderAST::VariableType::Vec4, 10, "other" }); - stageNode.uniforms.push_back(std::move(uboNode)); - stageNode.lightingBinding = uboNode.binding; + ShaderAST::StructNode& structNode = stageNode.structs.emplace_back(); + structNode.name = "Light"; + structNode.vars.push_back(ShaderAST::VariableNode{ ShaderAST::VariableType::Vec4, 1, "pos" }); + structNode.vars.push_back(ShaderAST::VariableNode{ ShaderAST::VariableType::Vec4, 1, "other" }); + structNode.vars.push_back(ShaderAST::VariableNode{ ShaderAST::VariableType::Vec4, 1, "shadowRect" }); + structNode.vars.push_back(ShaderAST::VariableNode{ ShaderAST::VariableType::Mat4, 1, "lightSpaceMatrix" }); + + ShaderAST::BufferNode& ssboNode = stageNode.buffers.emplace_back(); + ssboNode.bufferType = ShaderAST::BufferType::Storage; + ssboNode.name = "LIGHT"; + ssboNode.set = static_cast(UniformStructLayout::Usage::Object); + ssboNode.binding = lastObjectUniformBinding++; + ssboNode.vars.push_back(ShaderAST::VariableNode{ ShaderAST::VariableType::Int, 1, "count" }); + ssboNode.vars.push_back(ShaderAST::VariableNode::MakeDynamicArray(structNode, "lights")); + + stageNode.lightingBinding = ssboNode.binding; uboit = refreshUboIt(); } usingLIGHT = true; } } + else if (CheckToken(ShaderLexer::TokenType::TEXTURE_SHADOW)) + { + if (!usingTEXTURE_SHADOW) + { + auto it = std::find_if(stageNode.buffers.begin(), stageNode.buffers.end(), + [](const ShaderAST::BufferNode& ubo) { return ubo.name == "TEXTURE_SHADOW"; }); + if (it == stageNode.buffers.end()) + { + ShaderAST::BufferNode& samplerNode = stageNode.buffers.emplace_back(); + samplerNode.bufferType = ShaderAST::BufferType::Sampler; + samplerNode.name = "TEXTURE_SHADOW"; + samplerNode.set = static_cast(UniformStructLayout::Usage::Object); + samplerNode.binding = lastObjectUniformBinding++; + + stageNode.shadowMapBinding = samplerNode.binding; + + ShaderAST::VariableNode& varNode = curShaderNode.properties.emplace_back(); + varNode.name = "TEXTURE_SHADOW"; + varNode.attribute = ShaderAST::VariableAttribute::Local; + varNode.type = ShaderAST::VariableType::Sampler; + + uboit = refreshUboIt(); + } + usingTEXTURE_SHADOW = true; + } + } + else if (CheckToken(ShaderLexer::TokenType::SKIN) || CheckToken(ShaderLexer::TokenType::MATRIX_SKIN)) + { + registerAttributeFn(usingBoneWeights, "BONE_WEIGHTS", SkinnedMesh::BONE_WEIGHT_ID, ShaderAST::VariableType::Vec4); + registerAttributeFn(usingBoneIndices, "BONE_INDICES", SkinnedMesh::BONE_INDEX_ID, ShaderAST::VariableType::IVec4); + if (CheckToken(ShaderLexer::TokenType::MATRIX_SKIN)) + usingMATRIX_SKIN = true; + if (!usingSKIN) + { + auto it = std::find_if(stageNode.buffers.begin(), stageNode.buffers.end(), + [](const ShaderAST::BufferNode& ssbo) { return ssbo.name == "SKIN"; }); + if (it == stageNode.buffers.end()) + { + ShaderAST::BufferNode ssboNode{}; + ssboNode.bufferType = ShaderAST::BufferType::Storage; + ssboNode.name = "SKIN"; + ssboNode.set = static_cast(UniformStructLayout::Usage::Object); + ssboNode.binding = lastObjectUniformBinding++; + ssboNode.vars.push_back(ShaderAST::VariableNode::MakeDynamicArray(ShaderAST::VariableType::Mat4, "ibm")); + stageNode.buffers.push_back(std::move(ssboNode)); + stageNode.skinBinding = ssboNode.binding; + uboit = refreshUboIt(); + } + usingSKIN = true; + } + } else if (token.type == ShaderLexer::TokenType::Identifier) { - if (uboit != stageNode.uniforms.end()) + if (uboit != stageNode.buffers.end()) { auto varit = std::find_if(uboit->vars.begin(), uboit->vars.end(), [&](const ShaderAST::VariableNode& var) { return var.name == token.text; }); @@ -740,6 +820,15 @@ namespace sh::render code += SubstitutionFunctionToken(token) + " "; NextToken(); } + if (usingMATRIX_SKIN) + { + code = "mat4 MATRIX_SKIN = " + "BONE_WEIGHTS.x * SKIN.ibm[BONE_INDICES.x] + " + "BONE_WEIGHTS.y * SKIN.ibm[BONE_INDICES.y] + " + "BONE_WEIGHTS.z * SKIN.ibm[BONE_INDICES.z] + " + "BONE_WEIGHTS.w * SKIN.ibm[BONE_INDICES.w]; " + + code; + } return code; } @@ -747,7 +836,7 @@ namespace sh::render { ConsumeToken(ShaderLexer::TokenType::Layout); ShaderAST::LayoutNode layoutNode{}; - ShaderAST::UBONode uboNode{}; + ShaderAST::BufferNode uboNode{}; ConsumeToken(ShaderLexer::TokenType::LBracket); // ( ConsumeToken(ShaderLexer::TokenType::Identifier); @@ -812,7 +901,7 @@ namespace sh::render } else if (CheckToken(ShaderLexer::TokenType::Sampler2D)) { - uboNode.bSampler = true; + uboNode.bufferType = ShaderAST::BufferType::Sampler; NextToken(); ConsumeToken(ShaderLexer::TokenType::Identifier); uboNode.name = PreviousToken().text; @@ -821,7 +910,7 @@ namespace sh::render else throw ShaderParserException({ GetTokenErrorString("Not found Identifier or Sampler2D keyword").c_str()}); - stageNode.uniforms.push_back(std::move(uboNode)); + stageNode.buffers.push_back(std::move(uboNode)); } } void ShaderParser::ParseUniform(const ShaderAST::ShaderNode& shaderNode, ShaderAST::StageNode& stageNode) @@ -850,20 +939,20 @@ namespace sh::render if (PreviousToken().type == ShaderLexer::TokenType::LSquareBracket) { ConsumeToken(ShaderLexer::TokenType::Number); - varNode.size = std::stoi(PreviousToken().text); + varNode.arraySize = static_cast(std::stoi(PreviousToken().text)); ConsumeToken(ShaderLexer::TokenType::RSquareBracket); ConsumeToken(ShaderLexer::TokenType::Semicolon); } // 프로퍼티 구문에 있는지 검사 bool hasProperty = false; - uint32_t set = static_cast(UniformStructLayout::Type::Material); + uint32_t set = static_cast(UniformStructLayout::Usage::Material); for (auto& property : shaderNode.properties) { - if (property.type == varNode.type && property.size == varNode.size && property.name == varNode.name) + if (property.type == varNode.type && property.arraySize == varNode.arraySize && property.name == varNode.name) { set = (property.attribute == ShaderAST::VariableAttribute::Local) ? - static_cast(UniformStructLayout::Type::Object) : static_cast(UniformStructLayout::Type::Material); + static_cast(UniformStructLayout::Usage::Object) : static_cast(UniformStructLayout::Usage::Material); hasProperty = true; break; } @@ -873,40 +962,40 @@ namespace sh::render if (varNode.type != ShaderAST::VariableType::Sampler) { - auto it = std::find_if(stageNode.uniforms.begin(), stageNode.uniforms.end(), [](const ShaderAST::UBONode& uboNode) + auto it = std::find_if(stageNode.buffers.begin(), stageNode.buffers.end(), [](const ShaderAST::BufferNode& uboNode) { return uboNode.name == "UBO"; } ); - if (it == stageNode.uniforms.end()) + if (it == stageNode.buffers.end()) { - ShaderAST::UBONode uboNode{}; + ShaderAST::BufferNode uboNode{}; + uboNode.bufferType = ShaderAST::BufferType::Uniform; uboNode.set = set; uboNode.binding = (set == static_cast(ShaderAST::VariableAttribute::Local)) ? lastObjectUniformBinding++ : lastMaterialUniformBinding++; uboNode.name = "UBO"; - uboNode.bSampler = false; uboNode.vars.push_back(std::move(varNode)); - stageNode.uniforms.push_back(std::move(uboNode)); + stageNode.buffers.push_back(std::move(uboNode)); } else { - ShaderAST::UBONode& uboNode = *it; + ShaderAST::BufferNode& uboNode = *it; uboNode.vars.push_back(std::move(varNode)); } } else { - ShaderAST::UBONode uboNode{}; - uboNode.bSampler = true; + ShaderAST::BufferNode uboNode{}; + uboNode.bufferType = ShaderAST::BufferType::Sampler; uboNode.name = varNode.name; uboNode.set = set; - uboNode.binding = (set == static_cast(ShaderAST::VariableAttribute::Local)) ? + uboNode.binding = (set == static_cast(ShaderAST::VariableAttribute::Local)) ? lastObjectUniformBinding++ : lastMaterialUniformBinding++; - stageNode.uniforms.push_back(std::move(uboNode)); + stageNode.buffers.push_back(std::move(uboNode)); } } - void ShaderParser::ParseUniformBody(ShaderAST::UBONode& uboNode) + void ShaderParser::ParseUniformBody(ShaderAST::BufferNode& uboNode) { ConsumeToken(ShaderLexer::TokenType::LBrace); // { @@ -921,7 +1010,7 @@ namespace sh::render { NextToken(); ConsumeToken(ShaderLexer::TokenType::Number); - varNode.size = std::stoi(PreviousToken().text); + varNode.arraySize = static_cast(std::stoi(PreviousToken().text)); ConsumeToken(ShaderLexer::TokenType::RSquareBracket); } ConsumeToken(ShaderLexer::TokenType::Semicolon); @@ -966,14 +1055,14 @@ namespace sh::render for (auto& stageNode : passNode.stages) { // UBO 유니폼 변수 정렬 (레이아웃 재배치) - auto it = std::find_if(stageNode.uniforms.begin(), stageNode.uniforms.end(), [&](const ShaderAST::UBONode& uboNode) + auto it = std::find_if(stageNode.buffers.begin(), stageNode.buffers.end(), [&](const ShaderAST::BufferNode& uboNode) { return uboNode.name == "UBO"; }); - if (it == stageNode.uniforms.end()) + if (it == stageNode.buffers.end()) continue; - ShaderAST::UBONode& uboNode = *it; + ShaderAST::BufferNode& uboNode = *it; std::sort(uboNode.vars.begin(), uboNode.vars.end(), [&](const ShaderAST::VariableNode& varNode1, const ShaderAST::VariableNode& varNode2) { @@ -1001,27 +1090,58 @@ namespace sh::render for (auto& out : stageNode.out) code += fmt::format("layout(location = {}) out {} {};\n", out.binding, VariableTypeToString(out.var.type), out.var.name); - for (auto& uniform : stageNode.uniforms) + // 구조체 선언 + for (const ShaderAST::StructNode& structNode : stageNode.structs) + { + std::string variables; + for (const ShaderAST::VariableNode& varNode : structNode.vars) + { + assert(varNode.type != ShaderAST::VariableType::Struct); + variables += fmt::format("{} {};\n", VariableTypeToString(varNode.type), varNode.name); + } + code += fmt::format("struct {} {{ {} }};\n", structNode.name, variables); + } + + // 버퍼 선언 + for (const ShaderAST::BufferNode& uniform : stageNode.buffers) { - if (uniform.bSampler) + if (uniform.bufferType == ShaderAST::BufferType::Sampler) { code += fmt::format("layout(set = {}, binding = {}) uniform sampler2D {};\n", uniform.set, uniform.binding, uniform.name); continue; } std::string uniformMembers; - for (auto& member : uniform.vars) + bool bDynamicArrayFlag = false; + for (const ShaderAST::VariableNode& member : uniform.vars) { - if (member.size == 1) + if (member.bDynamicArray) + { + assert(!bDynamicArrayFlag); + if (bDynamicArrayFlag) + throw ShaderParserException{ "SSBO must have at most one dynamic array!" }; + + std::string varType; + if (member.type != ShaderAST::VariableType::Struct) + varType = VariableTypeToString(member.type); + else + varType = member.structType; + uniformMembers += fmt::format("{} {}[];\n", varType, member.name); + bDynamicArrayFlag = true; + } + else if (member.arraySize == 1) uniformMembers += fmt::format("{} {};\n", VariableTypeToString(member.type), member.name); else - uniformMembers += fmt::format("{} {}[{}];\n", VariableTypeToString(member.type), member.name, member.size); + uniformMembers += fmt::format("{} {}[{}];\n", VariableTypeToString(member.type), member.name, member.arraySize); } - if (uniform.bConstant) + if (uniform.bufferType == ShaderAST::BufferType::PushConstant) { code += fmt::format("layout(push_constant) uniform UNIFORM_{} {{\n {} }} {};\n", uniform.name, uniformMembers, uniform.name); continue; } - code += fmt::format("layout(set = {}, binding = {}) uniform {} {{\n {} }} {};\n", uniform.set, uniform.binding, "UNIFORM_" + uniform.name, uniformMembers, uniform.name); + if (uniform.bufferType == ShaderAST::BufferType::Storage) + code += fmt::format("layout(std430, set = {}, binding = {}) readonly buffer {} {{\n {} }} {};\n", uniform.set, uniform.binding, "UNIFORM_" + uniform.name, uniformMembers, uniform.name); + else + code += fmt::format("layout(set = {}, binding = {}) uniform {} {{\n {} }} {};\n", uniform.set, uniform.binding, "UNIFORM_" + uniform.name, uniformMembers, uniform.name); } for (int i = 0; i < passNode.constants.size(); ++i) { @@ -1070,4 +1190,4 @@ namespace sh::render } return shaderNode; } -}//namespace \ No newline at end of file +}//namespace diff --git a/src/Render/ShaderPass.cpp b/src/Render/ShaderPass.cpp index 2afe9cf2..577438b9 100644 --- a/src/Render/ShaderPass.cpp +++ b/src/Render/ShaderPass.cpp @@ -11,13 +11,14 @@ namespace sh::render bZTest = passNode.bZTest; colorMask = passNode.colorMask; FillAttributes(passNode); - for (auto& stage : passNode.stages) + for (const ShaderAST::StageNode& stage : passNode.stages) { if (stage.lightingBinding != -1) - { lightingBinding = stage.lightingBinding; - break; - } + if (stage.skinBinding != -1) + skinBinding = stage.skinBinding; + if (stage.shadowMapBinding != -1) + shadowMapBinding = stage.shadowMapBinding; } if (!passNode.constants.empty()) { @@ -60,12 +61,11 @@ namespace sh::render cull(other.cull), colorMask(other.colorMask), bZWrite(other.bZWrite), bHasConstant(other.bHasConstant), - lightingBinding(other.lightingBinding) - { - } - ShaderPass::~ShaderPass() + lightingBinding(other.lightingBinding), + skinBinding(other.skinBinding) { } + ShaderPass::~ShaderPass() = default; auto ShaderPass::operator=(ShaderPass&& other) noexcept -> ShaderPass& { @@ -85,15 +85,40 @@ namespace sh::render bZWrite = other.bZWrite; bHasConstant = other.bHasConstant; lightingBinding = other.lightingBinding; + skinBinding = other.skinBinding; return *this; } + SH_RENDER_API auto ShaderPass::Serialize() const -> core::Json + { + core::Json mainJson = Super::Serialize(); + core::Json& shaderPassJson = mainJson["shaderPass"]; + + shaderPassJson["vertShaderData"] = shaderCode.vert; + shaderPassJson["fragShaderData"] = shaderCode.frag; + + return mainJson; + } + SH_RENDER_API void ShaderPass::Deserialize(const core::Json& json) + { + Super::Deserialize(json); + + if (!json.contains("shaderPass")) + return; + + const core::Json& shaderPassJson = json["shaderPass"]; + if (shaderPassJson.contains("vertShaderData")) + shaderCode.vert = shaderPassJson["vertShaderData"].get>(); + if (shaderPassJson.contains("fragShaderData")) + shaderCode.frag = shaderPassJson["fragShaderData"].get>(); + } + SH_RENDER_API auto ShaderPass::HasUniformMember(const std::string& name, ShaderStage stage) const -> const UniformStructLayout* { if (stage == ShaderStage::Vertex) { - for (auto& uniformStruct : GetVertexUniforms()) + for (const UniformStructLayout& uniformStruct : GetVertexUniforms()) { if (uniformStruct.HasMember(name)) return &uniformStruct; @@ -101,12 +126,12 @@ namespace sh::render } else if (stage == ShaderStage::Fragment) { - for (auto& uniformStruct : GetFragmentUniforms()) + for (const UniformStructLayout& uniformStruct : GetFragmentUniforms()) { if (uniformStruct.HasMember(name)) return &uniformStruct; } - for (auto& uniformStruct : GetSamplerUniforms()) + for (const UniformStructLayout& uniformStruct : GetSamplerUniforms()) { if (uniformStruct.HasMember(name)) return &uniformStruct; @@ -152,31 +177,6 @@ namespace sh::render { this->shaderCode = std::move(shaderCode); } - SH_RENDER_API auto ShaderPass::Serialize() const -> core::Json - { - core::Json mainJson = Super::Serialize(); - - mainJson["shaderPass"] = core::Json{}; - core::Json& shaderPassJson = mainJson["shaderPass"]; - - shaderPassJson["vertShaderData"] = shaderCode.vert; - shaderPassJson["fragShaderData"] = shaderCode.frag; - - return mainJson; - } - SH_RENDER_API void ShaderPass::Deserialize(const core::Json& json) - { - Super::Deserialize(json); - - if (!json.contains("shaderPass")) - return; - - const auto& shaderPassJson = json["shaderPass"]; - if (shaderPassJson.contains("vertShaderData")) - shaderCode.vert = shaderPassJson["vertShaderData"].get>(); - if (shaderPassJson.contains("fragShaderData")) - shaderCode.frag = shaderPassJson["fragShaderData"].get>(); - } void ShaderPass::AddUniformLayout(ShaderStage stage, const UniformStructLayout& layout) { @@ -204,15 +204,15 @@ namespace sh::render } void ShaderPass::FillAttributes(const render::ShaderAST::PassNode& passNode) { - for (auto& stage : passNode.stages) + for (const ShaderAST::StageNode& stage : passNode.stages) { - auto stageType = (stage.type == render::ShaderAST::StageType::Vertex) ? + const ShaderStage stageType = (stage.type == render::ShaderAST::StageType::Vertex) ? render::ShaderStage::Vertex : render::ShaderStage::Fragment; if (stage.type == render::ShaderAST::StageType::Vertex) { - for (auto& in : stage.in) + for (const ShaderAST::LayoutNode& in : stage.in) { switch (in.var.type) { @@ -226,32 +226,58 @@ namespace sh::render } } } - for (auto& uniform : stage.uniforms) + for (const ShaderAST::BufferNode& bufferNode : stage.buffers) { - UniformStructLayout::Type uniformType = static_cast(uniform.set); + UniformStructLayout::Usage uniformUsage = static_cast(bufferNode.set); + + UniformStructLayout::Kind kind = UniformStructLayout::Kind::Uniform; + switch (bufferNode.bufferType) + { + case render::ShaderAST::BufferType::Storage: kind = UniformStructLayout::Kind::Storage; break; + case render::ShaderAST::BufferType::Sampler: kind = UniformStructLayout::Kind::Sampler; break; + case render::ShaderAST::BufferType::PushConstant: kind = UniformStructLayout::Kind::PushConstant; break; + case render::ShaderAST::BufferType::Uniform: kind = UniformStructLayout::Kind::Uniform; break; + } - render::UniformStructLayout uniformLayout{ uniform.name, uniform.binding, uniformType, stageType, uniform.bConstant }; + render::UniformStructLayout uniformLayout{ bufferNode.name, bufferNode.binding, uniformUsage, stageType, kind }; - bHasConstant |= uniform.bConstant; + bHasConstant |= (kind == UniformStructLayout::Kind::PushConstant); - if (!uniform.bSampler) + if (kind != UniformStructLayout::Kind::Sampler) { - for (auto& var : uniform.vars) + for (const ShaderAST::VariableNode& var : bufferNode.vars) { + auto addFn = [&](auto typeTag) + { + using T = decltype(typeTag); + if (var.bDynamicArray) + uniformLayout.AddDynamicArrayMember(var.name); + else + uniformLayout.AddArrayMember(var.name, var.arraySize); + }; switch (var.type) { - case render::ShaderAST::VariableType::Vec4: uniformLayout.AddArrayMember(var.name, var.size); break; - case render::ShaderAST::VariableType::Vec3: uniformLayout.AddArrayMember(var.name, var.size); break; - case render::ShaderAST::VariableType::Vec2: uniformLayout.AddArrayMember(var.name, var.size); break; - case render::ShaderAST::VariableType::Mat4: uniformLayout.AddArrayMember(var.name, var.size); break; - case render::ShaderAST::VariableType::Mat3: uniformLayout.AddArrayMember(var.name, var.size); break; - case render::ShaderAST::VariableType::Float: uniformLayout.AddArrayMember< float>(var.name, var.size); break; - case render::ShaderAST::VariableType::Int: uniformLayout.AddArrayMember< int>(var.name, var.size); break; + case render::ShaderAST::VariableType::Vec4: addFn(glm::vec4{}); break; + case render::ShaderAST::VariableType::Vec3: addFn(glm::vec3{}); break; + case render::ShaderAST::VariableType::Vec2: addFn(glm::vec2{}); break; + case render::ShaderAST::VariableType::Mat4: addFn(glm::mat4{}); break; + case render::ShaderAST::VariableType::Mat3: addFn(glm::mat3{}); break; + case render::ShaderAST::VariableType::Float: addFn(float{}); break; + case render::ShaderAST::VariableType::Int: addFn(int{}); break; + case render::ShaderAST::VariableType::Struct: + { + struct alignas(16) Dummy + { + }; + if (var.bDynamicArray) + uniformLayout.AddDynamicArrayMember(var.name); + break; + } } } } else - uniformLayout.AddMember(uniform.name); + uniformLayout.AddMember(bufferNode.name); AddUniformLayout(stageType, std::move(uniformLayout)); } @@ -259,11 +285,6 @@ namespace sh::render } auto ShaderPass::IsSamplerLayout(const UniformStructLayout& layout) const -> bool { - for (auto& member : layout.GetMembers()) - { - if (member.isSampler) - return true; - } - return false; + return layout.IsSampler(); } }//namespace \ No newline at end of file diff --git a/src/Render/ShadowMapManager.cpp b/src/Render/ShadowMapManager.cpp new file mode 100644 index 00000000..b47f7a56 --- /dev/null +++ b/src/Render/ShadowMapManager.cpp @@ -0,0 +1,156 @@ +#include "ShadowMapManager.h" +#include "RenderTexture.h" +#include "RenderData.h" +#include "Renderer.h" +#include "ShelfPacker.h" +#include "Formats.hpp" +#include "IRenderContext.h" + +#include "Core/Name.h" +#include "Core/SObject.h" +#include "Core/GarbageCollection.h" +#include "Core/Logger.h" + +#include + +namespace sh::render +{ + ShadowMapManager::ShadowMapManager() = default; + ShadowMapManager::~ShadowMapManager() + { + Clear(); + } + + SH_RENDER_API void ShadowMapManager::Init(IRenderContext& ctx, uint32_t atlasSize) + { + this->ctx = &ctx; + this->atlasSize = atlasSize; + packer = std::make_unique(static_cast(atlasSize), static_cast(atlasSize), 0); + + renderData.tag = core::Name{ "Depth" }; + renderData.priority = 1000; // 다른 패스보다 먼저 실행되도록 + } + + SH_RENDER_API void ShadowMapManager::Clear() + { + if (atlas != nullptr) + { + core::GarbageCollection::GetInstance()->RemoveRootSet(atlas); + atlas = nullptr; + } + casters.clear(); + casterSlots.clear(); + casterLightSpace.clear(); + renderData = RenderData{}; + packer.reset(); + ctx = nullptr; + } + + void ShadowMapManager::EnsureAtlas() + { + if (atlas != nullptr) + return; + if (ctx == nullptr) + { + SH_ERROR("ShadowMapManager: context is not initialized"); + return; + } + atlas = core::SObject::Create(TextureFormat::None, TextureFormat::D32, false); + atlas->SetSize(atlasSize, atlasSize); + atlas->Build(*ctx); + atlas->SetName("ShadowAtlas"); + // 매니저는 SObject가 아니므로 GC가 회수하지 않도록 루트셋에 등록 + core::GarbageCollection::GetInstance()->SetRootSet(atlas); + } + + SH_RENDER_API void ShadowMapManager::Register(IShadowCaster& caster) + { + if (std::find(casters.begin(), casters.end(), &caster) != casters.end()) + return; + casters.push_back(&caster); + } + + SH_RENDER_API void ShadowMapManager::Unregister(IShadowCaster& caster) + { + casters.erase(std::remove(casters.begin(), casters.end(), &caster), casters.end()); + casterSlots.erase(&caster); + casterLightSpace.erase(&caster); + } + + SH_RENDER_API void ShadowMapManager::Submit(Renderer& renderer) + { + if (casters.empty()) + return; + + EnsureAtlas(); + if (atlas == nullptr || packer == nullptr) + return; + + packer->Reset(); + casterSlots.clear(); + casterLightSpace.clear(); + + renderData.renderViewers.clear(); + renderData.SetRenderTarget(atlas); + renderData.renderViewers.reserve(casters.size()); + + for (const IShadowCaster* caster : casters) + { + if (caster == nullptr) + continue; + + const uint32_t res = caster->GetShadowMapResolution(); + if (res == 0) + continue; + + int x = 0, y = 0; + if (!packer->Alloc(static_cast(res), static_cast(res), x, y)) + { + SH_ERROR_FORMAT("ShadowMapManager: out of atlas space (atlas {}, requested {})", + atlasSize, res); + continue; + } + + Slot slot{}; + slot.uvOffset = glm::vec2{ static_cast(x) / atlasSize, static_cast(y) / atlasSize }; + slot.uvSize = glm::vec2{ static_cast(res) / atlasSize, static_cast(res) / atlasSize }; + slot.valid = true; + + const glm::mat4 view = caster->GetShadowViewMatrix(); + const glm::mat4 proj = caster->GetShadowProjMatrix(); + + casterSlots.emplace(caster, slot); + casterLightSpace.emplace(caster, proj * view); + + RenderViewer viewer{}; + viewer.viewMatrix = view; + viewer.projMatrix = proj; + viewer.pos = caster->GetShadowPos(); + viewer.to = caster->GetShadowLookAt(); + viewer.viewportRect = glm::uvec4{ atlasSize * slot.uvOffset.x, atlasSize * slot.uvOffset.y, atlasSize * slot.uvSize.x, atlasSize * slot.uvSize.y }; + viewer.viewportScissor = viewer.viewportRect; + renderData.renderViewers.push_back(viewer); + } + + if (renderData.renderViewers.empty()) + return; + + renderer.PushRenderData(renderData); + } + + SH_RENDER_API auto ShadowMapManager::GetSlot(const IShadowCaster& caster) const -> Slot + { + auto it = casterSlots.find(&caster); + if (it == casterSlots.end()) + return Slot{}; + return it->second; + } + + SH_RENDER_API auto ShadowMapManager::GetLightSpaceMatrix(const IShadowCaster& caster) const -> glm::mat4 + { + auto it = casterLightSpace.find(&caster); + if (it == casterLightSpace.end()) + return glm::mat4{ 1.f }; + return it->second; + } +}//namespace diff --git a/src/Render/Texture.cpp b/src/Render/Texture.cpp index 60d90f3e..0d005ef6 100644 --- a/src/Render/Texture.cpp +++ b/src/Render/Texture.cpp @@ -25,7 +25,7 @@ namespace sh::render int mipHeight = height; for (int i = 0; i < mipLevels; ++i) { - pixels[i].resize(mipWidth * mipHeight * GetTextureFormatChannel(format)); + pixels[i].resize(mipWidth * mipHeight * GetTextureFormatPixelSize(format)); mipWidth = std::max(1, mipWidth / 2); mipHeight = std::max(1, mipHeight / 2); } @@ -41,21 +41,44 @@ namespace sh::render if (other.bDirty.test_and_set(std::memory_order::memory_order_acquire)) bDirty.test_and_set(std::memory_order::memory_order_relaxed); } - Texture::~Texture() + Texture::~Texture() = default; + + SH_RENDER_API void Texture::SyncDirty() { + if (!bDirty.test_and_set(std::memory_order::memory_order_acquire)) + core::ThreadSyncManager::PushSyncable(*this); } + SH_RENDER_API void Texture::Sync() + { + if (bSetDataDirty) + { + CreateTextureBuffer(); + bSetDataDirty = false; + } - SH_RENDER_API void Texture::SetPixelData(const std::vector& pixels, uint32_t mipLevel) + bDirty.clear(std::memory_order::memory_order_relaxed); + } + SH_RENDER_API void Texture::OnPropertyChanged(const core::reflection::Property& prop) { - assert(this->pixels[mipLevel].size() == pixels.size()); - this->pixels[mipLevel] = pixels; - if (textureBuffer != nullptr) + if (prop.GetName() == core::Util::ConstexprHash("aniso")) { - bSetDataDirty = true; - SyncDirty(); + SetAnisoLevel(aniso); + } + else if (prop.GetName() == core::Util::ConstexprHash("bSRGB")) + { + SetSRGB(bSRGB); + } + else if (prop.GetName() == core::Util::ConstexprHash("bGenerateMipmap")) + { + SetGenerateMipmap(bGenerateMipmap); + } + else if (prop.GetName() == core::Util::ConstexprHash("filtering")) + { + SetFiltering(filtering); } } - SH_RENDER_API void Texture::SetPixelData(std::vector&& pixels, uint32_t mipLevel) + + SH_RENDER_API void Texture::SetPixelData(std::vector pixels, uint32_t mipLevel) { assert(this->pixels[mipLevel].size() == pixels.size()); this->pixels[mipLevel] = std::move(pixels); @@ -68,6 +91,7 @@ namespace sh::render SH_RENDER_API void Texture::SetPixelData(const uint8_t* pixels, std::size_t size, uint32_t mipLevel) { assert(this->pixels[mipLevel].size() == size); + this->pixels[mipLevel].resize(size); std::memcpy(this->pixels[mipLevel].data(), pixels, size); if (textureBuffer != nullptr) { @@ -75,11 +99,6 @@ namespace sh::render SyncDirty(); } } - - SH_RENDER_API auto Texture::GetPixelData() const -> const std::vector>& - { - return pixels; - } SH_RENDER_API auto Texture::GetPixelData(uint32_t mipLevel) const -> const std::vector& { assert(pixels.size() > mipLevel); @@ -99,18 +118,6 @@ namespace sh::render { return textureBuffer.get(); } - SH_RENDER_API auto Texture::GetTextureFormat() const -> TextureFormat - { - return format; - } - SH_RENDER_API auto Texture::GetWidth() const -> uint32_t - { - return width; - } - SH_RENDER_API auto Texture::GetHeight() const -> uint32_t - { - return height; - } SH_RENDER_API void Texture::ChangeTextureFormat(TextureFormat target) { @@ -126,10 +133,6 @@ namespace sh::render SyncDirty(); } } - SH_RENDER_API auto Texture::IsSRGB() const -> bool - { - return bSRGB; - } SH_RENDER_API auto Texture::GetMipLevel() const -> uint32_t { if (!bGenerateMipmap) @@ -145,35 +148,12 @@ namespace sh::render SyncDirty(); } } - SH_RENDER_API auto Texture::GetAnisoLevel() const -> uint32_t - { - return aniso; - } SH_RENDER_API void Texture::SetGenerateMipmap(bool bGenerate) { bGenerateMipmap = bGenerate; bSetDataDirty = true; SyncDirty(); } - SH_RENDER_API auto Texture::IsGenerateMipmap() const -> bool - { - return bGenerateMipmap; - } - SH_RENDER_API void Texture::SyncDirty() - { - if (!bDirty.test_and_set(std::memory_order::memory_order_acquire)) - core::ThreadSyncManager::PushSyncable(*this); - } - SH_RENDER_API void Texture::Sync() - { - if (bSetDataDirty) - { - CreateTextureBuffer(); - bSetDataDirty = false; - } - - bDirty.clear(std::memory_order::memory_order_relaxed); - } SH_RENDER_API void Texture::SetSRGB(bool bSRGB) { if (bSRGB) @@ -200,29 +180,6 @@ namespace sh::render SyncDirty(); } } - SH_RENDER_API auto Texture::GetFiltering() const -> Filtering - { - return filtering; - } - SH_RENDER_API void Texture::OnPropertyChanged(const core::reflection::Property& prop) - { - if (prop.GetName() == core::Util::ConstexprHash("aniso")) - { - SetAnisoLevel(aniso); - } - else if (prop.GetName() == core::Util::ConstexprHash("bSRGB")) - { - SetSRGB(bSRGB); - } - else if (prop.GetName() == core::Util::ConstexprHash("bGenerateMipmap")) - { - SetGenerateMipmap(bGenerateMipmap); - } - else if (prop.GetName() == core::Util::ConstexprHash("filtering")) - { - SetFiltering(filtering); - } - } SH_RENDER_API void Texture::ExportToPNG(const std::filesystem::path& path) { stbi_write_png(path.u8string().c_str(), width, height, GetTextureFormatChannel(format), pixels[0].data(), width * GetTextureFormatChannel(format)); @@ -260,4 +217,4 @@ namespace sh::render return true; return false; } -} \ No newline at end of file +}//namespace diff --git a/src/Render/UniformStructLayout.cpp b/src/Render/UniformStructLayout.cpp index d2e24f07..8d46ba40 100644 --- a/src/Render/UniformStructLayout.cpp +++ b/src/Render/UniformStructLayout.cpp @@ -2,14 +2,26 @@ namespace sh::render { - UniformStructLayout::UniformStructLayout(const std::string name, uint32_t binding, Type type, ShaderStage stage, bool bConstant, Layout layout) : - name(name), binding(binding), type(type), stage(stage), bConstant(bConstant), layout(layout) + static auto DefaultLayoutForKind(UniformStructLayout::Kind kind) -> UniformStructLayout::Layout { + return (kind == UniformStructLayout::Kind::Storage) + ? UniformStructLayout::Layout::STD430 + : UniformStructLayout::Layout::STD140; + } + UniformStructLayout::UniformStructLayout(std::string name, uint32_t binding, Usage usage, ShaderStage stage, Kind kind) : + kind(kind), layout(DefaultLayoutForKind(kind)), + usage(usage), stage(stage), binding(binding), name(std::move(name)) + { + } + UniformStructLayout::UniformStructLayout(std::string name, uint32_t binding, Usage usage, ShaderStage stage, Kind kind, Layout layout) : + kind(kind), layout(layout), + usage(usage), stage(stage), binding(binding), name(std::move(name)) + { } SH_RENDER_API auto UniformStructLayout::GetMember(const std::string& name) const -> const UniformMember* { - for (auto& member : members) + for (const UniformMember& member : members) { if (member.name == name) return &member; @@ -18,71 +30,21 @@ namespace sh::render } SH_RENDER_API auto UniformStructLayout::HasMember(const std::string& name) const -> bool { - for (auto& member : members) - { - if (member.name == name) - return true; - } - return false; + return GetMember(name) != nullptr; } SH_RENDER_API auto UniformStructLayout::GetSize() const -> std::size_t { if (members.empty()) return 0; - if (members.back().offset == 0) - return members.back().layoutSize; - return members.back().offset + members.back().layoutSize; - } - SH_RENDER_API auto UniformStructLayout::GetMembers() const -> const std::vector& - { - return members; - } - SH_RENDER_API auto UniformStructLayout::Serialize() const -> core::Json - { - core::Json json{}; - json["name"] = name; - json["binding"] = binding; - json["layout"] = (layout == Layout::STD140 ? "STD140" : "STD430"); - json["type"] = static_cast(type); - json["stage"] = static_cast(stage); - json["constant"] = bConstant; - json["size"] = GetSize(); - - core::Json membersJson = core::Json::array(); - for (auto const& m : members) - { - core::Json mjson; - mjson["name"] = m.name; - mjson["typeHash"] = m.typeHash; - mjson["offset"] = m.offset; - mjson["layoutSize"] = m.layoutSize; - mjson["count"] = m.count; - mjson["isArray"] = m.isArray; - mjson["isSampler"] = m.isSampler; - membersJson.push_back(std::move(mjson)); - } - json["members"] = std::move(membersJson); - - return json; - } - SH_RENDER_API void UniformStructLayout::Deserialize(const core::Json& json) - { - members.clear(); + const UniformMember& last = members.back(); + const uint32_t rawSize = last.offset + last.layoutSize; - auto const& memArr = json.at("members"); - for (auto const& mjson : memArr) - { - std::string mName = mjson.at("name").get(); - std::size_t typeHash = mjson.at("typeHash").get(); - uint32_t offset = mjson.at("offset").get(); - uint32_t layoutSize = mjson.at("layoutSize").get(); - uint32_t count = mjson.at("count").get(); - bool isArray = mjson.at("isArray").get(); - bool isSampler = mjson.at("isSampler").get(); - - UniformMember member{ mName, typeHash, offset, layoutSize, count, isArray, isSampler }; - members.push_back(std::move(member)); - } + uint32_t maxAlignment = 0; + for (const UniformMember& m : members) + maxAlignment = std::max(maxAlignment, m.baseAlignment); + if (maxAlignment == 0) + return rawSize; + return core::Util::AlignTo(rawSize, maxAlignment); } }//namespace \ No newline at end of file diff --git a/src/Render/VulkanImpl/VulkanBuffer.cpp b/src/Render/VulkanImpl/VulkanBuffer.cpp index 60e84b3b..24baba0f 100644 --- a/src/Render/VulkanImpl/VulkanBuffer.cpp +++ b/src/Render/VulkanImpl/VulkanBuffer.cpp @@ -1,5 +1,10 @@ #include "VulkanBuffer.h" #include "VulkanContext.h" +#include "VulkanCommandBufferPool.h" +#include "VulkanCommandBuffer.h" +#include "VulkanQueueManager.h" + +#include "Core/Logger.h" #include #include @@ -11,7 +16,7 @@ namespace sh::render::vk context(context), buffer(nullptr), bufferMem(nullptr), dataPtr(nullptr), bufferInfo(), memProperty(0), - persistentMapping(false), + bPersistentMapping(false), size(0) { } @@ -19,7 +24,7 @@ namespace sh::render::vk context(other.context), buffer(nullptr), bufferMem(nullptr), dataPtr(nullptr), bufferInfo(other.bufferInfo), memProperty(other.memProperty), - persistentMapping(other.persistentMapping), + bPersistentMapping(other.bPersistentMapping), size(other.size) { operator=(other); @@ -28,7 +33,7 @@ namespace sh::render::vk context(other.context), buffer(other.buffer), bufferMem(other.bufferMem), dataPtr(other.dataPtr), bufferInfo(other.bufferInfo), memProperty(other.memProperty), - persistentMapping(other.persistentMapping), + bPersistentMapping(other.bPersistentMapping), size(other.size) { other.buffer = nullptr; @@ -41,19 +46,28 @@ namespace sh::render::vk } SH_RENDER_API auto VulkanBuffer::operator=(const VulkanBuffer& other) -> VulkanBuffer& { - Create(other.size, other.bufferInfo.usage, other.bufferInfo.sharingMode, other.memProperty, other.persistentMapping); - SetData(other.dataPtr); + if (&other == this) + return *this; + + Create(other.size, other.bufferInfo.usage, other.bufferInfo.sharingMode, other.memProperty, other.bPersistentMapping); + std::vector temp = other.GetData(); + SetData(temp.data()); return *this; } SH_RENDER_API auto VulkanBuffer::operator=(VulkanBuffer&& other) noexcept -> VulkanBuffer& { + if (&other == this) + return *this; + + Clean(); + buffer = other.buffer; bufferMem = other.bufferMem; dataPtr = other.dataPtr; bufferInfo = other.bufferInfo; memProperty = other.memProperty; - persistentMapping = other.persistentMapping; + bPersistentMapping = other.bPersistentMapping; size = other.size; other.buffer = nullptr; @@ -62,6 +76,14 @@ namespace sh::render::vk return *this; } + SH_RENDER_API auto VulkanBuffer::Resize(std::size_t size) -> bool + { + Clean(); + const VkResult result = Create(size, bufferInfo.usage, bufferInfo.sharingMode, memProperty, this->bPersistentMapping); + if (result == VkResult::VK_SUCCESS) + return true; + return false; + } SH_RENDER_API void VulkanBuffer::Clean() { if (buffer) @@ -79,10 +101,11 @@ namespace sh::render::vk bufferMem = nullptr; }*/ } - SH_RENDER_API auto VulkanBuffer::Create(size_t size, VkBufferUsageFlags usageBits, VkSharingMode sharing, VkMemoryPropertyFlags memPropFlagBits, bool persistentMapping) -> VkResult + SH_RENDER_API auto VulkanBuffer::Create(size_t size, VkBufferUsageFlags usageBits, VkSharingMode sharing, VkMemoryPropertyFlags memPropFlagBits, bool bPersistentMapping) -> VkResult { this->size = size; - this->persistentMapping = persistentMapping; + this->bPersistentMapping = bPersistentMapping; + this->memProperty = memPropFlagBits; Clean(); bufferInfo.sType = VkStructureType::VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; @@ -107,22 +130,29 @@ namespace sh::render::vk result = vkAllocateMemory(device, &allocInfo, nullptr, &bufferMem);*/ - bool bUseMap = (memPropFlagBits & VkMemoryPropertyFlagBits::VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); + const bool bUseMap = (memPropFlagBits & VkMemoryPropertyFlagBits::VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); + const bool bGPUOnly = (memPropFlagBits & VkMemoryPropertyFlagBits::VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + if (bGPUOnly) + this->bPersistentMapping = false; VmaAllocationCreateInfo allocCreateInfo{}; allocCreateInfo.usage = VmaMemoryUsage::VMA_MEMORY_USAGE_AUTO; allocCreateInfo.flags = 0; if (bUseMap) allocCreateInfo.flags |= VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT; - if (persistentMapping) + if (this->bPersistentMapping) allocCreateInfo.flags |= VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_MAPPED_BIT; VmaAllocationInfo allocInfo{}; - auto result = vmaCreateBuffer(context.GetAllocator(), &bufferInfo, &allocCreateInfo, &buffer, &bufferMem, &allocInfo); - - assert(result == VkResult::VK_SUCCESS); + VkResult result = vmaCreateBuffer(context.GetAllocator(), &bufferInfo, &allocCreateInfo, &buffer, &bufferMem, &allocInfo); + if (result != VkResult::VK_SUCCESS) + { + assert(result == VkResult::VK_SUCCESS); + SH_ERROR_FORMAT("Failed to create buffer!: {}", string_VkResult(result)); + return result; + } - if (persistentMapping) + if (this->bPersistentMapping) dataPtr = allocInfo.pMappedData; //result = vkBindBufferMemory(device, buffer, bufferMem, 0); //assert(result == VkResult::VK_SUCCESS); @@ -130,53 +160,115 @@ namespace sh::render::vk return result; } SH_RENDER_API void VulkanBuffer::SetData(const void* data) + { + SetData(data, 0, static_cast(bufferInfo.size)); + } + SH_RENDER_API void VulkanBuffer::SetData(const void* data, std::size_t offset, std::size_t size) { assert(buffer); if (buffer == nullptr) return; - if (!persistentMapping) + const bool bGPUOnly = (memProperty & VkMemoryPropertyFlagBits::VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + if (!bGPUOnly) { - //vkMapMemory(device, bufferMem, 0, bufferInfo.size, 0, &this->data); - vmaMapMemory(context.GetAllocator(), bufferMem, &this->dataPtr); - std::memcpy(this->dataPtr, data, static_cast(bufferInfo.size)); - //vkUnmapMemory(device, bufferMem); - vmaUnmapMemory(context.GetAllocator(), bufferMem); + if (!bPersistentMapping) + { + vmaMapMemory(context.GetAllocator(), bufferMem, &this->dataPtr); + std::memcpy(reinterpret_cast(dataPtr) + offset, data, size); + vmaUnmapMemory(context.GetAllocator(), bufferMem); + dataPtr = nullptr; + } + else + std::memcpy(reinterpret_cast(dataPtr) + offset, data, size); } else - std::memcpy(this->dataPtr, data, static_cast(bufferInfo.size)); - } - SH_RENDER_API auto VulkanBuffer::GetData() const -> void* - { - return dataPtr; - } - SH_RENDER_API auto VulkanBuffer::GetBuffer() const -> VkBuffer - { - return buffer; - } - SH_RENDER_API auto VulkanBuffer::GetBufferInfo() const -> const VkBufferCreateInfo& - { - return bufferInfo; - } - SH_RENDER_API auto VulkanBuffer::GetBufferMemory() const -> VmaAllocation - { - return bufferMem; - } - SH_RENDER_API auto VulkanBuffer::GetSize() const -> size_t - { - return size; + { + VulkanBuffer stagingBuffer{ context }; + stagingBuffer.Create(size, VkBufferUsageFlagBits::VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VkSharingMode::VK_SHARING_MODE_EXCLUSIVE, + VkMemoryPropertyFlagBits::VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VkMemoryPropertyFlagBits::VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + stagingBuffer.SetData(data); + + VulkanCommandBuffer& cmd = *context.GetCommandBufferPool().AllocateCommandBuffer(std::this_thread::get_id(), VkQueueFlagBits::VK_QUEUE_TRANSFER_BIT); + cmd.Build( + [&]() + { + VkBufferCopy cpy{}; + cpy.srcOffset = 0; + cpy.dstOffset = offset; + cpy.size = size; + vkCmdCopyBuffer(cmd.GetCommandBuffer(), stagingBuffer.GetBuffer(), GetBuffer(), 1, &cpy); + } + , true); + VkFence fence = cmd.GetOrCreateFence(); + context.GetQueueManager().Submit(VulkanQueueManager::Role::Transfer, cmd, fence); + vkWaitForFences(context.GetDevice(), 1, &fence, true, std::numeric_limits::max()); + + context.GetCommandBufferPool().DeallocateCommandBuffer(cmd); + } } - auto VulkanBuffer::FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) -> uint32_t + SH_RENDER_API auto VulkanBuffer::GetData() const -> std::vector { - VkPhysicalDeviceMemoryProperties memProperties; - vkGetPhysicalDeviceMemoryProperties(context.GetGPU(), &memProperties); + if (buffer == VK_NULL_HANDLE) + return {}; - for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) { - if ((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) - return i; + const bool bGPUOnly = (memProperty & VkMemoryPropertyFlagBits::VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + const std::size_t size = static_cast(bufferInfo.size); + std::vector result; + + if (dataPtr != nullptr) + { + result.resize(size); + result.assign(reinterpret_cast(dataPtr), reinterpret_cast(dataPtr) + size); } + else + { + if (bGPUOnly) + { + VulkanBuffer temp{ context }; + temp.Create(size, VkBufferUsageFlagBits::VK_BUFFER_USAGE_TRANSFER_DST_BIT, VkSharingMode::VK_SHARING_MODE_EXCLUSIVE, + VkMemoryPropertyFlagBits::VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VkMemoryPropertyFlagBits::VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, true); + + VulkanCommandBuffer& cmd = *context.GetCommandBufferPool().AllocateCommandBuffer(std::this_thread::get_id(), VkQueueFlagBits::VK_QUEUE_TRANSFER_BIT); + cmd.Build( + [&]() + { + VkBufferCopy cpy{}; + cpy.srcOffset = 0; + cpy.dstOffset = 0; + cpy.size = size; + vkCmdCopyBuffer(cmd.GetCommandBuffer(), GetBuffer(), temp.GetBuffer(), 1, &cpy); + } + , true); + VkFence fence = cmd.GetOrCreateFence(); + context.GetQueueManager().Submit(VulkanQueueManager::Role::Transfer, cmd, fence); + vkWaitForFences(context.GetDevice(), 1, &fence, true, std::numeric_limits::max()); - throw std::runtime_error("Failed to find suitable memory type!"); + context.GetCommandBufferPool().DeallocateCommandBuffer(cmd); + return temp.GetData(); + } + + result.resize(size); + void* tempPtr = nullptr; + vmaMapMemory(context.GetAllocator(), bufferMem, &tempPtr); + result.assign(reinterpret_cast(tempPtr), reinterpret_cast(tempPtr) + size); + vmaUnmapMemory(context.GetAllocator(), bufferMem); + } + return result; } -} \ No newline at end of file + + //auto VulkanBuffer::FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) -> uint32_t + //{ + // VkPhysicalDeviceMemoryProperties memProperties; + // vkGetPhysicalDeviceMemoryProperties(context.GetGPU(), &memProperties); + + // for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) { + // if ((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) + // return i; + // } + + // throw std::runtime_error("Failed to find suitable memory type!"); + //} +}//namespace \ No newline at end of file diff --git a/src/Render/VulkanImpl/VulkanCameraBuffers.cpp b/src/Render/VulkanImpl/VulkanCameraBuffers.cpp deleted file mode 100644 index 031db5ad..00000000 --- a/src/Render/VulkanImpl/VulkanCameraBuffers.cpp +++ /dev/null @@ -1,88 +0,0 @@ -#include "VulkanCameraBuffers.h" - -#include "Core/Util.h" - -namespace sh::render::vk -{ - void VulkanCameraBuffers::Init(const VulkanContext& context) - { - this->context = &context; - - cameraData = std::make_unique(context); - std::size_t minUboAlignment = context.GetGPUProperty().limits.minUniformBufferOffsetAlignment; - - dynamicAlignment = core::Util::AlignTo(sizeof(Camera::BufferData), minUboAlignment); - - std::size_t bufferSize = cap * dynamicAlignment; - - cameraData->Create(bufferSize, - VkBufferUsageFlagBits::VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, - VkSharingMode::VK_SHARING_MODE_EXCLUSIVE, - VkMemoryPropertyFlagBits::VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VkMemoryPropertyFlagBits::VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, - true); - } - SH_RENDER_API void VulkanCameraBuffers::Clear() - { - cams.clear(); - emptyidx = std::stack{}; - } - void VulkanCameraBuffers::AddCamera(const Camera& camera) - { - assert(cams.size() < cap); - - auto it = std::find(cams.begin(), cams.end(), &camera); - if (it != cams.end()) - return; - - if (emptyidx.empty()) - { - cams.push_back(&camera); - } - else - { - uint32_t idx = emptyidx.top(); - emptyidx.pop(); - cams[idx] = &camera; - } - } - void VulkanCameraBuffers::RemoveCamera(const Camera& camera) - { - auto it = std::find(cams.begin(), cams.end(), &camera); - if (it == cams.end()) - return; - *it = nullptr; - emptyidx.push(std::distance(cams.begin(), it)); - } - void VulkanCameraBuffers::UploadDataToGPU(const Camera& camera) - { - auto it = std::find(cams.begin(), cams.end(), &camera); - if (it == cams.end()) - return; - auto offsetOpt = GetDynamicOffset(camera); - if (offsetOpt.has_value()) - { - Camera::BufferData* data = reinterpret_cast((std::size_t)cameraData->GetData() + offsetOpt.value()); - data->matView = camera.GetViewMatrix(core::ThreadType::Render); - data->matProj = camera.GetProjMatrix(core::ThreadType::Render); - } - } - auto VulkanCameraBuffers::GetDynamicOffset(const Camera& camera) const -> std::optional - { - auto it = std::find_if(cams.begin(), cams.end(), - [&](const Camera* other) - { - if (other == nullptr) - return false; - return (other->id == camera.id); - } - ); - if (it == cams.end()) - return {}; - uint32_t idx = std::distance(cams.begin(), it); - return idx * dynamicAlignment; - } - SH_RENDER_API auto sh::render::vk::VulkanCameraBuffers::GetCameraBuffer() const -> const VulkanBuffer& - { - return *cameraData.get(); - } -}//namespace \ No newline at end of file diff --git a/src/Render/VulkanImpl/VulkanCommandBuffer.cpp b/src/Render/VulkanImpl/VulkanCommandBuffer.cpp index 67c489da..94a86057 100644 --- a/src/Render/VulkanImpl/VulkanCommandBuffer.cpp +++ b/src/Render/VulkanImpl/VulkanCommandBuffer.cpp @@ -1,15 +1,77 @@ #include "VulkanCommandBuffer.h" #include "VulkanContext.h" #include "VulkanImageBuffer.h" -#include "RenderTexture.h" #include "VulkanBuffer.h" +#include "VulkanDescriptorSet.h" +#include "VulkanPipelineManager.h" +#include "VulkanComputePipeline.h" +#include "VulkanComputePipelineManager.h" +#include "VulkanSwapChain.h" +#include "VulkanShaderPass.h" +#include "VulkanVertexBuffer.h" +#include "VulkanSkinnedVertexBuffer.h" +#include "Render/RenderTexture.h" +#include "Render/ComputeShader.h" +#include "Render/Shader.h" +#include "Render/Material.h" +#include "Render/Mesh.h" +#include "Render/SkinnedMesh.h" +#include "Render/Drawable.h" +#include "Render/ShaderPass.h" +#include "Render/RenderData.h" +#include "Core/Reflection.hpp" #include "Core/Logger.h" #include - +#include namespace sh::render::vk { + namespace + { + auto HasStencil(TextureFormat format) -> bool + { + return format == TextureFormat::D32S8 || + format == TextureFormat::D24S8 || + format == TextureFormat::D16S8; + } + + auto BuildRenderTargetLayout(const VulkanContext& context, const std::vector& targets) -> RenderTargetLayout + { + RenderTargetLayout layout{}; + + std::vector colorFormats; + colorFormats.reserve(targets.size()); + + const RenderTexture* main = targets[0]; + if (main == nullptr) + { + layout.depthFormat = context.GetSwapChain().GetRenderTargetLayout().depthFormat; + layout.bUseMSAA = context.GetSampleCount() != VkSampleCountFlagBits::VK_SAMPLE_COUNT_1_BIT; + } + else + { + layout.depthFormat = main->GetDepthFormat(); + layout.bUseMSAA = main->GetMSAABuffer() != nullptr; + } + + for (const RenderTexture* target : targets) + { + if (target == nullptr) + colorFormats.push_back(context.GetSwapChain().GetRenderTargetLayout().colorFormats[0]); + else + { + if (target->IsDepthTexture()) + layout.depthFormat = target->GetDepthFormat(); + else + colorFormats.push_back(target->GetTextureFormat()); + } + } + layout.colorFormats = std::move(colorFormats); + return layout; + } + } + VulkanCommandBuffer::VulkanCommandBuffer(const VulkanContext& context) : context(context) {} @@ -31,6 +93,50 @@ namespace sh::render::vk Clear(); } + SH_RENDER_API void VulkanCommandBuffer::Begin(bool bOnetime) + { + renderCall = 0; + if (buffer == VK_NULL_HANDLE) + return; + + VkCommandBufferBeginInfo beginInfo{}; + beginInfo.sType = VkStructureType::VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = bOnetime ? VkCommandBufferUsageFlagBits::VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT : 0; + beginInfo.pInheritanceInfo = nullptr; + + VkResult result = vkBeginCommandBuffer(buffer, &beginInfo); + assert(result == VkResult::VK_SUCCESS); + if (result != VkResult::VK_SUCCESS) + { + const std::string err = fmt::format("Failed vkBeginCommandBuffer: {}", string_VkResult(result)); + SH_ERROR(err); + throw std::runtime_error{ err }; + } + } + SH_RENDER_API void VulkanCommandBuffer::End() + { + if (buffer == VK_NULL_HANDLE) + return; + + if (bBeginRender) + { + static PFN_vkCmdEndRenderingKHR pfnEnd = (PFN_vkCmdEndRenderingKHR)vkGetDeviceProcAddr(context.GetDevice(), "vkCmdEndRenderingKHR"); + pfnEnd(buffer); + bBeginRender = false; + } + renderState = RenderState{}; + + VkResult result = vkEndCommandBuffer(buffer); + assert(result == VkResult::VK_SUCCESS); + assert(result == VkResult::VK_SUCCESS); + if (result != VkResult::VK_SUCCESS) + { + const std::string err = fmt::format("Failed vkEndCommandBuffer: {}", string_VkResult(result)); + SH_ERROR(err); + throw std::runtime_error{ err }; + } + } + SH_RENDER_API void VulkanCommandBuffer::Blit(RenderTexture& src, int x, int y, IBuffer& dst) { // 배리어는 렌더 그래프에서 지정해줌 @@ -52,6 +158,359 @@ namespace sh::render::vk vkCmdCopyImageToBuffer(this->buffer, imgBuffer.GetImage(), VkImageLayout::VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, buffer.GetBuffer(), 1, &cpy); } + SH_RENDER_API void VulkanCommandBuffer::Dispatch(const ComputeShader& shader, uint32_t x, uint32_t y, uint32_t z) + { + const VulkanDescriptorSet* const descSet = static_cast(shader.GetShaderBinding()); + if (descSet == nullptr) + return; + const VulkanComputePipeline& pipeline = context.GetComputePipelineManager().GetOrCreatePipeline(shader); + VkDescriptorSet vkDescriptorSet = descSet->GetVkDescriptorSet(); + + vkCmdBindPipeline(buffer, VkPipelineBindPoint::VK_PIPELINE_BIND_POINT_COMPUTE, pipeline.GetPipeline()); + vkCmdBindDescriptorSets(buffer, VkPipelineBindPoint::VK_PIPELINE_BIND_POINT_COMPUTE, pipeline.GetPipelineLayout(), 0, 1, &vkDescriptorSet, 0, nullptr); + vkCmdDispatch(buffer, x, y, z); + } + + SH_RENDER_API void VulkanCommandBuffer::SetRenderData(const RenderData& renderData, bool bClearColor, bool bClearDepth, bool bStoreColor, bool bStoreDepth) + { + if (bBeginRender) + { + static PFN_vkCmdEndRenderingKHR pfnEndPrev = (PFN_vkCmdEndRenderingKHR)vkGetDeviceProcAddr(context.GetDevice(), "vkCmdEndRenderingKHR"); + pfnEndPrev(buffer); + bBeginRender = false; + } + + const VulkanImageBuffer* colorImgs[10]{}; // 힙 할당 최소화 + const VulkanImageBuffer* colorMSAAImgs[10]{}; // 힙 할당 최소화 + const VulkanImageBuffer* depthImg = nullptr; + std::size_t colorImgsCount = 0; + std::size_t colorMSAAImgsCount = 0; + // 메모) 깊이 버퍼는 제일 앞의 렌더 텍스쳐 것을 쓰고 깊이 버퍼 전용 텍스쳐가 있으면 그걸 우선시 함 + const RenderTexture* const main = renderData.GetRenderTargets()[0]; + if (main == nullptr) + depthImg = &context.GetSwapChain().GetSwapChainDepthImages()[renderData.GetFrameIdx()]; + else + depthImg = static_cast(main->GetDepthBuffer()); + + for (const RenderTexture* target : renderData.GetRenderTargets()) + { + if (target == nullptr) + { + colorImgs[colorImgsCount++] = &context.GetSwapChain().GetSwapChainImages()[renderData.GetFrameIdx()]; + const std::vector& swapChainMSAAImages = context.GetSwapChain().GetSwapChainMSAAImages(); + colorMSAAImgs[colorMSAAImgsCount++] = swapChainMSAAImages.empty() ? nullptr : &swapChainMSAAImages[renderData.GetFrameIdx()]; + } + else + { + if (!target->IsDepthTexture()) + { + colorImgs[colorImgsCount++] = static_cast(target->GetTextureBuffer()); + colorMSAAImgs[colorMSAAImgsCount++] = static_cast(target->GetMSAABuffer()); + } + else + depthImg = static_cast(target->GetDepthBuffer()); + } + } + RenderTargetLayout rtLayout = BuildRenderTargetLayout(context, renderData.GetRenderTargets()); + + const VulkanImageBuffer* const sizeRef = colorImgsCount != 0 ? colorImgs[0] : depthImg; + if (sizeRef == nullptr) + return; + const uint32_t width = sizeRef->GetWidth(); + const uint32_t height = sizeRef->GetHeight(); + const bool bHasDepth = (depthImg != nullptr); + const bool bDepthOnly = colorImgsCount == 0; + + VkRenderingAttachmentInfoKHR colorAttachments[10]{}; // 힙 할당 최소화 + const std::size_t colorAttachmentsCount = colorImgsCount; + for (std::size_t i = 0; i < colorAttachmentsCount; ++i) + { + assert(colorImgs[i] != nullptr); + assert(colorImgs[i]->GetWidth() == width && colorImgs[i]->GetHeight() == height); + + VkRenderingAttachmentInfoKHR& colorAttachment = colorAttachments[i]; + colorAttachment.sType = VkStructureType::VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO; + const bool bMSAA = (i < colorMSAAImgsCount && colorMSAAImgs[i] != nullptr); + colorAttachment.imageView = bMSAA ? colorMSAAImgs[i]->GetImageView() : colorImgs[i]->GetImageView(); + colorAttachment.imageLayout = VkImageLayout::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + colorAttachment.loadOp = bClearColor ? VkAttachmentLoadOp::VK_ATTACHMENT_LOAD_OP_CLEAR : VkAttachmentLoadOp::VK_ATTACHMENT_LOAD_OP_LOAD; + colorAttachment.storeOp = bStoreColor ? VkAttachmentStoreOp::VK_ATTACHMENT_STORE_OP_STORE : VkAttachmentStoreOp::VK_ATTACHMENT_STORE_OP_DONT_CARE; + colorAttachment.clearValue.color = { { 0.f, 0.f, 0.f, 1.f } }; + if (bMSAA) + { + colorAttachment.resolveImageView = colorImgs[i]->GetImageView(); + colorAttachment.resolveImageLayout = VkImageLayout::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + colorAttachment.resolveMode = VkResolveModeFlagBitsKHR::VK_RESOLVE_MODE_AVERAGE_BIT_KHR; + } + } + VkRenderingAttachmentInfoKHR depthAttachment{}; + if (bHasDepth) + { + depthAttachment.sType = VkStructureType::VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO; + depthAttachment.imageView = depthImg->GetImageView(); + depthAttachment.imageLayout = VkImageLayout::VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + depthAttachment.loadOp = bClearDepth ? VkAttachmentLoadOp::VK_ATTACHMENT_LOAD_OP_CLEAR : VkAttachmentLoadOp::VK_ATTACHMENT_LOAD_OP_LOAD; + depthAttachment.storeOp = bStoreDepth ? VkAttachmentStoreOp::VK_ATTACHMENT_STORE_OP_STORE : VkAttachmentStoreOp::VK_ATTACHMENT_STORE_OP_DONT_CARE; + depthAttachment.clearValue.depthStencil = { 1.0f, 0 }; + } + + VkRenderingInfoKHR renderingInfo{}; + renderingInfo.sType = VkStructureType::VK_STRUCTURE_TYPE_RENDERING_INFO_KHR; + renderingInfo.colorAttachmentCount = static_cast(colorAttachmentsCount); + renderingInfo.pColorAttachments = colorAttachmentsCount == 0 ? nullptr : colorAttachments; + renderingInfo.pDepthAttachment = bHasDepth ? &depthAttachment : nullptr; + // depth only는 스텐실 지원x + renderingInfo.pStencilAttachment = (bHasDepth && HasStencil(rtLayout.depthFormat) && !bDepthOnly) ? &depthAttachment : nullptr; + renderingInfo.renderArea = { { 0, 0 }, { width, height } }; + renderingInfo.layerCount = 1; + + static PFN_vkCmdBeginRenderingKHR pfnBegin = (PFN_vkCmdBeginRenderingKHR)vkGetDeviceProcAddr(context.GetDevice(), "vkCmdBeginRenderingKHR"); + pfnBegin(buffer, &renderingInfo); + bBeginRender = true; + + renderState = RenderState{}; + renderState.layout = rtLayout; + renderState.renderData = &renderData; + renderState.targetWidth = width; + renderState.targetHeight = height; + renderState.bDepthOnly = bDepthOnly; + } + + SH_RENDER_API void VulkanCommandBuffer::SetViewport(uint32_t x, uint32_t y, uint32_t width, uint32_t height) + { + VkViewport viewport{}; + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + viewport.x = static_cast(x); + viewport.y = static_cast(height) - static_cast(y); + viewport.width = static_cast(width); + viewport.height = -static_cast(height); + + vkCmdSetViewport(buffer, 0, 1, &viewport); + } + + SH_RENDER_API void VulkanCommandBuffer::SetScissor(uint32_t x, uint32_t y, uint32_t width, uint32_t height) + { + VkRect2D rect; + rect.offset.x = x; + rect.offset.y = y; + rect.extent.width = width; + rect.extent.height = height; + + vkCmdSetScissor(buffer, 0, 1, &rect); + } + + SH_RENDER_API void VulkanCommandBuffer::DrawMesh(const Drawable& drawable, core::Name passName, std::size_t viewerIdx) + { + if (!bBeginRender || renderState.renderData == nullptr) + return; + if (renderState.renderData->renderViewers.size() <= viewerIdx) + { + assert(renderState.renderData->renderViewers.size() <= viewerIdx); + return; + } + + const Material& mat = *drawable.GetMaterial(); + const Mesh& mesh = *drawable.GetMesh(); + const Mesh::Topology topology = drawable.GetTopology(); + const bool bSkinned = drawable.IsSkinnedMesh(); + const Shader* const shader = mat.GetShader(); + if (!core::IsValid(shader)) + return; + + const std::vector>* passes = shader->GetShaderPasses(passName); + if (passes == nullptr) + return; + + const uint32_t cameraOffset = renderState.renderData->renderViewers[viewerIdx].offset; + + for (const ShaderPass& pass : *passes) + { + if (pass.IsPendingKill()) + continue; + + const VulkanShaderPass& vkPass = static_cast(pass); + VkPipelineLayout pipelineLayout = vkPass.GetPipelineLayout(); + const std::vector* constantData = mat.GetConstantData(pass); + const uint32_t setSize = static_cast(vkPass.GetSetCount()); + VulkanPipelineManager::PipelineHandle handle = + context.GetPipelineManager().GetOrCreatePipelineHandle(vkPass, renderState.layout, topology, bSkinned, constantData); + + if (handle.index != renderState.lastPipelineIdx || handle.generation != renderState.lastPipelineGen) + { + context.GetPipelineManager().BindPipeline(buffer, handle); + renderState.lastPipelineIdx = handle.index; + renderState.lastPipelineGen = handle.generation; + } + // set 0=Camera, 1=Object, 2=Material + + if (setSize > 0) + BindCameraSet(mat, pass, pipelineLayout, cameraOffset); + if (setSize > 2) + BindMaterialSet(mat, pass, pipelineLayout); + if (setSize > 1) + BindObjectSet(drawable, pass, pipelineLayout); + + if (pass.HasConstantUniform()) + { + vkCmdPushConstants(buffer, pipelineLayout, + VkShaderStageFlagBits::VK_SHADER_STAGE_VERTEX_BIT, + 0, sizeof(glm::mat4), + &drawable.GetModelMatrix(core::ThreadType::Render)); + } + + BindMesh(mesh, drawable.GetSubMeshIndex(), bSkinned); + } + } + SH_RENDER_API void VulkanCommandBuffer::DrawMeshBatch(const std::vector& drawables, core::Name passName, std::size_t viewerIdx) + { + if (drawables.empty() || renderState.renderData == nullptr) + return; + + const Material& mat = *drawables.front()->GetMaterial(); + const Mesh::Topology topology = drawables.front()->GetTopology(); + const bool bSkinned = drawables.front()->IsSkinnedMesh(); + + const Shader* const shader = mat.GetShader(); + if (!core::IsValid(shader)) + return; + + const std::vector>* passes = shader->GetShaderPasses(passName); + if (passes == nullptr) + return; + + const uint32_t cameraOffset = renderState.renderData->renderViewers[viewerIdx].offset; + + for (const ShaderPass& pass : *passes) + { + if (pass.IsPendingKill()) + continue; + + const VulkanShaderPass& vkPass = static_cast(pass); + VkPipelineLayout pipelineLayout = vkPass.GetPipelineLayout(); + const std::vector* constantData = mat.GetConstantData(pass); + const uint32_t setSize = static_cast(vkPass.GetSetCount()); + VulkanPipelineManager::PipelineHandle handle = + context.GetPipelineManager().GetOrCreatePipelineHandle(vkPass, renderState.layout, topology, bSkinned, constantData); + + if (handle.index != renderState.lastPipelineIdx || handle.generation != renderState.lastPipelineGen) + { + context.GetPipelineManager().BindPipeline(buffer, handle); + renderState.lastPipelineIdx = handle.index; + renderState.lastPipelineGen = handle.generation; + } + // set 0=Camera, 1=Object, 2=Material + + if (setSize > 0) + BindCameraSet(mat, pass, pipelineLayout, cameraOffset); + if (setSize > 2) + BindMaterialSet(mat, pass, pipelineLayout); + + for (const Drawable* drawable : drawables) + { + if (setSize > 1) + BindObjectSet(*drawable, pass, pipelineLayout); + + if (pass.HasConstantUniform()) + { + vkCmdPushConstants(buffer, pipelineLayout, + VkShaderStageFlagBits::VK_SHADER_STAGE_VERTEX_BIT, + 0, sizeof(glm::mat4), + &drawable->GetModelMatrix(core::ThreadType::Render)); + } + + const Mesh& mesh = *drawable->GetMesh(); + BindMesh(mesh, drawable->GetSubMeshIndex(), bSkinned); + } + } + } + SH_RENDER_API void VulkanCommandBuffer::EmitBarrier(const std::vector& barriers) + { + for (const BarrierInfo& barrier : barriers) + { + VulkanImageBuffer* imgBuffer = nullptr; + VulkanImageBuffer* msaaBuffer = nullptr; + + if (std::holds_alternative(barrier.target)) + { + imgBuffer = static_cast(std::get(barrier.target)->GetTextureBuffer()); + msaaBuffer = static_cast(std::get(barrier.target)->GetMSAABuffer()); + } + else + { + const uint32_t imgIdx = std::get(barrier.target); + imgBuffer = &context.GetSwapChain().GetSwapChainImages()[imgIdx]; + const auto& swapChainMSAAImages = context.GetSwapChain().GetSwapChainMSAAImages(); + msaaBuffer = swapChainMSAAImages.empty() ? nullptr : &context.GetSwapChain().GetSwapChainMSAAImages()[imgIdx]; + } + + VkImageLayout srcLayout, dstLayout; + VkPipelineStageFlags srcStage, dstStage; + VkAccessFlags srcAccess, dstAccess; + + auto mapUsageFn = + [&]( + ResourceUsage u, + VkImageLayout& layout, + VkPipelineStageFlags& stage, + VkAccessFlags& access) + { + switch (u) + { + case ResourceUsage::Undefined: + layout = VkImageLayout::VK_IMAGE_LAYOUT_UNDEFINED; + stage = VkPipelineStageFlagBits::VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + access = 0; + return; + case ResourceUsage::ColorAttachment: + layout = VkImageLayout::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + stage = VkPipelineStageFlagBits::VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + access = VkAccessFlagBits::VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VkAccessFlagBits::VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; + return; + case ResourceUsage::SampledRead: + layout = VkImageLayout::VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + stage = VkPipelineStageFlagBits::VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT; + access = VkAccessFlagBits::VK_ACCESS_SHADER_READ_BIT; + return; + case ResourceUsage::Present: + layout = VkImageLayout::VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + stage = VkPipelineStageFlagBits::VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + access = 0; + return; + case ResourceUsage::TransferSrc: + layout = VkImageLayout::VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + stage = VkPipelineStageFlagBits::VK_PIPELINE_STAGE_TRANSFER_BIT; + access = VkAccessFlagBits::VK_ACCESS_TRANSFER_READ_BIT; + return; + case ResourceUsage::DepthStencilAttachment: + layout = VkImageLayout::VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + stage = VkPipelineStageFlagBits::VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | + VkPipelineStageFlagBits::VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + access = VkAccessFlagBits::VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | + VkAccessFlagBits::VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT; + return; + case ResourceUsage::DepthStencilSampledRead: + layout = VkImageLayout::VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + stage = VkPipelineStageFlagBits::VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + access = VkAccessFlagBits::VK_ACCESS_SHADER_READ_BIT; + return; + } + }; + + mapUsageFn(barrier.lastUsage, srcLayout, srcStage, srcAccess); + mapUsageFn(barrier.curUsage, dstLayout, dstStage, dstAccess); + + if (imgBuffer != nullptr) + VulkanImageBuffer::BarrierCommand(buffer, *imgBuffer, srcLayout, dstLayout, srcStage, dstStage, srcAccess, dstAccess); + + if (msaaBuffer != nullptr) + { + // MSAA 버퍼를 다른 용도로 쓸 일이 없을듯? + //if (barrier.curUsage != ResourceUsage::Present) + //VulkanImageBuffer::BarrierCommand(cmd.GetCommandBuffer(), *msaaBuffer, srcLayout, dstLayout, srcStage, dstStage, srcAccess, dstAccess); + } + } + } SH_RENDER_API auto VulkanCommandBuffer::Create(VkCommandPool pool, VkCommandBufferLevel level) -> VkResult { Clear(); @@ -74,13 +533,15 @@ namespace sh::render::vk End(); return VkResult::VK_SUCCESS; } - SH_RENDER_API auto VulkanCommandBuffer::ResetCommand(VkCommandBufferResetFlags flags) -> VkResult + SH_RENDER_API void VulkanCommandBuffer::Reset() { - if (buffer == VK_NULL_HANDLE) - return VkResult::VK_ERROR_UNKNOWN; - VkResult result = vkResetCommandBuffer(buffer, flags); + if (buffer == VK_NULL_HANDLE) + return; + VkResult result = vkResetCommandBuffer(buffer, 0); assert(result == VkResult::VK_SUCCESS); - return result; + + bBeginRender = false; + renderState = RenderState{}; } SH_RENDER_API auto VulkanCommandBuffer::GetOrCreateFence() -> VkFence { @@ -112,6 +573,10 @@ namespace sh::render::vk { waitSemaphores.clear(); signalSemaphores.clear(); + if (fence != VK_NULL_HANDLE) + { + vkResetFences(context.GetDevice(), 1, &fence); + } } SH_RENDER_API void VulkanCommandBuffer::SetWaitSemaphores(const std::vector& waits) { @@ -129,22 +594,6 @@ namespace sh::render::vk { signalSemaphores.push_back(s); } - SH_RENDER_API auto VulkanCommandBuffer::GetWaitSemaphores() const -> const std::vector& - { - return waitSemaphores; - } - SH_RENDER_API auto VulkanCommandBuffer::GetSignalSemaphores() const -> const std::vector& - { - return signalSemaphores; - } - SH_RENDER_API auto VulkanCommandBuffer::GetCommandBuffer() const -> VkCommandBuffer - { - return buffer; - } - SH_RENDER_API auto VulkanCommandBuffer::GetCommandPool() const -> VkCommandPool - { - return cmdPool; - } SH_RENDER_API void VulkanCommandBuffer::Clear() { ResetSyncObjects(); @@ -159,37 +608,74 @@ namespace sh::render::vk cmdPool = VK_NULL_HANDLE; } - void VulkanCommandBuffer::Begin(bool bOnetime) + void VulkanCommandBuffer::BindCameraSet(const Material& mat, const ShaderPass& pass, VkPipelineLayout pipelineLayout, uint32_t cameraOffset) { - if (buffer == VK_NULL_HANDLE) - return; - - VkCommandBufferBeginInfo beginInfo{}; - beginInfo.sType = VkStructureType::VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - beginInfo.flags = bOnetime ? VkCommandBufferUsageFlagBits::VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT : 0; - beginInfo.pInheritanceInfo = nullptr; + VulkanDescriptorSet* const cameraUBO = static_cast( + mat.GetMaterialData().GetShaderBinding(pass, UniformStructLayout::Usage::Camera)); + VkDescriptorSet cameraSet = cameraUBO ? cameraUBO->GetVkDescriptorSet() : context.GetEmptyDescriptorSet(); + const uint32_t dynamicCount = cameraUBO ? 1 : 0; + vkCmdBindDescriptorSets(buffer, VkPipelineBindPoint::VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, + static_cast(UniformStructLayout::Usage::Camera), + 1, &cameraSet, dynamicCount, &cameraOffset); + } + void VulkanCommandBuffer::BindMaterialSet(const Material& mat, const ShaderPass& pass, VkPipelineLayout pipelineLayout) + { + VulkanDescriptorSet* const matUBO = static_cast( + mat.GetMaterialData().GetShaderBinding(pass, UniformStructLayout::Usage::Material)); + VkDescriptorSet matSet = matUBO ? matUBO->GetVkDescriptorSet() : context.GetEmptyDescriptorSet(); + vkCmdBindDescriptorSets(buffer, VkPipelineBindPoint::VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, + static_cast(UniformStructLayout::Usage::Material), + 1, &matSet, 0, nullptr); + } + void VulkanCommandBuffer::BindObjectSet(const Drawable& drawable, const ShaderPass& pass, VkPipelineLayout pipelineLayout) + { + VulkanDescriptorSet* const objUBO = static_cast( + drawable.GetMaterialData().GetShaderBinding(pass, UniformStructLayout::Usage::Object)); + VkDescriptorSet objSet = objUBO ? objUBO->GetVkDescriptorSet() : context.GetEmptyDescriptorSet(); + vkCmdBindDescriptorSets(buffer, VkPipelineBindPoint::VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, + static_cast(UniformStructLayout::Usage::Object), + 1, &objSet, 0, nullptr); + } + void VulkanCommandBuffer::BindMesh(const Mesh& mesh, uint32_t subMeshIdx, bool bSkinned) + { + uint32_t indexCount = 0; + uint32_t firstIndex = 0; + const std::vector& subMeshes = mesh.GetSubMeshes(); + const uint32_t subMeshIndex = subMeshIdx; + if (subMeshIndex < static_cast(subMeshes.size())) + { + indexCount = static_cast(subMeshes[subMeshIndex].indexCount); + firstIndex = static_cast(subMeshes[subMeshIndex].indexOffset); + } + else + { + indexCount = static_cast(mesh.GetIndices().size()); + firstIndex = 0; + } - VkResult result = vkBeginCommandBuffer(buffer, &beginInfo); - assert(result == VkResult::VK_SUCCESS); - if (result != VkResult::VK_SUCCESS) + if (bSkinned) { - const std::string err = fmt::format("Failed vkBeginCommandBuffer: {}", string_VkResult(result)); - SH_ERROR(err); - throw std::runtime_error{ err }; + const SkinnedMesh* const skinned = static_cast(&mesh); + const VulkanSkinnedVertexBuffer* vkSkinnedVB = + static_cast(skinned->GetVertexBuffer()); + VkBuffer buffers[2] = { + vkSkinnedVB->GetVertexBuffer().GetBuffer(), + vkSkinnedVB->GetBoneBuffer().GetBuffer() + }; + VkDeviceSize offsets[2] = { 0, 0 }; + vkCmdBindVertexBuffers(buffer, 0, 2, buffers, offsets); + vkCmdBindIndexBuffer(buffer, vkSkinnedVB->GetIndexBuffer().GetBuffer(), 0, VK_INDEX_TYPE_UINT32); } - } - void VulkanCommandBuffer::End() - { - if (buffer == VK_NULL_HANDLE) - return; - VkResult result = vkEndCommandBuffer(buffer); - assert(result == VkResult::VK_SUCCESS); - assert(result == VkResult::VK_SUCCESS); - if (result != VkResult::VK_SUCCESS) + else { - const std::string err = fmt::format("Failed vkEndCommandBuffer: {}", string_VkResult(result)); - SH_ERROR(err); - throw std::runtime_error{ err }; + const VulkanVertexBuffer* vkVB = static_cast(mesh.GetVertexBuffer()); + VkBuffer vbs[1] = { vkVB->GetVertexBuffer().GetBuffer() }; + VkDeviceSize offsets[1] = { 0 }; + vkCmdBindVertexBuffers(buffer, 0, 1, vbs, offsets); + vkCmdBindIndexBuffer(buffer, vkVB->GetIndexBuffer().GetBuffer(), 0, VkIndexType::VK_INDEX_TYPE_UINT32); } + + vkCmdDrawIndexed(buffer, indexCount, 1, firstIndex, 0, 0); + ++renderCall; } -} +}//namespace diff --git a/src/Render/VulkanImpl/VulkanCommandBufferPool.cpp b/src/Render/VulkanImpl/VulkanCommandBufferPool.cpp index ed75a675..825eb753 100644 --- a/src/Render/VulkanImpl/VulkanCommandBufferPool.cpp +++ b/src/Render/VulkanImpl/VulkanCommandBufferPool.cpp @@ -4,8 +4,8 @@ #include "Core/Logger.h" namespace sh::render::vk { - VulkanCommandBufferPool::VulkanCommandBufferPool(const VulkanContext& context, uint32_t graphicsQueueFamily, uint32_t transferQueueFamily) : - context(context), graphicsQueueFamily(graphicsQueueFamily), transferQueueFamily(transferQueueFamily) + VulkanCommandBufferPool::VulkanCommandBufferPool(const VulkanContext& context, uint32_t graphicsQueueFamily, uint32_t transferQueueFamily, uint32_t computeQueueFamily) : + context(context), graphicsQueueFamily(graphicsQueueFamily), transferQueueFamily(transferQueueFamily), computeQueueFamily(computeQueueFamily) { } @@ -40,6 +40,8 @@ namespace sh::render::vk return QueueType::Graphics; if (qt == VK_QUEUE_TRANSFER_BIT) return QueueType::Transfer; + if (qt == VK_QUEUE_COMPUTE_BIT) + return QueueType::Compute; SH_ERROR_FORMAT("Incorrect queueType: {}", string_VkQueueFlagBits(qt)); assert(false); @@ -49,7 +51,7 @@ namespace sh::render::vk // thread 데이터 찾기 (없으면 생성) PerThreadData* td = nullptr; - for (auto& t : threadDatas) + for (PerThreadData& t : threadDatas) { if (t.id == thr) { @@ -120,7 +122,7 @@ namespace sh::render::vk { if (td.id == ad.tid) { - ad.ptr->ResetCommand(); + ad.ptr->Reset(); ad.ptr->ResetSyncObjects(); td.commands[ad.type].cmds.push(std::move(ad.ptr)); return; @@ -140,6 +142,8 @@ namespace sh::render::vk poolInfo.queueFamilyIndex = graphicsQueueFamily; else if (queueType == VkQueueFlagBits::VK_QUEUE_TRANSFER_BIT) poolInfo.queueFamilyIndex = transferQueueFamily; + else if (queueType == VkQueueFlagBits::VK_QUEUE_COMPUTE_BIT) + poolInfo.queueFamilyIndex = computeQueueFamily; else { assert(false); @@ -148,7 +152,7 @@ namespace sh::render::vk } poolInfo.flags = VkCommandPoolCreateFlagBits::VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; //명령 버퍼가 개별적으로 기록되도록 허용 - auto result = vkCreateCommandPool(context.GetDevice(), &poolInfo, nullptr, &pool); + VkResult result = vkCreateCommandPool(context.GetDevice(), &poolInfo, nullptr, &pool); assert(result == VkResult::VK_SUCCESS); if (result != VkResult::VK_SUCCESS) { diff --git a/src/Render/VulkanImpl/VulkanComputePipeline.cpp b/src/Render/VulkanImpl/VulkanComputePipeline.cpp new file mode 100644 index 00000000..72455d31 --- /dev/null +++ b/src/Render/VulkanImpl/VulkanComputePipeline.cpp @@ -0,0 +1,247 @@ +#include "VulkanComputePipeline.h" +#include "VulkanContext.h" + +#include "Render/ComputeShader.h" + +#include "Core/Logger.h" + +#include +#include + +namespace sh::render::vk +{ + SH_RENDER_API VulkanComputePipeline::VulkanComputePipeline(const VulkanContext& context, const ComputeShader& shader) : + context(context), + shader(&shader) + { + } + + SH_RENDER_API VulkanComputePipeline::VulkanComputePipeline(VulkanComputePipeline&& other) noexcept : + context(other.context), + shader(other.shader), + shaderModule(other.shaderModule), + setlayouts(std::move(other.setlayouts)), + pipelineLayout(other.pipelineLayout), + pipeline(other.pipeline) + { + other.shader = nullptr; + other.shaderModule = VK_NULL_HANDLE; + other.pipelineLayout = VK_NULL_HANDLE; + other.pipeline = VK_NULL_HANDLE; + } + + SH_RENDER_API VulkanComputePipeline::~VulkanComputePipeline() + { + Clean(); + } + + SH_RENDER_API void VulkanComputePipeline::Clean() + { + if (pipeline != VK_NULL_HANDLE) + { + vkDestroyPipeline(context.GetDevice(), pipeline, nullptr); + pipeline = VK_NULL_HANDLE; + } + CleanDescriptors(); + if (shaderModule != VK_NULL_HANDLE) + { + vkDestroyShaderModule(context.GetDevice(), shaderModule, nullptr); + shaderModule = VK_NULL_HANDLE; + } + } + + SH_RENDER_API auto VulkanComputePipeline::Build() -> VkResult + { + assert(shader != nullptr); + Clean(); + + VkResult result = CreateShaderModule(); + if (result != VkResult::VK_SUCCESS) + { + SH_ERROR_FORMAT("Failed to CreateShaderModule(): {}", string_VkResult(result)); + return result; + } + + // 디스크립터 바인딩 수집 + for (const ShaderAST::BufferNode& buf : shader->GetNode().buffers) + { + VkDescriptorType type; + switch (buf.bufferType) + { + case ShaderAST::BufferType::Storage: + type = VkDescriptorType::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + break; + case ShaderAST::BufferType::Uniform: + type = VkDescriptorType::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + break; + case ShaderAST::BufferType::Sampler: + type = VkDescriptorType::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + break; + case ShaderAST::BufferType::PushConstant: + continue; // 푸시 상수는 디스크립터 아님 + default: + continue; + } + AddDescriptorBinding(buf.set, buf.binding, type); + } + + result = CreateDescriptorLayout(); + if (result != VkResult::VK_SUCCESS) + { + SH_ERROR_FORMAT("Failed to CreateDescriptorLayout(): {}", string_VkResult(result)); + return result; + } + + result = CreatePipelineLayout(); + if (result != VkResult::VK_SUCCESS) + { + SH_ERROR_FORMAT("Failed to CreatePipelineLayout(): {}", string_VkResult(result)); + return result; + } + + result = CreatePipeline(); + if (result != VkResult::VK_SUCCESS) + { + SH_ERROR_FORMAT("Failed to CreateComputePipeline(): {}", string_VkResult(result)); + return result; + } + + return VkResult::VK_SUCCESS; + } + + SH_RENDER_API auto VulkanComputePipeline::GetDescriptorSetLayout(uint32_t set) const -> VkDescriptorSetLayout + { + if (set >= setlayouts.size()) + return VK_NULL_HANDLE; + return setlayouts[set].descriptorSetLayout; + } + + SH_RENDER_API auto VulkanComputePipeline::GetDescriptorBinding(uint32_t set, uint32_t binding) const -> std::optional + { + if (set >= setlayouts.size()) + return {}; + const SetLayout& setlayout = setlayouts[set]; + auto it = setlayout.descriptorSetLayoutBindings.find(binding); + if (it == setlayout.descriptorSetLayoutBindings.end()) + return {}; + return it->second; + } + + SH_RENDER_API auto VulkanComputePipeline::GetGroupSize() const -> std::array + { + if (shader == nullptr) + return { 1, 1, 1 }; + return { shader->GetNumthreadsX(), shader->GetNumthreadsY(), shader->GetNumthreadsZ() }; + } + + auto VulkanComputePipeline::CreateShaderModule() -> VkResult + { + const std::vector& spirv = shader->GetSpirv(); + + VkShaderModuleCreateInfo info{}; + info.sType = VkStructureType::VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + info.codeSize = spirv.size(); + info.pCode = reinterpret_cast(spirv.data()); + + return vkCreateShaderModule(context.GetDevice(), &info, nullptr, &shaderModule); + } + + void VulkanComputePipeline::AddDescriptorBinding(uint32_t set, uint32_t binding, VkDescriptorType type) + { + VkDescriptorSetLayoutBinding layoutBinding{}; + layoutBinding.binding = binding; + layoutBinding.descriptorCount = 1; + layoutBinding.descriptorType = type; + layoutBinding.pImmutableSamplers = nullptr; + layoutBinding.stageFlags = VkShaderStageFlagBits::VK_SHADER_STAGE_COMPUTE_BIT; + + if (setlayouts.size() <= set) + setlayouts.resize(set + 1); + + setlayouts[set].set = set; + setlayouts[set].descriptorSetLayoutBindings.insert_or_assign(binding, layoutBinding); + } + + auto VulkanComputePipeline::CreateDescriptorLayout() -> VkResult + { + VkResult result = VkResult::VK_SUCCESS; + for (std::size_t i = 0; i < setlayouts.size(); ++i) + { + if (setlayouts[i].descriptorSetLayoutBindings.empty()) + { + setlayouts[i].set = static_cast(i); + setlayouts[i].descriptorSetLayout = context.GetEmptyDescriptorSetLayout(); + continue; + } + std::vector bindings; + bindings.reserve(setlayouts[i].descriptorSetLayoutBindings.size()); + for (const auto& [binding, layoutBinding] : setlayouts[i].descriptorSetLayoutBindings) + bindings.push_back(layoutBinding); + + VkDescriptorSetLayoutCreateInfo info{}; + info.sType = VkStructureType::VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + info.bindingCount = static_cast(bindings.size()); + info.pBindings = bindings.data(); + + VkDescriptorSetLayout layout{}; + result = vkCreateDescriptorSetLayout(context.GetDevice(), &info, nullptr, &layout); + if (result != VkResult::VK_SUCCESS) + return result; + + setlayouts[i].descriptorSetLayout = layout; + } + return result; + } + + auto VulkanComputePipeline::CreatePipelineLayout() -> VkResult + { + std::vector layouts(setlayouts.size(), VK_NULL_HANDLE); + for (std::size_t i = 0; i < setlayouts.size(); ++i) + layouts[i] = setlayouts[i].descriptorSetLayout; + + VkPipelineLayoutCreateInfo pipelineLayoutInfo{}; + pipelineLayoutInfo.sType = VkStructureType::VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutInfo.setLayoutCount = static_cast(layouts.size()); + pipelineLayoutInfo.pSetLayouts = layouts.data(); + pipelineLayoutInfo.pushConstantRangeCount = 0; + pipelineLayoutInfo.pPushConstantRanges = nullptr; + + return vkCreatePipelineLayout(context.GetDevice(), &pipelineLayoutInfo, nullptr, &pipelineLayout); + } + + auto VulkanComputePipeline::CreatePipeline() -> VkResult + { + VkPipelineShaderStageCreateInfo stageInfo{}; + stageInfo.sType = VkStructureType::VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + stageInfo.stage = VkShaderStageFlagBits::VK_SHADER_STAGE_COMPUTE_BIT; + stageInfo.module = shaderModule; + stageInfo.pName = "main"; + + VkComputePipelineCreateInfo info{}; + info.sType = VkStructureType::VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; + info.stage = stageInfo; + info.layout = pipelineLayout; + info.basePipelineHandle = VK_NULL_HANDLE; + info.basePipelineIndex = -1; + + return vkCreateComputePipelines(context.GetDevice(), VK_NULL_HANDLE, 1, &info, nullptr, &pipeline); + } + + void VulkanComputePipeline::CleanDescriptors() + { + if (pipelineLayout != VK_NULL_HANDLE) + { + vkDestroyPipelineLayout(context.GetDevice(), pipelineLayout, nullptr); + pipelineLayout = VK_NULL_HANDLE; + } + for (std::size_t i = 0; i < setlayouts.size(); ++i) + { + if (setlayouts[i].descriptorSetLayout != VK_NULL_HANDLE && + setlayouts[i].descriptorSetLayout != context.GetEmptyDescriptorSetLayout()) + { + vkDestroyDescriptorSetLayout(context.GetDevice(), setlayouts[i].descriptorSetLayout, nullptr); + } + } + setlayouts.clear(); + } +}//namespace diff --git a/src/Render/VulkanImpl/VulkanComputePipelineManager.cpp b/src/Render/VulkanImpl/VulkanComputePipelineManager.cpp new file mode 100644 index 00000000..8435a044 --- /dev/null +++ b/src/Render/VulkanImpl/VulkanComputePipelineManager.cpp @@ -0,0 +1,40 @@ +#include "Render/VulkanImpl/VulkanComputePipelineManager.h" +#include "Render/VulkanImpl/VulkanComputePipeline.h" +#include "Render/VulkanImpl/VulkanContext.h" +#include "Render/ComputeShader.h" +namespace sh::render::vk +{ + VulkanComputePipelineManager::VulkanComputePipelineManager(const VulkanContext& context) : + ctx(context) + { + onShaderDestroy.SetCallback( + [this](const core::SObject* shader) + { + std::unique_lock writeLock{ mu }; + auto it = pipelines.find(static_cast(shader)); + if (it != pipelines.end()) + pipelines.erase(it); + } + ); + } + SH_RENDER_API auto VulkanComputePipelineManager::GetOrCreatePipeline(const ComputeShader& computeShader) -> VulkanComputePipeline& + { + { + std::shared_lock readLock{ mu }; + auto it = pipelines.find(&computeShader); + if (it != pipelines.end()) + return *it->second; + } + { + std::unique_lock writeLock{ mu }; + auto it = pipelines.find(&computeShader); + if (it != pipelines.end()) + return *it->second; + computeShader.onDestroy.Register(onShaderDestroy); + auto result = pipelines.insert({ &computeShader, std::make_unique(ctx, computeShader) }); + VulkanComputePipeline& pipeline = *result.first->second; + pipeline.Build(); + return pipeline; + } + } +}//namespace \ No newline at end of file diff --git a/src/Render/VulkanImpl/VulkanContext.cpp b/src/Render/VulkanImpl/VulkanContext.cpp index ec1ff079..c62992fc 100644 --- a/src/Render/VulkanImpl/VulkanContext.cpp +++ b/src/Render/VulkanImpl/VulkanContext.cpp @@ -6,7 +6,8 @@ #include "VulkanCommandBuffer.h" #include "VulkanDescriptorPool.h" #include "VulkanPipelineManager.h" -#include "VulkanRenderImpl.h" +#include "VulkanComputePipeline.h" +#include "VulkanComputePipelineManager.h" #include "Core/Util.h" #include "Core/Logger.h" @@ -41,6 +42,202 @@ namespace sh::render::vk SH_INFO_FORMAT("{}", pCallbackData->pMessage); return VK_FALSE; } + + VulkanContext::VulkanContext(const sh::window::Window& window) : + window(window), + sample(VkSampleCountFlagBits::VK_SAMPLE_COUNT_4_BIT) + { + bEnableValidationLayers = core::Util::IsDebug(); + + requestedDeviceExtension = + { + VK_KHR_SWAPCHAIN_EXTENSION_NAME, + VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME + }; + } + VulkanContext::~VulkanContext() + { + if (device != nullptr) + Clear(); + } + + SH_RENDER_API void VulkanContext::Init() + { + if (bInit) + return; + bInit = true; + + layers = std::make_unique(); + layers->Query(); + + if (bEnableValidationLayers) + PrepareValidationLayer(); + + PrepareSurfaceExtension(); + + CreateInstance(); + + if (bEnableValidationLayers) + InitDebugMessenger(); + + swapChain = std::make_unique(*this); + swapChain->CreateSurface(window); + + GetPhysicalDevices(); + + gpu = SelectPhysicalDevice(); + assert(gpu != nullptr); + if (gpu == nullptr) + throw std::runtime_error("Can't find suitable GPU"); + vkGetPhysicalDeviceProperties(gpu, &gpuProp); + + queueManager = std::make_unique(*this); + queueManager->QueryFamilies(swapChain->GetSurface()); + + CreateDevice(gpu); + + queueManager->FetchQueues(); + + CreateAllocator(); + + swapChain->CreateSwapChain( + queueManager->GetFamilyIndex(VulkanQueueManager::Role::Graphics), + queueManager->GetFamilyIndex(VulkanQueueManager::Role::Present), + false); + + CreateCommandPool(); + + descPool = std::make_unique(device); + CreateEmptyDescriptor(); + + pipelineManager = std::make_unique(*this); + computePipelineManager = std::make_unique(*this); + + renderDataManager.Init(*this); + } + SH_RENDER_API void VulkanContext::Clear() + { + bInit = false; + + vkDeviceWaitIdle(device); + + IRenderThrMethod::ClearBuffer(renderDataManager); + pipelineManager.reset(); + computePipelineManager.reset(); + if (emptyDescLayout) + { + vkDestroyDescriptorSetLayout(device, emptyDescLayout, nullptr); + emptyDescLayout = nullptr; + } + descPool.reset(); + DestroyCommandPool(); + swapChain.reset(); + queueManager.reset(); + DestroyAllocator(); + DestroyDevice(); + if (bEnableValidationLayers) + DestroyDebugMessenger(); + gpu = VK_NULL_HANDLE; + gpus.clear(); + DestroyInstance(); + + device = nullptr; + } + SH_RENDER_API auto VulkanContext::AllocateCommandBuffer(bool bCompute) -> CommandBuffer* + { + VkQueueFlagBits flag = VkQueueFlagBits::VK_QUEUE_GRAPHICS_BIT; + if (bCompute) + flag = VkQueueFlagBits::VK_QUEUE_COMPUTE_BIT; + VulkanCommandBuffer* const cmd = GetCommandBufferPool().AllocateCommandBuffer(std::this_thread::get_id(), flag); + return cmd; + } + SH_RENDER_API void VulkanContext::DeallocateCommandBuffer(CommandBuffer& cmd) + { + GetCommandBufferPool().DeallocateCommandBuffer(static_cast(cmd)); + } + SH_RENDER_API void VulkanContext::SubmitCommand(CommandBuffer& cmd) + { + VulkanCommandBuffer& vkCmd = static_cast(cmd); + VkFence fence = vkCmd.GetOrCreateFence(); + GetQueueManager().Submit(VulkanQueueManager::Role::Compute, vkCmd, fence); + vkWaitForFences(device, 1, &fence, true, std::numeric_limits::max()); + } + SH_RENDER_API void VulkanContext::SetViewport(const glm::vec2& start, const glm::vec2& end) + { + viewportStart = start; + viewportEnd = end; + } + SH_RENDER_API auto VulkanContext::ReSizing() -> bool + { + //swapChain->DestroySwapChain(); + swapChain->CreateSwapChain( + queueManager->GetFamilyIndex(VulkanQueueManager::Role::Graphics), + queueManager->GetFamilyIndex(VulkanQueueManager::Role::Present), + false); + return true; + } + SH_RENDER_API void VulkanContext::PrintLayers() + { + for (auto& prop : layers->GetLayerProperties()) + { + SH_INFO_FORMAT("LayerName: {} - {}", prop.properties.layerName, prop.properties.description); + for (auto& ext : prop.extensions) + SH_INFO_FORMAT("ExtensionName: {}", ext.extensionName); + } + SH_INFO("-----Vulkan Extensions-----"); + for (auto& i : layers->GetVulkanExtensions()) + SH_INFO_FORMAT("ExtensionName: {}", i.extensionName); + + SH_INFO("-----GPU Layer------"); + for (auto& i : layers->GetGPULayerProperties()) + { + SH_INFO_FORMAT("LayerName: {} - {}", i.properties.layerName, i.properties.description); + for (auto& ext : i.extensions) + SH_INFO_FORMAT("ExtensionName: {}", ext.extensionName); + } + SH_INFO("-----GPU Extensions------"); + for (auto& i : layers->GetGPUExtensions()) + SH_INFO_FORMAT("ExtensionName: {}", i.extensionName); + } + SH_RENDER_API auto VulkanContext::FindSupportedDepthFormat(bool bUseStencil) const -> VkFormat + { + const std::array formats = { VkFormat::VK_FORMAT_D32_SFLOAT_S8_UINT, VkFormat::VK_FORMAT_D24_UNORM_S8_UINT, VkFormat::VK_FORMAT_D16_UNORM_S8_UINT }; + + auto feature = VkFormatFeatureFlagBits::VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT; + for (VkFormat format : formats) + { + VkFormatProperties props; + vkGetPhysicalDeviceFormatProperties(gpu, format, &props); + + if (props.optimalTilingFeatures & feature) + return format; + } + + throw std::runtime_error("Failed to find supported Depth format!"); + } + SH_RENDER_API void VulkanContext::SetSampleCount(VkSampleCountFlagBits sample) + { + VkSampleCountFlagBits maxSample = GetMaxSampleCount(); + if (sample > maxSample) + sample = maxSample; + + this->sample = sample; + } + SH_RENDER_API auto VulkanContext::GetMaxSampleCount() const -> VkSampleCountFlagBits + { + VkSampleCountFlags supportedSampleCount = + std::min(gpuProp.limits.framebufferColorSampleCounts, gpuProp.limits.framebufferDepthSampleCounts); + + if (supportedSampleCount & VkSampleCountFlagBits::VK_SAMPLE_COUNT_64_BIT) return VkSampleCountFlagBits::VK_SAMPLE_COUNT_64_BIT; + if (supportedSampleCount & VkSampleCountFlagBits::VK_SAMPLE_COUNT_32_BIT) return VkSampleCountFlagBits::VK_SAMPLE_COUNT_32_BIT; + if (supportedSampleCount & VkSampleCountFlagBits::VK_SAMPLE_COUNT_16_BIT) return VkSampleCountFlagBits::VK_SAMPLE_COUNT_16_BIT; + if (supportedSampleCount & VkSampleCountFlagBits::VK_SAMPLE_COUNT_8_BIT) return VkSampleCountFlagBits::VK_SAMPLE_COUNT_8_BIT; + if (supportedSampleCount & VkSampleCountFlagBits::VK_SAMPLE_COUNT_4_BIT) return VkSampleCountFlagBits::VK_SAMPLE_COUNT_4_BIT; + if (supportedSampleCount & VkSampleCountFlagBits::VK_SAMPLE_COUNT_2_BIT) return VkSampleCountFlagBits::VK_SAMPLE_COUNT_2_BIT; + + return VkSampleCountFlagBits::VK_SAMPLE_COUNT_1_BIT; + } + void VulkanContext::CreateDebugInfo() { //validationEnables.push_back(VkValidationFeatureEnableEXT::VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT); @@ -165,9 +362,9 @@ namespace sh::render::vk bool swapchainExSupport = layers->FindGPUExtension(gpu, VK_KHR_SWAPCHAIN_EXTENSION_NAME); - if (prop.deviceType == VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU || - prop.deviceType == VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_CPU && - swapchainExSupport && + if (prop.deviceType == VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU || + prop.deviceType == VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_CPU && + swapchainExSupport && swapChain->IsSwapChainSupport(gpu)) return gpu; } @@ -241,9 +438,10 @@ namespace sh::render::vk void VulkanContext::CreateCommandPool() { cmdPool = std::make_unique( - *this, - queueManager->GetFamilyIndex(VulkanQueueManager::Role::Graphics), - queueManager->GetFamilyIndex(VulkanQueueManager::Role::Transfer)); + *this, + queueManager->GetFamilyIndex(VulkanQueueManager::Role::Graphics), + queueManager->GetFamilyIndex(VulkanQueueManager::Role::Transfer), + queueManager->GetFamilyIndex(VulkanQueueManager::Role::Compute)); } void VulkanContext::DestroyCommandPool() { @@ -254,202 +452,10 @@ namespace sh::render::vk VkDescriptorSetLayoutCreateInfo info{}; info.sType = VkStructureType::VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; info.bindingCount = 0; + info.pBindings = nullptr; vkCreateDescriptorSetLayout(device, &info, nullptr, &emptyDescLayout); emptyDescSet = descPool->AllocateDescriptorSet(emptyDescLayout); } - VulkanContext::VulkanContext(const sh::window::Window& window) : - window(window), - sample(VkSampleCountFlagBits::VK_SAMPLE_COUNT_4_BIT) - { - bEnableValidationLayers = core::Util::IsDebug(); - - requestedDeviceExtension = - { - VK_KHR_SWAPCHAIN_EXTENSION_NAME, - VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME - }; - } - VulkanContext::~VulkanContext() - { - - } - - SH_RENDER_API void VulkanContext::Init() - { - if (bInit) - return; - bInit = true; - - layers = std::make_unique(); - layers->Query(); - - if (bEnableValidationLayers) - PrepareValidationLayer(); - - PrepareSurfaceExtension(); - - CreateInstance(); - - if (bEnableValidationLayers) - InitDebugMessenger(); - - swapChain = std::make_unique(*this); - swapChain->CreateSurface(window); - - GetPhysicalDevices(); - - gpu = SelectPhysicalDevice(); - assert(gpu != nullptr); - if (gpu == nullptr) - throw std::runtime_error("Can't find suitable GPU"); - vkGetPhysicalDeviceProperties(gpu, &gpuProp); - - queueManager = std::make_unique(*this); - queueManager->QueryFamilies(swapChain->GetSurface()); - - CreateDevice(gpu); - - queueManager->FetchQueues(); - - CreateAllocator(); - - swapChain->CreateSwapChain( - queueManager->GetFamilyIndex(VulkanQueueManager::Role::Graphics), - queueManager->GetFamilyIndex(VulkanQueueManager::Role::Present), - false); - - CreateCommandPool(); - - descPool = std::make_unique(device); - CreateEmptyDescriptor(); - - pipelineManager = std::make_unique(*this); - - renderImpl = std::make_unique(*this); - } - SH_RENDER_API void VulkanContext::Clear() - { - bInit = false; - - vkDeviceWaitIdle(device); - - pipelineManager.reset(); - if (emptyDescLayout) - { - vkDestroyDescriptorSetLayout(device, emptyDescLayout, nullptr); - emptyDescLayout = nullptr; - } - descPool.reset(); - DestroyCommandPool(); - swapChain.reset(); - queueManager.reset(); - DestroyAllocator(); - DestroyDevice(); - if (bEnableValidationLayers) - DestroyDebugMessenger(); - gpu = VK_NULL_HANDLE; - gpus.clear(); - DestroyInstance(); - } - SH_RENDER_API auto VulkanContext::AllocateCommandBuffer() -> CommandBuffer* - { - auto cmd = GetCommandBufferPool().AllocateCommandBuffer(std::this_thread::get_id(), VkQueueFlagBits::VK_QUEUE_GRAPHICS_BIT); - return cmd; - } - SH_RENDER_API void VulkanContext::DeallocateCommandBuffer(CommandBuffer& cmd) - { - GetCommandBufferPool().DeallocateCommandBuffer(static_cast(cmd)); - } - SH_RENDER_API void VulkanContext::SetViewport(const glm::vec2& start, const glm::vec2& end) - { - viewportStart = start; - viewportEnd = end; - } - - SH_RENDER_API auto VulkanContext::GetViewportStart() const -> const glm::vec2& - { - return viewportStart; - } - SH_RENDER_API auto VulkanContext::GetViewportEnd() const -> const glm::vec2& - { - return viewportEnd; - } - - SH_RENDER_API auto VulkanContext::GetRenderImpl() const -> IRenderImpl& - { - return *renderImpl; - } - - SH_RENDER_API auto VulkanContext::ReSizing() -> bool - { - //swapChain->DestroySwapChain(); - swapChain->CreateSwapChain( - queueManager->GetFamilyIndex(VulkanQueueManager::Role::Graphics), - queueManager->GetFamilyIndex(VulkanQueueManager::Role::Present), - false); - return true; - } - SH_RENDER_API void VulkanContext::PrintLayers() - { - for (auto& prop : layers->GetLayerProperties()) - { - SH_INFO_FORMAT("LayerName: {} - {}", prop.properties.layerName, prop.properties.description); - for (auto& ext : prop.extensions) - SH_INFO_FORMAT("ExtensionName: {}", ext.extensionName); - } - SH_INFO("-----Vulkan Extensions-----"); - for (auto& i : layers->GetVulkanExtensions()) - SH_INFO_FORMAT("ExtensionName: {}", i.extensionName); - - SH_INFO("-----GPU Layer------"); - for (auto& i : layers->GetGPULayerProperties()) - { - SH_INFO_FORMAT("LayerName: {} - {}", i.properties.layerName, i.properties.description); - for (auto& ext : i.extensions) - SH_INFO_FORMAT("ExtensionName: {}", ext.extensionName); - } - SH_INFO("-----GPU Extensions------"); - for (auto& i : layers->GetGPUExtensions()) - SH_INFO_FORMAT("ExtensionName: {}", i.extensionName); - } - SH_RENDER_API auto VulkanContext::FindSupportedDepthFormat(bool bUseStencil) const -> VkFormat - { - const std::array formats = { VkFormat::VK_FORMAT_D32_SFLOAT_S8_UINT, VkFormat::VK_FORMAT_D24_UNORM_S8_UINT, VkFormat::VK_FORMAT_D16_UNORM_S8_UINT }; - - auto feature = VkFormatFeatureFlagBits::VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT; - for (VkFormat format : formats) - { - VkFormatProperties props; - vkGetPhysicalDeviceFormatProperties(gpu, format, &props); - - if (props.optimalTilingFeatures & feature) - return format; - } - - throw std::runtime_error("Failed to find supported Depth format!"); - } - SH_RENDER_API void VulkanContext::SetSampleCount(VkSampleCountFlagBits sample) - { - VkSampleCountFlagBits maxSample = GetMaxSampleCount(); - if (sample > maxSample) - sample = maxSample; - - this->sample = sample; - } - SH_RENDER_API auto VulkanContext::GetMaxSampleCount() const -> VkSampleCountFlagBits - { - VkSampleCountFlags supportedSampleCount = - std::min(gpuProp.limits.framebufferColorSampleCounts, gpuProp.limits.framebufferDepthSampleCounts); - - if (supportedSampleCount & VkSampleCountFlagBits::VK_SAMPLE_COUNT_64_BIT) return VkSampleCountFlagBits::VK_SAMPLE_COUNT_64_BIT; - if (supportedSampleCount & VkSampleCountFlagBits::VK_SAMPLE_COUNT_32_BIT) return VkSampleCountFlagBits::VK_SAMPLE_COUNT_32_BIT; - if (supportedSampleCount & VkSampleCountFlagBits::VK_SAMPLE_COUNT_16_BIT) return VkSampleCountFlagBits::VK_SAMPLE_COUNT_16_BIT; - if (supportedSampleCount & VkSampleCountFlagBits::VK_SAMPLE_COUNT_8_BIT) return VkSampleCountFlagBits::VK_SAMPLE_COUNT_8_BIT; - if (supportedSampleCount & VkSampleCountFlagBits::VK_SAMPLE_COUNT_4_BIT) return VkSampleCountFlagBits::VK_SAMPLE_COUNT_4_BIT; - if (supportedSampleCount & VkSampleCountFlagBits::VK_SAMPLE_COUNT_2_BIT) return VkSampleCountFlagBits::VK_SAMPLE_COUNT_2_BIT; - - return VkSampleCountFlagBits::VK_SAMPLE_COUNT_1_BIT; - } }//namespace \ No newline at end of file diff --git a/src/Render/VulkanImpl/VulkanDescriptorPool.cpp b/src/Render/VulkanImpl/VulkanDescriptorPool.cpp index 1ee573d6..37d3d8aa 100644 --- a/src/Render/VulkanImpl/VulkanDescriptorPool.cpp +++ b/src/Render/VulkanImpl/VulkanDescriptorPool.cpp @@ -157,10 +157,11 @@ namespace sh::render::vk } auto VulkanDescriptorPool::CreatePool(uint32_t setCapacity) -> VkDescriptorPool { - std::array poolSizes{}; + std::array poolSizes{}; poolSizes[0] = { VkDescriptorType::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, setCapacity }; poolSizes[1] = { VkDescriptorType::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, setCapacity }; poolSizes[2] = { VkDescriptorType::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, setCapacity }; + poolSizes[3] = { VkDescriptorType::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, setCapacity }; VkDescriptorPoolCreateInfo info{}; info.sType = VkStructureType::VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; diff --git a/src/Render/VulkanImpl/VulkanUniformBuffer.cpp b/src/Render/VulkanImpl/VulkanDescriptorSet.cpp similarity index 51% rename from src/Render/VulkanImpl/VulkanUniformBuffer.cpp rename to src/Render/VulkanImpl/VulkanDescriptorSet.cpp index 56525d95..8f15c785 100644 --- a/src/Render/VulkanImpl/VulkanUniformBuffer.cpp +++ b/src/Render/VulkanImpl/VulkanDescriptorSet.cpp @@ -1,59 +1,91 @@ -#include "VulkanUniformBuffer.h" +#include "VulkanDescriptorSet.h" #include "VulkanContext.h" #include "VulkanShaderPass.h" #include "VulkanBuffer.h" #include "VulkanImageBuffer.h" #include "VulkanDescriptorPool.h" +#include "VulkanComputePipelineManager.h" +#include "VulkanComputePipeline.h" +#include "ComputeShader.h" #include namespace sh::render::vk { - VulkanUniformBuffer::VulkanUniformBuffer() : + VulkanDescriptorSet::VulkanDescriptorSet() : context(nullptr), descSet(nullptr) { } - VulkanUniformBuffer::VulkanUniformBuffer(VulkanUniformBuffer&& other) noexcept : + VulkanDescriptorSet::VulkanDescriptorSet(VulkanDescriptorSet&& other) noexcept : context(other.context), descSet(other.descSet), - bDynamic(other.bDynamic) + set(other.set), + descriptorTypes(std::move(other.descriptorTypes)) { other.context = nullptr; other.descSet = nullptr; } - VulkanUniformBuffer::~VulkanUniformBuffer() + VulkanDescriptorSet::~VulkanDescriptorSet() { Clear(); } - void VulkanUniformBuffer::Create(const IRenderContext& context, const ShaderPass& shader, UniformStructLayout::Type type) + void VulkanDescriptorSet::Create(const IRenderContext& context, const ShaderPass& shader, UniformStructLayout::Usage usage) { Clear(); this->context = &static_cast(context); - auto& vkShader = static_cast(shader); + const VulkanShaderPass& vkShader = static_cast(shader); - set = static_cast(type); + set = static_cast(usage); - auto layoutInfo = vkShader.GetDescriptorSetLayout(set); - VkDescriptorSetLayout layout = layoutInfo.layout; - bDynamic = layoutInfo.bDynamic; + if (auto opt = vkShader.GetSetLayout(set); opt.has_value()) + { + const VulkanShaderPass::SetLayout& setLayout = opt.value(); + for (const auto& [binding, layoutBinding] : setLayout.descriptorSetLayoutBindings) + descriptorTypes[binding] = layoutBinding.descriptorType; + } - if (layout != VK_NULL_HANDLE) - descSet = this->context->GetDescriptorPool().AllocateDescriptorSet(layout); + const VkDescriptorSetLayout descLayout = vkShader.GetDescriptorSetLayout(set); + if (descLayout != VK_NULL_HANDLE) + descSet = this->context->GetDescriptorPool().AllocateDescriptorSet(descLayout); } - void VulkanUniformBuffer::Clear() + SH_RENDER_API void VulkanDescriptorSet::Create(const IRenderContext& ctx, const ComputeShader& shader) + { + Clear(); + context = &static_cast(ctx); + set = 0; + const VulkanComputePipeline& pipeline = context->GetComputePipelineManager().GetOrCreatePipeline(shader); + + const VkDescriptorSetLayout descLayout = pipeline.GetDescriptorSetLayout(set); + if (descLayout != VK_NULL_HANDLE) + descSet = this->context->GetDescriptorPool().AllocateDescriptorSet(descLayout); + + for (const VulkanComputePipeline::SetLayout& setLayout : pipeline.GetSetLayouts()) + { + if (set != setLayout.set) + continue; + for (const auto& [binding, layoutBinding] : setLayout.descriptorSetLayoutBindings) + descriptorTypes[binding] = layoutBinding.descriptorType; + } + } + void VulkanDescriptorSet::Clear() { if (descSet == nullptr || context == nullptr) return; context->GetDescriptorPool().FreeDescriptorSet(descSet); + descSet = VK_NULL_HANDLE; + descriptorTypes.clear(); } - void VulkanUniformBuffer::Link(uint32_t binding, const IBuffer& buffer, std::size_t bufferSize) + void VulkanDescriptorSet::Link(uint32_t binding, const IBuffer& buffer, std::size_t bufferSize) { if (context == nullptr || descSet == nullptr) return; + auto descriptorTypesIt = descriptorTypes.find(binding); + if (descriptorTypesIt == descriptorTypes.end()) + return; - auto& vkBuffer = static_cast(buffer); + const VulkanBuffer& vkBuffer = static_cast(buffer); VkDescriptorBufferInfo bufferInfo{}; bufferInfo.buffer = vkBuffer.GetBuffer(); @@ -65,8 +97,7 @@ namespace sh::render::vk descriptorWrite.dstSet = descSet; descriptorWrite.dstBinding = binding; descriptorWrite.dstArrayElement = 0; - descriptorWrite.descriptorType = bDynamic ? - VkDescriptorType::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC : VkDescriptorType::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptorWrite.descriptorType = descriptorTypesIt->second; descriptorWrite.descriptorCount = 1; descriptorWrite.pBufferInfo = &bufferInfo; descriptorWrite.pImageInfo = nullptr; @@ -74,7 +105,7 @@ namespace sh::render::vk vkUpdateDescriptorSets(context->GetDevice(), 1, &descriptorWrite, 0, nullptr); } - void VulkanUniformBuffer::Link(uint32_t binding, const Texture& texture) + void VulkanDescriptorSet::Link(uint32_t binding, const Texture& texture) { if (context == nullptr || descSet == nullptr) return; @@ -97,12 +128,4 @@ namespace sh::render::vk vkUpdateDescriptorSets(context->GetDevice(), 1, &descriptorWrite, 0, nullptr); } - SH_RENDER_API auto VulkanUniformBuffer::GetSetNumber() const -> uint32_t - { - return set; - } - SH_RENDER_API auto VulkanUniformBuffer::GetVkDescriptorSet() const -> VkDescriptorSet - { - return descSet; - } }//namespace \ No newline at end of file diff --git a/src/Render/VulkanImpl/VulkanImageBuffer.cpp b/src/Render/VulkanImpl/VulkanImageBuffer.cpp index 6f551891..174a7dc8 100644 --- a/src/Render/VulkanImpl/VulkanImageBuffer.cpp +++ b/src/Render/VulkanImpl/VulkanImageBuffer.cpp @@ -25,17 +25,22 @@ namespace sh::render::vk sample(other.sample), width(other.width), height(other.height), - channel(other.channel), + pixelSize(other.pixelSize), aniso(other.aniso), mipLevel(other.mipLevel), bSwapChainImg(other.bSwapChainImg), bRenderTarget(other.bRenderTarget) { - other.Clear(); + other.img = nullptr; + other.imgMem = nullptr; + other.imgView = nullptr; + other.sampler = nullptr; } SH_RENDER_API auto VulkanImageBuffer::operator=(VulkanImageBuffer&& other) noexcept -> VulkanImageBuffer& { + if (this == &other) + return *this; Clear(); ctx = other.ctx; @@ -51,7 +56,7 @@ namespace sh::render::vk sample = other.sample; width = other.width; height = other.height; - channel = other.channel; + pixelSize = other.pixelSize; aniso = other.aniso; mipLevel = other.mipLevel; @@ -83,14 +88,18 @@ namespace sh::render::vk const bool bColorImg = !IsDepthTexture(info.format); format = ConvertTextureFormat(info.format); sample = info.bMSAAImg ? ctx->GetSampleCount() : VkSampleCountFlagBits::VK_SAMPLE_COUNT_1_BIT; - aspect = bColorImg ? - (VkImageAspectFlagBits::VK_IMAGE_ASPECT_COLOR_BIT) : - (VkImageAspectFlagBits::VK_IMAGE_ASPECT_DEPTH_BIT | VkImageAspectFlagBits::VK_IMAGE_ASPECT_STENCIL_BIT); + + const bool bSampleableDepthRT = !bColorImg && info.bRenderTarget && !info.bMSAAImg; + aspect = bColorImg ? + (VkImageAspectFlagBits::VK_IMAGE_ASPECT_COLOR_BIT) : + (bSampleableDepthRT ? + VkImageAspectFlagBits::VK_IMAGE_ASPECT_DEPTH_BIT : + (VkImageAspectFlagBits::VK_IMAGE_ASPECT_DEPTH_BIT | VkImageAspectFlagBits::VK_IMAGE_ASPECT_STENCIL_BIT)); filter = info.filtering == 0 ? VkFilter::VK_FILTER_NEAREST : VkFilter::VK_FILTER_LINEAR; width = info.width == 0 ? 1 : info.width; height = info.height == 0 ? 1 : info.height; - channel = GetChannelCount(format); + pixelSize = GetFormatPixelSize(format); aniso = info.aniso; mipLevel = actualMip; @@ -119,7 +128,11 @@ namespace sh::render::vk VkImageUsageFlagBits::VK_IMAGE_USAGE_SAMPLED_BIT; } else + { usage = VkImageUsageFlagBits::VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + if (info.bRenderTarget) + usage |= VkImageUsageFlagBits::VK_IMAGE_USAGE_SAMPLED_BIT; + } } VkImageCreateInfo imageCi{}; imageCi.sType = VkStructureType::VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; @@ -200,7 +213,7 @@ namespace sh::render::vk filter = VkFilter::VK_FILTER_LINEAR; format = VkFormat::VK_FORMAT_UNDEFINED; width = height = 0; - channel = 4; + pixelSize = 4; aniso = 0; mipLevel = 1; @@ -242,7 +255,7 @@ namespace sh::render::vk mipWidth = std::max(1u, mipWidth / 2); mipHeight = std::max(1u, mipHeight / 2); } - std::size_t bufferSize = mipWidth * mipHeight * channel; + std::size_t bufferSize = mipWidth * mipHeight * pixelSize; stagingBuffer.Create(bufferSize, VkBufferUsageFlagBits::VK_BUFFER_USAGE_TRANSFER_SRC_BIT, @@ -318,7 +331,7 @@ namespace sh::render::vk sample = VkSampleCountFlagBits::VK_SAMPLE_COUNT_1_BIT; width = swapChain.GetSwapChainSize().width; height = swapChain.GetSwapChainSize().height; - channel = GetChannelCount(format); + pixelSize = GetFormatPixelSize(format); aniso = 0; mipLevel = 1; @@ -407,6 +420,8 @@ namespace sh::render::vk case TextureFormat::RGB24: [[fallthrough]]; // 대부분 지원 안 함 case TextureFormat::RGBA32: return VkFormat::VK_FORMAT_R8G8B8A8_UNORM; + case TextureFormat::RG32F: + return VkFormat::VK_FORMAT_R16G16_SFLOAT; case TextureFormat::BGR24: [[fallthrough]]; // 대부분 지원 안 함 case TextureFormat::BGRA32: return VkFormat::VK_FORMAT_B8G8R8A8_UNORM; @@ -418,11 +433,15 @@ namespace sh::render::vk return VkFormat::VK_FORMAT_D24_UNORM_S8_UINT; case TextureFormat::D16S8: return VkFormat::VK_FORMAT_D16_UNORM_S8_UINT; + case TextureFormat::D32: + return VkFormat::VK_FORMAT_D32_SFLOAT; + case TextureFormat::D16: + return VkFormat::VK_FORMAT_D16_UNORM; default: return VkFormat::VK_FORMAT_UNDEFINED; } } - auto VulkanImageBuffer::GetChannelCount(VkFormat format) -> uint32_t + auto VulkanImageBuffer::GetFormatPixelSize(VkFormat format) -> uint32_t { switch (format) { @@ -431,21 +450,17 @@ namespace sh::render::vk case VkFormat::VK_FORMAT_B8G8R8A8_UNORM: [[fallthrough]]; case VkFormat::VK_FORMAT_B8G8R8A8_SRGB: [[fallthrough]]; return 4; + case VkFormat::VK_FORMAT_R16G16_SFLOAT: + return sizeof(uint16_t) * 2; case VkFormat::VK_FORMAT_D32_SFLOAT_S8_UINT: [[fallthrough]]; case VkFormat::VK_FORMAT_D24_UNORM_S8_UINT: [[fallthrough]]; case VkFormat::VK_FORMAT_D16_UNORM_S8_UINT: [[fallthrough]]; + case VkFormat::VK_FORMAT_D32_SFLOAT: [[fallthrough]]; + case VkFormat::VK_FORMAT_D16_UNORM: [[fallthrough]]; case VkFormat::VK_FORMAT_R8_UNORM: return 1; default: return 0; } } - auto VulkanImageBuffer::IsDepthTexture(TextureFormat format) -> bool - { - if (format == TextureFormat::D32S8 || - format == TextureFormat::D24S8 || - format == TextureFormat::D16S8) - return true; - return false; - } -} \ No newline at end of file +}//namespace diff --git a/src/Render/VulkanImpl/VulkanPipeline.cpp b/src/Render/VulkanImpl/VulkanPipeline.cpp index 9a34084a..6cf8db32 100644 --- a/src/Render/VulkanImpl/VulkanPipeline.cpp +++ b/src/Render/VulkanImpl/VulkanPipeline.cpp @@ -6,6 +6,16 @@ #include namespace sh::render::vk { + namespace + { + auto HasStencil(TextureFormat format) -> bool + { + return format == TextureFormat::D32S8 || + format == TextureFormat::D24S8 || + format == TextureFormat::D16S8; + } + } + SH_RENDER_API VulkanPipeline::VulkanPipeline(VkDevice device, const RenderTargetLayout& rt) : device(device), pipeline(nullptr), shader(nullptr), @@ -223,12 +233,15 @@ namespace sh::render::vk colorBlendAttachment.alphaBlendOp = VkBlendOp::VK_BLEND_OP_ADD; colorBlendAttachment.dstAlphaBlendFactor = VkBlendFactor::VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;; + const uint32_t colorAttachmentCount = static_cast(rtLayout.colorFormats.size()); + VkPipelineColorBlendStateCreateInfo colorBlending{}; + std::vector colorBlendAttachments(colorAttachmentCount, colorBlendAttachment); colorBlending.sType = VkStructureType::VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; colorBlending.logicOpEnable = VK_FALSE; colorBlending.logicOp = VkLogicOp::VK_LOGIC_OP_COPY; - colorBlending.attachmentCount = 1; - colorBlending.pAttachments = &colorBlendAttachment; + colorBlending.attachmentCount = colorAttachmentCount; + colorBlending.pAttachments = colorBlendAttachments.empty() ? nullptr : colorBlendAttachments.data(); colorBlending.blendConstants[0] = 0.0f; colorBlending.blendConstants[1] = 0.0f; colorBlending.blendConstants[2] = 0.0f; @@ -257,15 +270,20 @@ namespace sh::render::vk stageCreateInfo.pSpecializationInfo = &specializationInfo; } - VkFormat colorFormat = VulkanImageBuffer::ConvertTextureFormat(rtLayout.format); + std::vector colorFormats; + colorFormats.reserve(colorAttachmentCount); + for (TextureFormat format : rtLayout.colorFormats) + colorFormats.push_back(VulkanImageBuffer::ConvertTextureFormat(format)); + VkFormat depthFormat = VulkanImageBuffer::ConvertTextureFormat(rtLayout.depthFormat); VkPipelineRenderingCreateInfoKHR pipelineRenderingCI{}; pipelineRenderingCI.sType = VkStructureType::VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR; - pipelineRenderingCI.colorAttachmentCount = 1; - pipelineRenderingCI.pColorAttachmentFormats = &colorFormat; + pipelineRenderingCI.colorAttachmentCount = colorAttachmentCount; + pipelineRenderingCI.pColorAttachmentFormats = colorFormats.empty() ? nullptr : colorFormats.data(); pipelineRenderingCI.depthAttachmentFormat = depthFormat; - pipelineRenderingCI.stencilAttachmentFormat = depthFormat; + // depth-only RT는 view aspect=DEPTH-only로 만들었으므로 stencil 포맷도 비활성화. + pipelineRenderingCI.stencilAttachmentFormat = (!rtLayout.IsDepthOnly() && HasStencil(rtLayout.depthFormat)) ? depthFormat : VkFormat::VK_FORMAT_UNDEFINED; VkGraphicsPipelineCreateInfo pipelineInfo{}; pipelineInfo.sType = VkStructureType::VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; @@ -362,4 +380,4 @@ namespace sh::render::vk { specializationData = dataPtr; } -} \ No newline at end of file +} diff --git a/src/Render/VulkanImpl/VulkanQueueManager.cpp b/src/Render/VulkanImpl/VulkanQueueManager.cpp index 23494f31..f85eadd8 100644 --- a/src/Render/VulkanImpl/VulkanQueueManager.cpp +++ b/src/Render/VulkanImpl/VulkanQueueManager.cpp @@ -3,7 +3,8 @@ #include "VulkanCommandBuffer.h" #include -#include +#include +#include namespace sh::render::vk { @@ -20,22 +21,25 @@ namespace sh::render::vk graphicsQueue(other.graphicsQueue), presentQueue(other.presentQueue), transferQueue(other.transferQueue), + computeQueue(other.computeQueue), graphicsPriorities(std::move(other.graphicsPriorities)), presentPriorities(std::move(other.presentPriorities)), transferPriorities(std::move(other.transferPriorities)), + computePriorities(std::move(other.computePriorities)), bFamiliesReady(other.bFamiliesReady), bQueuesReady(other.bQueuesReady) { other.graphicsQueue = VK_NULL_HANDLE; other.presentQueue = VK_NULL_HANDLE; other.transferQueue = VK_NULL_HANDLE; + other.computeQueue = VK_NULL_HANDLE; other.bFamiliesReady = false; other.bQueuesReady = false; - // mutex 포인터는 move 이후 재결합이 필요 → FetchQueues에서 재설정하게 둔다 graphicsLock = &graphicsMtx; presentLock = &presentMtx; transferLock = &transferMtx; + computeLock = &computeMtx; } SH_RENDER_API void VulkanQueueManager::QueryFamilies(VkSurfaceKHR surface) { @@ -51,9 +55,14 @@ namespace sh::render::vk if (!t.has_value()) throw std::runtime_error("No transfer queue family found"); + auto c = PickComputeFamily(); + if (!c.has_value()) + throw std::runtime_error("No compute queue family found"); + selection.graphics = *g; selection.present = *p; selection.transfer = *t; + selection.compute = *c; ComputeRequestedCountsAndIndices(); @@ -75,7 +84,7 @@ namespace sh::render::vk auto addReqFn = [&](const Family& family) { - for (auto& r : reqs) + for (Req& r : reqs) if (r.family == family.index) { r.count = std::max(r.count, family.requestedCount); @@ -87,6 +96,7 @@ namespace sh::render::vk addReqFn(selection.graphics); addReqFn(selection.present); addReqFn(selection.transfer); + addReqFn(selection.compute); std::vector infos; infos.reserve(reqs.size()); @@ -94,28 +104,23 @@ namespace sh::render::vk graphicsPriorities.clear(); presentPriorities.clear(); transferPriorities.clear(); + computePriorities.clear(); - struct Blob - { - uint32_t family; - std::vector* prios; - }; - std::vector blobs; - blobs.reserve(reqs.size()); - - auto pickStorageFn = + auto pickPriorityFn = [&](uint32_t family) -> std::vector& { if (family == selection.graphics.index) return graphicsPriorities; if (family == selection.present.index) return presentPriorities; - return transferPriorities; + if (family == selection.transfer.index) + return transferPriorities; + return computePriorities; }; - for (auto& req : reqs) + for (const Req& req : reqs) { - auto& prios = pickStorageFn(req.family); + auto& prios = pickPriorityFn(req.family); prios.assign(req.count, 1.0f); VkDeviceQueueCreateInfo q{}; @@ -139,19 +144,27 @@ namespace sh::render::vk vkGetDeviceQueue(device, selection.graphics.index, selection.graphicsQIndex, &graphicsQueue); vkGetDeviceQueue(device, selection.present.index, selection.presentQIndex, &presentQueue); vkGetDeviceQueue(device, selection.transfer.index, selection.transferQIndex, &transferQueue); + vkGetDeviceQueue(device, selection.compute.index, selection.computeQIndex, &computeQueue); // 동일 VkQueue면 락도 공유 graphicsLock = &graphicsMtx; presentLock = &presentMtx; transferLock = &transferMtx; + computeLock = &computeMtx; if (presentQueue == graphicsQueue) presentLock = graphicsLock; + if (transferQueue == graphicsQueue) transferLock = graphicsLock; if (transferQueue == presentQueue) transferLock = presentLock; + if (computeQueue == graphicsQueue) + computeLock = graphicsLock; + if (computeQueue == transferQueue) + computeLock = transferLock; + bQueuesReady = true; } SH_RENDER_API auto VulkanQueueManager::GetQueue(Role role) const -> VkQueue @@ -166,6 +179,8 @@ namespace sh::render::vk return presentQueue; case Role::Transfer: return transferQueue; + case Role::Compute: + return computeQueue; } return VK_NULL_HANDLE; } @@ -179,6 +194,8 @@ namespace sh::render::vk return selection.present.index; case Role::Transfer: return selection.transfer.index; + case Role::Compute: + return selection.compute.index; } return UINT32_MAX; } @@ -204,7 +221,7 @@ namespace sh::render::vk bool bHasTimeline = false; - for (auto& wait : waits) + for (const VulkanCommandBuffer::WaitSemaphore& wait : waits) { waitSems.push_back(wait.semaphore); waitStages.push_back(wait.stageMask); @@ -219,7 +236,7 @@ namespace sh::render::vk signalSems.reserve(signals.size()); signalValues.reserve(signals.size()); - for (auto& signal : signals) + for (const VulkanCommandBuffer::SignalSemaphore& signal : signals) { signalSems.push_back(signal.semaphore); signalValues.push_back(signal.value); @@ -247,7 +264,7 @@ namespace sh::render::vk si.signalSemaphoreCount = (uint32_t)signalSems.size(); si.pSignalSemaphores = signalSems.empty() ? nullptr : signalSems.data(); - std::mutex* muPtr = GetMutexForRole(role); + std::mutex* const muPtr = GetMutexForRole(role); std::lock_guard lock(*muPtr); VkResult result = vkQueueSubmit(q, 1, &si, fence); @@ -315,10 +332,10 @@ namespace sh::render::vk } auto VulkanQueueManager::PickTransferFamily(uint32_t avoidFamily) const -> std::optional { - // 전용 transfer (TRANSFER 있고 GRAPHICS/COMPUTE 없는 패밀리) 우선 + // 전용 transfer 패밀리 우선 for (int i = 0; i < props.size(); ++i) { - const auto& p = props[i]; + const VkQueueFamilyProperties& p = props[i]; if (p.queueCount == 0) continue; const bool hasTransfer = (p.queueFlags & VkQueueFlagBits::VK_QUEUE_TRANSFER_BIT) != 0; @@ -336,7 +353,7 @@ namespace sh::render::vk // 그래픽과 다른 transfer 가능 패밀리 선호 for (int i = 0; i < props.size(); ++i) { - const auto& p = props[i]; + const VkQueueFamilyProperties& p = props[i]; if (p.queueCount == 0) continue; if (!(p.queueFlags & VkQueueFlagBits::VK_QUEUE_TRANSFER_BIT)) @@ -350,7 +367,7 @@ namespace sh::render::vk return fam; } - // 일치하는 게 하나도 없는 경우 - 그래픽 패밀리(대부분 transfer 포함) + // 일치하는 게 하나도 없는 경우 그래픽 패밀리(대부분 transfer 포함) if (avoidFamily != UINT32_MAX) { const auto& p = props[avoidFamily]; @@ -365,63 +382,77 @@ namespace sh::render::vk return {}; } - void VulkanQueueManager::ComputeRequestedCountsAndIndices() + auto VulkanQueueManager::PickComputeFamily() const -> std::optional { - selection.graphics.requestedCount = 1; - selection.present.requestedCount = 1; - selection.transfer.requestedCount = 1; - - // 같은 패밀리 공유 시, 가능한 한 큐 인덱스를 분리해서 받도록 요청 개수 증가 - if (selection.present.index == selection.graphics.index) - selection.graphics.requestedCount += 1; - - if (selection.transfer.index == selection.graphics.index) - selection.graphics.requestedCount += 1; - - if (selection.transfer.index == selection.present.index && selection.transfer.index != selection.graphics.index) - selection.present.requestedCount += 1; - - selection.graphics.requestedCount = ClampRequested(selection.graphics.requestedCount, selection.graphics.availableCount); - selection.present.requestedCount = ClampRequested(selection.present.requestedCount, selection.present.availableCount); - selection.transfer.requestedCount = ClampRequested(selection.transfer.requestedCount, selection.transfer.availableCount); - - // 역할별 queue index 결정 - selection.graphicsQIndex = 0; - - // present - if (selection.present.index == selection.graphics.index) - { - selection.presentQIndex = (selection.graphics.requestedCount >= 2) ? 1 : 0; - } - else + // Compute전용 큐 패밀리 우선 + // 메모) Vulkan 스펙상 GRAPHICS나 COMPUTE 비트가 켜진 패밀리는 TRANSFER 작업을 반드시 지원 + for (int i = 0; i < props.size(); ++i) { - selection.presentQIndex = 0; + const VkQueueFamilyProperties& p = props[i]; + if (p.queueCount == 0) + continue; + const bool hasGraphics = (p.queueFlags & VkQueueFlagBits::VK_QUEUE_GRAPHICS_BIT) != 0; + const bool hasCompute = (p.queueFlags & VkQueueFlagBits::VK_QUEUE_COMPUTE_BIT) != 0; + if (hasCompute && !hasGraphics) + { + Family fam{}; + fam.index = i; + fam.availableCount = p.queueCount; + return fam; + } } - // transfer - if (selection.transfer.index == selection.graphics.index) + for (int i = 0; i < props.size(); ++i) { - // graphics/present가 이미 0/1을 썼을 가능성이 있으니 가능한 인덱스를 고름 - uint32_t want = 1; - if (selection.present.index == selection.graphics.index && selection.presentQIndex == 1) - want = 2; - selection.transferQIndex = (selection.graphics.requestedCount >= (want + 1)) ? want : 0; + const VkQueueFamilyProperties& p = props[i]; + if (p.queueCount == 0) + continue; + if (p.queueFlags & VkQueueFlagBits::VK_QUEUE_COMPUTE_BIT) + { + Family fam{}; + fam.index = i; + fam.availableCount = p.queueCount; + return fam; + } } - else if (selection.transfer.index == selection.present.index) + return {}; + } + void VulkanQueueManager::ComputeRequestedCountsAndIndices() + { + // 역할을 우선순위 순으로 처리하며 같은 패밀리 안에서 다음 큐 인덱스를 발급한다. + // 패밀리 큐가 모자라면 0번으로 fallback (= 같은 VkQueue 공유, lock도 공유됨). + struct RoleSlot { - selection.transferQIndex = (selection.present.requestedCount >= 2) ? 1 : 0; - } - else + Family* family; + uint32_t* qIndex; + }; + const RoleSlot slots[] = + { + { &selection.graphics, &selection.graphicsQIndex }, + { &selection.present,&selection.presentQIndex }, + { &selection.transfer, &selection.transferQIndex }, + { &selection.compute, &selection.computeQIndex }, + }; + + std::map nextIndexInFamily; + + for (const RoleSlot& slot : slots) { - selection.transferQIndex = 0; + Family& fam = *slot.family; + const uint32_t want = nextIndexInFamily[fam.index]; // 0부터 시작 + const uint32_t needCount = want + 1; + + fam.requestedCount = std::max(fam.requestedCount, needCount); + fam.requestedCount = std::clamp(fam.requestedCount, 0u, fam.availableCount); + + // 요청한 만큼 못 받았으면 0번으로 fallback + const bool granted = fam.requestedCount >= needCount; + *slot.qIndex = granted ? want : 0; + + if (granted) + nextIndexInFamily[fam.index] = want + 1; } } - uint32_t VulkanQueueManager::ClampRequested(uint32_t want, uint32_t avail) const - { - if (avail == 0) - return 0; - return (want > avail) ? avail : want; - } auto VulkanQueueManager::GetMutexForRole(Role role) const -> std::mutex* { switch (role) @@ -432,6 +463,8 @@ namespace sh::render::vk return presentLock; case Role::Transfer: return transferLock; + case Role::Compute: + return computeLock; } return graphicsLock; } diff --git a/src/Render/VulkanImpl/VulkanRenderImpl.cpp b/src/Render/VulkanImpl/VulkanRenderImpl.cpp deleted file mode 100644 index 01d3948c..00000000 --- a/src/Render/VulkanImpl/VulkanRenderImpl.cpp +++ /dev/null @@ -1,429 +0,0 @@ -#include "VulkanRenderImpl.h" -#include "VulkanCommandBuffer.h" -#include "VulkanSwapChain.h" -#include "VulkanUniformBuffer.h" -#include "VulkanVertexBuffer.h" -#include "VulkanSkinnedVertexBuffer.h" -#include "VulkanPipelineManager.h" -#include "VulkanShaderPass.h" -#include "../Material.h" -#include "../ShaderPass.h" -#include "../Drawable.h" -#include "../SkinnedMesh.h" - -#include "Core/Reflection.hpp" - -namespace sh::render::vk -{ - VulkanRenderImpl::VulkanRenderImpl(VulkanContext& context) : - ctx(context) - { - cameraManager = VulkanCameraBuffers::GetInstance(); - } - SH_RENDER_API void VulkanRenderImpl::EmitBarrier(CommandBuffer& _cmd, const std::vector& barriers) const - { - auto& cmd = static_cast(_cmd); - for (const BarrierInfo& barrier : barriers) - { - VulkanImageBuffer* imgBuffer = nullptr; - VulkanImageBuffer* msaaBuffer = nullptr; - - if (barrier.target.index() == 0) - { - imgBuffer = (static_cast(std::get<0>(barrier.target)->GetTextureBuffer())); - msaaBuffer = (static_cast(std::get<0>(barrier.target)->GetMSAABuffer())); - } - else - { - const uint32_t imgIdx = std::get<1>(barrier.target); - imgBuffer = &ctx.GetSwapChain().GetSwapChainImages()[imgIdx]; - msaaBuffer = &ctx.GetSwapChain().GetSwapChainMSAAImages()[imgIdx]; - } - - VkImageLayout srcLayout, dstLayout; - VkPipelineStageFlags srcStage, dstStage; - VkAccessFlags srcAccess, dstAccess; - - auto mapUsageFn = - [&]( - ImageUsage u, - VkImageLayout& layout, - VkPipelineStageFlags& stage, - VkAccessFlags& access) - { - switch (u) - { - case ImageUsage::Undefined: - layout = VkImageLayout::VK_IMAGE_LAYOUT_UNDEFINED; - stage = VkPipelineStageFlagBits::VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; - access = 0; - return; - case ImageUsage::ColorAttachment: - layout = VkImageLayout::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - stage = VkPipelineStageFlagBits::VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - access = VkAccessFlagBits::VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VkAccessFlagBits::VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; - return; - case ImageUsage::SampledRead: - layout = VkImageLayout::VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - stage = VkPipelineStageFlagBits::VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT; - access = VkAccessFlagBits::VK_ACCESS_SHADER_READ_BIT; - return; - case ImageUsage::Present: - layout = VkImageLayout::VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - stage = VkPipelineStageFlagBits::VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; - access = 0; - return; - case ImageUsage::Src: - layout = VkImageLayout::VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - stage = VkPipelineStageFlagBits::VK_PIPELINE_STAGE_TRANSFER_BIT; - access = VkAccessFlagBits::VK_ACCESS_TRANSFER_READ_BIT; - } - }; - - mapUsageFn(barrier.lastUsage, srcLayout, srcStage, srcAccess); - mapUsageFn(barrier.curUsage, dstLayout, dstStage, dstAccess); - - VulkanImageBuffer::BarrierCommand(cmd.GetCommandBuffer(), *imgBuffer, srcLayout, dstLayout, srcStage, dstStage, srcAccess, dstAccess); - - if (msaaBuffer != nullptr) - { - // MSAA 버퍼를 다른 용도로 쓸 일이 없을듯? - //if (barrier.curUsage != ImageUsage::Present) - //VulkanImageBuffer::BarrierCommand(cmd.GetCommandBuffer(), *msaaBuffer, srcLayout, dstLayout, srcStage, dstStage, srcAccess, dstAccess); - } - } - } - SH_RENDER_API void VulkanRenderImpl::RecordCommand(CommandBuffer& _cmd, const core::Name& passName, const RenderTarget& renderData, const DrawList& drawList, bool bStoreImage) const - { - // 드로우 목적이 아닌 경우 - if (renderData.camera == nullptr) - { - for (auto& call : drawList.drawCall) - call(_cmd); - return; - } - - auto& cmd = static_cast(_cmd); - - const float width = ctx.GetViewportEnd().x - ctx.GetViewportStart().x; - const float height = ctx.GetViewportEnd().y - ctx.GetViewportStart().y; - const float surfWidth = static_cast(ctx.GetSwapChain().GetSwapChainSize().width); - const float surfHeight = static_cast(ctx.GetSwapChain().GetSwapChainSize().height); - - VkViewport viewport{}; - viewport.minDepth = 0.0f; - viewport.maxDepth = 1.0f; - viewport.x = ctx.GetViewportStart().x; - viewport.y = ctx.GetViewportEnd().y; - viewport.width = std::min(width, surfWidth); - viewport.height = -std::min(height, surfHeight); - - VkRect2D scissor{}; - scissor.offset = { 0, 0 }; - scissor.extent = ctx.GetSwapChain().GetSwapChainSize(); - - VulkanImageBuffer* imgBufferMSAA = nullptr; - VulkanImageBuffer* imgBuffer = nullptr; - VulkanImageBuffer* depthBuffer = nullptr; - - RenderTargetLayout rtLayout; - - if (core::IsValid(renderData.target)) - { - float w = renderData.target->GetSize().x; - float h = renderData.target->GetSize().y; - - viewport.x = 0; - viewport.y = h; - viewport.width = w; - viewport.height = -h; - scissor.extent = { static_cast(w), static_cast(h) }; - - imgBuffer = static_cast(renderData.target->GetTextureBuffer()); - imgBufferMSAA = static_cast(renderData.target->GetMSAABuffer()); - depthBuffer = static_cast(renderData.target->GetDepthBuffer()); - - rtLayout = renderData.target->GetLayout(); - } - else - { - imgBufferMSAA = &ctx.GetSwapChain().GetSwapChainMSAAImages()[renderData.frameIndex]; - imgBuffer = &ctx.GetSwapChain().GetSwapChainImages()[renderData.frameIndex]; - depthBuffer = &ctx.GetSwapChain().GetSwapChainDepthImages()[renderData.frameIndex]; - - rtLayout = ctx.GetSwapChain().GetRenderTargetLayout(); - } - - VkCommandBuffer commandBuffer = cmd.GetCommandBuffer(); - - const bool bMSAA = imgBufferMSAA != nullptr; - - VkRenderingAttachmentInfoKHR colorAttachment{}; - colorAttachment.sType = VkStructureType::VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO; - colorAttachment.imageView = bMSAA ? imgBufferMSAA->GetImageView() : imgBuffer->GetImageView(); - colorAttachment.imageLayout = VkImageLayout::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - colorAttachment.loadOp = drawList.bClearColor ? VkAttachmentLoadOp::VK_ATTACHMENT_LOAD_OP_CLEAR : VkAttachmentLoadOp::VK_ATTACHMENT_LOAD_OP_LOAD; - colorAttachment.storeOp = bStoreImage ? VkAttachmentStoreOp::VK_ATTACHMENT_STORE_OP_STORE : VkAttachmentStoreOp::VK_ATTACHMENT_STORE_OP_DONT_CARE; - colorAttachment.clearValue = { 0.f, 0.f, 0.f, 1.f }; - if (bMSAA) - { - colorAttachment.resolveImageView = imgBuffer->GetImageView(); - colorAttachment.resolveImageLayout = VkImageLayout::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - colorAttachment.resolveMode = VkResolveModeFlagBitsKHR::VK_RESOLVE_MODE_AVERAGE_BIT_KHR; - } - - VkRenderingAttachmentInfoKHR depthAttachment{}; - if (depthBuffer != nullptr) - { - depthAttachment.sType = VkStructureType::VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO; - depthAttachment.imageView = depthBuffer->GetImageView(); - depthAttachment.imageLayout = VkImageLayout::VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - depthAttachment.loadOp = drawList.bClearDepth ? VkAttachmentLoadOp::VK_ATTACHMENT_LOAD_OP_CLEAR : VkAttachmentLoadOp::VK_ATTACHMENT_LOAD_OP_LOAD; - depthAttachment.storeOp = bStoreImage ? VkAttachmentStoreOp::VK_ATTACHMENT_STORE_OP_STORE : VkAttachmentStoreOp::VK_ATTACHMENT_STORE_OP_DONT_CARE; - depthAttachment.clearValue = { 1.0f, 0.f }; - } - - VkRenderingInfoKHR renderingInfo{}; - renderingInfo.sType = VkStructureType::VK_STRUCTURE_TYPE_RENDERING_INFO_KHR; - renderingInfo.colorAttachmentCount = 1; - renderingInfo.pColorAttachments = &colorAttachment; - renderingInfo.pDepthAttachment = depthBuffer != nullptr ? &depthAttachment : nullptr; - renderingInfo.pStencilAttachment = depthBuffer != nullptr ? &depthAttachment : nullptr; - renderingInfo.renderArea = { 0, 0, imgBuffer->GetWidth(), imgBuffer->GetHeight() }; - renderingInfo.layerCount = 1; - - static PFN_vkCmdBeginRenderingKHR pfnCmdBeginRenderingKHR = (PFN_vkCmdBeginRenderingKHR)vkGetDeviceProcAddr(ctx.GetDevice(), "vkCmdBeginRenderingKHR"); - pfnCmdBeginRenderingKHR(commandBuffer, &renderingInfo); - vkCmdSetViewport(commandBuffer, 0, 1, &viewport); - vkCmdSetScissor(commandBuffer, 0, 1, &scissor); - - if (drawList.renderData.index() == 0 && !std::get<0>(drawList.renderData).empty() || - drawList.renderData.index() == 1 && !std::get<1>(drawList.renderData).empty()) - RenderDrawable(cmd, passName, renderData, drawList, rtLayout); - - for (auto& call : drawList.drawCall) - call(_cmd); - - static PFN_vkCmdEndRenderingKHR pfnCmdEndRenderingKHR = (PFN_vkCmdEndRenderingKHR)vkGetDeviceProcAddr(ctx.GetDevice(), "vkCmdEndRenderingKHR");; - pfnCmdEndRenderingKHR(commandBuffer); - } - - void VulkanRenderImpl::BindCameraSet(VulkanCommandBuffer& cmd, VkPipelineLayout layout, const ShaderPass& pass, const Material& mat, uint32_t cameraOffset) const - { - // 카메라 데이터는 다이나믹 디스크립터셋 - auto cameraUBO = static_cast( - mat.GetMaterialData().GetUniformBuffer(pass, UniformStructLayout::Type::Camera)); - - VkDescriptorSet cameraSet = cameraUBO ? cameraUBO->GetVkDescriptorSet() : ctx.GetEmptyDescriptorSet(); - uint32_t dynamicCount = cameraUBO ? 1 : 0; - - vkCmdBindDescriptorSets(cmd.GetCommandBuffer(), - VkPipelineBindPoint::VK_PIPELINE_BIND_POINT_GRAPHICS, layout, - static_cast(UniformStructLayout::Type::Camera), - 1, &cameraSet, dynamicCount, &cameraOffset); - } - void VulkanRenderImpl::BindMaterialSet(VulkanCommandBuffer& cmd, VkPipelineLayout layout, const ShaderPass& pass, const Material& mat) const - { - auto materialUniformBuffer = static_cast( - mat.GetMaterialData().GetUniformBuffer(pass, UniformStructLayout::Type::Material)); - - VkDescriptorSet materialDescriptorSet = materialUniformBuffer ? materialUniformBuffer->GetVkDescriptorSet() : ctx.GetEmptyDescriptorSet(); - - vkCmdBindDescriptorSets(cmd.GetCommandBuffer(), - VkPipelineBindPoint::VK_PIPELINE_BIND_POINT_GRAPHICS, layout, - static_cast(UniformStructLayout::Type::Material), - 1, &materialDescriptorSet, 0, nullptr); - } - void VulkanRenderImpl::BindObjectSet(VulkanCommandBuffer& cmd, VkPipelineLayout layout, const ShaderPass& pass, Drawable& drawable) const - { - auto objectUniformBuffer = static_cast( - drawable.GetMaterialData().GetUniformBuffer(pass, UniformStructLayout::Type::Object)); - - VkDescriptorSet objectDescriptorSet = objectUniformBuffer ? objectUniformBuffer->GetVkDescriptorSet() : ctx.GetEmptyDescriptorSet(); - - vkCmdBindDescriptorSets(cmd.GetCommandBuffer(), - VkPipelineBindPoint::VK_PIPELINE_BIND_POINT_GRAPHICS, layout, - static_cast(UniformStructLayout::Type::Object), - 1, &objectDescriptorSet, 0, nullptr); - } - void VulkanRenderImpl::DrawMesh(VulkanCommandBuffer& cmd, const ShaderPass& pass, const Mesh& mesh, int subMeshIndex) const - { - uint32_t indexCount; - uint32_t firstIndex; - const auto& subMeshes = mesh.GetSubMeshes(); - if (subMeshIndex >= 0 && subMeshIndex < static_cast(subMeshes.size())) - { - indexCount = static_cast(subMeshes[subMeshIndex].indexCount); - firstIndex = static_cast(subMeshes[subMeshIndex].indexOffset); - } - else - { - indexCount = static_cast(mesh.GetIndices().size()); - firstIndex = 0; - } - - const SkinnedMesh* skinnedMesh = core::reflection::Cast(&mesh); - if (skinnedMesh) - { - const VulkanSkinnedVertexBuffer* vkSkinnedVB = - static_cast(skinnedMesh->GetVertexBuffer()); - - VkBuffer buffers[2] = { - vkSkinnedVB->GetVertexBuffer().GetBuffer(), - vkSkinnedVB->GetBoneBuffer().GetBuffer() - }; - VkDeviceSize offsets[2] = { 0, 0 }; - vkCmdBindVertexBuffers(cmd.GetCommandBuffer(), 0, 2, buffers, offsets); - vkCmdBindIndexBuffer(cmd.GetCommandBuffer(), vkSkinnedVB->GetIndexBuffer().GetBuffer(), 0, VK_INDEX_TYPE_UINT32); - } - else - { - const VulkanVertexBuffer* vkVertexBuffer = static_cast(mesh.GetVertexBuffer()); - - std::array buffers = { vkVertexBuffer->GetVertexBuffer().GetBuffer() }; - - VkDeviceSize offsets[1] = { 0 }; - vkCmdBindVertexBuffers(cmd.GetCommandBuffer(), 0, 1, buffers.data(), offsets); - vkCmdBindIndexBuffer(cmd.GetCommandBuffer(), vkVertexBuffer->GetIndexBuffer().GetBuffer(), 0, VkIndexType::VK_INDEX_TYPE_UINT32); - } - vkCmdDrawIndexed(cmd.GetCommandBuffer(), indexCount, 1, firstIndex, 0, 0); - } - void VulkanRenderImpl::RenderDrawable(VulkanCommandBuffer& cmd, const core::Name& passName, const RenderTarget& renderData, const DrawList& drawList, const RenderTargetLayout& layout) const - { - auto cameraOffsetOpt = cameraManager->GetDynamicOffset(*renderData.camera); - if (!cameraOffsetOpt.has_value()) - return; - - VulkanPipelineManager::PipelineHandle lastPipelineHandle{ 0xffffffff, 0xffffffff }; - if (drawList.renderData.index() == 0) - { - for (const DrawList::RenderGroup& renderGroup : std::get<0>(drawList.renderData)) - { - const Material* mat = renderGroup.material; - assert(mat); - - const auto passVectorPtr = renderGroup.material->GetShader()->GetShaderPasses(passName); - if (passVectorPtr == nullptr) - continue; - - // 첫 번째 유효 drawable의 mesh로 스킨드 여부 판단 - bool bGroupSkinned = false; - for (auto drawable : renderGroup.drawables) - { - if (drawable && drawable->GetMesh()) - { - bGroupSkinned = (core::reflection::Cast(drawable->GetMesh()) != nullptr); - break; - } - } - - for (const ShaderPass& pass : *passVectorPtr) - { - if (pass.IsPendingKill()) - continue; - - const std::vector* constantData = mat->GetConstantData(pass); - - auto pipelineHandle = ctx.GetPipelineManager(). - GetOrCreatePipelineHandle(static_cast(pass), layout, renderGroup.topology, bGroupSkinned, constantData); - - if (lastPipelineHandle != pipelineHandle) - { - ctx.GetPipelineManager().BindPipeline(cmd.GetCommandBuffer(), pipelineHandle); - lastPipelineHandle = pipelineHandle; - } - VkPipelineLayout pipelineLayout = static_cast(pass).GetPipelineLayout(); - uint32_t setSize = static_cast(pass).GetSetCount(); - - // set = 0 카메라 - // set = 1 객체 고유 - // set = 2 메테리얼 - if (setSize > 0) - BindCameraSet(cmd, pipelineLayout, pass, *mat, cameraOffsetOpt.value()); - if (setSize > 2) - BindMaterialSet(cmd, pipelineLayout, pass, *mat); - - for (auto drawable : renderGroup.drawables) - { - if (drawable == nullptr) - continue; - const Mesh* mesh = drawable->GetMesh(); - if (!core::IsValid(mesh)) - continue; - - if (setSize > 1) - BindObjectSet(cmd, pipelineLayout, pass, *drawable); - - if (pass.HasConstantUniform()) - vkCmdPushConstants(cmd.GetCommandBuffer(), pipelineLayout, - VkShaderStageFlagBits::VK_SHADER_STAGE_VERTEX_BIT, - 0, sizeof(glm::mat4), - &drawable->GetModelMatrix(core::ThreadType::Render)); - - DrawMesh(cmd, pass, *mesh, drawable->GetSubMeshIndex()); - } - } - } - } - else - { - for (const DrawList::RenderItem& renderItem : std::get<1>(drawList.renderData)) - { - const Material* mat = renderItem.material; - assert(mat); - - const auto passVectorPtr = mat->GetShader()->GetShaderPasses(passName); - if (passVectorPtr == nullptr) - continue; - - bool bItemSkinned = false; - if (renderItem.drawable && renderItem.drawable->GetMesh()) - bItemSkinned = (core::reflection::Cast(renderItem.drawable->GetMesh()) != nullptr); - - for (const ShaderPass& pass : *passVectorPtr) - { - if (pass.IsPendingKill()) - continue; - - const std::vector* constantData = mat->GetConstantData(pass); - - auto pipelineHandle = ctx.GetPipelineManager(). - GetOrCreatePipelineHandle(static_cast(pass), layout, renderItem.topology, bItemSkinned, constantData); - - if (lastPipelineHandle != pipelineHandle) - { - ctx.GetPipelineManager().BindPipeline(cmd.GetCommandBuffer(), pipelineHandle); - lastPipelineHandle = pipelineHandle; - } - VkPipelineLayout pipelineLayout = static_cast(pass).GetPipelineLayout(); - uint32_t setSize = static_cast(pass).GetSetCount(); - - // set = 0 카메라 - // set = 1 객체 고유 - // set = 2 메테리얼 - if (setSize > 0) - BindCameraSet(cmd, pipelineLayout, pass, *mat, cameraOffsetOpt.value()); - if (setSize > 2) - BindMaterialSet(cmd, pipelineLayout, pass, *mat); - - if (renderItem.drawable == nullptr) - continue; - const Mesh* mesh = renderItem.drawable->GetMesh(); - if (!core::IsValid(mesh)) - continue; - - if (setSize > 1) - BindObjectSet(cmd, pipelineLayout, pass, *renderItem.drawable); - - if (pass.HasConstantUniform()) - vkCmdPushConstants(cmd.GetCommandBuffer(), pipelineLayout, - VkShaderStageFlagBits::VK_SHADER_STAGE_VERTEX_BIT, - 0, sizeof(glm::mat4), - &renderItem.drawable->GetModelMatrix(core::ThreadType::Render)); - - DrawMesh(cmd, pass, *mesh, renderItem.drawable->GetSubMeshIndex()); - } - } - } - } -}//namespace \ No newline at end of file diff --git a/src/Render/VulkanImpl/VulkanRenderer.cpp b/src/Render/VulkanImpl/VulkanRenderer.cpp index 1b56817a..2062443f 100644 --- a/src/Render/VulkanImpl/VulkanRenderer.cpp +++ b/src/Render/VulkanImpl/VulkanRenderer.cpp @@ -3,7 +3,6 @@ #include "VulkanSwapChain.h" #include "VulkanQueueManager.h" #include "VulkanPipelineManager.h" -#include "VulkanCameraBuffers.h" #include "VulkanCommandBufferPool.h" #include "Drawable.h" @@ -34,80 +33,31 @@ namespace sh::render::vk if (isInit) { Clear(); - - camManager->Destroy(); DestroySyncObjects(); - context->Clear(); } } SH_RENDER_API void VulkanRenderer::Clear() { vkDeviceWaitIdle(context->GetDevice()); - - frameFences.clear(); - camManager->Clear(); - Renderer::Clear(); + frameFences.clear(); } - auto VulkanRenderer::CreateSyncObjects() -> VkResult - { - VkSemaphoreCreateInfo semaphoreInfo{}; - semaphoreInfo.sType = VkStructureType::VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - - VkFenceCreateInfo fenceInfo{}; - fenceInfo.sType = VkStructureType::VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; - fenceInfo.flags = 0; - - VkResult result; - result = vkCreateSemaphore(context->GetDevice(), &semaphoreInfo, nullptr, &imageAvailableSemaphore); - if (result != VK_SUCCESS) return result; - - result = vkCreateSemaphore(context->GetDevice(), &semaphoreInfo, nullptr, &renderFinishedSemaphore); - if (result != VK_SUCCESS) return result; - - VkSemaphoreTypeCreateInfo timelineCreateInfo{}; - timelineCreateInfo.sType = VkStructureType::VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO; - timelineCreateInfo.semaphoreType = VkSemaphoreType::VK_SEMAPHORE_TYPE_TIMELINE; - timelineCreateInfo.initialValue = 0; - semaphoreInfo.pNext = &timelineCreateInfo; - - result = vkCreateSemaphore(context->GetDevice(), &semaphoreInfo, nullptr, &timelineSemaphore); - if (result != VK_SUCCESS) return result; - - result = vkCreateFence(context->GetDevice(), &fenceInfo, nullptr, &inFlightFence); - if (result != VK_SUCCESS) return result; - - return VK_SUCCESS; - } - - void VulkanRenderer::DestroySyncObjects() + void VulkanRenderer::CreateContext(const window::Window& win) { - vkDestroySemaphore(context->GetDevice(), imageAvailableSemaphore, nullptr); - vkDestroySemaphore(context->GetDevice(), renderFinishedSemaphore, nullptr); - vkDestroySemaphore(context->GetDevice(), timelineSemaphore, nullptr); - vkDestroyFence(context->GetDevice(), inFlightFence, nullptr); - } - void VulkanRenderer::OnCameraAdded(const Camera* camera) - { - VulkanCameraBuffers::GetInstance()->AddCamera(*camera); + context = std::make_unique(win); + context->Init(); } - void VulkanRenderer::OnCameraRemoved(const Camera* camera) + SH_RENDER_API void VulkanRenderer::DestroyContext() { - VulkanCameraBuffers::GetInstance()->RemoveCamera(*camera); + context->Clear(); + context.reset(); } - SH_RENDER_API bool VulkanRenderer::Init(sh::window::Window& win) { Renderer::Init(win); - context = std::make_unique(win); - context->Init(); - - camManager = VulkanCameraBuffers::GetInstance(); - camManager->Init(*context); - if (CreateSyncObjects() != VkResult::VK_SUCCESS) return false; @@ -128,11 +78,6 @@ namespace sh::render::vk return context->ReSizing(); } - SH_RENDER_API bool VulkanRenderer::IsInit() const - { - return isInit; - } - SH_RENDER_API void VulkanRenderer::WaitForCurrentFrame() { vkDeviceWaitIdle(context->GetDevice()); @@ -142,14 +87,13 @@ namespace sh::render::vk { using SignalSemaphore = VulkanCommandBuffer::SignalSemaphore; using WaitSemaphore = VulkanCommandBuffer::WaitSemaphore; - Renderer::Render(); if (!isInit || bPause.load(std::memory_order::memory_order_acquire)) return; + + Renderer::Render(); if (renderer == nullptr) return; - if (cameras.size() == 0) - return; // 스왑체인에서 이미지를 가져오고, 변경 사항이 있다면 리사이징 uint32_t imgIdx; @@ -168,46 +112,25 @@ namespace sh::render::vk return; } assert(result == VkResult::VK_SUCCESS); - - // 카메라 데이터 GPU에 업로드 - std::vector cams{}; - cams.reserve(cameras.size()); - for (auto camera : cameras) + core::ArrayView renderDatas = IRenderThrMethod::GetRenderDatas(context->GetRenderDataManager()); + std::size_t viewIdx = 0; + for (RenderData& rd : renderDatas) { - if (!camera->GetActive()) - continue; - camManager->UploadDataToGPU(*camera); - cams.push_back(camera); - } + IRenderThrMethod::SetFrameIndex(rd, imgIdx); + IRenderThrMethod::SetDrawablesPtr(rd, &drawables); - SetDrawCallCount(0); - uint32_t renderCallCount = 0; - for (const Camera* cam : cams) - { - std::vector filteredDrawables; - - RenderTarget rt{}; - rt.frameIndex = imgIdx; - rt.camera = cam; - rt.target = cam->GetRenderTexture(); - rt.drawables = &filteredDrawables; - - for (Drawable* drawable : drawables) - { - if (cam->CheckRenderTag(drawable->GetRenderTagId())) - filteredDrawables.push_back(drawable); - } - IRenderThrMethod::Setup(*renderer, rt); - IRenderThrMethod::Execute(*renderer, rt); // ScriptableRenderer에 등록된 패스들을 렌더큐 순서대로, 병렬적으로 커맨드에 기록 - renderCallCount += IRenderThrMethod::GetRenderCallCount(*renderer); + IRenderThrMethod::Setup(*renderer, rd); + IRenderThrMethod::Execute(*renderer, rd); // ScriptableRenderer에 등록된 패스들을 렌더큐 순서대로, 병렬적으로 커맨드에 기록 } - SetDrawCallCount(renderCallCount); + IRenderThrMethod::ExecuteTransfer(*renderer, imgIdx); - const auto& submittedCmds = renderer->GetSubmittedCommands(); - if (submittedCmds.size() == 1) + uint32_t renderCall = 0; + const std::vector& recordedCmds = renderer->GetRecordedCommands(); + if (recordedCmds.size() == 1) { - VulkanCommandBuffer& cmd = static_cast(submittedCmds.front().cmd); + VulkanCommandBuffer& cmd = static_cast(recordedCmds.front().cmd); + renderCall += IRenderThrMethod::GetRenderCall(cmd); cmd.AddWaitSemaphore( WaitSemaphore { @@ -220,14 +143,15 @@ namespace sh::render::vk } else { - VulkanCommandBuffer& endCmd = static_cast(submittedCmds.back().cmd); + VulkanCommandBuffer& endCmd = static_cast(recordedCmds.back().cmd); endCmd.AddSignalSemaphore(SignalSemaphore{ renderFinishedSemaphore }); bool bUsedImageAvailableSemaphore = false; - for (const auto& submittedCmd : submittedCmds) + for (const ScriptableRenderer::RecordedCommand& recordedCmd : recordedCmds) { - ScriptableRenderPass& pass = submittedCmd.pass; - VulkanCommandBuffer& cmd = static_cast(submittedCmd.cmd); + ScriptableRenderPass& pass = recordedCmd.pass; + VulkanCommandBuffer& cmd = static_cast(recordedCmd.cmd); + renderCall += IRenderThrMethod::GetRenderCall(cmd); // 스왑체인을 쓰는 첫 패스는 imageAvailableSemaphore대기 if (!bUsedImageAvailableSemaphore) @@ -252,6 +176,7 @@ namespace sh::render::vk context->GetQueueManager().Submit(VulkanQueueManager::Role::Graphics, cmd, fence); } } + SetDrawCallCount(renderCall); VkPresentInfoKHR presentInfo{}; presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; @@ -277,10 +202,6 @@ namespace sh::render::vk IRenderThrMethod::ResetSubmittedCommands(*renderer, *context); } - SH_RENDER_API auto VulkanRenderer::GetCurrentFrame() const -> int - { - return currentFrame; - } SH_RENDER_API auto VulkanRenderer::GetWidth() const -> uint32_t { return context->GetSwapChain().GetSwapChainSize().width; @@ -289,14 +210,6 @@ namespace sh::render::vk { return context->GetSwapChain().GetSwapChainSize().height; } - SH_RENDER_API auto VulkanRenderer::GetTimelineSemaphore() const -> VkSemaphore - { - return timelineSemaphore; - } - SH_RENDER_API auto VulkanRenderer::GetTimelineValue() const -> uint64_t - { - return timelineValueAtomic.load(std::memory_order::memory_order_acquire); - } SH_RENDER_API auto VulkanRenderer::GetContext() const -> IRenderContext* { return context.get(); @@ -305,5 +218,44 @@ namespace sh::render::vk { Renderer::Sync(); } + + auto VulkanRenderer::CreateSyncObjects() -> VkResult + { + VkSemaphoreCreateInfo semaphoreInfo{}; + semaphoreInfo.sType = VkStructureType::VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + + VkFenceCreateInfo fenceInfo{}; + fenceInfo.sType = VkStructureType::VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fenceInfo.flags = 0; + + VkResult result; + result = vkCreateSemaphore(context->GetDevice(), &semaphoreInfo, nullptr, &imageAvailableSemaphore); + if (result != VK_SUCCESS) return result; + + result = vkCreateSemaphore(context->GetDevice(), &semaphoreInfo, nullptr, &renderFinishedSemaphore); + if (result != VK_SUCCESS) return result; + + VkSemaphoreTypeCreateInfo timelineCreateInfo{}; + timelineCreateInfo.sType = VkStructureType::VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO; + timelineCreateInfo.semaphoreType = VkSemaphoreType::VK_SEMAPHORE_TYPE_TIMELINE; + timelineCreateInfo.initialValue = 0; + semaphoreInfo.pNext = &timelineCreateInfo; + + result = vkCreateSemaphore(context->GetDevice(), &semaphoreInfo, nullptr, &timelineSemaphore); + if (result != VK_SUCCESS) return result; + + result = vkCreateFence(context->GetDevice(), &fenceInfo, nullptr, &inFlightFence); + if (result != VK_SUCCESS) return result; + + return VK_SUCCESS; + } + + void VulkanRenderer::DestroySyncObjects() + { + vkDestroySemaphore(context->GetDevice(), imageAvailableSemaphore, nullptr); + vkDestroySemaphore(context->GetDevice(), renderFinishedSemaphore, nullptr); + vkDestroySemaphore(context->GetDevice(), timelineSemaphore, nullptr); + vkDestroyFence(context->GetDevice(), inFlightFence, nullptr); + } }//namespace diff --git a/src/Render/VulkanImpl/VulkanShaderPass.cpp b/src/Render/VulkanImpl/VulkanShaderPass.cpp index 46a0f5d3..bc95a194 100644 --- a/src/Render/VulkanImpl/VulkanShaderPass.cpp +++ b/src/Render/VulkanImpl/VulkanShaderPass.cpp @@ -1,14 +1,17 @@ #include "VulkanShaderPass.h" -#include "VulkanShaderPass.h" #include "VulkanContext.h" +#include "Core/Logger.h" + +#include + namespace sh::render::vk { VulkanShaderPass::VulkanShaderPass(const VulkanContext& context, const ShaderModules& shaderModules, const ShaderAST::PassNode& passNode) : ShaderPass(passNode, ShaderType::SPIR), context(context), vertShader(shaderModules.vert), fragShader(shaderModules.frag), - descriptorSetLayout(1), pipelineLayout(nullptr) + pipelineLayout(nullptr) { // Specialization constant (컴파일 상수) if (!passNode.constants.empty()) @@ -37,9 +40,9 @@ namespace sh::render::vk } for (auto& stage : passNode.stages) { - for (auto& uniform : stage.uniforms) + for (auto& uniform : stage.buffers) { - if (uniform.bConstant) + if (uniform.bufferType == ShaderAST::BufferType::PushConstant) { bUseMatrixModel = true; break; @@ -54,8 +57,7 @@ namespace sh::render::vk ShaderPass(std::move(other)), context(other.context), vertShader(other.vertShader), fragShader(other.fragShader), - descriptorBindings(std::move(other.descriptorBindings)), - descriptorSetLayout(std::move(other.descriptorSetLayout)), + setlayouts(std::move(other.setlayouts)), pipelineLayout(other.pipelineLayout), specializationEntry(std::move(other.specializationEntry)), bUseMatrixModel(other.bUseMatrixModel) @@ -69,6 +71,7 @@ namespace sh::render::vk { Clear(); } + SH_RENDER_API void VulkanShaderPass::Clear() { specializationEntry.clear(); @@ -86,35 +89,96 @@ namespace sh::render::vk fragShader = nullptr; } } - void VulkanShaderPass::CleanDescriptors() + SH_RENDER_API void VulkanShaderPass::Build() { - if (pipelineLayout) + CleanDescriptors(); + // 유니폼 레이아웃 생성 + for (const std::vector* const uniforms : { &vertexUniforms, &fragmentUniforms }) { - vkDestroyPipelineLayout(context.GetDevice(), pipelineLayout, nullptr); - pipelineLayout = nullptr; - } + for (const UniformStructLayout& uniformLayout : *uniforms) + { + if (uniformLayout.IsPushConstant()) + continue; + const uint32_t set = static_cast(uniformLayout.usage); + + // set == 0 카메라 데이터 + VkDescriptorType type = (set == 0) ? + VkDescriptorType::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC : VkDescriptorType::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + if (uniformLayout.GetKind() == UniformStructLayout::Kind::Storage) + type = VkDescriptorType::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; - descriptorBindings.clear(); + VkShaderStageFlagBits stage = VkShaderStageFlagBits::VK_SHADER_STAGE_VERTEX_BIT; + if (uniforms == &fragmentUniforms) + stage = VkShaderStageFlagBits::VK_SHADER_STAGE_FRAGMENT_BIT; - for (std::size_t i = 0; i < descriptorSetLayout.size(); ++i) + AddDescriptorBinding(set, uniformLayout.binding, type, stage); + } + } + for (const UniformStructLayout& uniformLayout : samplerUniforms) { - if (descriptorSetLayout[i] != context.GetEmptyDescriptorSetLayout()) - vkDestroyDescriptorSetLayout(context.GetDevice(), descriptorSetLayout[i], nullptr); + const uint32_t set = static_cast(uniformLayout.usage); + AddDescriptorBinding(set, uniformLayout.binding, + VkDescriptorType::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + VkShaderStageFlagBits::VK_SHADER_STAGE_FRAGMENT_BIT); } - descriptorSetLayout.clear(); + VkResult result = CreateDescriptorLayout(); + assert(result == VkResult::VK_SUCCESS); + if (result != VkResult::VK_SUCCESS) + { + SH_ERROR_FORMAT("Failed to CreateDescriptorLayout(): {}", string_VkResult(result)); + throw std::runtime_error{ "Failed to CreateDescriptorLayout()" }; + } + + result = CreatePipelineLayout(); + assert(result == VkResult::VK_SUCCESS); + if (result != VkResult::VK_SUCCESS) + { + SH_ERROR_FORMAT("Failed to CreatePipelineLayout(): {}", string_VkResult(result)); + throw std::runtime_error{ "Failed to CreatePipelineLayout()" }; + } + } + + SH_RENDER_API auto VulkanShaderPass::GetDescriptorSetLayout(uint32_t set) const -> VkDescriptorSetLayout + { + if (setlayouts.size() < set) + return VK_NULL_HANDLE; + return setlayouts[set].descriptorSetLayout; } - SH_RENDER_API auto VulkanShaderPass::GetVertexShader() const -> const VkShaderModule + SH_RENDER_API auto VulkanShaderPass::GetSetLayout(uint32_t set) const -> std::optional { - return vertShader; + if (setlayouts.size() < set) + return {}; + return setlayouts[set]; } - SH_RENDER_API auto VulkanShaderPass::GetFragmentShader() const -> const VkShaderModule + + SH_RENDER_API auto VulkanShaderPass::GetDescriptorBinding(uint32_t set, uint32_t binding) const -> std::optional { - return fragShader; + if (setlayouts.size() < set) + return {}; + const SetLayout& setlayout = setlayouts[set]; + auto it = setlayout.descriptorSetLayoutBindings.find(binding); + if (it == setlayout.descriptorSetLayoutBindings.end()) + return{}; + return it->second; } void VulkanShaderPass::AddDescriptorBinding(uint32_t set, uint32_t binding, VkDescriptorType type, VkShaderStageFlagBits stage) { + if (setlayouts.size() <= set) + { + setlayouts.resize(set + 1); + } + setlayouts[set].set = set; + + auto it = setlayouts[set].descriptorSetLayoutBindings.find(binding); + if (it != setlayouts[set].descriptorSetLayoutBindings.end()) + { + assert(it->second.descriptorType == type); + it->second.stageFlags |= stage; + return; + } + VkDescriptorSetLayoutBinding layoutBinding{}; layoutBinding.binding = binding; layoutBinding.descriptorCount = 1; @@ -122,35 +186,35 @@ namespace sh::render::vk layoutBinding.pImmutableSamplers = nullptr; layoutBinding.stageFlags = stage; - if (descriptorBindings.size() <= set) - descriptorBindings.resize(set + 1, std::vector()); - - descriptorBindings[set].push_back(layoutBinding); + setlayouts[set].descriptorSetLayoutBindings.insert_or_assign(binding, layoutBinding); } auto VulkanShaderPass::CreateDescriptorLayout() -> VkResult { - descriptorSetLayout.resize(descriptorBindings.size()); - VkResult result = VkResult::VK_SUCCESS; - for (int i = 0; i < descriptorBindings.size(); ++i) + for (int i = 0; i < setlayouts.size(); ++i) { - if (descriptorBindings[i].empty()) + if (setlayouts[i].descriptorSetLayoutBindings.empty()) { - descriptorSetLayout[i] = context.GetEmptyDescriptorSetLayout(); + setlayouts[i].set = i; + setlayouts[i].descriptorSetLayout = context.GetEmptyDescriptorSetLayout(); continue; } + std::vector bindings; + bindings.reserve(setlayouts[i].descriptorSetLayoutBindings.size()); + for (const auto& [binding, descriptorSetLayoytBinding] : setlayouts[i].descriptorSetLayoutBindings) + bindings.push_back(descriptorSetLayoytBinding); + VkDescriptorSetLayoutCreateInfo info{}; info.sType = VkStructureType::VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; - info.bindingCount = descriptorBindings[i].size(); - info.pBindings = descriptorBindings[i].data(); + info.bindingCount = bindings.size(); + info.pBindings = bindings.data(); VkDescriptorSetLayout layout{}; result = vkCreateDescriptorSetLayout(context.GetDevice(), &info, nullptr, &layout); - assert(result == VkResult::VK_SUCCESS); if (result != VkResult::VK_SUCCESS) - throw std::runtime_error{ std::string{"Failed to create descriptor layout!: "} + string_VkResult(result) }; + return result; - descriptorSetLayout[i] = layout; + setlayouts[i].descriptorSetLayout = layout; } return result; } @@ -162,76 +226,32 @@ namespace sh::render::vk pushConstantRange.offset = 0; pushConstantRange.size = sizeof(glm::mat4); + std::vector layouts(setlayouts.size(), VK_NULL_HANDLE); + for (int i = 0; i < setlayouts.size(); ++i) + layouts[i] = setlayouts[i].descriptorSetLayout; + VkPipelineLayoutCreateInfo pipelineLayoutInfo{}; pipelineLayoutInfo.sType = VkStructureType::VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - pipelineLayoutInfo.setLayoutCount = descriptorSetLayout.size(); - pipelineLayoutInfo.pSetLayouts = descriptorSetLayout.data(); + pipelineLayoutInfo.setLayoutCount = layouts.size(); + pipelineLayoutInfo.pSetLayouts = layouts.data(); pipelineLayoutInfo.pushConstantRangeCount = bUseMatrixModel ? 1 : 0; pipelineLayoutInfo.pPushConstantRanges = &pushConstantRange; return vkCreatePipelineLayout(context.GetDevice(), &pipelineLayoutInfo, nullptr, &pipelineLayout); } - - SH_RENDER_API void VulkanShaderPass::Build() + void VulkanShaderPass::CleanDescriptors() { - CleanDescriptors(); - // 유니폼 레이아웃 생성 - for (auto& uniformLayout : vertexUniforms) + if (pipelineLayout != VK_NULL_HANDLE) { - if (uniformLayout.bConstant) - continue; - uint32_t set = static_cast(uniformLayout.type); - - VkDescriptorType type = (set == 0) ? - VkDescriptorType::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC : VkDescriptorType::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - - AddDescriptorBinding(set, uniformLayout.binding, type, - VkShaderStageFlagBits::VK_SHADER_STAGE_VERTEX_BIT); + vkDestroyPipelineLayout(context.GetDevice(), pipelineLayout, nullptr); + pipelineLayout = VK_NULL_HANDLE; } - for (auto& uniformLayout : fragmentUniforms) - { - if (uniformLayout.bConstant) - continue; - uint32_t set = static_cast(uniformLayout.type); - VkDescriptorType type = VkDescriptorType::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - if (set == 0) - type = VkDescriptorType::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; - - AddDescriptorBinding(set, uniformLayout.binding, type, - VkShaderStageFlagBits::VK_SHADER_STAGE_FRAGMENT_BIT); - } - for (auto& uniformLayout : samplerUniforms) + for (std::size_t i = 0; i < setlayouts.size(); ++i) { - uint32_t set = static_cast(uniformLayout.type); - AddDescriptorBinding(set, uniformLayout.binding, - VkDescriptorType::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - VkShaderStageFlagBits::VK_SHADER_STAGE_FRAGMENT_BIT); + if (setlayouts[i].descriptorSetLayout != context.GetEmptyDescriptorSetLayout()) + vkDestroyDescriptorSetLayout(context.GetDevice(), setlayouts[i].descriptorSetLayout, nullptr); } - auto result = CreateDescriptorLayout(); - assert(result == VkResult::VK_SUCCESS); - - result = CreatePipelineLayout(); - assert(result == VkResult::VK_SUCCESS); - } - - SH_RENDER_API auto VulkanShaderPass::GetDescriptorSetLayout(uint32_t set) const -> DescryptorSetLayoutInfo - { - DescryptorSetLayoutInfo info{}; - info.layout = descriptorSetLayout[set]; - info.bDynamic = (set == 0); - return info; - } - SH_RENDER_API auto VulkanShaderPass::GetSetCount() const -> uint32_t - { - return static_cast(descriptorSetLayout.size()); - } - SH_RENDER_API auto VulkanShaderPass::GetPipelineLayout() const -> VkPipelineLayout - { - return pipelineLayout; - } - SH_RENDER_API auto sh::render::vk::VulkanShaderPass::GetSpecializationMapEntry() const -> const std::vector& - { - return specializationEntry; + setlayouts.clear(); } -} \ No newline at end of file +}//namespace diff --git a/src/Render/VulkanImpl/VulkanShaderPassBuilder.cpp b/src/Render/VulkanImpl/VulkanShaderPassBuilder.cpp index 859b7767..65f15672 100644 --- a/src/Render/VulkanImpl/VulkanShaderPassBuilder.cpp +++ b/src/Render/VulkanImpl/VulkanShaderPassBuilder.cpp @@ -11,9 +11,7 @@ namespace sh::render::vk { } - VulkanShaderPassBuilder::~VulkanShaderPassBuilder() - { - } + VulkanShaderPassBuilder::~VulkanShaderPassBuilder() = default; SH_RENDER_API auto VulkanShaderPassBuilder::GetContext() const -> const VulkanContext& { @@ -64,7 +62,7 @@ namespace sh::render::vk shaderModules.vert = vertShader; shaderModules.frag = fragShader; - auto retShader = core::SObject::Create(context, shaderModules, passNode); + render::vk::VulkanShaderPass* const retShader = core::SObject::Create(context, shaderModules, passNode); retShader->StoreShaderCode(std::move(shaderCode)); retShader->Build(); diff --git a/src/Render/VulkanImpl/VulkanSwapChain.cpp b/src/Render/VulkanImpl/VulkanSwapChain.cpp index 4a6280cf..d78eec3a 100644 --- a/src/Render/VulkanImpl/VulkanSwapChain.cpp +++ b/src/Render/VulkanImpl/VulkanSwapChain.cpp @@ -163,7 +163,7 @@ namespace sh::render::vk if (swapChainImageFormat == VkFormat::VK_FORMAT_B8G8R8A8_SRGB) colorFormat = TextureFormat::SBGRA32; - rtLayout.format = colorFormat; + rtLayout.colorFormats[0] = colorFormat; rtLayout.depthFormat = TextureFormat::None; rtLayout.bUseMSAA = bMSAA; for (VkImage img : images) diff --git a/src/Render/VulkanImpl/VulkanTextureBuffer.cpp b/src/Render/VulkanImpl/VulkanTextureBuffer.cpp deleted file mode 100644 index e4fc7f79..00000000 --- a/src/Render/VulkanImpl/VulkanTextureBuffer.cpp +++ /dev/null @@ -1,153 +0,0 @@ -//#include "VulkanTextureBuffer.h" -//#include "VulkanContext.h" -//#include "VulkanBuffer.h" -//#include "VulkanFramebuffer.h" -//#include "VulkanQueueManager.h" -//#include "VulkanCommandBuffer.h" -//#include "VulkanCommandBufferPool.h" -// -//#include -//#include -//namespace sh::render::vk -//{ -// VulkanTextureBuffer::VulkanTextureBuffer() : -// isRenderTexture(false) -// { -// } -// VulkanTextureBuffer::VulkanTextureBuffer(VulkanTextureBuffer&& other) noexcept : -// context(other.context), queueManager(other.queueManager), -// imgBuffer(std::move(other.imgBuffer)), -// isRenderTexture(other.isRenderTexture), -// width(other.width), height(other.height) -// { -// other.context = nullptr; -// other.queueManager = nullptr; -// } -// VulkanTextureBuffer::~VulkanTextureBuffer() -// { -// } -// SH_RENDER_API void VulkanTextureBuffer::Clean() -// { -// isRenderTexture = false; -// imgBuffer.reset(); -// } -// -// void VulkanTextureBuffer::CopyBufferToImageCommand(VkCommandBuffer cmd, VkBuffer buffer, VkImage image, uint32_t width, uint32_t height, uint32_t mipLevel) -// { -// VkBufferImageCopy region{}; -// region.bufferOffset = 0; -// region.bufferRowLength = 0; -// region.bufferImageHeight = 0; -// -// region.imageSubresource.aspectMask = VkImageAspectFlagBits::VK_IMAGE_ASPECT_COLOR_BIT; -// region.imageSubresource.mipLevel = mipLevel; -// region.imageSubresource.baseArrayLayer = 0; -// region.imageSubresource.layerCount = 1; -// -// region.imageOffset = { 0, 0, 0 }; -// region.imageExtent = -// { -// width, -// height, -// 1 -// }; -// -// vkCmdCopyBufferToImage(cmd, buffer, image, VkImageLayout::VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); -// } -// -// SH_RENDER_API void VulkanTextureBuffer::Create(const IRenderContext& context, const CreateInfo& info) -// { -// this->context = &static_cast(context); -// queueManager = &this->context->GetQueueManager(); -// -// isRenderTexture = false; -// width = info.width; -// height = info.height; -// uint32_t mipLevels = info.bGenerateMipmap ? static_cast(std::floor(std::log2(std::max(width, height)))) + 1 : 1; -// switch (info.format) -// { -// case Texture::TextureFormat::SRGB24: [[fallthrough]]; // 일부 GPU는 3채널을 지원하지 않음 -// case Texture::TextureFormat::SRGBA32: -// format = VkFormat::VK_FORMAT_R8G8B8A8_SRGB; -// channel = 4; -// break; -// case Texture::TextureFormat::RGB24: [[fallthrough]]; // 일부 GPU는 3채널을 지원하지 않음 -// case Texture::TextureFormat::RGBA32: -// format = VkFormat::VK_FORMAT_R8G8B8A8_UNORM; -// channel = 4; -// break; -// } -// if (imgBuffer == nullptr) -// imgBuffer = std::make_unique(*this->context); -// imgBuffer->SetAnisotropy(info.aniso). -// SetFilter(info.filtering == 0 ? VkFilter::VK_FILTER_LINEAR : VkFilter::VK_FILTER_NEAREST); -// imgBuffer->Create(width, height, format, -// VkImageUsageFlagBits::VK_IMAGE_USAGE_TRANSFER_DST_BIT | -// VkImageUsageFlagBits::VK_IMAGE_USAGE_SAMPLED_BIT, -// VkImageAspectFlagBits::VK_IMAGE_ASPECT_COLOR_BIT, -// VkSampleCountFlagBits::VK_SAMPLE_COUNT_1_BIT, -// mipLevels); -// } -// SH_RENDER_API void VulkanTextureBuffer::Create(const Framebuffer& framebuffer) -// { -// isRenderTexture = true; -// this->framebuffer = &framebuffer; -// } -// -// SH_RENDER_API void VulkanTextureBuffer::SetData(const void* data, uint32_t mipLevel) -// { -// VulkanBuffer stagingBuffer{ *context }; -// -// uint32_t mipWidth = width; -// uint32_t mipHeight = height; -// for (int i = 0; i < mipLevel; ++i) -// { -// mipWidth = std::max(1u, mipWidth / 2); -// mipHeight = std::max(1u, mipHeight / 2); -// } -// std::size_t bufferSize = mipWidth * mipHeight * channel; -// -// stagingBuffer.Create(bufferSize, -// VkBufferUsageFlagBits::VK_BUFFER_USAGE_TRANSFER_SRC_BIT, -// VkSharingMode::VK_SHARING_MODE_EXCLUSIVE, -// VkMemoryPropertyFlagBits::VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VkMemoryPropertyFlagBits::VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); -// stagingBuffer.SetData(data); -// -// VulkanCommandBuffer* cmd = context->GetCommandBufferPool().AllocateCommandBuffer(std::this_thread::get_id(), VkQueueFlagBits::VK_QUEUE_GRAPHICS_BIT); -// cmd->Build([&] -// { -// imgBuffer->ChangeLayoutCommand(cmd->GetCommandBuffer(), -// VkImageLayout::VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, -// VkPipelineStageFlagBits::VK_PIPELINE_STAGE_TRANSFER_BIT, -// VkAccessFlagBits::VK_ACCESS_TRANSFER_WRITE_BIT); -// -// CopyBufferToImageCommand(cmd->GetCommandBuffer(), stagingBuffer.GetBuffer(), imgBuffer->GetImage(), mipWidth, mipHeight, mipLevel); -// -// imgBuffer->ChangeLayoutCommand(cmd->GetCommandBuffer(), -// VkImageLayout::VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, -// VkPipelineStageFlagBits::VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, -// VkAccessFlagBits::VK_ACCESS_SHADER_READ_BIT); -// }, -// true -// ); -// -// VkFence fence = cmd->GetOrCreateFence(); -// queueManager->Submit(VulkanQueueManager::Role::Graphics, *cmd, fence); -// vkWaitForFences(context->GetDevice(), 1, &fence, true, std::numeric_limits::max()); -// vkResetFences(context->GetDevice(), 1, &fence); -// -// context->GetCommandBufferPool().DeallocateCommandBuffer(*cmd); -// } -// -// SH_RENDER_API auto VulkanTextureBuffer::GetImageBuffer() const -> VulkanImageBuffer* -// { -// if(!isRenderTexture) -// return imgBuffer.get(); -// else -// return static_cast(framebuffer)->GetColorImg(); -// } -// SH_RENDER_API auto VulkanTextureBuffer::GetSize() const -> std::size_t -// { -// return width * height * channel; -// } -//} \ No newline at end of file diff --git a/src/Render/VulkanImpl/VulkanVertexBuffer.cpp b/src/Render/VulkanImpl/VulkanVertexBuffer.cpp index fee3ecf9..c51b57e1 100644 --- a/src/Render/VulkanImpl/VulkanVertexBuffer.cpp +++ b/src/Render/VulkanImpl/VulkanVertexBuffer.cpp @@ -101,70 +101,25 @@ namespace sh::render::vk } return attribDescriptions; } - SH_RENDER_API auto VulkanVertexBuffer::GetVertexBuffer() const -> const VulkanBuffer& - { - return vertexBuffer; - } - SH_RENDER_API auto VulkanVertexBuffer::GetIndexBuffer() const -> const VulkanBuffer& - { - return indexBuffer; - } void VulkanVertexBuffer::CreateVertexBuffer(const Mesh& mesh) { if (mesh.GetVertexCount() == 0) return; - VulkanCommandBuffer* cmd = context.GetCommandBufferPool().AllocateCommandBuffer(std::this_thread::get_id(), VkQueueFlagBits::VK_QUEUE_TRANSFER_BIT); - assert(cmd != nullptr); // 버텍스 버퍼 - size_t vertexBufferSize = sizeof(Mesh::Vertex) * mesh.GetVertexCount(); - VulkanBuffer stagingBuffer1{ context }; - stagingBuffer1.Create(vertexBufferSize, VkBufferUsageFlagBits::VK_BUFFER_USAGE_TRANSFER_SRC_BIT, - VkSharingMode::VK_SHARING_MODE_EXCLUSIVE, - VkMemoryPropertyFlagBits::VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VkMemoryPropertyFlagBits::VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); - stagingBuffer1.SetData(mesh.GetVertex().data()); - + const size_t vertexBufferSize = sizeof(Mesh::Vertex) * mesh.GetVertexCount(); vertexBuffer.Create(vertexBufferSize, VkBufferUsageFlagBits::VK_BUFFER_USAGE_TRANSFER_DST_BIT | VkBufferUsageFlagBits::VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VkSharingMode::VK_SHARING_MODE_EXCLUSIVE, VkMemoryPropertyFlagBits::VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + vertexBuffer.SetData(mesh.GetVertex().data()); // 인덱스 버퍼 - size_t indicesSize = sizeof(uint32_t) * mesh.GetIndices().size(); - VulkanBuffer stagingBuffer2{ context }; - stagingBuffer2.Create(indicesSize, VkBufferUsageFlagBits::VK_BUFFER_USAGE_TRANSFER_SRC_BIT, - VkSharingMode::VK_SHARING_MODE_EXCLUSIVE, - VkMemoryPropertyFlagBits::VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VkMemoryPropertyFlagBits::VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); - stagingBuffer2.SetData(mesh.GetIndices().data()); - + const size_t indicesSize = sizeof(uint32_t) * mesh.GetIndices().size(); indexBuffer.Create(indicesSize, VkBufferUsageFlagBits::VK_BUFFER_USAGE_TRANSFER_DST_BIT | VkBufferUsageFlagBits::VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VkSharingMode::VK_SHARING_MODE_EXCLUSIVE, VkMemoryPropertyFlagBits::VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - - cmd->Build( - [&]() - { - VkBufferCopy cpy{}; - cpy.srcOffset = 0; - cpy.dstOffset = 0; - cpy.size = vertexBufferSize; - vkCmdCopyBuffer(cmd->GetCommandBuffer(), stagingBuffer1.GetBuffer(), vertexBuffer.GetBuffer(), 1, &cpy); - - VkBufferCopy cpyIndices{}; - cpyIndices.srcOffset = 0; - cpyIndices.dstOffset = 0; - cpyIndices.size = indicesSize; - vkCmdCopyBuffer(cmd->GetCommandBuffer(), stagingBuffer2.GetBuffer(), indexBuffer.GetBuffer(), 1, &cpyIndices); - }, - true - ); - - VkFence fence = cmd->GetOrCreateFence(); - context.GetQueueManager().Submit(VulkanQueueManager::Role::Transfer, *cmd, fence); - vkWaitForFences(context.GetDevice(), 1, &fence, true, std::numeric_limits::max()); - vkResetFences(context.GetDevice(), 1, &fence); - - context.GetCommandBufferPool().DeallocateCommandBuffer(*cmd); + indexBuffer.SetData(mesh.GetIndices().data()); } -} \ No newline at end of file +}//namespace \ No newline at end of file diff --git a/src/Sound/SoundSystem.cpp b/src/Sound/SoundSystem.cpp index 890bd188..8e266b3e 100644 --- a/src/Sound/SoundSystem.cpp +++ b/src/Sound/SoundSystem.cpp @@ -137,7 +137,12 @@ namespace sh::sound std::string requestedDevice{ deviceName }; impl->device = alcOpenDevice(requestedDevice.empty() ? nullptr : requestedDevice.c_str()); if (impl->device == nullptr) - throw std::runtime_error{ "Failed to open OpenAL device." }; + { + // 아마도 기본 사운드 시스템이 없는 경우임 + const ALenum err = alcGetError(nullptr); + SH_ERROR_FORMAT("Failed to open OpenAL device: {}", GetAlcErrorString(err)); + return; + } impl->context = alcCreateContext(impl->device, nullptr); if (impl->context == nullptr)