// SPDX-FileCopyrightText: 2021 Jorrit Rouwe // SPDX-License-Identifier: MIT #pragma once #include #include #include #include #include #include #include #include // Forward declares class Texture; /// Camera setup struct CameraState { CameraState() : mPos(Vec3::sZero()), mForward(0, 0, -1), mUp(0, 1, 0), mFOVY(DegreesToRadians(70.0f)), mFarPlane(100.0f) { } Vec3 mPos; ///< Camera position Vec3 mForward; ///< Camera forward vector Vec3 mUp; ///< Camera up vector float mFOVY; ///< Field of view in radians in up direction float mFarPlane; ///< Distance of far plane }; /// Responsible for rendering primitives to the screen class Renderer { public: /// Destructor ~Renderer(); /// Initialize DirectX void Initialize(); /// Callback when the window resizes and the back buffer needs to be adjusted void OnWindowResize(); /// Get window size int GetWindowWidth() { return mWindowWidth; } int GetWindowHeight() { return mWindowHeight; } /// Access to the window handle HWND GetWindowHandle() const { return mhWnd; } /// Access to the most important DirectX structures ID3D12Device * GetDevice() { return mDevice.Get(); } ID3D12RootSignature * GetRootSignature() { return mRootSignature.Get(); } ID3D12GraphicsCommandList * GetCommandList() { return mCommandList.Get(); } CommandQueue & GetUploadQueue() { return mUploadQueue; } DescriptorHeap & GetDSVHeap() { return mDSVHeap; } DescriptorHeap & GetSRVHeap() { return mSRVHeap; } /// Start / end drawing a frame void BeginFrame(const CameraState &inCamera, float inWorldScale); void EndFrame(); /// Switch between orthographic and 3D projection mode void SetProjectionMode(); void SetOrthoMode(); /// Create texture from an image surface Ref CreateTexture(const Surface *inSurface); /// Create a texture to render to (currently depth buffer only) Ref CreateRenderTarget(int inWidth, int inHeight); /// Change the render target to a texture. Use nullptr to set back to the main render target. void SetRenderTarget(Texture *inRenderTarget); /// Compile a vertex shader ComPtr CreateVertexShader(const char *inFileName); /// Compile a pixel shader ComPtr CreatePixelShader(const char *inFileName); /// Create a constant buffer for the shader unique_ptr CreateConstantBuffer(uint inBufferSize); /// Create pipeline state object that defines the complete state of how primitives should be rendered unique_ptr CreatePipelineState(ID3DBlob *inVertexShader, const D3D12_INPUT_ELEMENT_DESC *inInputDescription, uint inInputDescriptionCount, ID3DBlob *inPixelShader, D3D12_FILL_MODE inFillMode, D3D12_PRIMITIVE_TOPOLOGY_TYPE inTopology, PipelineState::EDepthTest inDepthTest, PipelineState::EBlendMode inBlendMode, PipelineState::ECullMode inCullMode); /// Get the camera state / frustum (only valid between BeginFrame() / EndFrame()) const CameraState & GetCameraState() const { return mCameraState; } const Frustum & GetCameraFrustum() const { return mCameraFrustum; } /// Get the light frustum (only valid between BeginFrame() / EndFrame()) const Frustum & GetLightFrustum() const { return mLightFrustum; } /// How many frames our pipeline is static const uint cFrameCount = 2; /// Which frame is currently rendering (to keep track of which buffers are free to overwrite) uint GetCurrentFrameIndex() const { return mFrameIndex; } /// Create a buffer on the default heap (usable for permanent buffers) ComPtr CreateD3DResourceOnDefaultHeap(const void *inData, uint64 inSize); /// Create buffer on the upload heap (usable for temporary buffers). ComPtr CreateD3DResourceOnUploadHeap(uint64 inSize); /// Recycle a buffer on the upload heap. This puts it back in a cache and will reuse it when it is certain the GPU is no longer referencing it. void RecycleD3DResourceOnUploadHeap(ID3D12Resource *inResource, uint64 inSize); /// Keeps a reference to the resource until the current frame has finished void RecycleD3DObject(ID3D12Object *inResource); private: // Wait for pending GPU work to complete void WaitForGpu(); // Create render targets and their views void CreateRenterTargets(); // Create a depth buffer for the back buffer void CreateDepthBuffer(); // Function to create a ID3D12Resource on specified heap with specified state ComPtr CreateD3DResource(D3D12_HEAP_TYPE inHeapType, D3D12_RESOURCE_STATES inResourceState, uint64 inSize); // Copy CPU memory into a ID3D12Resource void CopyD3DResource(ID3D12Resource *inDest, const void *inSrc, uint64 inSize); // Copy a CPU resource to a GPU resource void CopyD3DResource(ID3D12Resource *inDest, ID3D12Resource *inSrc, uint64 inSize); HWND mhWnd; int mWindowWidth = 1920; int mWindowHeight = 1080; unique_ptr mVertexShaderConstantBufferProjection[cFrameCount]; unique_ptr mVertexShaderConstantBufferOrtho[cFrameCount]; unique_ptr mPixelShaderConstantBuffer[cFrameCount]; CameraState mCameraState; Frustum mCameraFrustum; Frustum mLightFrustum; // DirectX interfaces ComPtr mDXGIFactory; ComPtr mDevice; DescriptorHeap mRTVHeap; ///< Render target view heap DescriptorHeap mDSVHeap; ///< Depth stencil view heap DescriptorHeap mSRVHeap; ///< Shader resource view heap ComPtr mSwapChain; ComPtr mRenderTargets[cFrameCount]; ///< Two render targets (we're double buffering in order for the CPU to continue while the GPU is rendering) D3D12_CPU_DESCRIPTOR_HANDLE mRenderTargetViews[cFrameCount]; ///< The two render views corresponding to the render targets ComPtr mDepthStencilBuffer; ///< The main depth buffer D3D12_CPU_DESCRIPTOR_HANDLE mDepthStencilView { 0 }; ///< A view for binding the depth buffer ComPtr mCommandAllocators[cFrameCount]; ///< Two command allocator lists (one per frame) ComPtr mCommandQueue; ///< The command queue that will execute commands (there's only 1 since we want to finish rendering 1 frame before moving onto the next) ComPtr mCommandList; ///< The command list ComPtr mRootSignature; ///< The root signature, we have a simple application so we only need 1, which is suitable for all our shaders Ref mRenderTargetTexture; ///< When rendering to a texture, this is the active texture CommandQueue mUploadQueue; ///< Queue used to upload resources to GPU memory // Synchronization objects used to finish rendering and swapping before reusing a command queue uint mFrameIndex; ///< Current frame index (0 or 1) HANDLE mFenceEvent; ///< Fence event to wait for the previous frame rendering to complete (in order to free 1 of the buffers) ComPtr mFence; ///< Fence object, used to signal the end of a frame UINT64 mFenceValues[cFrameCount] = {}; ///< Values that were used to signal completion of one of the two frames using ResourceCache = UnorderedMap>>; ResourceCache mResourceCache; ///< Cache items ready to be reused ResourceCache mDelayCached[cFrameCount]; ///< List of reusable ID3D12Resources that are potentially referenced by the GPU so can be used only when the GPU finishes Array> mDelayReleased[cFrameCount]; ///< Objects that are potentially referenced by the GPU so can only be freed when the GPU finishes bool mIsExiting = false; ///< When exiting we don't want to add references too buffers };