mirror of
https://bitbucket.org/CPMADevs/cnq3
synced 2024-11-23 12:32:14 +00:00
677 lines
20 KiB
C++
677 lines
20 KiB
C++
/*
|
|
===========================================================================
|
|
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/>.
|
|
===========================================================================
|
|
*/
|
|
// Cinematic Rendering Pipeline - main interface
|
|
|
|
|
|
#include "crp_local.h"
|
|
#include "../client/cl_imgui.h"
|
|
#include "shaders/crp/oit.h.hlsli"
|
|
namespace blit
|
|
{
|
|
#include "compshaders/crp/blit_vs.h"
|
|
#include "compshaders/crp/blit_ps.h"
|
|
}
|
|
namespace ui
|
|
{
|
|
#include "compshaders/crp/ui_vs.h"
|
|
#include "compshaders/crp/ui_ps.h"
|
|
}
|
|
namespace imgui
|
|
{
|
|
#include "compshaders/crp/imgui_vs.h"
|
|
#include "compshaders/crp/imgui_ps.h"
|
|
}
|
|
namespace nuklear
|
|
{
|
|
#include "compshaders/crp/nuklear_vs.h"
|
|
#include "compshaders/crp/nuklear_ps.h"
|
|
}
|
|
namespace mip_1
|
|
{
|
|
#include "compshaders/crp/mip_1_cs.h"
|
|
}
|
|
namespace mip_2
|
|
{
|
|
#include "compshaders/crp/mip_2_cs.h"
|
|
}
|
|
namespace mip_3
|
|
{
|
|
#include "compshaders/crp/mip_3_cs.h"
|
|
}
|
|
|
|
|
|
CRP crp;
|
|
IRenderPipeline* crpp = &crp;
|
|
|
|
cvar_t* crp_dof;
|
|
cvar_t* crp_dof_overlay;
|
|
cvar_t* crp_dof_blades;
|
|
cvar_t* crp_dof_angle;
|
|
cvar_t* crp_gatherDof_focusNearDist;
|
|
cvar_t* crp_gatherDof_focusNearRange;
|
|
cvar_t* crp_gatherDof_focusFarDist;
|
|
cvar_t* crp_gatherDof_focusFarRange;
|
|
cvar_t* crp_gatherDof_brightness;
|
|
cvar_t* crp_accumDof_focusDist;
|
|
cvar_t* crp_accumDof_radius;
|
|
cvar_t* crp_accumDof_samples;
|
|
cvar_t* crp_accumDof_preview;
|
|
|
|
static const cvarTableItem_t crp_cvars[] =
|
|
{
|
|
{
|
|
&crp_dof, "crp_dof", "1", CVAR_ARCHIVE, CVART_INTEGER, "0", "2",
|
|
"enables depth of field\n"
|
|
S_COLOR_VAL " 0 " S_COLOR_HELP "= Disabled\n"
|
|
S_COLOR_VAL " 1 " S_COLOR_HELP "= Gather (fast, more flexible, issues with transparency)\n"
|
|
S_COLOR_VAL " 2 " S_COLOR_HELP "= Accumulation (slow, less flexible, great IQ)\n",
|
|
"DoF mode", CVARCAT_GRAPHICS, "Depth of field mode", "",
|
|
CVAR_GUI_VALUE("0", "Disabled", "")
|
|
CVAR_GUI_VALUE("1", "Gather", "Fast, lower IQ")
|
|
CVAR_GUI_VALUE("2", "Accumulation", "Very slow, great IQ")
|
|
},
|
|
{
|
|
&crp_dof_overlay, "crp_dof_overlay", "0", CVAR_ARCHIVE, CVART_INTEGER, "0", "2",
|
|
"debug overlay mode\n"
|
|
S_COLOR_VAL " 0 " S_COLOR_HELP "= Disabled\n"
|
|
S_COLOR_VAL " 1 " S_COLOR_HELP "= Colorized Blur\n"
|
|
S_COLOR_VAL " 2 " S_COLOR_HELP "= Focus Plane",
|
|
"DoF overlay mode", CVARCAT_GRAPHICS, "Debug overlay mode", "",
|
|
CVAR_GUI_VALUE("0", "Disabled", "")
|
|
CVAR_GUI_VALUE("1", "Colorized Blur", "")
|
|
CVAR_GUI_VALUE("2", "Focus Plane", "")
|
|
},
|
|
{
|
|
&crp_dof_blades, "crp_dof_blades", "6", CVAR_ARCHIVE, CVART_FLOAT, "0", "16",
|
|
"aperture blade count\n"
|
|
"Set to less than 3 for a disk shape.",
|
|
"DoF blade count", CVARCAT_GRAPHICS, "Aperture blade count", "Set to less than 3 for a disk shape."
|
|
},
|
|
{
|
|
&crp_dof_angle, "crp_dof_angle", "20", CVAR_ARCHIVE, CVART_FLOAT, "0", "360", "aperture angle, in degrees",
|
|
"DoF aperture angle", CVARCAT_GRAPHICS, "Aperture angle, in degrees", ""
|
|
},
|
|
{
|
|
&crp_accumDof_focusDist, "crp_accumDof_focusDist", "256", CVAR_ARCHIVE, CVART_FLOAT, "2", "2048", "focus distance",
|
|
"Accum DoF focus distance", CVARCAT_GRAPHICS, "Focus distance", ""
|
|
},
|
|
{
|
|
&crp_accumDof_radius, "crp_accumDof_blurRadius", "0.1", CVAR_ARCHIVE, CVART_FLOAT, "0.001", "20", "aperture radius in world units",
|
|
"Accum DoF aperture radius", CVARCAT_GRAPHICS, "Aperture radius in world units", ""
|
|
},
|
|
{
|
|
&crp_accumDof_samples, "crp_accumDof_samples", "2", CVAR_ARCHIVE, CVART_INTEGER, "1", "12",
|
|
"per-axis sampling density\n"
|
|
"Density N means (2N + 1)(2N + 1) scene renders in total.",
|
|
"Accum DoF sample count", CVARCAT_GRAPHICS, "Per-axis sampling density", "Density N means (2N + 1)^2 scene renders in total."
|
|
},
|
|
{
|
|
&crp_accumDof_preview, "crp_accumDof_preview", "0", CVAR_ARCHIVE, CVART_INTEGER, "0", "2",
|
|
"low-res preview mode\n"
|
|
S_COLOR_VAL " 0 " S_COLOR_HELP "= Disabled\n"
|
|
S_COLOR_VAL " 1 " S_COLOR_HELP "= 1/4 pixel count, 9 samples total\n"
|
|
S_COLOR_VAL " 2 " S_COLOR_HELP "= 1/16 pixel count, 25 samples total",
|
|
"Accum DoF preview mode", CVARCAT_GRAPHICS, "Low-resolution preview modes", "",
|
|
CVAR_GUI_VALUE("0", "Disabled", "")
|
|
CVAR_GUI_VALUE("1", "1/4 pixel count", "9 samples total")
|
|
CVAR_GUI_VALUE("2", "1/16 pixel count", "25 samples total")
|
|
},
|
|
{
|
|
&crp_gatherDof_focusNearDist, "crp_gatherDof_focusNearDist", "192", CVAR_ARCHIVE, CVART_FLOAT, "1", "2048", "near focus distance",
|
|
"Gather DoF near focus distance", CVARCAT_GRAPHICS, "Near focus distance", ""
|
|
},
|
|
{
|
|
&crp_gatherDof_focusNearRange, "crp_gatherDof_focusNearRange", "256", CVAR_ARCHIVE, CVART_FLOAT, "1", "2048", "near focus range",
|
|
"Gather DoF near focus range", CVARCAT_GRAPHICS, "Near focus range", ""
|
|
},
|
|
{
|
|
&crp_gatherDof_focusFarDist, "crp_gatherDof_focusFarDist", "512", CVAR_ARCHIVE, CVART_FLOAT, "1", "2048", "far focus distance",
|
|
"Gather DoF far focus distance", CVARCAT_GRAPHICS, "Far focus distance", ""
|
|
},
|
|
{
|
|
&crp_gatherDof_focusFarRange, "crp_gatherDof_focusFarRange", "384", CVAR_ARCHIVE, CVART_FLOAT, "1", "2048", "far focus range",
|
|
"Gather DoF far focus range", CVARCAT_GRAPHICS, "Far focus range", ""
|
|
},
|
|
{
|
|
&crp_gatherDof_brightness, "crp_gatherDof_brightness", "2", CVAR_ARCHIVE, CVART_FLOAT, "0", "8", "blur brightness weight",
|
|
"Gather DoF bokeh brightness", CVARCAT_GRAPHICS, "Blur brightness weight", ""
|
|
}
|
|
};
|
|
|
|
|
|
void PSOCache::Init(Entry* entries_, uint32_t maxEntryCount_)
|
|
{
|
|
entries = entries_;
|
|
maxEntryCount = maxEntryCount_;
|
|
entryCount = 1; // we treat index 0 as invalid
|
|
}
|
|
|
|
int PSOCache::AddPipeline(const GraphicsPipelineDesc& desc, const char* name)
|
|
{
|
|
// we treat index 0 as invalid, so start at 1
|
|
for(uint32_t i = 1; i < entryCount; ++i)
|
|
{
|
|
Entry& entry = entries[i];
|
|
if(memcmp(&entry.desc, &desc, sizeof(desc)) == 0)
|
|
{
|
|
return (int)i;
|
|
}
|
|
}
|
|
|
|
ASSERT_OR_DIE(entryCount < maxEntryCount, "Not enough entries in the PSO cache");
|
|
|
|
GraphicsPipelineDesc namedDesc = desc;
|
|
namedDesc.name = name;
|
|
|
|
const uint32_t index = entryCount++;
|
|
Entry& entry = entries[index];
|
|
entry.desc = desc; // keep the original desc for proper comparison results
|
|
entry.handle = CreateGraphicsPipeline(namedDesc);
|
|
|
|
return (int)index;
|
|
}
|
|
|
|
void CRP::Init()
|
|
{
|
|
static bool veryFirstInit = true;
|
|
if(veryFirstInit)
|
|
{
|
|
ri.Cvar_RegisterTable(crp_cvars, ARRAY_LEN(crp_cvars));
|
|
veryFirstInit = false;
|
|
}
|
|
|
|
InitDesc initDesc;
|
|
initDesc.directDescriptorHeapIndexing = true;
|
|
srp.firstInit = RHI::Init(initDesc);
|
|
srp.psoStatsValid = false;
|
|
|
|
if(srp.firstInit)
|
|
{
|
|
srp.CreateShaderTraceBuffers();
|
|
|
|
for(uint32_t f = 0; f < FrameCount; ++f)
|
|
{
|
|
// the doubled index count is for the depth pre-pass
|
|
const int MaxDynamicVertexCount = 16 << 20;
|
|
const int MaxDynamicIndexCount = MaxDynamicVertexCount * 4;
|
|
GeoBuffers& db = dynBuffers[f];
|
|
db.Create(va("world #%d", f + 1), MaxDynamicVertexCount, MaxDynamicIndexCount);
|
|
}
|
|
}
|
|
|
|
// we recreate the samplers on every vid_restart to create the right level
|
|
// of anisotropy based on the latched CVar
|
|
for(uint32_t w = 0; w < TW_COUNT; ++w)
|
|
{
|
|
for(uint32_t f = 0; f < TextureFilter::Count; ++f)
|
|
{
|
|
for(uint32_t m = 0; m < MaxTextureMips; ++m)
|
|
{
|
|
const textureWrap_t wrap = (textureWrap_t)w;
|
|
const TextureFilter::Id filter = (TextureFilter::Id)f;
|
|
const uint32_t s = GetBaseSamplerIndex(wrap, filter, m);
|
|
SamplerDesc desc(wrap, filter, (float)m);
|
|
desc.shortLifeTime = true;
|
|
samplers[s] = CreateSampler(desc);
|
|
samplerIndices[s] = RHI::GetSamplerIndex(samplers[s]);
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
renderTargetFormat = TextureFormat::RGBA64_Float;
|
|
|
|
TextureDesc desc("render target #1", glConfig.vidWidth, glConfig.vidHeight);
|
|
desc.initialState = ResourceStates::RenderTargetBit;
|
|
desc.allowedState = ResourceStates::RenderTargetBit | ResourceStates::PixelShaderAccessBit;
|
|
Vector4Clear(desc.clearColor);
|
|
desc.usePreferredClearValue = true;
|
|
desc.committedResource = true;
|
|
desc.format = renderTargetFormat;
|
|
desc.shortLifeTime = true;
|
|
renderTargets[0] = RHI::CreateTexture(desc);
|
|
desc.name = "render target #2";
|
|
renderTargets[1] = RHI::CreateTexture(desc);
|
|
renderTargetIndex = 0;
|
|
renderTarget = renderTargets[0];
|
|
}
|
|
|
|
{
|
|
TextureDesc desc("readback render target", glConfig.vidWidth, glConfig.vidHeight);
|
|
desc.initialState = ResourceStates::RenderTargetBit;
|
|
desc.allowedState = ResourceStates::RenderTargetBit | ResourceStates::PixelShaderAccessBit;
|
|
Vector4Clear(desc.clearColor);
|
|
desc.usePreferredClearValue = true;
|
|
desc.committedResource = true;
|
|
desc.format = TextureFormat::RGBA32_UNorm;
|
|
desc.shortLifeTime = true;
|
|
readbackRenderTarget = RHI::CreateTexture(desc);
|
|
}
|
|
|
|
{
|
|
TextureDesc desc("OIT index", glConfig.vidWidth, glConfig.vidHeight);
|
|
desc.initialState = ResourceStates::UnorderedAccessBit;
|
|
desc.allowedState = ResourceStates::UnorderedAccessBit | ResourceStates::PixelShaderAccessBit | ResourceStates::ComputeShaderAccessBit;
|
|
desc.committedResource = true;
|
|
desc.format = TextureFormat::R32_UInt;
|
|
desc.shortLifeTime = true;
|
|
oitIndexTexture = RHI::CreateTexture(desc);
|
|
}
|
|
|
|
uint32_t oitMaxFragmentCount = 0;
|
|
{
|
|
const int byteCountPerFragment = sizeof(OIT_Fragment);
|
|
const int fragmentCount = glConfig.vidWidth * glConfig.vidHeight * OIT_AVG_FRAGMENTS_PER_PIXEL;
|
|
const int byteCount = byteCountPerFragment * fragmentCount;
|
|
oitMaxFragmentCount = fragmentCount;
|
|
|
|
BufferDesc desc("OIT fragment", byteCount, ResourceStates::UnorderedAccessBit);
|
|
desc.committedResource = true;
|
|
desc.memoryUsage = MemoryUsage::GPU;
|
|
desc.structureByteCount = byteCountPerFragment;
|
|
desc.shortLifeTime = true;
|
|
oitFragmentBuffer = CreateBuffer(desc);
|
|
}
|
|
|
|
{
|
|
const int byteCount = sizeof(OIT_Counter);
|
|
|
|
{
|
|
BufferDesc desc("OIT counter", byteCount, ResourceStates::UnorderedAccessBit);
|
|
desc.committedResource = true;
|
|
desc.memoryUsage = MemoryUsage::GPU;
|
|
desc.structureByteCount = byteCount;
|
|
desc.shortLifeTime = true;
|
|
oitCounterBuffer = CreateBuffer(desc);
|
|
}
|
|
{
|
|
BufferDesc desc("OIT counter staging", byteCount, ResourceStates::Common);
|
|
desc.committedResource = false;
|
|
desc.memoryUsage = MemoryUsage::Upload;
|
|
desc.structureByteCount = byteCount;
|
|
desc.shortLifeTime = true;
|
|
oitCounterStagingBuffer = CreateBuffer(desc);
|
|
|
|
uint32_t* dst = (uint32_t*)MapBuffer(oitCounterStagingBuffer);
|
|
dst[0] = 1; // fragment index 0 is the end-of-list value
|
|
dst[1] = oitMaxFragmentCount;
|
|
dst[2] = 0;
|
|
UnmapBuffer(oitCounterStagingBuffer);
|
|
}
|
|
}
|
|
|
|
{
|
|
TextureDesc desc("depth buffer", glConfig.vidWidth, glConfig.vidHeight);
|
|
desc.committedResource = true;
|
|
desc.shortLifeTime = true;
|
|
desc.initialState = ResourceStates::DepthWriteBit;
|
|
desc.allowedState = ResourceStates::DepthAccessBits | ResourceStates::PixelShaderAccessBit;
|
|
desc.format = TextureFormat::Depth32_Float;
|
|
desc.SetClearDepthStencil(0.0f, 0);
|
|
depthTexture = RHI::CreateTexture(desc);
|
|
}
|
|
|
|
{
|
|
GraphicsPipelineDesc desc("blit LDR");
|
|
desc.vertexShader = ShaderByteCode(blit::g_vs);
|
|
desc.pixelShader = ShaderByteCode(blit::g_ps);
|
|
desc.depthStencil.DisableDepth();
|
|
desc.rasterizer.cullMode = CT_TWO_SIDED;
|
|
desc.AddRenderTarget(0, TextureFormat::RGBA32_UNorm);
|
|
blitPipelineLDR = CreateGraphicsPipeline(desc);
|
|
desc.name = "blit HDR";
|
|
desc.renderTargets[0].format = TextureFormat::RGBA64_Float;
|
|
blitPipelineHDR = CreateGraphicsPipeline(desc);
|
|
}
|
|
|
|
ui.Init(true, ShaderByteCode(ui::g_vs), ShaderByteCode(ui::g_ps), renderTargetFormat, RHI_MAKE_NULL_HANDLE(), NULL);
|
|
imgui.Init(true, ShaderByteCode(imgui::g_vs), ShaderByteCode(imgui::g_ps), renderTargetFormat, RHI_MAKE_NULL_HANDLE(), NULL);
|
|
nuklear.Init(true, ShaderByteCode(nuklear::g_vs), ShaderByteCode(nuklear::g_ps), renderTargetFormat, RHI_MAKE_NULL_HANDLE(), NULL);
|
|
mipMapGen.Init(true, ShaderByteCode(mip_1::g_cs), ShaderByteCode(mip_2::g_cs), ShaderByteCode(mip_3::g_cs));
|
|
opaque.Init();
|
|
transp.Init();
|
|
transpResolve.Init();
|
|
toneMap.Init();
|
|
gatherDof.Init();
|
|
accumDof.Init();
|
|
fog.Init();
|
|
|
|
srp.firstInit = false;
|
|
}
|
|
|
|
void CRP::ShutDown(bool fullShutDown)
|
|
{
|
|
RHI::ShutDown(fullShutDown);
|
|
}
|
|
|
|
void CRP::BeginFrame()
|
|
{
|
|
renderTargetIndex = 0;
|
|
renderTarget = renderTargets[0];
|
|
|
|
srp.BeginFrame();
|
|
|
|
// have it be first to we can use ImGUI in the other components too
|
|
imgui.BeginFrame();
|
|
|
|
RHI::BeginFrame();
|
|
ui.BeginFrame();
|
|
nuklear.BeginFrame();
|
|
|
|
const float clearColor[4] = { 0.0f, 0.5f, 0.0f, 0.0f };
|
|
const TextureBarrier barrier(renderTarget, ResourceStates::RenderTargetBit);
|
|
CmdBarrier(1, &barrier);
|
|
CmdClearColorTarget(renderTarget, clearColor);
|
|
|
|
frameSeed = (float)rand() / (float)RAND_MAX;
|
|
|
|
dynBuffers[GetFrameIndex()].Rewind();
|
|
}
|
|
|
|
void CRP::EndFrame()
|
|
{
|
|
srp.DrawGUI();
|
|
imgui.Draw(renderTarget);
|
|
toneMap.DrawToneMap();
|
|
BlitRenderTarget(GetSwapChainTexture(), "Blit to Swap Chain");
|
|
BlitRenderTarget(readbackRenderTarget, "Blit to Readback Texture");
|
|
srp.EndFrame();
|
|
}
|
|
|
|
void CRP::Blit(HTexture destination, HTexture source, const char* passName, bool hdr, const vec2_t tcScale, const vec2_t tcBias)
|
|
{
|
|
SCOPED_RENDER_PASS(passName, 0.125f, 0.125f, 0.5f);
|
|
|
|
const TextureBarrier barriers[2] =
|
|
{
|
|
TextureBarrier(source, ResourceStates::PixelShaderAccessBit),
|
|
TextureBarrier(destination, ResourceStates::RenderTargetBit)
|
|
};
|
|
CmdBarrier(ARRAY_LEN(barriers), barriers);
|
|
|
|
#pragma pack(push, 4)
|
|
struct BlitRC
|
|
{
|
|
uint32_t textureIndex;
|
|
uint32_t samplerIndex;
|
|
float tcScale[2];
|
|
float tcBias[2];
|
|
};
|
|
#pragma pack(pop)
|
|
|
|
BlitRC rc;
|
|
rc.textureIndex = GetTextureIndexSRV(source);
|
|
rc.samplerIndex = GetSamplerIndex(TW_CLAMP_TO_EDGE, TextureFilter::Linear);
|
|
rc.tcScale[0] = tcScale[0];
|
|
rc.tcScale[1] = tcScale[1];
|
|
rc.tcBias[0] = tcBias[0];
|
|
rc.tcBias[1] = tcBias[0];
|
|
CmdSetViewportAndScissor(0, 0, glConfig.vidWidth, glConfig.vidHeight);
|
|
CmdBindRenderTargets(1, &destination, NULL);
|
|
CmdBindPipeline(hdr ? blitPipelineHDR : blitPipelineLDR);
|
|
CmdSetGraphicsRootConstants(0, sizeof(rc), &rc);
|
|
CmdDraw(3, 0);
|
|
}
|
|
|
|
void CRP::BlitRenderTarget(HTexture destination, const char* passName)
|
|
{
|
|
Blit(destination, crp.renderTarget, passName, false, vec2_one, vec2_zero);
|
|
}
|
|
|
|
void CRP::CreateTexture(image_t* image, int mipCount, int width, int height)
|
|
{
|
|
TextureDesc desc(image->name, width, height, mipCount);
|
|
desc.committedResource = width * height >= (1 << 20);
|
|
desc.shortLifeTime = true;
|
|
if(mipCount > 1)
|
|
{
|
|
desc.allowedState |= ResourceStates::UnorderedAccessBit; // for mip-map generation
|
|
}
|
|
|
|
image->texture = ::RHI::CreateTexture(desc);
|
|
image->textureIndex = GetTextureIndexSRV(image->texture);
|
|
}
|
|
|
|
void CRP::UpoadTextureAndGenerateMipMaps(image_t* image, const byte* data)
|
|
{
|
|
MappedTexture texture;
|
|
RHI::BeginTextureUpload(texture, image->texture);
|
|
for(uint32_t r = 0; r < texture.rowCount; ++r)
|
|
{
|
|
memcpy(texture.mappedData + r * texture.dstRowByteCount, data + r * texture.srcRowByteCount, texture.srcRowByteCount);
|
|
}
|
|
RHI::EndTextureUpload();
|
|
|
|
mipMapGen.GenerateMipMaps(image->texture);
|
|
}
|
|
|
|
void CRP::BeginTextureUpload(MappedTexture& mappedTexture, image_t* image)
|
|
{
|
|
RHI::BeginTextureUpload(mappedTexture, image->texture);
|
|
}
|
|
|
|
void CRP::EndTextureUpload()
|
|
{
|
|
RHI::EndTextureUpload();
|
|
}
|
|
|
|
void CRP::ProcessWorld(world_t&)
|
|
{
|
|
}
|
|
|
|
void CRP::ProcessModel(model_t&)
|
|
{
|
|
}
|
|
|
|
void CRP::ProcessShader(shader_t& shader)
|
|
{
|
|
if(shader.isOpaque)
|
|
{
|
|
opaque.ProcessShader(shader);
|
|
}
|
|
else
|
|
{
|
|
transp.ProcessShader(shader);
|
|
}
|
|
}
|
|
|
|
void CRP::ExecuteRenderCommands(const byte* data, bool /*readbackRequested*/)
|
|
{
|
|
// @NOTE: the CRP always blits the final result to the readback texture
|
|
|
|
for(;;)
|
|
{
|
|
const int commandId = ((const renderCommandBase_t*)data)->commandId;
|
|
|
|
if(commandId < 0 || commandId >= RC_COUNT)
|
|
{
|
|
assert(!"Invalid render command type");
|
|
return;
|
|
}
|
|
|
|
if(commandId == RC_END_OF_LIST)
|
|
{
|
|
return;
|
|
}
|
|
|
|
switch(commandId)
|
|
{
|
|
case RC_UI_SET_COLOR:
|
|
ui.CmdSetColor(*(const uiSetColorCommand_t*)data);
|
|
break;
|
|
case RC_UI_DRAW_QUAD:
|
|
ui.CmdDrawQuad(*(const uiDrawQuadCommand_t*)data);
|
|
break;
|
|
case RC_UI_DRAW_TRIANGLE:
|
|
ui.CmdDrawTriangle(*(const uiDrawTriangleCommand_t*)data);
|
|
break;
|
|
case RC_DRAW_SCENE_VIEW:
|
|
DrawSceneView(*(const drawSceneViewCommand_t*)data);
|
|
break;
|
|
case RC_BEGIN_FRAME:
|
|
BeginFrame();
|
|
break;
|
|
case RC_SWAP_BUFFERS:
|
|
EndFrame();
|
|
break;
|
|
case RC_BEGIN_UI:
|
|
ui.Begin(renderTarget);
|
|
break;
|
|
case RC_END_UI:
|
|
ui.End();
|
|
break;
|
|
case RC_BEGIN_3D:
|
|
// @TODO:
|
|
srp.renderMode = RenderMode::None;
|
|
break;
|
|
case RC_END_3D:
|
|
// @TODO:
|
|
srp.renderMode = RenderMode::None;
|
|
break;
|
|
case RC_END_SCENE:
|
|
// @TODO: post-processing
|
|
break;
|
|
case RC_BEGIN_NK:
|
|
nuklear.Begin(renderTarget);
|
|
break;
|
|
case RC_END_NK:
|
|
nuklear.End();
|
|
break;
|
|
case RC_NK_UPLOAD:
|
|
nuklear.Upload(*(const nuklearUploadCommand_t*)data);
|
|
break;
|
|
case RC_NK_DRAW:
|
|
nuklear.Draw(*(const nuklearDrawCommand_t*)data);
|
|
break;
|
|
default:
|
|
Q_assert(!"Unsupported render command type");
|
|
return;
|
|
}
|
|
|
|
data += renderCommandSizes[commandId];
|
|
}
|
|
}
|
|
|
|
void CRP::TessellationOverflow()
|
|
{
|
|
switch(tess.tessellator)
|
|
{
|
|
case Tessellator::Opaque: opaque.TessellationOverflow(); break;
|
|
case Tessellator::Transp: transp.TessellationOverflow(); break;
|
|
default: break;
|
|
}
|
|
tess.numIndexes = 0;
|
|
tess.numVertexes = 0;
|
|
}
|
|
|
|
void CRP::DrawSceneView(const drawSceneViewCommand_t& cmd)
|
|
{
|
|
const viewParms_t& vp = cmd.viewParms;
|
|
if(cmd.shouldClearColor)
|
|
{
|
|
const Rect rect(vp.viewportX, vp.viewportY, vp.viewportWidth, vp.viewportHeight);
|
|
const TextureBarrier tb(renderTarget, ResourceStates::RenderTargetBit);
|
|
CmdBarrier(1, &tb);
|
|
CmdClearColorTarget(renderTarget, cmd.clearColor, &rect);
|
|
}
|
|
|
|
if(cmd.numDrawSurfs <= 0 || !cmd.shouldDrawScene)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(crp_dof->integer == DOFMethod::Accumulation &&
|
|
vp.viewportX == 0 &&
|
|
vp.viewportY == 0 &&
|
|
vp.viewportWidth == glConfig.vidWidth &&
|
|
vp.viewportHeight == glConfig.vidHeight)
|
|
{
|
|
const Rect rect(0, 0, glConfig.vidWidth, glConfig.vidHeight);
|
|
accumDof.Begin(cmd);
|
|
const uint32_t sampleCount = accumDof.GetSampleCount();
|
|
for(uint32_t y = 0; y < sampleCount; y++)
|
|
{
|
|
for(uint32_t x = 0; x < sampleCount; x++)
|
|
{
|
|
srp.enableRenderPassQueries = x == 0 && y == 0;
|
|
drawSceneViewCommand_t newCmd;
|
|
accumDof.FixCommand(newCmd, cmd, x, y);
|
|
const TextureBarrier tb(renderTarget, ResourceStates::RenderTargetBit);
|
|
CmdBarrier(1, &tb);
|
|
CmdClearColorTarget(renderTarget, cmd.clearColor, &rect);
|
|
opaque.Draw(newCmd);
|
|
fog.Draw();
|
|
transp.Draw(newCmd);
|
|
transpResolve.Draw(newCmd);
|
|
accumDof.Accumulate();
|
|
|
|
// geometry allocation is a linear allocation instead of a ring buffer
|
|
// we force a CPU-GPU sync point after every full scene render
|
|
// that way, we can keep the buffer sizes at least somewhat reasonable
|
|
SubmitAndContinue();
|
|
dynBuffers[GetFrameIndex()].Rewind();
|
|
}
|
|
}
|
|
CmdSetViewportAndScissor(backEnd.viewParms);
|
|
srp.enableRenderPassQueries = true;
|
|
accumDof.Normalize();
|
|
backEnd.viewParms = cmd.viewParms;
|
|
backEnd.refdef = cmd.refdef;
|
|
accumDof.DrawDebug();
|
|
}
|
|
else
|
|
{
|
|
opaque.Draw(cmd);
|
|
fog.Draw();
|
|
transp.Draw(cmd);
|
|
transpResolve.Draw(cmd);
|
|
CmdSetViewportAndScissor(vp.viewportX, vp.viewportY, vp.viewportWidth, vp.viewportHeight);
|
|
gatherDof.Draw();
|
|
}
|
|
}
|
|
|
|
void CRP::ReadPixels(int w, int h, int alignment, colorSpace_t colorSpace, void* outPixels)
|
|
{
|
|
ReadTextureImage(outPixels, readbackRenderTarget, w, h, alignment, colorSpace);
|
|
}
|
|
|
|
uint32_t CRP::GetSamplerDescriptorIndexFromBaseIndex(uint32_t baseIndex)
|
|
{
|
|
Q_assert(baseIndex < ARRAY_LEN(samplerIndices));
|
|
|
|
return samplerIndices[baseIndex];
|
|
}
|
|
|
|
HTexture CRP::GetReadRenderTarget()
|
|
{
|
|
return renderTargets[renderTargetIndex ^ 1];
|
|
}
|
|
|
|
HTexture CRP::GetWriteRenderTarget()
|
|
{
|
|
return renderTargets[renderTargetIndex];
|
|
}
|
|
|
|
void CRP::SwapRenderTargets()
|
|
{
|
|
renderTargetIndex ^= 1;
|
|
renderTarget = GetWriteRenderTarget();
|
|
}
|