2024-01-13 21:40:13 +00:00
|
|
|
/*
|
|
|
|
===========================================================================
|
|
|
|
Copyright (C) 2023-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 <https://www.gnu.org/licenses/>.
|
|
|
|
===========================================================================
|
|
|
|
*/
|
|
|
|
// Shared Rendering Pipeline - private declarations
|
|
|
|
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
|
|
#include "tr_local.h"
|
|
|
|
#include "rhi_local.h"
|
|
|
|
|
|
|
|
|
|
|
|
using namespace RHI;
|
|
|
|
|
|
|
|
|
|
|
|
struct BufferBase
|
|
|
|
{
|
|
|
|
bool CanAdd(uint32_t count_)
|
|
|
|
{
|
|
|
|
return batchFirst + batchCount + count_ <= totalCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
void EndBatch()
|
|
|
|
{
|
|
|
|
batchFirst += batchCount;
|
|
|
|
batchCount = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void EndBatch(uint32_t size)
|
|
|
|
{
|
|
|
|
batchFirst += size;
|
|
|
|
batchCount = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Rewind()
|
|
|
|
{
|
|
|
|
batchFirst = 0;
|
|
|
|
batchCount = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t totalCount = 0;
|
|
|
|
uint32_t batchFirst = 0;
|
|
|
|
uint32_t batchCount = 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct IndexBuffer : BufferBase
|
|
|
|
{
|
|
|
|
void Create(const char* name, MemoryUsage::Id memoryUsage, uint32_t indexCount)
|
|
|
|
{
|
|
|
|
totalCount = indexCount;
|
|
|
|
|
|
|
|
BufferDesc desc = {};
|
|
|
|
desc.committedResource = true;
|
|
|
|
desc.initialState = ResourceStates::IndexBufferBit;
|
|
|
|
desc.memoryUsage = memoryUsage;
|
|
|
|
desc.name = va("%s index", name);
|
|
|
|
desc.byteCount = indexCount * sizeof(uint32_t);
|
|
|
|
buffer = CreateBuffer(desc);
|
|
|
|
}
|
|
|
|
|
|
|
|
void BeginUpload()
|
|
|
|
{
|
|
|
|
mapped = (uint32_t*)BeginBufferUpload(buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
void EndUpload()
|
|
|
|
{
|
|
|
|
EndBufferUpload(buffer);
|
|
|
|
mapped = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Upload()
|
|
|
|
{
|
|
|
|
Q_assert(mapped != NULL);
|
|
|
|
|
|
|
|
uint32_t* const idx = mapped + batchFirst + batchCount;
|
|
|
|
memcpy(idx, &tess.indexes[0], tess.numIndexes * sizeof(uint32_t));
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t* GetCurrentAddress()
|
|
|
|
{
|
|
|
|
return mapped + batchFirst + batchCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
HBuffer buffer = RHI_MAKE_NULL_HANDLE();
|
|
|
|
uint32_t* mapped = NULL;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct GeometryBuffer : BufferBase
|
|
|
|
{
|
|
|
|
void Init(uint32_t count_, uint32_t stride_)
|
|
|
|
{
|
|
|
|
buffer = RHI_MAKE_NULL_HANDLE();
|
|
|
|
byteCount = count_ * stride_;
|
|
|
|
stride = stride_;
|
|
|
|
totalCount = count_;
|
|
|
|
batchFirst = 0;
|
|
|
|
batchCount = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CreateVertexBuffer(const char* name, MemoryUsage::Id memoryUsage, uint32_t count, uint32_t stride_)
|
|
|
|
{
|
|
|
|
BufferDesc desc = {};
|
|
|
|
desc.committedResource = true;
|
|
|
|
desc.initialState = ResourceStates::VertexBufferBit;
|
|
|
|
desc.memoryUsage = memoryUsage;
|
|
|
|
desc.name = name;
|
|
|
|
desc.byteCount = count * stride_;
|
|
|
|
buffer = CreateBuffer(desc);
|
|
|
|
byteCount = count * stride_;
|
|
|
|
stride = stride_;
|
|
|
|
totalCount = count;
|
|
|
|
batchFirst = 0;
|
|
|
|
batchCount = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void BeginUpload()
|
|
|
|
{
|
|
|
|
Q_assert(mapped == NULL);
|
|
|
|
mapped = BeginBufferUpload(buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
void EndUpload()
|
|
|
|
{
|
|
|
|
Q_assert(mapped != NULL);
|
|
|
|
EndBufferUpload(buffer);
|
|
|
|
mapped = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
HBuffer buffer = RHI_MAKE_NULL_HANDLE();
|
|
|
|
uint32_t byteCount = 0;
|
|
|
|
uint32_t stride = 0;
|
|
|
|
uint8_t* mapped = NULL;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct RenderMode
|
|
|
|
{
|
|
|
|
enum Id
|
|
|
|
{
|
|
|
|
None,
|
|
|
|
UI,
|
|
|
|
World,
|
|
|
|
ImGui,
|
|
|
|
Nuklear,
|
|
|
|
Count
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
struct RenderPassQueries
|
|
|
|
{
|
|
|
|
char name[64];
|
|
|
|
uint32_t gpuDurationUS;
|
|
|
|
uint32_t cpuDurationUS;
|
|
|
|
int64_t cpuStartUS;
|
|
|
|
uint32_t queryIndex;
|
|
|
|
};
|
|
|
|
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
MaxRenderPasses = 64, // cg_draw3dIcons forces tons of 2D/3D transitions...
|
|
|
|
MaxStatsFrameCount = 64
|
|
|
|
};
|
|
|
|
|
|
|
|
struct RenderPassStats
|
|
|
|
{
|
|
|
|
void EndFrame(uint32_t cpu, uint32_t gpu);
|
|
|
|
|
|
|
|
uint32_t samplesCPU[MaxStatsFrameCount];
|
|
|
|
uint32_t samplesGPU[MaxStatsFrameCount];
|
|
|
|
stats_t statsCPU;
|
|
|
|
stats_t statsGPU;
|
|
|
|
uint32_t count;
|
|
|
|
uint32_t index;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct RenderPassFrame
|
|
|
|
{
|
|
|
|
RenderPassQueries passes[MaxRenderPasses];
|
|
|
|
uint32_t count;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct FrameStats
|
|
|
|
{
|
|
|
|
enum { MaxFrames = 1024 };
|
|
|
|
|
|
|
|
void EndFrame();
|
|
|
|
|
|
|
|
float temp[MaxFrames];
|
|
|
|
float p2pMS[MaxFrames];
|
|
|
|
stats_t p2pStats;
|
|
|
|
int frameCount;
|
|
|
|
int frameIndex;
|
|
|
|
int skippedFrames;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct MipMapGenerator
|
|
|
|
{
|
|
|
|
void Init(bool ddhi, const ShaderByteCode& g2l, const ShaderByteCode& down, const ShaderByteCode& l2g);
|
|
|
|
void GenerateMipMaps(HTexture texture);
|
|
|
|
|
|
|
|
private:
|
|
|
|
struct Stage
|
|
|
|
{
|
|
|
|
enum Id
|
|
|
|
{
|
|
|
|
Start, // gamma to linear
|
|
|
|
DownSample, // down sample on 1 axis
|
|
|
|
End, // linear to gamma
|
|
|
|
Count
|
|
|
|
};
|
|
|
|
|
|
|
|
HRootSignature rootSignature;
|
|
|
|
HDescriptorTable descriptorTable;
|
|
|
|
HPipeline pipeline;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct MipSlice
|
|
|
|
{
|
|
|
|
enum Id
|
|
|
|
{
|
|
|
|
Float16_0,
|
|
|
|
Float16_1,
|
|
|
|
Count
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
HTexture textures[MipSlice::Count];
|
|
|
|
Stage stages[3];
|
|
|
|
bool ddhi = false;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct UI
|
|
|
|
{
|
|
|
|
void Init(bool ddhi_, const ShaderByteCode& vs, const ShaderByteCode& ps, TextureFormat::Id rtFormat,
|
|
|
|
HDescriptorTable descTable, RootSignatureDesc* rootSigDesc);
|
|
|
|
void BeginFrame();
|
|
|
|
void Begin(HTexture renderTarget);
|
|
|
|
void End();
|
|
|
|
void CmdSetColor(const uiSetColorCommand_t& cmd);
|
|
|
|
void CmdDrawQuad(const uiDrawQuadCommand_t& cmd);
|
|
|
|
void CmdDrawTriangle(const uiDrawTriangleCommand_t& cmd);
|
|
|
|
|
|
|
|
private:
|
|
|
|
void DrawBatch();
|
|
|
|
|
|
|
|
// 32-bit needed until the render logic is fixed!
|
|
|
|
typedef uint32_t Index;
|
|
|
|
const IndexType::Id indexType = IndexType::UInt32;
|
|
|
|
|
|
|
|
uint32_t renderPassIndex;
|
|
|
|
|
|
|
|
#pragma pack(push, 1)
|
|
|
|
struct Vertex
|
|
|
|
{
|
|
|
|
vec2_t position;
|
|
|
|
vec2_t texCoords;
|
|
|
|
uint32_t color;
|
|
|
|
};
|
|
|
|
#pragma pack(pop)
|
|
|
|
int maxIndexCount;
|
|
|
|
int maxVertexCount;
|
|
|
|
int firstIndex;
|
|
|
|
int firstVertex;
|
|
|
|
int indexCount;
|
|
|
|
int vertexCount;
|
|
|
|
HRootSignature rootSignature;
|
|
|
|
HDescriptorTable descriptorTable;
|
|
|
|
HPipeline pipeline;
|
|
|
|
HBuffer indexBuffer;
|
|
|
|
HBuffer vertexBuffer;
|
|
|
|
Index* indices;
|
|
|
|
Vertex* vertices;
|
|
|
|
uint32_t color;
|
|
|
|
const shader_t* shader;
|
|
|
|
bool ddhi; // direct descriptor heap indexing
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ImGUI
|
|
|
|
{
|
|
|
|
HTexture Init(bool ddhi, const ShaderByteCode& vs, const ShaderByteCode& ps, TextureFormat::Id rtFormat, HDescriptorTable descTable, RootSignatureDesc* rootSigDesc);
|
|
|
|
void RegisterFontAtlas(uint32_t fontIndex);
|
|
|
|
void Draw(HTexture renderTarget);
|
|
|
|
void BeginFrame();
|
|
|
|
void EndFrame();
|
|
|
|
|
|
|
|
private:
|
|
|
|
struct FrameResources
|
|
|
|
{
|
|
|
|
HBuffer indexBuffer;
|
|
|
|
HBuffer vertexBuffer;
|
|
|
|
};
|
|
|
|
|
|
|
|
HRootSignature rootSignature;
|
|
|
|
HDescriptorTable descriptorTable;
|
|
|
|
HPipeline pipeline;
|
|
|
|
HTexture fontAtlas;
|
|
|
|
FrameResources frameResources[FrameCount];
|
|
|
|
bool frameStarted = false;
|
|
|
|
bool ddhi = false;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Nuklear
|
|
|
|
{
|
|
|
|
void Init(bool ddhi, const ShaderByteCode& vs, const ShaderByteCode& ps, TextureFormat::Id rtFormat, HDescriptorTable descTable, RootSignatureDesc* rootSigDesc);
|
|
|
|
void BeginFrame();
|
|
|
|
void Begin(HTexture renderTarget);
|
|
|
|
void End();
|
|
|
|
void Upload(const nuklearUploadCommand_t& cmd);
|
|
|
|
void Draw(const nuklearDrawCommand_t& cmd);
|
|
|
|
|
|
|
|
private:
|
|
|
|
struct FrameResources
|
|
|
|
{
|
|
|
|
HBuffer indexBuffer;
|
|
|
|
HBuffer vertexBuffer;
|
|
|
|
};
|
|
|
|
|
|
|
|
HRootSignature rootSignature;
|
|
|
|
HDescriptorTable descriptorTable;
|
|
|
|
HPipeline pipeline;
|
|
|
|
FrameResources frameResources[FrameCount];
|
|
|
|
uint32_t renderPassIndex;
|
|
|
|
int prevScissorRect[4];
|
|
|
|
|
|
|
|
// reset every frame
|
|
|
|
int firstVertex;
|
|
|
|
int firstIndex;
|
|
|
|
int numVertexes; // set in Upload
|
|
|
|
int numIndexes; // set in Upload
|
|
|
|
bool ddhi = false;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct SRP
|
|
|
|
{
|
|
|
|
uint32_t BeginRenderPass(const char* name, float r, float g, float b);
|
|
|
|
void EndRenderPass(uint32_t index);
|
|
|
|
|
|
|
|
// @NOTE: SRP::BeginFrame doesn't call RHI::BeginFrame
|
|
|
|
// @NOTE: SRP::EndFrame calls RHI::EndFrame and Sys_V_EndFrame
|
|
|
|
void BeginFrame(); // call at the start of IRenderPipeline::BeginFrame
|
|
|
|
void EndFrame(); // call at the end of IRenderPipeline::EndFrame
|
|
|
|
|
|
|
|
void DrawGUI();
|
|
|
|
|
|
|
|
// call this in Init but only on srp.firstInit
|
|
|
|
// you need to register them in your own local descriptor table(s)
|
|
|
|
void CreateShaderTraceBuffers();
|
|
|
|
|
|
|
|
bool firstInit = true; // first RP init after a RHI init?
|
|
|
|
RenderMode::Id renderMode; // necessary for sampler selection, useful for debugging
|
|
|
|
|
|
|
|
// shader trace
|
|
|
|
HBuffer traceRenderBuffer;
|
|
|
|
HBuffer traceReadbackBuffer;
|
|
|
|
|
|
|
|
// data for frame breakdown and frame graph
|
|
|
|
RenderPassFrame renderPasses[FrameCount];
|
|
|
|
RenderPassFrame tempRenderPasses;
|
|
|
|
RenderPassStats renderPassStats[MaxRenderPasses];
|
|
|
|
RenderPassStats wholeFrameStats;
|
|
|
|
FrameStats frameStats;
|
|
|
|
bool enableRenderPassQueries = true;
|
|
|
|
|
|
|
|
// PSO stats
|
|
|
|
bool psoStatsValid = false;
|
|
|
|
int psoCount = 0;
|
|
|
|
int psoChangeCount = 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
extern SRP srp;
|
|
|
|
|
|
|
|
struct ScopedRenderPass
|
|
|
|
{
|
|
|
|
ScopedRenderPass(const char* name, float r, float g, float b)
|
|
|
|
{
|
|
|
|
index = srp.BeginRenderPass(name, r, g, b);
|
|
|
|
}
|
|
|
|
|
|
|
|
~ScopedRenderPass()
|
|
|
|
{
|
|
|
|
srp.EndRenderPass(index);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t index;
|
|
|
|
};
|
|
|
|
|
2024-03-29 01:38:29 +00:00
|
|
|
struct ScopedDebugLabel
|
|
|
|
{
|
|
|
|
ScopedDebugLabel(const char* name, float r, float g, float b)
|
|
|
|
{
|
|
|
|
CmdBeginDebugLabel(name, r, g, b);
|
|
|
|
}
|
|
|
|
|
|
|
|
~ScopedDebugLabel()
|
|
|
|
{
|
|
|
|
CmdEndDebugLabel();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2024-01-13 21:40:13 +00:00
|
|
|
#define SCOPED_RENDER_PASS(Name, R, G, B) ScopedRenderPass CONCAT(rp_, __LINE__)(Name, R, G, B)
|
2024-03-29 01:38:29 +00:00
|
|
|
#define SCOPED_DEBUG_LABEL(Name, R, G, B) ScopedDebugLabel CONCAT(rp_, __LINE__)(Name, R, G, B)
|
2024-01-13 21:40:13 +00:00
|
|
|
|
|
|
|
#define BASE_SAMPLER_COUNT ((int)(TW_COUNT * TextureFilter::Count * MaxTextureMips))
|
|
|
|
|
|
|
|
const image_t* GetBundleImage(const textureBundle_t& bundle);
|
|
|
|
uint32_t GetBaseSamplerIndex(textureWrap_t wrap, TextureFilter::Id filter, uint32_t minLOD);
|
|
|
|
uint32_t GetSamplerIndex(textureWrap_t wrap, TextureFilter::Id filter, uint32_t minLOD = 0);
|
|
|
|
uint32_t GetSamplerIndex(const image_t* image);
|
|
|
|
void ReadTextureImage(void* outPixels, HTexture hreadback, int w, int h, int alignment, colorSpace_t colorSpace);
|
|
|
|
void UpdateEntityData(bool& depthHack, int entityNum, double originalTime);
|
|
|
|
cullType_t GetMirrorredCullType(cullType_t cullType);
|
|
|
|
uint32_t AlphaTestShaderConstFromStateBits(unsigned int stateBits);
|
|
|
|
|
|
|
|
inline void CmdSetViewportAndScissor(uint32_t x, uint32_t y, uint32_t w, uint32_t h)
|
|
|
|
{
|
|
|
|
CmdSetViewport(x, y, w, h);
|
|
|
|
CmdSetScissor(x, y, w, h);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void CmdSetViewportAndScissor(const viewParms_t& vp)
|
|
|
|
{
|
|
|
|
CmdSetViewportAndScissor(vp.viewportX, vp.viewportY, vp.viewportWidth, vp.viewportHeight);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool IsDepthFadeEnabled(const shader_t& shader)
|
|
|
|
{
|
|
|
|
return
|
|
|
|
r_depthFade->integer != 0 &&
|
|
|
|
shader.dfType > DFT_NONE &&
|
|
|
|
shader.dfType < DFT_TBD;
|
|
|
|
}
|
2024-02-06 22:15:31 +00:00
|
|
|
|
|
|
|
inline bool IsViewportFullscreen(const viewParms_t& vp)
|
|
|
|
{
|
|
|
|
return
|
|
|
|
vp.viewportX == 0 &&
|
|
|
|
vp.viewportY == 0 &&
|
|
|
|
vp.viewportWidth == glConfig.vidWidth &&
|
|
|
|
vp.viewportHeight == glConfig.vidHeight;
|
|
|
|
}
|