/* =========================================================================== Copyright (C) 2022-2024 Gian 'myT' Schellenbaum This file is part of Challenge Quake 3 (CNQ3). Challenge Quake 3 is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Challenge Quake 3 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Challenge Quake 3. If not, see . =========================================================================== */ // Gameplay Rendering Pipeline - private declarations #pragma once #include "srp_local.h" #pragma pack(push, 4) struct WorldVertexRC { float modelViewMatrix[16]; float projectionMatrix[16]; float clipPlane[4]; }; struct WorldPixelRC { // general uint32_t stageIndices[8]; // sampler: 16 - texture: 16 float greyscale; // r_shaderTrace - dynamically enabled uint32_t shaderTrace; // shader index: 14 - enable: 1 uint16_t centerPixelX; uint16_t centerPixelY; // r_depthFade - statically enabled uint16_t hFadeDistance; uint16_t hFadeOffset; uint32_t depthFadeColorTex; // texture index: 12 - color bias: 4 - color scale: 4 // r_dither - statically enabled uint16_t hFrameSeed; uint16_t hNoiseScale; uint16_t hInvGamma; uint16_t hInvBrightness; }; #pragma pack(pop) struct VertexBuffers : BufferBase { enum BaseId { BasePosition, BaseNormal, BaseCount }; enum StageId { StageTexCoords, StageColors, StageCount }; void Create(const char* name, MemoryUsage::Id memoryUsage, uint32_t vertexCount); void BeginUpload(); void EndUpload(); void Upload(uint32_t firstStage, uint32_t stageCount); static const uint32_t BufferCount = BaseCount + StageCount * MAX_SHADER_STAGES; HBuffer buffers[BufferCount] = {}; uint32_t strides[BufferCount] = {}; uint8_t* mapped[BufferCount] = {}; }; struct GeometryBuffers { void Rewind() { vertexBuffers.Rewind(); indexBuffer.Rewind(); } VertexBuffers vertexBuffers; IndexBuffer indexBuffer; }; struct StaticGeometryChunk { uint32_t vertexCount; uint32_t indexCount; uint32_t firstGPUVertex; uint32_t firstGPUIndex; uint32_t firstCPUIndex; }; struct BatchType { enum Id { Standard, DynamicLight, DepthFade, Count }; }; struct World { void Init(); void BeginFrame(); void EndFrame(); void Begin(); void End(); void DrawPrePass(const drawSceneViewCommand_t& cmd); void BeginBatch(const shader_t* shader, bool hasStaticGeo, BatchType::Id batchType); void EndBatch(); void EndSkyBatch(); void RestartBatch(); void DrawGUI(); void ProcessWorld(world_t& world); void DrawSceneView(const drawSceneViewCommand_t& cmd); void BindVertexBuffers(bool staticGeo, uint32_t firstStage, uint32_t stageCount); void BindIndexBuffer(bool staticGeo); void DrawFog(); void DrawLitSurfaces(dlight_t* dl, bool opaque); void DrawDynamicLights(bool opaque); void DrawSkyBox(); void DrawClouds(); typedef uint32_t Index; const IndexType::Id indexType = IndexType::UInt32; HTexture depthTexture; uint32_t depthTextureIndex; float clipPlane[4]; struct BufferFamily { enum Id { Invalid, PrePass, Static, Dynamic }; }; // Z pre-pass HRootSignature zppRootSignature; HDescriptorTable zppDescriptorTable; HPipeline zppPipeline; GeometryBuffer zppVertexBuffer; // shared BufferFamily::Id boundVertexBuffers; BufferFamily::Id boundIndexBuffer; uint32_t boundStaticVertexBuffersFirst; uint32_t boundStaticVertexBuffersCount; HPipeline batchPSO; BatchType::Id batchType; bool batchHasStaticGeo; int psoChangeCount; bool batchDepthHack; bool batchOldDepthHack; ShadingRate::Id batchShadingRate; ShadingRate::Id batchOldShadingRate; // dynamic GeometryBuffers dynBuffers[FrameCount]; // static GeometryBuffers statBuffers; StaticGeometryChunk statChunks[32768]; uint32_t statChunkCount; uint32_t statIndices[1 << 20]; uint32_t statIndexCount; // fog HRootSignature fogRootSignature; HDescriptorTable fogDescriptorTable; HPipeline fogOutsidePipeline; HPipeline fogInsidePipeline; HBuffer boxVertexBuffer; HBuffer boxIndexBuffer; // dynamic lights HRootSignature dlRootSignature; HPipeline dlPipelines[CT_COUNT * 2 * 2]; // { cull type, polygon offset, depth test } bool dlOpaque; float dlIntensity; // 1 for most surfaces, but can be scaled down for liquids etc. bool dlDepthTestEqual; // quick explanation on why dlOpaque is useful in the first place: // - opaque surfaces can have a diffuse texture whose alpha isn't 255 everywhere // - when that happens and we multiply the color by the the alpha (DL uses additive blending), // we get "light holes" in opaque surfaces, which is not what we want }; #pragma pack(push, 1) struct PSODesc { cullType_t cullType; bool polygonOffset; bool clampDepth; bool depthFade; }; #pragma pack(pop) struct CachedPSO { #if defined(_DEBUG) // lets us know which Q3 shader // triggered the creation of this PSO char name[MAX_QPATH]; #endif PSODesc desc; uint32_t stageStateBits[MAX_SHADER_STAGES]; uint32_t stageCount; HPipeline pipeline; }; struct PostProcess { void Init(); void Draw(const char* renderPassName, HTexture renderTarget); void ToneMap(); void InverseToneMap(int colorFormat); void SetToneMapInput(HTexture toneMapInput); void SetInverseToneMapInput(HTexture inverseToneMapInput); private: HPipeline toneMapPipeline; HRootSignature toneMapRootSignature; HDescriptorTable toneMapDescriptorTable; HPipeline inverseToneMapPipelines[RTCF_COUNT]; HRootSignature inverseToneMapRootSignature; HDescriptorTable inverseToneMapDescriptorTable; }; struct SMAA { void Init(); void Update(); void Draw(const viewParms_t& parms); // matches r_smaa struct Mode { enum Id { Disabled, Low, Medium, High, Ultra, Count }; }; private: // fixed HTexture areaTexture; HTexture searchTexture; HRootSignature rootSignature; HDescriptorTable descriptorTable; // depends on render target resolution HTexture edgeTexture; HTexture blendTexture; HTexture stencilTexture; HTexture inputTexture; // tone mapped HTexture outputTexture; // tone mapped // depends on selected preset/mode // SMAA has 3 passes: // 1. edge detection // 2. blend weight computation // 3. neighborhood blending HPipeline firstPassPipeline; HPipeline secondPassPipeline; HPipeline thirdPassPipeline; Mode::Id mode = Mode::Disabled; int width = -1; int height = -1; bool fixedLoaded = false; }; struct GRP : IRenderPipeline { void Init() override; void ShutDown(bool fullShutDown) override; void CreateTexture(image_t* image, int mipCount, int width, int height) override; void UpoadTextureAndGenerateMipMaps(image_t* image, const byte* data) override; void BeginTextureUpload(MappedTexture& mappedTexture, image_t* image) override; void EndTextureUpload() override; void ProcessWorld(world_t& world) override; void ProcessModel(model_t& model) override; void ProcessShader(shader_t& shader) override; void ExecuteRenderCommands(const byte* data, bool readbackRequested) override; void TessellationOverflow() override { world.RestartBatch(); } void DrawSkyBox() override { world.DrawSkyBox(); } void DrawClouds() override { world.DrawClouds(); } void ReadPixels(int w, int h, int alignment, colorSpace_t colorSpace, void* out) override; uint32_t GetSamplerDescriptorIndexFromBaseIndex(uint32_t baseIndex) override { return baseIndex; } void BeginFrame(); void EndFrame(); uint32_t RegisterTexture(HTexture htexture); uint32_t CreatePSO(CachedPSO& cache, const char* name); void UpdateReadbackTexture(); UI ui; World world; MipMapGenerator mipMapGen; ImGUI imgui; PostProcess post; SMAA smaa; Nuklear nuklear; float frameSeed; bool updateReadbackTexture; HTexture renderTarget; TextureFormat::Id renderTargetFormat; HTexture readbackRenderTarget; RootSignatureDesc rootSignatureDesc; HRootSignature rootSignature; HDescriptorTable descriptorTable; uint32_t textureIndex; HSampler samplers[BASE_SAMPLER_COUNT]; // all base samplers CachedPSO psos[1024]; uint32_t psoCount; HRootSignature uberRootSignature; }; extern GRP grp;