Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: simplified shadow map cache #97

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions HPL2/include/graphics/CommandBufferPool.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#pragma once

#include "graphics/ForgeHandles.h"
#include "graphics/Renderer.h"
#include <cstdint>

#include "Common_3/Graphics/Interfaces/IGraphics.h"
#include "FixPreprocessor.h"

namespace hpl {
template<uint32_t COUNT>
class CommandBufferPool {
public:
struct PoolEntry {
public:
SharedCmdPool m_pool;
SharedCmd m_cmd;
SharedFence m_fence;
};
CommandBufferPool() {}
CommandBufferPool(CommandBufferPool<COUNT>&&) = default;
CommandBufferPool(const CommandBufferPool<COUNT>&) = delete;

void operator=(const CommandBufferPool<COUNT>&) = delete;
CommandBufferPool<COUNT>& operator=(CommandBufferPool<COUNT>&&) = default;

CommandBufferPool(Renderer* renderer, Queue* queue): m_renderer(renderer){
for(auto& entry: m_pool) {
entry.m_pool.Load(renderer, [&](CmdPool** pool) {
CmdPoolDesc cmdPoolDesc = {};
cmdPoolDesc.pQueue = queue;
addCmdPool(renderer, &cmdPoolDesc, pool);
return true;
});
entry.m_cmd.Load(renderer, [&](Cmd** cmd) {
CmdDesc cmdDesc = {};
cmdDesc.pPool = entry.m_pool.m_handle;
addCmd(renderer, &cmdDesc, cmd);
return true;
});
entry.m_fence.Load(renderer, [&](Fence** fence) {
addFence(renderer, fence);
return true;
});
}
}

void waitAll() {
for(auto& entry: m_pool) {
FenceStatus status{};
getFenceStatus(m_renderer, entry.m_fence.m_handle, &status);
if(status == FENCE_STATUS_INCOMPLETE) {
waitForFences(m_renderer, 1, &entry.m_handle);
}
}
}
PoolEntry* findAvaliableEntry() {
for(auto& entry: m_pool) {
FenceStatus status{};
getFenceStatus(m_renderer, entry.m_fence.m_handle, &status);
if(status == FENCE_STATUS_COMPLETE) {
return &entry;
}
}
return nullptr;
}
private:
Renderer* m_renderer;
std::array<PoolEntry, COUNT> m_pool;
};
}
2 changes: 2 additions & 0 deletions HPL2/include/graphics/IndexPool.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ namespace hpl {
IndexPool(uint32_t reserve);

uint32_t requestId();
inline uint32_t reserve() {return m_reserve;}
void returnId(uint32_t);
private:
struct IdRange {
uint32_t m_start;
uint32_t m_end;
};
uint32_t m_reserve;
folly::small_vector<IdRange, 256> m_avaliable;
};

Expand Down
8 changes: 8 additions & 0 deletions HPL2/include/graphics/RendererDeferred.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@

#include "engine/RTTI.h"

#include "graphics/CommandBufferPool.h"
#include "graphics/ShadowCache.h"
#include "scene/Viewport.h"
#include "scene/World.h"
#include "windowing/NativeWindow.h"
Expand Down Expand Up @@ -457,6 +459,11 @@ namespace hpl {
std::unique_ptr<iVertexBuffer> m_box;
std::array<folly::small_vector<ShadowMapData, 32>, eShadowMapResolution_LastEnum> m_shadowMapData;


SharedRenderTarget m_shadowTarget;
ShadowCache<ShadowMapData> m_shadowCache;
CommandBufferPool<16> m_shadowCmdPool;

UniqueViewportData<ViewportData> m_boundViewportData;

SharedTexture m_shadowJitterTexture;
Expand Down Expand Up @@ -597,6 +604,7 @@ namespace hpl {
std::array<std::array<LightResourceEntry, MaxLightUniforms>, ForgeRenderer::SwapChainLength> m_lightResources{};
// z pass
SharedShader m_zPassShader;
SharedShader m_zPassShadowShader;
SharedPipeline m_zPassPipelineCCW;
SharedPipeline m_zPassPipelineCW;

Expand Down
156 changes: 156 additions & 0 deletions HPL2/include/graphics/ShadowCache.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
#pragma once

#include "graphics/IndexPool.h"
#include <cstdint>
#include <optional>
#include <vector>

#include <folly/small_vector.h>

#include <Common_3/Graphics/Interfaces/IGraphics.h>
#include <Common_3/Utilities/RingBuffer.h>
#include <FixPreprocessor.h>

namespace hpl {
template<typename MetaInfo>
class ShadowCache {
public:
static constexpr uint32_t MinimumAge = 3;
static constexpr uint4 InvalidQuad = uint4(0, 0, 0, 0);
struct ShadowSlot {
uint32_t m_age = 0;
uint16_t m_hash = 0;
MetaInfo m_info = {};
};

struct ShadowContainer {
public:
uint16_t m_x = 0;
uint16_t m_y = 0;
uint8_t m_level = 0;
uint32_t m_age = 0;
folly::small_vector<ShadowSlot, 24> m_slots;
};

ShadowCache() = default;
ShadowCache(uint32_t width, uint32_t height, uint32_t numColumns, uint32_t numRows, uint8_t maxLevels)
: m_maxLevels(maxLevels)
, m_numRows(numRows)
, m_numColumns(numColumns)
, m_size(width, height)
, m_quadSize(width / numColumns, height / numRows) {
m_containers.resize(numColumns * numRows);
for(size_t i = 0; i < m_containers.size(); i++) {
m_containers[i].m_slots.resize(1);
m_containers[i].m_x = m_quadSize.x * (i % numColumns);
m_containers[i].m_y = m_quadSize.y * (i / numColumns);
}

}
struct ShadowMapResult {
public:
ShadowMapResult(ShadowCache<MetaInfo>* cache, ShadowContainer* container, uint32_t slotIdx, uint32_t age)
: m_cache(cache)
, m_container(container)
, m_slotIdx(slotIdx)
, m_age(age) {
}
// mark shadowmap as used
void Mark() {
m_container->m_age = m_age;
m_container->m_slots[m_slotIdx].m_age = m_age;
}

inline float4 NormalizedRect() {
uint4 quad = Rect();
return float4(
static_cast<float>(quad.x) / static_cast<float>(m_cache->m_size.x),
static_cast<float>(quad.y) / static_cast<float>(m_cache->m_size.y),
static_cast<float>(quad.z) / static_cast<float>(m_cache->m_size.x),
static_cast<float>(quad.w) / static_cast<float>(m_cache->m_size.y));
}

inline MetaInfo& Meta() {
return m_container->m_slots[m_slotIdx].m_info;
}

inline uint4 Rect() {
const uint8_t div = (1 << m_container->m_level);
const uint2 size = (m_cache->m_quadSize / div);
return uint4(
m_container->m_x + ((m_slotIdx % div) * size.x),
m_container->m_y + ((m_slotIdx / div) * size.y),
size.x, size.y);
}

private:
ShadowCache<MetaInfo>* m_cache;
ShadowContainer* m_container;
size_t m_slotIdx;
uint32_t m_age;
};

std::optional<ShadowMapResult> Search(uint16_t hash, uint8_t targetLevel, uint32_t age) {
uint8_t level = std::min(m_maxLevels, targetLevel);
const uint8_t divisions = (1 << level);
uint2 shadowSize = m_quadSize / divisions;
struct Candidate {
ShadowContainer* m_container;
uint32_t m_slotIdx;
};

Candidate bestCandidate{};
ShadowContainer* replaceCandidate = nullptr;
for (auto& container : m_containers) {
if (container.m_level == level) {
for (size_t i = 0; i < (divisions * divisions); i++) {
auto& slot = container.m_slots[i];
if (age - container.m_age > MinimumAge &&
(bestCandidate.m_container == nullptr ||
bestCandidate.m_container->m_slots[bestCandidate.m_slotIdx].m_age > slot.m_age)) {
bestCandidate.m_container = &container;
bestCandidate.m_slotIdx = i;
if (slot.m_hash == hash) {
return ShadowMapResult(this, &container, i, age);
}
}
}
} else {
if (age - container.m_age > MinimumAge && (replaceCandidate == nullptr || replaceCandidate->m_age > container.m_age)) {
replaceCandidate = &container;
}
}
}
if (bestCandidate.m_container != nullptr) {
return ShadowMapResult(this, bestCandidate.m_container, bestCandidate.m_slotIdx, age);
}

if (replaceCandidate) {
replaceCandidate->m_level = level;
replaceCandidate->m_slots.clear();
replaceCandidate->m_slots.resize(divisions * divisions);
return ShadowMapResult(this, replaceCandidate, 0, age);
}

return std::nullopt;
}

inline uint2 quadSize() {
return m_quadSize;
}
inline uint2 size() {
return m_size;
}
inline uint8_t maxLevels() {
return m_maxLevels;
}

private:
uint8_t m_maxLevels;
uint16_t m_numRows;
uint16_t m_numColumns;
uint2 m_size;
uint2 m_quadSize;
std::vector<ShadowContainer> m_containers;
};
} // namespace hpl
12 changes: 10 additions & 2 deletions HPL2/resource/ShaderList.fsl
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
/// Copyright © 2009-2020 Frictional Games
/// Copyright 2023 Michael Pollind
/// SPDX-License-Identifier: GPL-3.0

#vert solid_z.vert
#vert solid_z_shadow.vert
#include "solid_z.vert.fsl"
#end

#frag solid_z_shadow.frag
#define ALPHA_REJECT 0.5
#include "solid_z_shadow.frag.fsl"
#end

#vert solid_z_shadow.vert
#include "solid_z_shadow.vert.fsl"
#end

#frag solid_z.frag
#define SEARCH_SAMPLE_COUNT 4
#define PARALLAX_ENABLED 1
Expand Down
17 changes: 17 additions & 0 deletions HPL2/resource/pbr.h.fsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf
//
float3 F_Schlick (float3 f0 , float f90 , float u ){
return f0 + ( f90 - f0 ) * pow (1. f - u , 5. f ) ;
}

float Fr_DisneyDiffuse(float NdotV ,float NdotL ,float LdotH ,float linearRoughness)
{
float energyBias = lerp (0 , 0.5 , linearRoughness ) ;
float energyFactor = lerp (1.0 , 1.0 / 1.51 , linearRoughness ) ;
float fd90 = energyBias + 2.0 * LdotH * LdotH * linearRoughness ;
float3 f0 = float3 (1.0 f , 1.0 f , 1.0 f ) ;
float lightScatter = F_Schlick ( f0 , fd90 , NdotL ) * r ;
float viewScatter = F_Schlick ( f0 , fd90 , NdotV ) * r ;

return lightScatter * viewScatter * energyFactor ;
}
32 changes: 31 additions & 1 deletion HPL2/resource/solid_diffuse.frag.fsl
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,38 @@ PsOut PS_MAIN(PsIn In)
}
#endif

float diffuseAlpha;
if(HasAlpha(Get(uniformMaterialBuffer)[materialID].materialConfig)) {
if(IsAlphaSingleChannel(Get(uniformMaterialBuffer)[materialID].materialConfig)) {
diffuseAlpha = SampleTex2D(Get(alphaMap), Get(materialSampler), texCoord.xy).r;
} else {
diffuseAlpha = SampleTex2D(Get(alphaMap), Get(materialSampler), texCoord.xy).a;
}
} else {
diffuseAlpha = 1.0;
}

const float dissolveAmount = Get(uniformObjectBuffer)[Get(objectId)].dissolveAmount;

if(dissolveAmount < 1.0 || HasDissolveAlpha(Get(uniformMaterialBuffer)[materialID].materialConfig) || HasDissolveFilter(Get(uniformMaterialBuffer)[materialID].materialConfig)) {
float2 vDissolveCoords = In.Position.xy * (1.0/128.0); //128 = size of dissolve texture.
float fDissolve = SampleTex2D(dissolveMap, Get(dissolveSampler), vDissolveCoords).x;

if(HasDissolveAlpha(Get(uniformMaterialBuffer)[materialID].materialConfig)) {
//Get in 0.75 - 1 range
fDissolve = fDissolve*0.25 + 0.75;

float fDissolveAlpha = SampleTex2D(Get(dissolveAlphaMap), Get(materialSampler), texCoord.xy).r;
fDissolve -= (0.25 - fDissolveAlpha * 0.25);
} else {
//Get in 0.5 - 1 range.
fDissolve = fDissolve*0.5 + 0.5;
}
diffuseAlpha = fDissolve - (1.0 - (dissolveAmount * diffuseAlpha)) * 0.5;
}

float4 diffuseColor = SampleTex2D(Get(diffuseMap), Get(materialSampler), texCoord.xy);
if(diffuseColor.w < ALPHA_REJECT ) {
if(diffuseColor.w < ALPHA_REJECT || diffuseAlpha < ALPHA_REJECT) {
discard;
}

Expand Down
Loading